diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for z-botan
+
+## 0.1.0.0  -- 2020-05-11
+
+* The very first release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2020, Haskell Z Team
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of winter nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+## Z-Botan
+
+[![Hackage](https://img.shields.io/hackage/v/Z-Botan.svg?style=flat)](https://hackage.haskell.org/package/Z-Botan)
+[![Linux Build Status](https://github.com/ZHaskell/z-botan/workflows/ubuntu-ci/badge.svg)](https://github.com/ZHaskell/z-botan/actions)
+[![MacOS Build Status](https://github.com/haskell-Z/z-botan/workflows/osx-ci/badge.svg)](https://github.com/ZHaskell/z-botan/actions)
+[![Windows Build Status](https://github.com/ZHaskell/z-botan/workflows/win-ci/badge.svg)](https://github.com/ZHaskell/z-botan/actions)
+[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.svg)](https://gitter.im/Z-Haskell/community)
+<a href="https://opencollective.com/zhaskell/donate" target="_blank">
+  <img src="https://opencollective.com/zhaskell/donate/button@2x.png?color=blue" width=128 />
+</a>
+
+This package is part of [ZHaskell](https://z.haskell.world) project, providing comprehensive crypto primitives based on [botan](https://github.com/randombit/botan).
+
+* Random number generators.
+* Block Cipher and symmetric cipher.
+* Hash.
+* MAC.
+* Key derivation functions.
+* Password hash.
+* Constant time multiple precision integers.
+* Public key creation, import and export.
+* Public key encryption/decryption
+* Diffie-Hellman key exchange.
+* Signature generation & signature verification.
+* X.509 certificates & X.509 certificate revocation lists.
+* One time password.
+* AES Key Wrapping
+* Format Preserving Encryption.
+
+## Requirements
+
+* A working haskell compiler system, GHC(>=8.6), cabal-install(>=2.4), hsc2hs.
+
+* A working python interpreter in `$PATH`.
+
+* Tests need [hspec-discover](https://hackage.haskell.org/package/hspec-discover).
+
+## Example usage
+
+```haskell
+> :set -XOverloadedLists
+> :m + Z.Crypto.Hash Z.Data.Vector.Hex 
+>
+> hash MD5 "hello, world"
+[228,215,241,180,237,46,66,209,88,152,244,178,123,1,157,164]
+> HexBytes $ hash SHA256  "hello, world"
+09CA7E4EAA6E8AE9C7D261167129184883644D07DFBA7CBFBC4C8A2E08360D5B
+>
+> :m + Z.Crypto.RNG Z.Crypto.PwdHash 
+> genBcrypt "mypass" rng 8
+[36,50,97,36,48,56,36,102,82,102,78,76,71,122,106,78,80,99,100,68,69,77,75,70,81,104,76,97,117,110,86,52,88,53,89,47,88,101,81,117,80,111,111,111,65,109,122,48,97,50,66,55,79,104,56,104,54,66,121,109]
+> validBcrypt "mypass" [36,50,97,36,48,56,36,102,82,102,78,76,71,122,106,78,80,99,100,68,69,77,75,70,81,104,76,97,117,110,86,52,88,53,89,47,88,101,81,117,80,111,111,111,65,109,122,48,97,50,66,55,79,104,56,104,54,66,121,109]
+True
+```
+
+## Dev guide
+
+```bash
+# get code
+git clone --recursive git@github.com:ZHaskell/z-botan.git
+cd z-botan
+# build
+cabal build
+# test
+cabal test --enable-tests --test-show-details=direct
+# install
+cabal install
+# generate document
+cabal haddock
+```
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE CPP #-}
+module Main (main) where
+
+import Control.Monad
+import Distribution.Pretty
+import Distribution.Simple
+import Distribution.Simple.Setup
+import Distribution.Simple.LocalBuildInfo
+import Distribution.Verbosity
+import Distribution.Simple.Utils
+import Distribution.Simple.Program
+import Distribution.Simple.Program.Db
+import Distribution.System
+import Distribution.Utils.NubList
+import Distribution.Types.Library
+import Distribution.Types.BuildInfo
+import Distribution.Types.LocalBuildInfo
+import Distribution.Types.PackageDescription
+import Data.Maybe
+import Data.List
+import System.Directory
+import System.FilePath
+import qualified System.Environment as System
+
+main :: IO ()
+main = do
+    mainArgs <- System.getArgs
+    if head mainArgs == "configure"
+    then defaultMainWithHooksArgs simpleUserHooks {
+            postConf = \ args flags pkg_descr lbi -> do
+                let verbosity = fromFlag (configVerbosity flags)
+                    baseDir lbi' = fromMaybe "" (takeDirectory <$> cabalFilePath lbi')
+                    configFile = baseDir lbi </> "third_party" </> "botan" </> "configure.py"
+                    configFolder = baseDir lbi </> "third_party" </> "botan"
+                confExists <- doesFileExist configFile
+                if confExists
+                  then runConfigureScript configFolder configFile verbosity flags lbi
+                  else die' verbosity "botan configure script not found."
+
+                postConf simpleUserHooks args flags pkg_descr lbi
+        ,   regHook = \ _ _ _ _ -> return ()
+#if __GLASGOW_HASKELL__ < 810
+        } (mainArgs)
+#else
+        } mainArgs
+#endif
+    else defaultMain
+
+
+runConfigureScript :: FilePath -> FilePath -> Verbosity -> ConfigFlags -> LocalBuildInfo -> IO ()
+runConfigureScript configFolder configFile verbosity flags lbi = do
+    env <- System.getEnvironment
+    configureFile <- makeAbsolute configFile
+
+    let extraPath = fromNubList $ configProgramPathExtra flags
+        spSep = [searchPathSeparator]
+        pathEnv = maybe (intercalate spSep extraPath)
+                ((intercalate spSep extraPath ++ spSep)++) $ lookup "PATH" env
+        pyProgs = simpleProgram <$> ["python", "python2", "python3"]
+        progDb = modifyProgramSearchPath
+            (\p -> map ProgramSearchPathDir extraPath ++ p) emptyProgramDb
+        overEnv = [("PATH", Just pathEnv) | not (null extraPath)]
+        hp@(Platform  arch os) = hostPlatform lbi
+        -- use gcc/mingw bunlded with GHC
+        osStr = if os == Windows then "mingw" else (show (pretty os))
+        hostFlag = [ "--cpu=" ++ show (pretty arch), "--os=" ++ osStr]
+        -- pass amalgamation to produce botan_all.cpp
+        args = configureFile:"--amalgamation":"--disable-shared":hostFlag
+
+    pyConfiguredProg <- forM pyProgs $ \ pyProg ->
+        lookupProgram pyProg <$> configureProgram verbosity pyProg progDb
+
+    case msum (pyConfiguredProg) of
+      Just py -> runProgramInvocation verbosity $
+                 (programInvocation (py {programOverrideEnv = overEnv}) args)
+                 { progInvokeCwd = Just configFolder }
+      Nothing -> die' verbosity notFoundMsg
+  where
+      notFoundMsg = "The package's dep(botan) has a 'configure.py' script. "
+               ++ "This requires python is discoverable in your path."
+
+
+
diff --git a/Z-Botan.cabal b/Z-Botan.cabal
new file mode 100644
--- /dev/null
+++ b/Z-Botan.cabal
@@ -0,0 +1,167 @@
+cabal-version:      2.4
+name:               Z-Botan
+version:            0.1.1.0
+synopsis:           Crypto for Haskell
+description:        Crypto for Haskell, based on <http://botan.randombit.net/ Botan>
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Dong Han, Tao He
+maintainer:         winterland1989@gmail.com
+copyright:
+  (c) Z.Haskell Contributors, 2017-2020
+
+category:           Data
+build-type:         Custom
+homepage:           https://github.com/ZHaskell/z-botan
+bug-reports:        https://github.com/ZHaskell/z-botan/issues
+extra-source-files:
+  ChangeLog.md
+  LICENSE
+  README.md
+  third_party/botan/configure.py
+  third_party/botan/src/**/*.cpp
+  third_party/botan/src/**/*.h
+  third_party/botan/build/include/external/cabal.placeholder
+  third_party/cacert.md
+  third_party/cacert.pem
+
+custom-setup
+  setup-depends:
+    , base       >=4.5     && <5.0
+    , Cabal      >=2.4.0.0 && <4
+    , directory  >=1.2.3.0
+    , filepath
+
+source-repository head
+  type:     git
+  location: git://github.com/haskell-Z/z-botan.git
+
+library
+  exposed-modules:
+    Z.Botan.Errno
+    Z.Botan.Exception
+    Z.Botan.FFI
+    Z.Crypto.Cipher
+    Z.Crypto.FPE
+    Z.Crypto.Hash
+    Z.Crypto.KDF
+    Z.Crypto.KeyWrap
+    Z.Crypto.MAC
+    Z.Crypto.MPI
+    Z.Crypto.OTP
+    Z.Crypto.PubKey
+    Z.Crypto.PwdHash
+    Z.Crypto.RNG
+    Z.Crypto.X509
+
+  build-depends:
+    , base         >=4.12  && <5.0
+    , ghc-prim     >=0.5.3
+    , integer-gmp  >=1     && <2
+    , scientific   ^>=0.3
+    , stm          ^>=2.5
+    , time         == 1.11.*
+    , Z-Data       >=0.8.2 && < 0.9
+    , Z-IO         ^>=0.8
+
+  include-dirs:
+    include third_party/botan third_party/botan/build/include
+    third_party/botan/build/include/external
+
+  includes:           hs_botan.h
+  install-includes:   hs_botan.h
+  c-sources:          cbits/hs_botan.c
+  cc-options:         -march=native
+  cxx-sources:        third_party/botan/botan_all.cpp
+
+  if os(windows)
+    extra-libraries:
+      Ws2_32
+      crypt32
+
+    cxx-options:
+      -std=c++11 -march=native -ffixed-xmm16 -ffixed-xmm17 -ffixed-xmm18
+      -ffixed-xmm19 -ffixed-xmm20 -ffixed-xmm21 -ffixed-xmm22
+      -ffixed-xmm23 -ffixed-xmm24 -ffixed-xmm25 -ffixed-xmm26
+      -ffixed-xmm27 -ffixed-xmm28 -ffixed-xmm29 -ffixed-xmm30
+      -ffixed-xmm31
+
+  else
+    cxx-options: -std=c++11 -march=native
+
+  if os(osx)
+    extra-libraries: c++
+
+  else
+    extra-libraries: stdc++
+
+  default-language:   Haskell2010
+  default-extensions:
+    BangPatterns
+    CApiFFI
+    CPP
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveAnyClass
+    DeriveGeneric
+    DerivingStrategies
+    ExistentialQuantification
+    FlexibleContexts
+    FlexibleInstances
+    GADTs
+    GeneralizedNewtypeDeriving
+    KindSignatures
+    LambdaCase
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    OverloadedStrings
+    PartialTypeSignatures
+    PatternSynonyms
+    PolyKinds
+    QuantifiedConstraints
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeFamilyDependencies
+    TypeOperators
+    UnboxedTuples
+    UnliftedFFITypes
+    ViewPatterns
+
+  build-tool-depends: hsc2hs:hsc2hs -any, hspec-discover:hspec-discover -any
+  ghc-options:        -Wall
+
+test-suite Z-Botan-Test
+  type:             exitcode-stdio-1.0
+  main-is:          Spec.hs
+  hs-source-dirs:   test/
+  build-depends:
+    , base
+    , hspec                 >=2.5.4
+    , HUnit
+    , QuickCheck            >=2.10
+    , quickcheck-instances
+    , Z-Botan
+    , Z-Data
+    , Z-IO
+
+  other-modules:
+    Utils
+    Z.Crypto.CipherSpec
+    Z.Crypto.HashSpec
+    Z.Crypto.KDFSpec
+    Z.Crypto.KeyWrapSpec
+    Z.Crypto.MACSpec
+    Z.Crypto.PwdHashSpec
+
+  ghc-options:      -threaded
+  default-language: Haskell2010
diff --git a/Z/Botan/Errno.hsc b/Z/Botan/Errno.hsc
new file mode 100644
--- /dev/null
+++ b/Z/Botan/Errno.hsc
@@ -0,0 +1,64 @@
+{-|
+Module      : Z.Botan.Errno
+Description : Errno provided by botan
+Copyright   : (c) Dong Han, 2020 - 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+INTERNAL MODULE, provides all botan errno.
+
+-}
+
+module Z.Botan.Errno where
+
+import Foreign.C.Types
+
+#include "hs_botan.h"
+
+-- | Generally returned to indicate success
+pattern BOTAN_FFI_SUCCESS                         :: CInt
+-- | Note this value is positive, but still represents an error condition. In indicates that the function completed successfully, but the value provided was not correct. For example botan_bcrypt_is_valid returns this value if the password did not match the hash.
+pattern BOTAN_FFI_INVALID_VERIFIER                :: CInt
+-- | The input was invalid. (Currently this error return is not used.)
+pattern BOTAN_FFI_ERROR_INVALID_INPUT             :: CInt
+-- | While decrypting in an AEAD mode, the tag failed to verify.
+pattern BOTAN_FFI_ERROR_BAD_MAC                   :: CInt
+-- | Functions which write a variable amount of space return this if the indicated buffer length was insufficient to write the data. In that case, the output length parameter is set to the size that is required.
+pattern BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE :: CInt
+-- | An exception was thrown while processing this request, but no further details are available.
+pattern BOTAN_FFI_ERROR_EXCEPTION_THROWN          :: CInt
+-- | Memory allocation failed
+pattern BOTAN_FFI_ERROR_OUT_OF_MEMORY             :: CInt
+-- | A value provided in a flag variable was unknown.
+pattern BOTAN_FFI_ERROR_BAD_FLAG                  :: CInt
+-- | A null pointer was provided as an argument where that is not allowed.
+pattern BOTAN_FFI_ERROR_NULL_POINTER              :: CInt
+-- | An argument did not match the function.
+pattern BOTAN_FFI_ERROR_BAD_PARAMETER             :: CInt
+-- | An object that requires a key normally must be keyed before use (eg before encrypting or MACing data). If this is not done, the operation will fail and return this error code.
+pattern BOTAN_FFI_ERROR_KEY_NOT_SET               :: CInt
+-- | An invalid key length was provided with a call to x_set_key.
+pattern BOTAN_FFI_ERROR_INVALID_KEY_LENGTH        :: CInt
+-- | This is returned if the functionality is not available for some reason. For example if you call botan_hash_init with a named hash function which is not enabled, this error is returned.
+pattern BOTAN_FFI_ERROR_NOT_IMPLEMENTED           :: CInt
+-- | This is used if an object provided did not match the function. For example calling botan_hash_destroy on a botan_rng_t object will cause this return.
+pattern BOTAN_FFI_ERROR_INVALID_OBJECT            :: CInt
+-- | Something bad happened, but we are not sure why or how.
+pattern BOTAN_FFI_ERROR_UNKNOWN_ERROR             :: CInt
+pattern BOTAN_FFI_SUCCESS                         = (#const BOTAN_FFI_SUCCESS)
+pattern BOTAN_FFI_INVALID_VERIFIER                = (#const BOTAN_FFI_INVALID_VERIFIER)
+pattern BOTAN_FFI_ERROR_INVALID_INPUT             = (#const BOTAN_FFI_ERROR_INVALID_INPUT)
+pattern BOTAN_FFI_ERROR_BAD_MAC                   = (#const BOTAN_FFI_ERROR_BAD_MAC)
+pattern BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = (#const BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE)
+pattern BOTAN_FFI_ERROR_EXCEPTION_THROWN          = (#const BOTAN_FFI_ERROR_EXCEPTION_THROWN)
+pattern BOTAN_FFI_ERROR_OUT_OF_MEMORY             = (#const BOTAN_FFI_ERROR_OUT_OF_MEMORY)
+pattern BOTAN_FFI_ERROR_BAD_FLAG                  = (#const BOTAN_FFI_ERROR_BAD_FLAG)
+pattern BOTAN_FFI_ERROR_NULL_POINTER              = (#const BOTAN_FFI_ERROR_NULL_POINTER)
+pattern BOTAN_FFI_ERROR_BAD_PARAMETER             = (#const BOTAN_FFI_ERROR_BAD_PARAMETER)
+pattern BOTAN_FFI_ERROR_KEY_NOT_SET               = (#const BOTAN_FFI_ERROR_KEY_NOT_SET)
+pattern BOTAN_FFI_ERROR_INVALID_KEY_LENGTH        = (#const BOTAN_FFI_ERROR_INVALID_KEY_LENGTH)
+pattern BOTAN_FFI_ERROR_NOT_IMPLEMENTED           = (#const BOTAN_FFI_ERROR_NOT_IMPLEMENTED)
+pattern BOTAN_FFI_ERROR_INVALID_OBJECT            = (#const BOTAN_FFI_ERROR_INVALID_OBJECT)
+pattern BOTAN_FFI_ERROR_UNKNOWN_ERROR             = (#const BOTAN_FFI_ERROR_UNKNOWN_ERROR)
diff --git a/Z/Botan/Exception.hs b/Z/Botan/Exception.hs
new file mode 100644
--- /dev/null
+++ b/Z/Botan/Exception.hs
@@ -0,0 +1,116 @@
+{-|
+Module      : Z.Botan.Exception
+Description : Errno provided by botan
+Copyright   : (c) Dong Han, 2020 - 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Provides botan exception hierarchy.
+
+-}
+
+module Z.Botan.Exception (
+  -- * Botan exceptions
+    SomeBotanException(..)
+  , botanExceptionToException
+  , botanExceptionFromException
+  -- * Concrete botan exception types
+  , InvalidVerifier(..)
+  , InvalidInput(..)
+  , BadMac(..)
+  , InsufficientBufferSpace(..)
+  , ExceptionThrown(..)
+  , OutOfMemory(..)
+  , BadFlag(..)
+  , NullPointer(..)
+  , BadParameter(..)
+  , KeyNotSet(..)
+  , InvalidKeyLength(..)
+  , NotImplemented(..)
+  , InvalidObject(..)
+  , UnknownError(..)
+  -- * Throw botan exceptions
+  , throwBotanIfMinus
+  , throwBotanIfMinus_
+  , throwBotanError
+  -- * re-export
+  , module Z.Botan.Errno
+  , module Z.IO.Exception
+ ) where
+
+import Control.Monad
+import Foreign.C.Types
+import Data.Typeable
+import Z.Botan.Errno
+import Z.IO.Exception
+
+-- | The root type of all botan exceptions, you can catch all botan exception by catching this root type.
+--
+data SomeBotanException = forall e . Exception e => SomeBotanException e
+
+instance Show SomeBotanException where
+    show (SomeBotanException e) = show e
+
+instance Exception SomeBotanException
+
+botanExceptionToException :: Exception e => e -> SomeException
+botanExceptionToException = toException . SomeBotanException
+
+botanExceptionFromException :: Exception e => SomeException -> Maybe e
+botanExceptionFromException x = do
+    SomeBotanException a <- fromException x
+    cast a
+
+#define BotanE(e) data e = e CInt CallStack deriving Show;  \
+           instance Exception e where                     \
+               { toException = botanExceptionToException     \
+               ; fromException = botanExceptionFromException \
+               }
+
+BotanE(InvalidVerifier)
+BotanE(InvalidInput)
+BotanE(BadMac)
+BotanE(InsufficientBufferSpace)
+BotanE(ExceptionThrown)
+BotanE(OutOfMemory)
+BotanE(BadFlag)
+BotanE(NullPointer)
+BotanE(BadParameter)
+BotanE(KeyNotSet)
+BotanE(InvalidKeyLength)
+BotanE(NotImplemented)
+BotanE(InvalidObject)
+BotanE(UnknownError)
+
+
+throwBotanIfMinus :: (HasCallStack, Integral a) => IO a -> IO a
+throwBotanIfMinus f = do
+    r <- f
+    when (r < 0) (throwBotanError_ (fromIntegral r) callStack)
+    return r
+
+throwBotanIfMinus_ :: (HasCallStack, Integral a) => IO a -> IO ()
+throwBotanIfMinus_ f = do
+    r <- f
+    when (r < 0) (throwBotanError_ (fromIntegral r) callStack)
+
+throwBotanError :: HasCallStack => CInt -> IO x
+throwBotanError r = throwBotanError_ r callStack
+
+throwBotanError_ :: CInt -> CallStack -> IO x
+throwBotanError_ r cs =  case r of
+    BOTAN_FFI_ERROR_INVALID_INPUT             -> throwIO (InvalidInput r cs)
+    BOTAN_FFI_ERROR_BAD_MAC                   -> throwIO (BadMac r cs)
+    BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE -> throwIO (InsufficientBufferSpace r cs)
+    BOTAN_FFI_ERROR_EXCEPTION_THROWN          -> throwIO (ExceptionThrown r cs)
+    BOTAN_FFI_ERROR_OUT_OF_MEMORY             -> throwIO (OutOfMemory r cs)
+    BOTAN_FFI_ERROR_BAD_FLAG                  -> throwIO (BadFlag r cs)
+    BOTAN_FFI_ERROR_NULL_POINTER              -> throwIO (NullPointer r cs)
+    BOTAN_FFI_ERROR_BAD_PARAMETER             -> throwIO (BadParameter r cs)
+    BOTAN_FFI_ERROR_KEY_NOT_SET               -> throwIO (KeyNotSet r cs)
+    BOTAN_FFI_ERROR_INVALID_KEY_LENGTH        -> throwIO (InvalidKeyLength r cs)
+    BOTAN_FFI_ERROR_NOT_IMPLEMENTED           -> throwIO (NotImplemented r cs)
+    BOTAN_FFI_ERROR_INVALID_OBJECT            -> throwIO (InvalidObject r cs)
+    _                                         -> throwIO (UnknownError r cs)
diff --git a/Z/Botan/FFI.hsc b/Z/Botan/FFI.hsc
new file mode 100644
--- /dev/null
+++ b/Z/Botan/FFI.hsc
@@ -0,0 +1,744 @@
+{-|
+Module      : Z.Botan.Errno
+Description : Errno provided by botan
+Copyright   : (c) Dong Han, 2020 - 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+INTERNAL MODULE, provides all botan FFI defs.
+
+-}
+
+module Z.Botan.FFI where
+
+import           Data.Word
+import           Data.Bits
+import           Foreign.Ptr
+import           GHC.Generics
+import           Z.IO.Exception
+import           Z.Botan.Exception
+import           Z.Data.CBytes
+import           Z.Data.JSON            (JSON)
+import qualified Z.Data.Vector          as V
+import qualified Z.Data.Vector.Extra    as V
+import qualified Z.Data.Text            as T
+import           Z.Foreign
+import           Z.Foreign.CPtr
+
+#include "hs_botan.h"
+
+--------------------------------------------------------------------------------
+
+foreign import ccall unsafe hs_botan_hex_encode :: BA## Word8 -> Int -> Int -> MBA## Word8 -> IO ()
+foreign import ccall unsafe hs_botan_hex_encode_lower :: BA## Word8 -> Int -> Int -> MBA## Word8 -> IO ()
+foreign import ccall unsafe hs_botan_hex_decode :: BA## Word8 -> Int -> Int -> MBA## Word8 -> IO ()
+
+allocBotanBufferUTF8Unsafe :: (HasCallStack, Integral r)
+                           => Int -> (MBA## Word8 -> MBA## CSize -> IO r) -> IO T.Text
+allocBotanBufferUTF8Unsafe len f = T.validate . V.unsafeInit <$> allocBotanBufferUnsafe len f
+
+allocBotanBufferUnsafe :: (HasCallStack, Integral r)
+                       => Int -> (MBA## Word8 -> MBA## CSize -> IO r) -> IO V.Bytes
+allocBotanBufferUnsafe len f = do
+    (bs, (r1, r2)) <- allocPrimVectorUnsafe len (\ buf ->
+        withPrimUnsafe (fromIntegral len :: CSize) (\ size ->
+            f buf size))
+    if fromIntegral r2 == BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE
+    then allocBotanBufferUnsafe (len `unsafeShiftL` 1) f
+    else if r2 >= 0
+        then return $! V.unsafeTake (fromIntegral r1) bs
+        else throwBotanError (fromIntegral r2)
+
+--------------------------------------------------------------------------------
+
+-- | Internal type to representation botan struct, botan_xxx_t is always pointer type.
+
+type BotanStruct = CPtr ()
+type BotanStructT = Ptr ()
+
+withBotanStruct :: BotanStruct -> (BotanStructT -> IO a) -> IO a
+withBotanStruct = withCPtr
+
+newBotanStruct :: HasCallStack
+               => (MBA## BotanStructT -> IO CInt)   -- ^ init function
+               -> FunPtr (BotanStructT -> IO a)     -- ^ destroy function pointer
+               -> IO BotanStruct
+newBotanStruct init_ destroy = do
+    (bts, _) <- newCPtrUnsafe (\ pp -> throwBotanIfMinus_ (init_ pp)) destroy
+    return bts
+
+newBotanStruct' :: HasCallStack
+                => (Ptr BotanStructT -> IO CInt)    -- ^ init function
+                -> FunPtr (BotanStructT -> IO a)    -- ^ destroy function pointer
+                -> IO BotanStruct
+newBotanStruct' init_ destroy = do
+    (bts, _) <- newCPtr (\ pp -> throwBotanIfMinus_ (init_ pp)) destroy
+    return bts
+
+--------------------------------------------------------------------------------
+-- RNG
+
+foreign import ccall unsafe botan_rng_init :: MBA## BotanStructT -> BA## Word8 -> IO CInt
+foreign import ccall unsafe "&botan_rng_destroy" botan_rng_destroy :: FunPtr (BotanStructT -> IO ())
+foreign import ccall unsafe botan_rng_get :: BotanStructT -> MBA## Word8 -> CSize -> IO CInt
+foreign import ccall unsafe botan_rng_reseed :: BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_rng_reseed_from_rng :: BotanStructT -> BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe hs_botan_rng_add_entropy :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+
+
+--------------------------------------------------------------------------------
+-- MPI
+
+foreign import ccall unsafe botan_mp_init :: MBA## BotanStructT -> IO CInt
+foreign import ccall unsafe "&botan_mp_destroy" botan_mp_destroy :: FunPtr (BotanStructT -> IO ())
+foreign import ccall unsafe botan_mp_set_from_int :: BotanStructT -> CInt -> IO CInt
+foreign import ccall unsafe botan_mp_set_from_mp :: BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_num_bytes :: BotanStructT -> MBA## CSize -> IO CInt
+foreign import ccall unsafe botan_mp_num_bits :: BotanStructT -> MBA## CSize -> IO CInt
+foreign import ccall unsafe hs_botan_mp_to_hex :: BotanStructT -> MBA## Word8 -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_mp_to_dec :: BotanStructT -> MBA## Word8 -> Int -> IO Int
+foreign import ccall unsafe hs_botan_mp_set_from_hex :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_mp_set_from_dec :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_mp_to_bin :: BotanStructT -> MBA## Word8 -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_mp_from_bin :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe botan_mp_flip_sign :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_add :: BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_sub :: BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_mul :: BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_div :: BotanStructT -> BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_mod_mul :: BotanStructT -> BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_equal :: BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_zero :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_odd :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_even :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_positive :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_negative :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_to_uint32 :: BotanStructT -> MBA## Word32 -> IO CInt
+foreign import ccall unsafe botan_mp_cmp :: MBA## CInt -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_swap :: BotanStructT -> BotanStructT -> IO ()
+foreign import ccall unsafe botan_mp_powmod :: BotanStructT -> BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_lshift :: BotanStructT -> BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_rshift :: BotanStructT -> BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_mod_inverse :: BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_rand_bits :: BotanStructT -> BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_rand_range :: BotanStructT -> BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_gcd :: BotanStructT -> BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_mp_is_prime :: BotanStructT -> BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_get_bit :: BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_set_bit :: BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_mp_clear_bit :: BotanStructT -> CSize -> IO CInt
+
+--------------------------------------------------------------------------------
+
+foreign import ccall unsafe botan_block_cipher_init :: MBA## BotanStructT -> BA## Word8 -> IO CInt
+foreign import ccall unsafe "&botan_block_cipher_destroy"
+    botan_block_cipher_destroy :: FunPtr (BotanStructT -> IO ())
+foreign import ccall unsafe botan_block_cipher_block_size :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_block_cipher_get_keyspec :: BotanStructT
+                                                           -> MBA## Int   -- ^ minimum_keylength
+                                                           -> MBA## Int   -- ^ maximum_keylength
+                                                           -> MBA## Int   -- ^ keylength_modulo
+                                                           -> IO CInt
+foreign import ccall unsafe botan_block_cipher_clear :: BotanStructT -> IO CInt
+foreign import ccall unsafe hs_botan_block_cipher_set_key
+    :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_block_cipher_encrypt_blocks
+    :: BotanStructT
+    -> BA## Word8   -- ^ in_buf
+    -> Int          -- ^ in offset
+    -> MBA## Word8  -- ^ out buffer
+    -> Int          -- ^ number of block
+    -> IO CInt
+foreign import ccall unsafe hs_botan_block_cipher_decrypt_blocks
+    :: BotanStructT
+    -> BA## Word8   -- ^ in_buf
+    -> Int          -- ^ in offset
+    -> MBA## Word8  -- ^ out buffer
+    -> Int          -- ^ number of block
+    -> IO CInt
+
+--------------------------------------------------------------------------------
+
+foreign import ccall unsafe botan_hash_init :: MBA## BotanStructT -> BA## Word8 -> Word32 -> IO CInt
+foreign import ccall unsafe "&botan_hash_destroy"
+    botan_hash_destroy ::FunPtr (BotanStructT -> IO ())
+foreign import ccall unsafe botan_hash_copy_state :: MBA## BotanStructT -> BotanStructT -> IO CInt
+foreign import ccall unsafe botan_hash_clear :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_hash_output_length :: BotanStructT -> MBA## CSize -> IO CInt
+foreign import ccall unsafe hs_botan_hash_update :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe botan_hash_final :: BotanStructT -> MBA## Word8 -> IO CInt
+
+--------------------------------------------------------------------------------
+
+data CipherDirection = CipherEncrypt | CipherDecrypt
+    deriving (Show, Eq, Ord, Generic)
+    deriving anyclass (T.Print, JSON)
+
+cipherDirectionToFlag ::  CipherDirection -> Word32
+cipherDirectionToFlag CipherEncrypt = #const BOTAN_CIPHER_INIT_FLAG_ENCRYPT
+cipherDirectionToFlag CipherDecrypt = #const BOTAN_CIPHER_INIT_FLAG_DECRYPT
+
+foreign import ccall unsafe botan_cipher_init :: MBA## BotanStructT -> BA## Word8 -> Word32 -> IO CInt
+foreign import ccall unsafe "&botan_cipher_destroy"
+    botan_cipher_destroy :: FunPtr (BotanStructT -> IO ())
+foreign import ccall unsafe botan_cipher_clear :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_cipher_reset :: BotanStructT -> IO CInt
+foreign import ccall unsafe botan_cipher_get_keyspec :: BotanStructT
+                                                     -> MBA## Int   -- ^ minimum_keylength
+                                                     -> MBA## Int   -- ^ maximum_keylength
+                                                     -> MBA## Int   -- ^ keylength_modulo
+                                                     -> IO CInt
+foreign import ccall unsafe hs_botan_cipher_set_key
+    :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_cipher_start
+    :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+foreign import ccall unsafe hs_botan_cipher_update
+    :: BotanStructT
+    -> MBA## Word8  -- ^ output
+    -> BA## Word8   -- ^ input
+    -> Int          -- ^ input offset
+    -> Int          -- ^ input len
+    -> IO Int       -- ^ output written
+
+foreign import ccall unsafe hs_botan_cipher_finish
+    :: BotanStructT
+    -> MBA## Word8  -- ^ output
+    -> Int          -- ^ output size
+    -> BA## Word8   -- ^ input
+    -> Int          -- ^ input offset
+    -> Int          -- ^ input len
+    -> IO Int       -- ^ output written
+
+foreign import ccall unsafe hs_botan_cipher_set_associated_data
+    :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+
+foreign import ccall unsafe botan_cipher_valid_nonce_length :: BotanStructT -> CSize -> IO CInt
+foreign import ccall unsafe botan_cipher_get_default_nonce_length :: BotanStructT -> MBA## Int -> IO CInt
+foreign import ccall unsafe botan_cipher_get_update_granularity :: BotanStructT -> MBA## Int -> IO CInt
+foreign import ccall unsafe botan_cipher_get_tag_length :: BotanStructT -> MBA## Int -> IO CInt
+
+--------------------------------------------------------------------------------
+-- PBKDF
+
+foreign import ccall unsafe hs_botan_pwdhash :: BA## Word8
+                                             -> Int -> Int -> Int
+                                             -> MBA## Word8 -> Int              -- ^ output
+                                             -> BA## Word8 -> Int               -- ^ passphrase
+                                             -> BA## Word8 -> Int -> Int        -- ^ salt
+                                             -> IO CInt
+
+foreign import ccall unsafe hs_botan_pwdhash_timed :: BA## Word8
+                                                   -> Int
+                                                   -> MBA## Word8 -> Int        -- ^ output
+                                                   -> BA## Word8 -> Int         -- ^ passphrase
+                                                   -> BA## Word8 -> Int -> Int  -- ^ salt
+                                                   -> IO CInt
+
+foreign import ccall safe "hs_botan_pwdhash_timed"
+    hs_botan_pwdhash_timed_safe :: Ptr Word8
+                                -> Int
+                                -> Ptr Word8 -> Int         -- ^ output
+                                -> Ptr Word8 -> Int         -- ^ passphrase
+                                -> Ptr Word8 -> Int -> Int  -- ^ salt
+                                -> IO CInt
+
+--------------------------------------------------------------------------------
+-- KDF
+
+foreign import ccall unsafe hs_botan_kdf :: BA## Word8
+                                         -> MBA## Word8 -> Int
+                                         -> BA## Word8 -> Int -> Int
+                                         -> BA## Word8 -> Int -> Int
+                                         -> BA## Word8 -> Int -> Int
+                                         -> IO CInt
+
+
+--------------------------------------------------------------------------------
+-- Password Hashing
+
+foreign import ccall unsafe hs_botan_bcrypt_generate :: MBA## Word8
+                                                     -> BA## Word8 -> Int -> Int
+                                                     -> BotanStructT
+                                                     -> Int
+                                                     -> Word32
+                                                     -> IO Int
+
+foreign import ccall unsafe hs_botan_bcrypt_is_valid :: BA## Word8 -> Int -> Int
+                                                     -> BA## Word8 -> Int -> Int
+                                                     -> IO CInt
+
+---------------------------------------------------------------------------------
+-- MAC
+
+foreign import ccall unsafe botan_mac_init :: MBA## BotanStructT -> BA## Word8 -> Word32 -> IO CInt
+foreign import ccall unsafe "&botan_mac_destroy" botan_mac_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_mac_output_length ::BotanStructT -> MBA## CSize -> IO CInt
+
+foreign import ccall unsafe botan_mac_final :: BotanStructT -> MBA## Word8 -> IO CInt
+
+foreign import ccall unsafe hs_botan_mac_set_key :: BotanStructT -> BA## Word8 -> Int -> Int -> IO CInt
+
+foreign import ccall unsafe hs_botan_mac_update :: BotanStructT -> BA## Word8 -> Int -> Int-> IO CInt
+
+foreign import ccall unsafe hs_botan_mac_clear :: BotanStructT -> IO CInt
+
+foreign import ccall unsafe hs_botan_mac_name ::  BotanStructT -> MBA## Word8 -> MBA## Int -> IO CInt
+
+foreign import ccall unsafe hs_botan_mac_get_keyspec :: BotanStructT -> MBA## Int -> MBA## Int -> MBA## Int -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Public Key Creation, Import and Export (at Z.Crypto.PubKey)
+
+foreign import ccall safe botan_privkey_create :: Ptr BotanStructT    -- ^ botan_privkey_t* key
+                                               -> Ptr Word8           -- ^ const char* algo_name
+                                               -> Ptr Word8           -- ^ const char* algo_params
+                                               -> BotanStructT        -- ^ botan_rng_t rng
+                                               -> IO CInt
+
+foreign import ccall unsafe hs_botan_privkey_load :: MBA## BotanStructT
+                                                  -> BotanStructT
+                                                  -> BA## Word8 -> Int -> Int
+                                                  -> BA## Word8
+                                                  -> IO CInt
+
+foreign import ccall unsafe botan_privkey_export :: BotanStructT -- ^ botan_privkey_t key
+                                                 -> MBA## Word8 -> MBA## CSize -- ^ uint8_t out[], size_t* out_len
+                                                 -> Word32 -- ^ uint32_t flags
+                                                 -> IO CInt
+
+foreign import ccall unsafe botan_privkey_export_encrypted
+    :: BotanStructT                     -- ^ botan_privkey_t key
+    -> MBA## Word8 -> MBA## CSize       -- ^ uint8_t out[], size_t* out_len
+    -> BotanStructT                     -- ^ botan_rng_t 
+    -> BA## Word8                       -- ^ passphrase
+    -> BA## Word8                       -- ^ encryption_algo, currently ignored by botan
+    -> Word32                           -- ^ uint32_t flags
+    -> IO CInt
+
+foreign import ccall unsafe botan_privkey_export_pubkey :: MBA## BotanStructT
+                                                        -> BotanStructT
+                                                        -> IO CInt
+
+foreign import ccall unsafe botan_privkey_get_field :: BotanStructT -- ^ botan_mp_t output
+                                                    -> BotanStructT -- ^ botan_privkey_t key
+                                                    -> BA## Word8 -- ^ const char* field_name
+                                                    -> IO CInt
+
+foreign import ccall unsafe "&botan_privkey_destroy" botan_privkey_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe hs_botan_pubkey_load :: MBA## BotanStructT -- ^ botan_pubkey_t* key
+                                                 -> BA## Word8 -> Int -> Int
+                                                 -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_export :: BotanStructT
+                                                -> MBA## Word8 -> MBA## CSize
+                                                -> Word32
+                                                -> IO CInt
+
+foreign import ccall unsafe botan_privkey_algo_name :: BotanStructT
+                                                   -> MBA## Word8 -> MBA## CSize
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_algo_name :: BotanStructT
+                                                   -> MBA## Word8 -> MBA## CSize
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_estimated_strength :: BotanStructT
+                                                            -> MBA## CSize
+                                                            -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_fingerprint :: BotanStructT
+                                                     -> BA## Word8
+                                                     -> MBA## Word8 -> MBA## CSize
+                                                     -> IO CInt
+
+foreign import ccall unsafe "&botan_pubkey_destroy" botan_pubkey_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_pubkey_get_field :: BotanStructT
+                                                   -> BotanStructT
+                                                   -> BA## Word8
+                                                   -> IO CInt
+
+--------------------------------------------------------------------------------
+-- RSA specific functions
+
+
+foreign import ccall unsafe botan_privkey_load_rsa :: MBA## BotanStructT
+                                                   -> BotanStructT -> BotanStructT -> BotanStructT
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_load_rsa :: MBA## BotanStructT
+                                                  -> BotanStructT -> BotanStructT
+                                                  -> IO CInt
+
+--------------------------------------------------------------------------------
+-- DSA specific functions
+
+foreign import ccall unsafe botan_privkey_load_dsa :: MBA## BotanStructT
+                                                   -> BotanStructT -> BotanStructT -> BotanStructT
+                                                   -> BotanStructT
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_load_dsa :: MBA## BotanStructT
+                                                  -> BotanStructT -> BotanStructT -> BotanStructT
+                                                  -> BotanStructT
+                                                  -> IO CInt
+
+--------------------------------------------------------------------------------
+-- ElGamal specific functions
+
+foreign import ccall unsafe botan_privkey_load_elgamal :: MBA## BotanStructT
+                                                       -> BotanStructT -> BotanStructT -> BotanStructT
+                                                       -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_load_elgamal :: MBA## BotanStructT
+                                                      -> BotanStructT -> BotanStructT -> BotanStructT
+                                                      -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Diffie-Hellman specific functions
+
+foreign import ccall unsafe botan_privkey_load_dh :: MBA## BotanStructT
+                                                  -> BotanStructT -> BotanStructT -> BotanStructT
+                                                  -> IO CInt
+
+foreign import ccall unsafe botan_pubkey_load_dh :: MBA## BotanStructT
+                                                 -> BotanStructT -> BotanStructT -> BotanStructT
+                                                 -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Public Key Encryption / Decryption
+
+foreign import ccall unsafe botan_pk_op_encrypt_create :: MBA## BotanStructT
+                                                       -> BotanStructT
+                                                       -> BA## Word8
+                                                       -> Word32
+                                                       -> IO CInt
+
+foreign import ccall unsafe "&botan_pk_op_encrypt_destroy" botan_pk_op_encrypt_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_pk_op_encrypt_output_length :: BotanStructT
+                                                              -> CSize
+                                                              -> MBA## CSize
+                                                              -> IO CInt
+
+foreign import ccall unsafe hs_botan_pk_op_encrypt :: BotanStructT
+                                                   -> BotanStructT
+                                                   -> MBA## Word8 -> MBA## Int
+                                                   -> BA## Word8 -> Int -> Int
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_pk_op_decrypt_create :: MBA## BotanStructT
+                                                       -> BotanStructT
+                                                       -> BA## Word8
+                                                       -> Word32
+                                                       -> IO CInt
+
+foreign import ccall unsafe "&botan_pk_op_decrypt_destroy" botan_pk_op_decrypt_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_pk_op_decrypt_output_length :: BotanStructT
+                                                              -> CSize
+                                                              -> MBA## CSize
+                                                              -> IO CInt
+
+foreign import ccall unsafe hs_botan_pk_op_decrypt :: BotanStructT
+                                                   -> MBA## Word8 -> MBA## Int
+                                                   -> BA## Word8 -> Int -> Int
+                                                   -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Signature Generation
+
+foreign import ccall unsafe botan_pk_op_sign_create :: MBA## BotanStructT
+                                                    -> BotanStructT
+                                                    -> BA## Word8
+                                                    -> Word32
+                                                    -> IO CInt
+
+foreign import ccall unsafe botan_pk_op_sign_output_length :: BotanStructT
+                                                           -> MBA## CSize
+                                                           -> IO CInt
+
+foreign import ccall unsafe hs_botan_pk_op_sign_update :: BotanStructT
+                                                       -> BA## Word8 -> Int -> Int
+                                                       -> IO CInt
+
+foreign import ccall unsafe botan_pk_op_sign_finish :: BotanStructT
+                                                    -> BotanStructT
+                                                    -> MBA## Word8
+                                                    -> MBA## Int
+                                                    -> IO CInt
+
+foreign import ccall unsafe "&botan_pk_op_sign_destroy" botan_pk_op_sign_destroy :: FunPtr (BotanStructT -> IO ())
+
+--------------------------------------------------------------------------------
+-- Signature Verification
+
+foreign import ccall unsafe botan_pk_op_verify_create :: MBA## BotanStructT
+                                                      -> BotanStructT
+                                                      -> BA## Word8
+                                                      -> Word32
+                                                      -> IO CInt
+
+foreign import ccall unsafe "&botan_pk_op_verify_destroy" botan_pk_op_verify_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe hs_botan_pk_op_verify_update :: BotanStructT
+                                                         -> BA## Word8 -> Int -> Int
+                                                         -> IO CInt
+
+foreign import ccall unsafe hs_botan_pk_op_verify_finish :: BotanStructT
+                                                         -> BA## Word8 -> Int -> Int
+                                                         -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Key Agreement
+
+foreign import ccall unsafe botan_pk_op_key_agreement_create :: MBA## BotanStructT
+                                                             -> BotanStructT
+                                                             -> BA## Word8
+                                                             -> Word32
+                                                             -> IO CInt
+
+foreign import ccall unsafe "&botan_pk_op_key_agreement_destroy" botan_pk_op_key_agreement_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_pk_op_key_agreement_export_public :: BotanStructT
+                                                                    -> MBA## Word8 -> MBA## Int
+                                                                    -> IO CInt
+
+foreign import ccall unsafe botan_pk_op_key_agreement_size :: BotanStructT
+                                                           -> MBA## CSize
+                                                           -> IO CInt
+
+foreign import ccall unsafe hs_botan_pk_op_key_agreement :: BotanStructT
+                                                         -> MBA## Word8 -> MBA## Int
+                                                         -> BA## Word8 -> Int -> Int
+                                                         -> BA## Word8 -> Int -> Int
+                                                         -> IO CInt
+
+{-
+foreign import ccall unsafe hs_botan_mceies_encrypt :: BotanStructT -> BotanStructT
+                                                    -> BA## Word8
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> MBA## Word8 -> MBA## Int
+                                                    -> IO CInt
+
+foreign import ccall unsafe hs_botan_mceies_decrypt :: BotanStructT
+                                                    -> BA## Word8
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> MBA## Word8 -> MBA## Int
+                                                    -> IO CInt
+-}
+
+--------------------------------------------------------------------------------
+-- X.509 Certificates
+
+-- | Certificate key usage constraints.
+type KeyUsageConstraint = CUInt
+
+pattern NO_CONSTRAINTS     :: KeyUsageConstraint
+pattern DIGITAL_SIGNATURE  :: KeyUsageConstraint
+pattern NON_REPUDIATION    :: KeyUsageConstraint
+pattern KEY_ENCIPHERMENT   :: KeyUsageConstraint
+pattern DATA_ENCIPHERMENT  :: KeyUsageConstraint
+pattern KEY_AGREEMENT      :: KeyUsageConstraint
+pattern KEY_CERT_SIGN      :: KeyUsageConstraint
+pattern CRL_SIGN           :: KeyUsageConstraint
+pattern ENCIPHER_ONLY      :: KeyUsageConstraint
+pattern DECIPHER_ONLY      :: KeyUsageConstraint
+pattern NO_CONSTRAINTS      = #const NO_CONSTRAINTS
+pattern DIGITAL_SIGNATURE   = #const DIGITAL_SIGNATURE
+pattern NON_REPUDIATION     = #const NON_REPUDIATION
+pattern KEY_ENCIPHERMENT    = #const KEY_ENCIPHERMENT
+pattern DATA_ENCIPHERMENT   = #const DATA_ENCIPHERMENT
+pattern KEY_AGREEMENT       = #const KEY_AGREEMENT
+pattern KEY_CERT_SIGN       = #const KEY_CERT_SIGN
+pattern CRL_SIGN            = #const CRL_SIGN
+pattern ENCIPHER_ONLY       = #const ENCIPHER_ONLY
+pattern DECIPHER_ONLY       = #const DECIPHER_ONLY     
+
+foreign import ccall unsafe "&botan_x509_cert_destroy" botan_x509_cert_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe hs_botan_x509_cert_load :: MBA## BotanStructT -- ^ botan_x509_cert_t* cert_obj
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_load_file :: MBA## BotanStructT
+                                                      -> BA## Word8
+                                                      -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_dup :: MBA## BotanStructT
+                                                -> BotanStructT
+                                                -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_time_starts :: BotanStructT
+                                                            -> MBA## Word8
+                                                            -> MBA## Int
+                                                            -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_time_expires :: BotanStructT
+                                                             -> MBA## Word8
+                                                             -> MBA## Int
+                                                             -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_not_before :: BotanStructT
+                                                       -> MBA## Word64
+                                                       -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_not_after :: BotanStructT
+                                                      -> MBA## Word64
+                                                      -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_fingerprint :: BotanStructT
+                                                            -> BA## Word8
+                                                            -> MBA## Word8 -> MBA## Int
+                                                            -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_serial_number :: BotanStructT
+                                                              -> MBA## Word8
+                                                              -> MBA## Int
+                                                              -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_authority_key_id :: BotanStructT
+                                                                 -> MBA## Word8 -> MBA## Int
+                                                                 -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_subject_key_id :: BotanStructT -> MBA## Word8 -> MBA## Int -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_public_key_bits :: BotanStructT -> MBA## Word8 -> MBA## Int -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_public_key :: BotanStructT -> MBA## BotanStructT -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_issuer_dn :: BotanStructT -> BA## Word8 -> Int -> MBA## Word8 -> MBA## Int -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_get_subject_dn :: BotanStructT
+                                                           -> BA## Word8
+                                                           -> Int
+                                                           -> MBA## Word8 -> MBA## Int
+                                                           -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_to_string :: BotanStructT
+                                                      -> MBA## Word8 -> MBA## Int
+                                                      -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_allowed_usage :: BotanStructT -> CUInt -> IO CInt
+
+foreign import ccall unsafe hs_botan_x509_cert_verify :: BotanStructT
+                                                      -> BA## BotanStructT -> Int 
+                                                      -> BA## BotanStructT -> Int 
+                                                      -> Int -> BA## Word8 -> Word64
+                                                      -> IO CInt
+
+foreign import ccall unsafe hs_botan_x509_cert_verify_with_crl :: BotanStructT
+                                                               -> BA## BotanStructT -> Int 
+                                                               -> BA## BotanStructT -> Int
+                                                               -> BA## BotanStructT -> Int
+                                                               -> Int -> BA## Word8 -> Word64
+                                                               -> IO CInt
+
+foreign import ccall unsafe hs_botan_x509_cert_verify_with_certstore_crl
+    :: BotanStructT
+    -> BA## BotanStructT -> Int 
+    -> BotanStructT 
+    -> BA## BotanStructT -> Int
+    -> Int -> BA## Word8 -> Word64
+    -> IO CInt
+
+foreign import ccall unsafe botan_x509_cert_validation_status :: CInt -> IO CString
+
+--------------------------------------------------------------------------------
+-- X.509 Certificate Revocation Lists
+
+foreign import ccall unsafe hs_botan_x509_crl_load :: MBA## BotanStructT
+                                                   -> BA## Word8 -> Int -> Int
+                                                   -> IO CInt
+
+foreign import ccall unsafe botan_x509_crl_load_file :: MBA## BotanStructT -> BA## Word8 -> IO CInt
+
+foreign import ccall unsafe "&botan_x509_crl_destroy" botan_x509_crl_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_x509_is_revoked :: BotanStructT -> BotanStructT -> IO CInt
+
+--------------------------------------------------------------------------------
+-- X.509 Certificate Store
+
+foreign import ccall unsafe botan_x509_certstore_load_file :: MBA## BotanStructT -> BA## Word8 -> IO CInt
+
+foreign import ccall unsafe "&botan_x509_certstore_destroy" botan_x509_certstore_destroy :: FunPtr (BotanStructT -> IO ())
+
+--------------------------------------------------------------------------------
+-- Advanced Encryption Standard (AES) Key Wrap Algorithm
+
+foreign import ccall unsafe hs_botan_key_wrap3394 :: BA## Word8 -> Int -> Int
+                                                  -> BA## Word8 -> Int -> Int
+                                                  -> MBA## Word8 -> MBA## CSize
+                                                  -> IO CInt
+
+foreign import ccall unsafe hs_botan_key_unwrap3394 :: BA## Word8 -> Int -> Int
+                                                    -> BA## Word8 -> Int -> Int
+                                                    -> MBA## Word8 -> MBA## CSize
+                                                    -> IO CInt
+
+--------------------------------------------------------------------------------
+-- OTP
+
+foreign import ccall unsafe hs_botan_hotp_init :: MBA## BotanStructT
+                                               -> BA## Word8 -> Int -> Int
+                                               -> BA## Word8
+                                               -> Int
+                                               -> IO CInt
+
+foreign import ccall unsafe "&botan_hotp_destroy" botan_hotp_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_hotp_generate :: BotanStructT
+                                                -> MBA## Word32
+                                                -> Word64
+                                                -> IO CInt
+
+foreign import ccall unsafe botan_hotp_check :: BotanStructT
+                                             -> MBA## Word64
+                                             -> Word32
+                                             -> Word64
+                                             -> CSize
+                                             -> IO CInt
+
+foreign import ccall unsafe hs_botan_totp_init :: MBA## BotanStructT
+                                               -> BA## Word8 -> Int -> Int
+                                               -> BA## Word8
+                                               -> Int -> Int
+                                               -> IO CInt
+
+foreign import ccall unsafe "&botan_totp_destroy" botan_totp_destroy :: FunPtr (BotanStructT -> IO ())
+
+foreign import ccall unsafe botan_totp_generate :: BotanStructT
+                                                -> MBA## Word32
+                                                -> Word64
+                                                -> IO CInt
+
+foreign import ccall unsafe botan_totp_check :: BotanStructT
+                                             -> Word32
+                                             -> Word64
+                                             -> CSize
+                                             -> IO CInt
+
+--------------------------------------------------------------------------------
+-- Format Preserving Encryption
+
+foreign import ccall unsafe hs_botan_fpe_fe1_init :: MBA## BotanStructT -> BotanStructT
+                                                  -> BA## Word8 -> Int -> Int
+                                                  -> Int -> Word32
+                                                  -> IO CInt
+
+foreign import ccall unsafe hs_botan_fpe_encrypt :: BotanStructT -> BotanStructT
+                                                 -> BA## Word8 -> Int -> Int
+                                                 -> IO CInt
+
+foreign import ccall unsafe hs_botan_fpe_decrypt :: BotanStructT -> BotanStructT
+                                                 -> BA## Word8 -> Int -> Int
+                                                 -> IO CInt
+
+foreign import ccall unsafe "&botan_fpe_destroy" botan_fpe_destroy :: FunPtr (BotanStructT -> IO ())
diff --git a/Z/Crypto/Cipher.hs b/Z/Crypto/Cipher.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/Cipher.hs
@@ -0,0 +1,672 @@
+{-|
+Module      : Z.Crypto.Cipher
+Description : Block Cipher and Cipher modes
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides raw block cipher and various cipher mode.
+
+Block ciphers are a n-bit permutation for some small n, typically 64 or 128 bits. They are a cryptographic primitive used to generate higher level operations such as authenticated encryption.
+
+A block cipher by itself, is only able to securely encrypt a single data block. To be able to securely encrypt data of arbitrary length, a mode of operation applies the block cipher’s single block operation repeatedly to encrypt an entire message.
+
+-}
+module Z.Crypto.Cipher
+  ( -- * Block Cipher
+    BlockCipherType(..)
+  , BlockCipher, blockCipherName, blockCipherKeySpec, blockCipherSize
+  , newBlockCipher, setBlockCipherKey, clearBlockCipher
+  , encryptBlocks, decryptBlocks
+    -- * Stream Cipher & Cipher Mode
+  , StreamCipherType(..), newStreamCipher
+  , CipherMode(..), CipherDirection(..)
+  , Cipher, cipherName, cipherUpdateGranularity
+  , cipherKeySpec, cipherTagLength, defaultNonceLength
+  , newCipher
+    -- * Cipher operations
+  , setCipherKey, clearCipher, resetCipher, setAssociatedData
+  , startCipher, updateCipher, finishCipher, cipherBIO
+    -- * Internal helps
+  , blockCipherTypeToCBytes
+  , withBlockCipher
+  , withCipher
+  ) where
+
+import           Control.Monad
+import           Data.IORef
+import           GHC.Generics
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.Hash
+import           Z.Data.CBytes       as CB
+import           Z.Data.JSON         (JSON)
+import qualified Z.Data.Text         as T
+import qualified Z.Data.Vector.Base  as V
+import qualified Z.Data.Vector.Extra as V
+import           Z.Foreign
+import           Z.IO.BIO
+
+-- | Available Block Ciphers
+--
+-- Botan includes a number of block ciphers that are specific to particular countries, as well as a few that are included mostly due to their use in specific protocols such as PGP but not widely used elsewhere. If you are developing new code and have no particular opinion, use AES-256. If you desire an alternative to AES, consider Serpent, SHACAL2 or Threefish.
+--
+-- Warning: Avoid any 64-bit block cipher in new designs. There are combinatoric issues that affect any 64-bit cipher that render it insecure when large amounts of data are processed.
+--
+data BlockCipherType
+      -- | AES
+      --
+      -- Comes in three variants, AES-128, AES-192, and AES-256.
+      -- The standard 128-bit block cipher. Many modern platforms offer hardware acceleration.
+      -- However, on platforms without hardware support, AES implementations typically are vulnerable
+      -- to side channel attacks.
+      -- For x86 systems with SSSE3 but without AES-NI, Botan has an implementation which avoids known side channels.
+    = AES128
+    | AES192
+    | AES256
+      -- | ARIA
+      --
+      -- South Korean cipher used in industry there. No reason to use it otherwise.
+    | ARIA128
+    | ARIA192
+    | ARIA256
+      -- | Blowfish
+      --
+      -- A 64-bit cipher popular in the pre-AES era. Very slow key setup.
+      -- Also used (with bcrypt) for password hashing.
+    | Blowfish
+      -- | Camellia
+      --
+      -- Comes in three variants, Camellia-128, Camellia-192, and Camellia-256.
+      -- A Japanese design standardized by ISO, NESSIE and CRYPTREC. Rarely used outside of Japan.
+    | Camellia128
+    | Camellia192
+    | Camellia256
+      -- | Cascade
+      --
+      -- Creates a block cipher cascade, where each block is encrypted by two ciphers with independent keys.
+      -- Useful if you're very paranoid. In practice any single good cipher (such as Serpent, SHACAL2, or AES-256)
+      -- is more than sufficient.
+      --
+      -- Please set a key with size = max_key_size_A + max_key_size_B.
+    | Cascade BlockCipherType BlockCipherType
+      -- | CAST-128
+      --
+      -- A 64-bit cipher, commonly used in OpenPGP.
+    | CAST128
+      -- | CAST-256
+      --
+      -- A 128-bit cipher that was a contestant in the NIST AES competition.
+      -- Almost never used in practice. Prefer AES or Serpent.
+      -- Warning: Support for CAST-256 is deprecated and will be removed in a future major release.
+    | CAST256
+      -- | DES, 3DES, DESX
+      --
+      -- Originally designed by IBM and NSA in the 1970s. Today, DES's 56-bit key renders it insecure
+      -- to any well-resourced attacker. DESX and 3DES extend the key length, and are still thought to be secure,
+      -- modulo the limitation of a 64-bit block.
+      -- All are somewhat common in some industries such as finance. Avoid in new code.
+      -- Warning: Support for DESX is deprecated and it will be removed in a future major release.
+    | DES
+    | DESX
+    | TripleDES
+      -- | IDEA
+      --
+      -- An older but still unbroken 64-bit cipher with a 128-bit key.
+      -- Somewhat common due to its use in PGP. Avoid in new designs.
+    | IDEA
+      -- | Kasumi
+      --
+      -- A 64-bit cipher used in 3GPP mobile phone protocols. There is no reason to use it outside of this context.
+      -- Warning: Support for Kasumi is deprecated and will be removed in a future major release.
+    | KASUMI
+      -- | Lion
+      --
+      -- A "block cipher construction" which can encrypt blocks of nearly arbitrary length.
+      -- Built from a stream cipher and a hash function.
+      -- Useful in certain protocols where being able to encrypt large or arbitrary length blocks is necessary.
+    | Lion HashType StreamCipherType Int
+      -- | MISTY1
+      --
+      -- A 64-bit Japanese cipher standardized by NESSIE and ISO.
+      -- Seemingly secure, but quite slow and saw little adoption. No reason to use it in new code.
+      -- Warning: Support for MISTY1 is deprecated and will be removed in a future major release.
+    | MISTY1
+      -- | Noekeon
+      --
+      -- A fast 128-bit cipher by the designers of AES. Easily secured against side channels.
+    | Noekeon
+      -- | SEED
+      --
+      -- A older South Korean cipher, widely used in industry there. No reason to choose it otherwise.
+    | SEED
+      -- | Serpent
+      --
+      -- An AES contender. Widely considered the most conservative design.
+      -- Fairly slow unless SIMD instructions are available.
+    | Serpent
+      -- | SHACAL2
+      --
+      -- The 256-bit block cipher used inside SHA-256. Accepts up to a 512-bit key.
+      -- Fast, especially when SIMD or SHA-2 acceleration instructions are available.
+      -- Standardized by NESSIE but otherwise obscure.
+    | SHACAL2
+      -- | Twofish
+      --
+      -- A 128-bit block cipher that was one of the AES finalists.
+      -- Has a somewhat complicated key setup and a "kitchen sink" design.
+    | Twofish
+      -- | SM4
+      --
+      -- A 128-bit Chinese national cipher, required for use in certain commercial applications in China.
+      -- Quite slow. Probably no reason to use it outside of legal requirements.
+    | SM4
+      -- | Threefish-512
+      --
+      -- A 512-bit tweakable block cipher that was used in the Skein hash function. Very fast on 64-bit processors.
+    | Threefish512
+      -- | XTEA
+      --
+      -- A 64-bit cipher popular for its simple implementation. Avoid in new code.
+    | XTEA
+  deriving (Show, Read, Eq, Ord, Generic)
+  deriving anyclass (T.Print, JSON)
+
+blockCipherTypeToCBytes :: BlockCipherType -> CBytes
+blockCipherTypeToCBytes b = case b of
+    AES128           ->    "AES-128"
+    AES192           ->    "AES-192"
+    AES256           ->    "AES-256"
+    ARIA128          ->    "ARIA-128"
+    ARIA192          ->    "ARIA-192"
+    ARIA256          ->    "ARIA-256"
+    Serpent          ->    "Serpent"
+    SHACAL2          ->    "SHACAL2"
+    Twofish          ->    "Twofish"
+    Threefish512     ->    "Threefish-512"
+    Blowfish         ->    "Blowfish"
+    Camellia128      ->    "Camellia-128"
+    Camellia192      ->    "Camellia-192"
+    Camellia256      ->    "Camellia-256"
+    DES              ->    "DES"
+    DESX             ->    "DESX"
+    TripleDES        ->    "TripleDES"
+    Noekeon          ->    "Noekeon"
+    CAST128          ->    "CAST-128"
+    CAST256          ->    "CAST-256"
+    IDEA             ->    "IDEA"
+    KASUMI           ->    "KASUMI"
+    MISTY1           ->    "MISTY1"
+    SEED             ->    "SEED"
+    SM4              ->    "SM4"
+    XTEA             ->    "XTEA"
+    Cascade b1 b2    ->    CB.concat [ "Cascade("
+                                     , blockCipherTypeToCBytes b1
+                                     , ","
+                                     , blockCipherTypeToCBytes b2
+                                     , ")"]
+    Lion hasht st siz -> CB.concat [ "Lion("
+                                  , hashTypeToCBytes hasht
+                                  , ","
+                                  , streamCipherTypeToCBytes st
+                                  , ","
+                                  , CB.fromText (T.toText siz)
+                                  , ")"]
+
+-- | A Botan block cipher.
+--
+-- In almost all cases, a bare block cipher is not what you should be using.
+-- You probably want an authenticated cipher mode instead (see 'CipherMode'),
+-- This interface is used to build higher level operations (such as cipher modes or MACs),
+-- or in the very rare situation where ECB is required, eg for compatibility with an existing system.
+--
+data BlockCipher = BlockCipher
+    { blockCipher        :: {-# UNPACK #-} !BotanStruct
+    , blockCipherName    :: {-# UNPACK #-} !CBytes
+    , blockCipherSize    :: {-# UNPACK #-} !Int
+    , blockCipherKeySpec :: {-# UNPACK #-} !KeySpec
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Pass 'BlockCipher' to FFI as 'botan_block_cipher_t'.
+withBlockCipher :: HasCallStack => BlockCipher -> (BotanStructT -> IO r) -> IO r
+withBlockCipher (BlockCipher bc _ _ _) = withBotanStruct bc
+
+data KeySpec = KeySpec
+    { keyLenMin :: {-# UNPACK #-} !Int  -- ^ minimum keylength
+    , keyLenMax :: {-# UNPACK #-} !Int  -- ^ maximum keylength
+    , keyLenMod :: {-# UNPACK #-} !Int  -- ^ keylength modulo
+    }
+    deriving (Eq, Ord, Show, Generic)
+    deriving anyclass T.Print
+
+-- | Create a new block cipher.
+--
+newBlockCipher :: HasCallStack => BlockCipherType -> IO BlockCipher
+newBlockCipher typ = do
+    let name = blockCipherTypeToCBytes typ
+    bc <- newBotanStruct
+        (\ bts -> withCBytesUnsafe name (botan_block_cipher_init bts))
+        botan_block_cipher_destroy
+
+    bsiz <- withBotanStruct bc botan_block_cipher_block_size
+
+    (a, (b, (c, _))) <- withBotanStruct bc $ \ pbc ->
+        allocPrimUnsafe $ \ pa ->
+            allocPrimUnsafe $ \ pb ->
+                allocPrimUnsafe $ \ pc ->
+                    throwBotanIfMinus_
+                        (botan_block_cipher_get_keyspec pbc pa pb pc)
+
+    return (BlockCipher bc name (fromIntegral bsiz) (KeySpec a b c))
+
+-- | Set the cipher key, which is required before encrypting or decrypting.
+--
+setBlockCipherKey :: HasCallStack => BlockCipher -> V.Bytes -> IO ()
+setBlockCipherKey (BlockCipher bc _ _ _) key =
+    withBotanStruct bc $ \ pbc -> do
+        withPrimVectorUnsafe key $ \ pkey key_off key_len -> do
+            throwBotanIfMinus_ (hs_botan_block_cipher_set_key
+                pbc pkey key_off key_len)
+
+-- | Clear the internal state (such as keys) of this cipher object.
+clearBlockCipher :: HasCallStack => BlockCipher -> IO ()
+clearBlockCipher (BlockCipher bc _ _ _) =
+    withBotanStruct bc (throwBotanIfMinus_ . botan_block_cipher_clear)
+
+-- | Encrypt blocks of data.
+--
+-- The key must have been set first with 'setBlockCipherKey'.
+encryptBlocks :: HasCallStack
+              => BlockCipher
+              -> V.Bytes    -- ^ blocks of data, length must be equal to block_size * number_of_blocks
+              -> Int        -- ^ number of blocks
+              -> IO V.Bytes
+encryptBlocks (BlockCipher bc _ blockSiz _) blocks n = do
+    let inputLen = V.length blocks
+    when (inputLen /= blockSiz * n) $
+        throwBotanError BOTAN_FFI_ERROR_INVALID_INPUT
+    withBotanStruct bc $ \ pbc -> do
+        withPrimVectorUnsafe blocks $ \ pb pboff _ ->
+            fst <$> allocPrimVectorUnsafe inputLen (\ pbuf ->
+                throwBotanIfMinus_ (hs_botan_block_cipher_encrypt_blocks
+                    pbc pb pboff pbuf n))
+
+-- | Decrypt blocks of data.
+--
+-- The key must have been set first with 'setBlockCipherKey'.
+decryptBlocks :: HasCallStack
+              => BlockCipher
+              -> V.Bytes    -- ^ blocks of data, length must be equal to block_size * number_of_blocks
+              -> Int        -- ^ number of blocks
+              -> IO V.Bytes
+decryptBlocks (BlockCipher bc _ blockSiz _) blocks n = do
+    let inputLen = V.length blocks
+    when (inputLen /= blockSiz * n) $
+        throwBotanError BOTAN_FFI_ERROR_INVALID_INPUT
+    withBotanStruct bc $ \ pbc -> do
+        withPrimVectorUnsafe blocks $ \ pb pboff _ ->
+            fst <$> allocPrimVectorUnsafe inputLen (\ pbuf ->
+                throwBotanIfMinus_ (hs_botan_block_cipher_decrypt_blocks
+                    pbc pb pboff pbuf n))
+
+--------------------------------------------------------------------------------
+
+-- | In contrast to block ciphers, stream ciphers operate on a plaintext stream instead of blocks. Thus encrypting data results in changing the internal state of the cipher and encryption of plaintext with arbitrary length is possible in one go (in byte amounts).
+data StreamCipherType
+      -- | A cipher mode that converts a block cipher into a stream cipher.
+      --  It offers parallel execution and can seek within the output stream, both useful properties.
+    = CTR_BE BlockCipherType
+      -- | Another stream cipher based on a block cipher.
+      -- Unlike CTR mode, it does not allow parallel execution or seeking within the output stream. Prefer CTR.
+    | OFB BlockCipherType
+      -- | A very fast cipher, now widely deployed in TLS as part of the ChaCha20Poly1305 AEAD.
+      -- Can be used with 8 (fast but dangerous), 12 (balance), or 20 rounds (conservative).
+      -- Even with 20 rounds, ChaCha is very fast. Use 20 rounds.
+    | ChaCha8
+    | ChaCha12
+    | ChaCha20
+      -- | An earlier iteration of the ChaCha design,
+      -- this cipher is popular due to its use in the libsodium library. Prefer ChaCha.
+    | Salsa20
+      -- | This is the SHAKE-128 XOF exposed as a stream cipher.
+      -- It is slower than ChaCha and somewhat obscure.
+      -- It does not support IVs or seeking within the cipher stream.
+    | SHAKE128'
+      -- | An old and very widely deployed stream cipher notable for its simplicity.
+      -- It does not support IVs or seeking within the cipher stream.
+      -- Warning: RC4 is now badly broken. Avoid in new code and use only if
+      -- required for compatibility with existing systems.
+    | RC4
+  deriving (Show, Read, Eq, Ord, Generic)
+  deriving anyclass (T.Print, JSON)
+
+streamCipherTypeToCBytes :: StreamCipherType -> CBytes
+streamCipherTypeToCBytes s = case s of
+    CTR_BE b  -> CB.concat ["CTR-BE(", blockCipherTypeToCBytes b, ")"]
+    OFB b     -> CB.concat ["OFB(", blockCipherTypeToCBytes b, ")"]
+    ChaCha8   -> "ChaCha(8)"
+    ChaCha12  -> "ChaCha(12)"
+    ChaCha20  -> "ChaCha(20)"
+    Salsa20   -> "Salsa20"
+    SHAKE128' ->  "SHAKE-128"
+    RC4       -> "RC4"
+
+-- | Create a new stream cipher.
+--
+newStreamCipher :: HasCallStack => StreamCipherType -> CipherDirection -> IO Cipher
+newStreamCipher typ dir = do
+    let name = streamCipherTypeToCBytes typ
+    ci <- newBotanStruct
+        (\ bts -> withCBytesUnsafe name $ \ pb ->
+            botan_cipher_init bts pb (cipherDirectionToFlag dir))
+        botan_cipher_destroy
+
+    withBotanStruct ci $ \ pci -> do
+        (g, _) <- allocPrimUnsafe $ \ pg ->
+            botan_cipher_get_update_granularity pci pg
+
+        (a, (b, (c, _))) <- allocPrimUnsafe $ \ pa ->
+            allocPrimUnsafe $ \ pb ->
+                allocPrimUnsafe $ \ pc ->
+                    throwBotanIfMinus_
+                        (botan_cipher_get_keyspec pci pa pb pc)
+
+        (t, _) <- allocPrimUnsafe $ \ pt ->
+            botan_cipher_get_tag_length pci pt
+
+        (n, _) <- allocPrimUnsafe $ \ pn ->
+            botan_cipher_get_default_nonce_length pci pn
+
+        return (Cipher ci name g (KeySpec a b c) t n)
+
+--------------------------------------------------------------------------------
+--
+
+-- | All available cipher types.
+--
+-- A block cipher by itself, is only able to securely encrypt a single data block.
+-- To be able to securely encrypt data of arbitrary length, a mode of operation applies
+-- the block cipher’s single block operation repeatedly to encrypt an entire message.
+--
+-- Notes on the AEAD modes(CCM, ChaCha20Poly1305, EAX, GCM, OCB, SIV):
+--
+-- AEAD (Authenticated Encryption with Associated Data) modes provide message encryption,
+-- message authentication, and the ability to authenticate additional data that is not included
+-- in the ciphertext (such as a sequence number or header).
+--
+data CipherMode
+    -- | ChaCha20Poly1305
+    --
+    -- Unlike the other AEADs which are based on block ciphers,
+    -- this mode is based on the ChaCha stream cipher and the Poly1305 authentication code.
+    -- It is very fast on all modern platforms.
+    --
+    -- ChaCha20Poly1305 supports 64-bit, 96-bit, and (since 2.8) 192-bit nonces.
+    -- 64-bit nonces are the “classic” ChaCha20Poly1305 design.
+    -- 96-bit nonces are used by the IETF standard version of ChaCha20Poly1305.
+    -- And 192-bit nonces is the XChaCha20Poly1305 construction, which is somewhat less common.
+    --
+    -- For best interop use the IETF version with 96-bit nonces.
+    -- However 96 bits is small enough that it can be dangerous to generate nonces randomly
+    -- if more than ~ 2^32 messages are encrypted under a single key,
+    -- since if a nonce is ever reused ChaCha20Poly1305 becomes insecure.
+    -- It is better to use a counter for the nonce in this case.
+    --
+    -- If you are encrypting many messages under a single key and cannot maintain a counter for the nonce,
+    -- prefer XChaCha20Poly1305 since a 192 bit nonce is large enough that randomly chosen nonces
+    -- are extremely unlikely to repeat.
+    = ChaCha20Poly1305
+    -- | GCM
+    --
+    -- NIST standard, commonly used. Requires a 128-bit block cipher.
+    -- Fairly slow, unless hardware support for carryless multiplies is available.
+    | GCM BlockCipherType
+    -- | OCB
+    --
+    -- A block cipher based AEAD. Supports 128-bit, 256-bit and 512-bit block ciphers.
+    -- This mode is very fast and easily secured against side channels.
+    -- Adoption has been poor because it is patented in the United States,
+    -- though a license is available allowing it to be freely used by open source software.
+    | OCB BlockCipherType
+    -- | EAX
+    -- A secure composition of CTR mode and CMAC. Supports 128-bit, 256-bit and 512-bit block ciphers.
+    | EAX BlockCipherType
+    -- | SIV
+    --
+    -- Requires a 128-bit block cipher. Unlike other AEADs, SIV is “misuse resistant”;
+    -- if a nonce is repeated, SIV retains security, with the exception that if the same nonce
+    -- is used to encrypt the same message multiple times,
+    -- an attacker can detect the fact that the message was duplicated
+    -- (this is simply because if both the nonce and the message are reused,
+    -- SIV will output identical ciphertexts).
+    | SIV BlockCipherType
+    -- | CCM
+    --
+    -- A composition of CTR mode and CBC-MAC. Requires a 128-bit block cipher.
+    -- This is a NIST standard mode, but that is about all to recommend it. Prefer EAX.
+    | CCM BlockCipherType
+    -- | CFB
+    --
+    -- CFB uses a block cipher to create a self-synchronizing stream cipher.
+    -- It is used for example in the OpenPGP protocol. There is no reason to prefer it,
+    -- as it has worse performance characteristics than modes such as CTR or CBC.
+    | CFB BlockCipherType Int
+    -- | XTS
+    --
+    -- XTS is a mode specialized for encrypting disk or database storage where ciphertext expansion
+    -- is not possible. XTS requires all inputs be at least one full block (16 bytes for AES),
+    -- however for any acceptable input length, there is no ciphertext expansion.
+    | XTS BlockCipherType
+    -- | CBC
+    --
+    -- CBC requires the plaintext be padded using a reversible rule. The following padding schemes are implemented
+    --
+    --  * PKCS#7 (RFC5652)
+    --    The last byte in the padded block defines the padding length p,
+    --    the remaining padding bytes are set to p as well.
+    | CBC_PKCS7 BlockCipherType
+    -- | CBC
+    --
+    --  * OneAndZeros (ISO/IEC 7816-4)
+    --    The first padding byte is set to 0x80, the remaining padding bytes are set to 0x00.
+    | CBC_OneAndZeros BlockCipherType
+    -- | CBC
+    --
+    --  * ANSI X9.23
+    --    The last byte in the padded block defines the padding length,
+    --    the remaining padding is filled with 0x00.
+    | CBC_X9'23 BlockCipherType
+    -- | CBC
+    --
+    --  * ESP (RFC4303)
+    --    Padding with 0x01, 0x02, 0x03...
+    | CBC_ESP BlockCipherType
+    -- | CTS
+    --
+    -- This scheme allows the ciphertext to have the same length as the plaintext,
+    -- however using CTS requires the input be at least one full block plus one byte.
+    -- It is also less commonly implemented.
+    | CBC_CTS BlockCipherType
+    -- | No padding CBC
+    --
+    -- Only use this mode when input length is a multipler of cipher block size.
+    | CBC_NoPadding BlockCipherType
+  deriving (Show, Read, Eq, Ord, Generic)
+  deriving anyclass (T.Print, JSON)
+
+cipherTypeToCBytes :: CipherMode -> CBytes
+cipherTypeToCBytes ct = case ct of
+    ChaCha20Poly1305 -> "ChaCha20Poly1305"
+    GCM        bct -> blockCipherTypeToCBytes bct <> "/GCM"
+    OCB        bct -> blockCipherTypeToCBytes bct <> "/OCB"
+    EAX        bct -> blockCipherTypeToCBytes bct <> "/EAX"
+    SIV        bct -> blockCipherTypeToCBytes bct <> "/SIV"
+    CCM        bct -> blockCipherTypeToCBytes bct <> "/CCM"
+    CFB           bct 0 -> blockCipherTypeToCBytes bct <> "/CFB"
+    CFB           bct x -> CB.concat [ blockCipherTypeToCBytes bct
+                                     , "/CFB("
+                                     , CB.fromText (T.toText x)
+                                     , ")"
+                                     ]
+    XTS             bct -> blockCipherTypeToCBytes bct <> "/XTS"
+    CBC_PKCS7       bct -> blockCipherTypeToCBytes bct <> "/CBC/PKCS7"
+    CBC_OneAndZeros bct -> blockCipherTypeToCBytes bct <> "/CBC/OneAndZeros"
+    CBC_X9'23       bct -> blockCipherTypeToCBytes bct <> "/CBC/X9.23"
+    CBC_ESP         bct -> blockCipherTypeToCBytes bct <> "/CBC/ESP"
+    CBC_CTS         bct -> blockCipherTypeToCBytes bct <> "/CBC/CTS"
+    CBC_NoPadding   bct -> blockCipherTypeToCBytes bct <> "/CBC/NoPadding"
+
+-- | A Botan cipher.
+data Cipher = Cipher
+    { cipher                  :: {-# UNPACK #-} !BotanStruct
+    , cipherName              :: {-# UNPACK #-} !CBytes
+    , cipherUpdateGranularity :: {-# UNPACK #-} !Int
+    , cipherKeySpec           :: {-# UNPACK #-} !KeySpec
+    , cipherTagLength         :: {-# UNPACK #-} !Int -- ^ This will be zero for non-authenticated ciphers.
+    , defaultNonceLength      :: {-# UNPACK #-} !Int
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Pass 'Cipher' to FFI as 'botan_cipher_t'.
+withCipher :: HasCallStack => Cipher -> (BotanStructT -> IO r) -> IO r
+withCipher (Cipher c _ _ _ _ _) = withBotanStruct c
+
+-- | Create a new cipher.
+--
+newCipher :: HasCallStack => CipherMode -> CipherDirection -> IO Cipher
+newCipher typ dir = do
+    let name = cipherTypeToCBytes typ
+    ci <- newBotanStruct
+        (\ bts -> withCBytesUnsafe name $ \ pb ->
+            botan_cipher_init bts pb (cipherDirectionToFlag dir))
+        botan_cipher_destroy
+
+    withBotanStruct ci $ \ pci -> do
+        (g, _) <- allocPrimUnsafe $ \ pg ->
+            botan_cipher_get_update_granularity pci pg
+
+        (a, (b, (c, _))) <- allocPrimUnsafe $ \ pa ->
+            allocPrimUnsafe $ \ pb ->
+                allocPrimUnsafe $ \ pc ->
+                    throwBotanIfMinus_
+                        (botan_cipher_get_keyspec pci pa pb pc)
+
+        (t, _) <- allocPrimUnsafe $ \ pt ->
+            botan_cipher_get_tag_length pci pt
+
+        (n, _) <- allocPrimUnsafe $ \ pn ->
+            botan_cipher_get_default_nonce_length pci pn
+
+        return (Cipher ci name g (KeySpec a b c) t n)
+
+-- | Clear the internal state (such as keys) of this cipher object.
+--
+clearCipher :: HasCallStack => Cipher -> IO ()
+clearCipher (Cipher ci _ _ _ _ _) =
+    withBotanStruct ci (throwBotanIfMinus_ . botan_cipher_clear)
+
+-- | Reset the message specific state for this cipher.
+-- Without resetting the keys, this resets the nonce, and any state
+-- associated with any message bits that have been processed so far.
+--
+-- It is conceptually equivalent to calling botan_cipher_clear followed
+-- by botan_cipher_set_key with the original key.
+--
+resetCipher :: HasCallStack => Cipher -> IO ()
+resetCipher (Cipher ci _ _ _ _ _) =
+    withBotanStruct ci (throwBotanIfMinus_ . botan_cipher_reset)
+
+-- | Set the key for this cipher object
+--
+setCipherKey :: HasCallStack => Cipher -> V.Bytes -> IO ()
+setCipherKey (Cipher ci _ _ _ _ _) key =
+    withBotanStruct ci $ \ pci -> do
+        withPrimVectorUnsafe key $ \ pkey key_off key_len -> do
+            throwBotanIfMinus_ (hs_botan_cipher_set_key
+                pci pkey key_off key_len)
+
+-- | Set the associated data. Will fail if cipher is not an AEAD.
+--
+setAssociatedData :: HasCallStack => Cipher -> V.Bytes -> IO ()
+setAssociatedData (Cipher ci _ _ _ _ _) ad =
+    withBotanStruct ci $ \ pci -> do
+        withPrimVectorUnsafe ad $ \ pad ad_off ad_len -> do
+            throwBotanIfMinus_ (hs_botan_cipher_set_associated_data
+                pci pad ad_off ad_len)
+
+-- | Begin processing a new message using the provided nonce.
+--
+startCipher :: HasCallStack
+            => Cipher
+            -> V.Bytes      -- ^ nonce
+            -> IO ()
+startCipher (Cipher ci _ _ _ _ _) nonce =
+    withBotanStruct ci $ \ pci -> do
+        withPrimVectorUnsafe nonce $ \ pnonce nonce_off nonce_len -> do
+            throwBotanIfMinus_ (hs_botan_cipher_start
+                pci pnonce nonce_off nonce_len)
+
+-- | Update cipher with some data.
+--
+-- If the input size is not a multiplier of 'cipherUpdateGranularity', there'll
+-- be some trailing input.
+updateCipher :: HasCallStack
+             => Cipher
+             -> V.Bytes
+             -> IO (V.Bytes, V.Bytes)   -- ^ trailing input, output
+updateCipher (Cipher ci _ _ _ _ _) input =
+    withBotanStruct ci $ \ pci -> do
+        withPrimVectorUnsafe input $ \ in_p in_off in_len -> do
+            (out, r) <- allocPrimVectorUnsafe in_len $ \ out_p ->
+                throwBotanIfMinus (hs_botan_cipher_update pci
+                    out_p in_p in_off in_len)
+            return (V.unsafeDrop r input, V.unsafeTake r out)
+
+-- | Finish cipher with some data.
+--
+-- You can directly call this function to encrypt a whole message,
+-- Note some cipher modes have a minimal input length requirement for last chunk(CTS, XTS, etc.).
+finishCipher :: HasCallStack
+             => Cipher
+             -> V.Bytes
+             -> IO V.Bytes
+finishCipher (Cipher ci _ ug _ tag_len _) input =
+    withBotanStruct ci $ \ pci -> do
+        withPrimVectorUnsafe input $ \ in_p in_off in_len -> do
+            let !out_len = in_len + ug + tag_len
+            (pa, r) <- allocPrimArrayUnsafe out_len $ \ out_p -> do
+                throwBotanIfMinus (hs_botan_cipher_finish pci
+                    out_p out_len in_p in_off in_len)
+            mpa <- unsafeThawPrimArray pa
+            shrinkMutablePrimArray mpa r
+            pa' <- unsafeFreezePrimArray mpa
+            return (V.PrimVector pa' 0 r)
+
+-- | Wrap a cipher into a 'BIO' node(experimental).
+--
+-- The cipher should have already started by setting key, nounce, etc.
+--
+-- Note some cipher modes have a minimal input length requirement for last chunk(CBC_CTS, XTS, etc.),
+-- which may not be suitable for arbitrary bytes streams.
+--
+cipherBIO :: HasCallStack => Cipher -> IO (BIO V.Bytes V.Bytes)
+cipherBIO c = do
+    trailingRef <- newIORef V.empty
+    return $ \ k mbs -> case mbs of
+        Just bs -> do
+            trailing <- readIORef trailingRef
+            let chunk =  trailing `V.append` bs
+            (rest, out) <- updateCipher c chunk
+            writeIORef trailingRef rest
+            unless (V.null out) (k (Just out))
+        _ -> do
+            trailing <- readIORef trailingRef
+            bs <- finishCipher c trailing
+            if V.null bs
+            then k (Just bs) >> k EOF
+            else k EOF
diff --git a/Z/Crypto/FPE.hs b/Z/Crypto/FPE.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/FPE.hs
@@ -0,0 +1,70 @@
+{-|
+Module      : Z.Crypto.FPE
+Description : Format Preserving Encryption
+Copyright   : Anjie, Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Format preserving encryption (FPE) refers to a set of techniques for encrypting data such that the ciphertext has the same format as the plaintext. For instance, you can use FPE to encrypt credit card numbers with valid checksums such that the ciphertext is also an credit card number with a valid checksum, or similarly for bank account numbers, US Social Security numbers, or even more general mappings like English words onto other English words.
+
+The scheme currently implemented in botan is called FE1, and described in the paper Format Preserving Encryption by Mihir Bellare, Thomas Ristenpart, Phillip Rogaway, and Till Stegers. FPE is an area of ongoing standardization and it is likely that other schemes will be included in the future.
+
+To encrypt an arbitrary value using FE1, you need to use a ranking method. Basically, the idea is to assign an integer to every value you might encrypt. For instance, a 16 digit credit card number consists of a 15 digit code plus a 1 digit checksum. So to encrypt a credit card number, you first remove the checksum, encrypt the 15 digit value modulo 1015, and then calculate what the checksum is for the new (ciphertext) number. Or, if you were encrypting words in a dictionary, you could rank the words by their lexicographical order, and choose the modulus to be the number of words in the dictionary.
+
+-}
+module Z.Crypto.FPE ( FPE, newFPE, encryptFPE, decryptFPE ) where
+
+import           GHC.Generics
+import           Z.Botan.FFI
+import           Z.Botan.Exception
+import           Z.Crypto.MPI
+import qualified Z.Data.Vector as V
+import qualified Z.Data.Text   as T
+import           Z.Foreign
+
+newtype FPE = FPE BotanStruct
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Initialize an FPE operation to encrypt/decrypt integers less than n. It is
+--   expected that n is trivially factorable into small integers. Common usage
+--   would be n to be a power of 10.
+newFPE :: MPI     -- ^ mod (n)
+       -> V.Bytes -- ^ key
+       -> IO FPE
+newFPE mpi key =
+    withMPI mpi $ \ mpi' ->
+    withPrimVectorUnsafe key $ \ key' keyOff keyLen ->
+        FPE <$> newBotanStruct (\ fpe ->
+            hs_botan_fpe_fe1_init fpe mpi' key' keyOff keyLen 3 1)
+            botan_fpe_destroy
+
+-- | Encrypts the value x modulo the value n using the key and tweak specified. Returns an integer less than n. The tweak is a value that does not need to be secret that parameterizes the encryption function. For instance, if you were encrypting a database column with a single key, you could use a per-row-unique integer index value as the tweak. The same tweak value must be used during decryption.
+encryptFPE :: FPE
+           -> MPI
+           -> V.Bytes   -- ^ tweak
+           -> IO MPI
+encryptFPE (FPE fpe) mpi tweak = do
+    mpi' <- copyMPI mpi
+    withBotanStruct fpe $ \ fpe' ->
+        withMPI mpi' $ \ mpi'' ->
+        withPrimVectorUnsafe tweak $ \ t toff tlen ->
+            throwBotanIfMinus_ (hs_botan_fpe_encrypt fpe' mpi'' t toff tlen)
+    return mpi'
+
+
+-- | Decrypts an FE1 ciphertext. The tweak must be the same as that provided to the encryption function. Returns the plaintext integer.
+-- Note that there is not any implicit authentication or checking of data in FE1, so if you provide an incorrect key or tweak the result is simply a random integer.
+decryptFPE :: FPE
+           -> MPI
+           -> V.Bytes   -- ^ tweak
+           -> IO MPI
+decryptFPE (FPE fpe) mpi tweak = do
+    mpi' <- copyMPI mpi
+    withBotanStruct fpe $ \ fpe' ->
+        withMPI mpi' $ \ mpi'' ->
+        withPrimVectorUnsafe tweak $ \ t toff tlen ->
+            throwBotanIfMinus_ (hs_botan_fpe_decrypt fpe' mpi'' t toff tlen)
+    return mpi'
diff --git a/Z/Crypto/Hash.hs b/Z/Crypto/Hash.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/Hash.hs
@@ -0,0 +1,266 @@
+{-|
+Module      : Z.Crypto.Hash
+Description : Hash Functions and Checksums
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Hash functions are one-way functions, which map data of arbitrary size to a fixed output length. Most of the hash functions in Botan are designed to be cryptographically secure, which means that it is computationally infeasible to create a collision (finding two inputs with the same hash) or preimages (given a hash output, generating an arbitrary input with the same hash). But note that not all such hash functions meet their goals, in particular MD4 and MD5 are trivially broken. However they are still included due to their wide adoption in various protocols.
+
+Using a hash function is typically split into three stages: initialization, update, and finalization (often referred to as a IUF interface). The initialization stage is implicit: after creating a 'Hash' function object, it is ready to process data. Then update is called one or more times. Calling update several times is equivalent to calling it once with all of the arguments concatenated. After completing a hash computation (eg using final), the internal state is reset to begin hashing a new message.
+
+-}
+module Z.Crypto.Hash(
+    -- * Hash type
+    HashType(..)
+  , Hash, hashName, hashSize
+    -- * IUF interface
+  , newHash
+  , updateHash
+  , finalHash
+  , copyHash
+  , clearHash
+  -- * function interface
+  , hash, hashChunks
+  -- * BIO interface
+  , sinkToHash
+  -- * Internal helper
+  , hashTypeToCBytes
+  , withHash
+  ) where
+
+import           GHC.Generics      (Generic)
+import           System.IO.Unsafe  (unsafePerformIO)
+import           Z.Botan.Exception (HasCallStack, throwBotanIfMinus_)
+import           Z.Botan.FFI
+import           Z.Data.CBytes     as CB
+import           Z.Data.JSON       (JSON)
+import qualified Z.Data.Text       as T
+import qualified Z.Data.Vector     as V
+import           Z.Foreign
+import           Z.IO.BIO          (pattern EOF, Sink)
+
+-- | Available Hashs
+data HashType
+      -- | A recently designed hash function. Very fast on 64-bit processors.
+      -- Can output a hash of any length between 1 and 64 bytes,
+      -- this is specified by passing desired byte length.
+    = BLAKE2b Int
+      -- | Alias for @Blake2b 32@
+    | BLAKE2b256
+      -- | Alias for @Blake2b 64@
+    | BLAKE2b512
+      -- | An older (and incompatible) variant of SHA-3, but sometimes used. Prefer SHA-3 in new code.
+    | Keccak1600_224
+    | Keccak1600_256
+    | Keccak1600_384
+    | Keccak1600_512
+      -- | An old hash function that is now known to be trivially breakable.
+      -- It is very fast, and may still be suitable as a (non-cryptographic) checksum.
+    | MD4
+      -- | Widely used, now known to be broken.
+    | MD5
+      -- | A 160 bit hash function, quite old but still thought to be secure
+      -- (up to the limit of 2**80 computation required for a collision which is possible
+      -- with any 160 bit hash function). Somewhat deprecated these days.
+    | RIPEMD160
+      -- | Widely adopted NSA designed hash function.
+      -- Starting to show significant signs of weakness, and collisions can now be generated. Avoid in new designs.
+    | SHA160
+      -- | Relatively fast 256 bit hash function, thought to be secure.
+      -- Also includes the variant SHA-224. There is no real reason to use SHA-224.
+    | SHA256
+    | SHA224
+      -- | SHA-512 is faster than SHA-256 on 64-bit processors.
+      -- Also includes the truncated variants SHA-384 and SHA-512/256,
+      -- which have the advantage of avoiding message extension attacks.
+    | SHA512
+    | SHA384
+    | SHA512_256
+      -- | The new NIST standard hash. Fairly slow.
+      -- Supports 224, 256, 384 or 512 bit outputs.
+      -- SHA-3 is faster with smaller outputs. Use as “SHA3_256” or “SHA3_512”.
+      -- Plain “SHA-3” selects default 512 bit output.
+    | SHA3_224
+    | SHA3_256
+    | SHA3_384
+    | SHA3_512
+      -- | These are actually XOFs (extensible output functions) based on SHA-3,
+      -- which can output a value of any byte length. For example “SHAKE128 @128”
+      -- will produce 1024 bits of output.
+    | SHAKE128 Int
+    | SHAKE256 Int
+      -- | Chinese national hash function, 256 bit output. Widely used in industry there.
+      -- Fast and seemingly secure, but no reason to prefer it over SHA-2 or SHA-3 unless required.
+    | SM3
+      -- | A contender for the NIST SHA-3 competition. Very fast on 64-bit systems. Can output a hash of any length between 1 and 64 bytes. It also accepts an optional “personalization string” which can create variants of the hash. This is useful for domain separation.
+    | Skein512 Int CBytes
+      -- | Newly designed Russian national hash function.
+      -- Due to use of input-dependent table lookups, it is vulnerable to side channels.
+      -- There is no reason to use it unless compatibility is needed.
+      -- Warning: The Streebog Sbox has recently been revealed to have a hidden structure
+      -- which interacts with its linear layer in a way which may provide a backdoor when used in certain ways.
+      -- Avoid Streebog if at all possible.
+    | Streebog256
+    | Streebog512
+      -- | A 512-bit hash function standardized by ISO and NESSIE.
+      -- Relatively slow, and due to the table based implementation it is potentially vulnerable
+      -- to cache based side channels.
+    | Whirlpool
+      -- | Parallel simply concatenates multiple hash functions.
+      --   For example “Parallel SHA256 SHA512 outputs a 256+512 bit hash created by hashing the input
+      --   with both SHA256 and SHA512 and concatenating the outputs.
+    | Parallel HashType HashType
+      -- | This combines two cryptographic hashes in such a way that preimage and collision attacks are
+      --   provably at least as hard as a preimage or collision attack on the strongest hash.
+    | Comb4P HashType HashType
+      -- | Checksums, not suitable for cryptographic use, but can be used for error checking purposes.
+    | Adler32
+    | CRC24
+    | CRC32
+  deriving (Show, Read, Eq, Ord, Generic)
+  deriving anyclass (T.Print, JSON)
+
+hashTypeToCBytes :: HashType -> CBytes
+hashTypeToCBytes h = case h of
+    BLAKE2b siz  -> CB.concat [ "Blake2b(" , sizeCBytes siz, ")"]
+    BLAKE2b256   -> "Blake2b(256)"
+    BLAKE2b512   -> "Blake2b(512)"
+    Keccak1600_224 -> "Keccak-1600(224)"
+    Keccak1600_256 -> "Keccak-1600(256)"
+    Keccak1600_384 -> "Keccak-1600(384)"
+    Keccak1600_512 -> "Keccak-1600(512)"
+    MD4          -> "MD4"
+    MD5          -> "MD5"
+    RIPEMD160    -> "RIPEMD-160"
+    SHA160       -> "SHA-160"
+    SHA224       -> "SHA-224"
+    SHA256       -> "SHA-256"
+    SHA512       -> "SHA-512"
+    SHA384       -> "SHA-384"
+    SHA512_256   -> "SHA-512-256"
+    SHA3_224     -> "SHA-3(224)"
+    SHA3_256     -> "SHA-3(256)"
+    SHA3_384     -> "SHA-3(384)"
+    SHA3_512     -> "SHA-3(512)"
+    SHAKE128 siz -> CB.concat [ "SHAKE-128(" , sizeCBytes siz , ")"]
+    SHAKE256 siz -> CB.concat [ "SHAKE-256(" , sizeCBytes siz , ")"]
+    SM3          -> "SM3"
+    Skein512 siz b -> CB.concat [ "Skein-512(" , sizeCBytes siz, "," , b , ")"]
+    Streebog256  -> "Streebog-256"
+    Streebog512  -> "Streebog-512"
+    Whirlpool    -> "Whirlpool"
+    Parallel h1 h2 -> CB.concat [ "Parallel("
+                              , hashTypeToCBytes h1
+                              , ","
+                              , hashTypeToCBytes h2
+                              , ")"]
+    Comb4P h1 h2 -> CB.concat [ "Comb4P("
+                              , hashTypeToCBytes h1
+                              , ","
+                              , hashTypeToCBytes h2
+                              , ")"]
+    Adler32      -> "Adler32"
+    CRC24        -> "CRC24"
+    CRC32        -> "CRC32"
+  where
+    sizeCBytes = CB.fromText . T.toText
+
+-- | A Botan Hash Object.
+data Hash = Hash
+    { hashStruct :: {-# UNPACK #-} !BotanStruct
+    , hashName   :: {-# UNPACK #-} !CBytes              -- ^ hash algo name
+    , hashSize   :: {-# UNPACK #-} !Int                 -- ^ hash output size in bytes
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Pass Hash to FFI as @botan_hash_t@
+withHash :: HasCallStack => Hash -> (BotanStructT -> IO r) -> IO r
+withHash (Hash h _ _) = withBotanStruct h
+
+-- | Create a new 'Hash' object.
+newHash :: HasCallStack => HashType -> IO Hash
+newHash typ = do
+    let name = hashTypeToCBytes typ
+    bs <- newBotanStruct
+        (\ bts -> withCBytesUnsafe name $ \ pt ->
+            (botan_hash_init bts pt 0))
+        botan_hash_destroy
+    (osiz, _) <- withBotanStruct bs $ \ pbs ->
+        allocPrimUnsafe @CSize $ \ pl ->
+            botan_hash_output_length pbs pl
+    return (Hash bs name (fromIntegral osiz))
+
+-- | Copies the state of the hash object to a new hash object.
+copyHash :: HasCallStack => Hash -> IO Hash
+copyHash (Hash bts0 name siz) = do
+    s <- newBotanStruct
+        (\ bts -> withBotanStruct bts0 $ \ pbts0 ->
+            botan_hash_copy_state bts pbts0)
+        botan_hash_destroy
+    return (Hash s name siz)
+
+-- | Reset the state of Hash object back to clean, as if no input has been supplied.
+clearHash :: HasCallStack => Hash -> IO ()
+clearHash (Hash bts _ _) =
+    throwBotanIfMinus_ (withBotanStruct bts botan_hash_clear)
+
+-- | Feed a chunk of input into a hash object.
+updateHash :: Hash -> V.Bytes -> IO ()
+updateHash (Hash bts _ _) bs =
+    withBotanStruct bts $ \ pbts ->
+        withPrimVectorUnsafe bs $ \ pbs off len ->
+            throwBotanIfMinus_ (hs_botan_hash_update pbts pbs off len)
+
+-- | Compute hash value.
+finalHash :: HasCallStack => Hash -> IO V.Bytes
+{-# INLINABLE finalHash #-}
+finalHash (Hash bts _ siz) =
+    withBotanStruct bts $ \ pbts -> do
+        fst <$> allocPrimVectorUnsafe siz (\ pout ->
+            throwBotanIfMinus_ (botan_hash_final pbts pout))
+
+{-| Trun 'Hash' to a 'V.Bytes' sink, update 'Hash' by write bytes to the sink.
+
+@
+import Z.Data.CBytes
+import Z.Data.Vector.Hex
+import Z.Botan.Hash
+import Z.IO
+
+-- | Calculate SHA256 and MD5 checksum for a file in one streaming pass.
+sha256AndMd5File :: CBytes -> IO (HexBytes, HexBytes)
+sha256AndMd5File f =
+    withResource (sourceFromFile f) $ \ src -> do
+        md5 <- newHash MD5
+        sha256 <- newHash SHA256
+        runBIO $ src . (joinSink (sinkToHash md5) (sinkToHash sha256))
+        h1 <- finalHash md5
+        h2 <- finalHash sha256
+        return (HexBytes h1, HexBytes h2)
+@
+-}
+sinkToHash :: HasCallStack => Hash -> Sink V.Bytes
+{-# INLINABLE sinkToHash #-}
+sinkToHash h = \ k mbs -> case mbs of
+    Just bs -> updateHash h bs
+    _       -> k EOF
+
+-- | Directly compute a message's hash.
+hash :: HasCallStack => HashType -> V.Bytes -> V.Bytes
+{-# INLINABLE hash #-}
+hash ht inp = unsafePerformIO $ do
+    h <- newHash ht
+    updateHash h inp
+    finalHash h
+
+-- | Directly compute a chunked message's hash.
+hashChunks:: HasCallStack => HashType -> [V.Bytes] -> V.Bytes
+{-# INLINABLE hashChunks #-}
+hashChunks ht inp = unsafePerformIO $ do
+    h <- newHash ht
+    mapM_ (updateHash h) inp
+    finalHash h
diff --git a/Z/Crypto/KDF.hs b/Z/Crypto/KDF.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/KDF.hs
@@ -0,0 +1,206 @@
+{-|
+Module      : Z.Crypto.KDF
+Description : Key Derivation Functions
+Copyright   : Dong Han, 2021
+              AnJie Dong, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+KDF(Key Derivation Function) and PBKDF(Password Based Key Derivation Function).
+
+-}
+module Z.Crypto.KDF (
+  -- * KDF
+    KDFType(..)
+  , BlockCipherType (..)
+  , HashType(..)
+  , MACType(..)
+  , kdf
+  , kdf'
+  -- * PBKDF
+  , PBKDFType(..)
+  , pbkdf
+  , pbkdfTimed
+  -- * Internal helps
+  , kdfTypeToCBytes
+  , pbkdfTypeToParam
+  ) where
+
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.Cipher   (BlockCipherType (..))
+import           Z.Crypto.Hash     (HashType (..), hashTypeToCBytes)
+import           Z.Crypto.MAC      (MACType (..), macTypeToCBytes)
+import           Z.Data.CBytes     (CBytes, withCBytes, withCBytesUnsafe)
+import qualified Z.Data.CBytes     as CB
+import qualified Z.Data.Vector     as V
+import           Z.Foreign
+
+-----------------------------
+-- Key Derivation Function --
+-----------------------------
+
+-- | Key derivation functions are used to turn some amount of shared secret material into uniform random keys
+-- suitable for use with symmetric algorithms. An example of an input which is useful for a KDF is a shared
+-- secret created using Diffie-Hellman key agreement.
+data KDFType
+    = HKDF MACType
+    | HKDF_Extract MACType
+    | HKDF_Expand MACType
+    -- ^ Defined in RFC 5869, HKDF uses HMAC to process inputs.
+    -- Also available are variants HKDF-Extract and HKDF-Expand.
+    -- HKDF is the combined Extract+Expand operation.
+    -- Use the combined HKDF unless you need compatibility with some other system.
+    | KDF2 HashType
+    -- ^ KDF2 comes from IEEE 1363. It uses a hash function.
+    | KDF1_18033 HashType
+    -- ^ KDF1 from ISO 18033-2. Very similar to (but incompatible with) KDF2.
+    | KDF1 HashType
+    -- ^ KDF1 from IEEE 1363. It can only produce an output at most the length of the hash function used.
+    | TLS_PRF
+    -- ^ A KDF from ANSI X9.42. Sometimes used for Diffie-Hellman.
+    | TLS_12_PRF MACType
+    | SP800_108_Counter MACType
+    -- ^ KDFs from NIST SP 800-108. Variants include “SP800-108-Counter”, “SP800-108-Feedback” and “SP800-108-Pipeline”.
+    | SP800_108_Feedback MACType
+    | SP800_108_Pipeline MACType
+    | SP800_56AHash HashType
+    -- ^ NIST SP 800-56A KDF using hash function
+    | SP800_56AMAC MACType
+    -- ^ NIST SP 800-56A KDF using HMAC
+    | SP800_56C MACType
+    -- ^ NIST SP 800-56C KDF using HMAC
+
+kdfTypeToCBytes :: KDFType -> CBytes
+kdfTypeToCBytes (HKDF mt        ) = CB.concat [ "HKDF(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (HKDF_Extract mt) = CB.concat [ "HKDF-Extract(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (HKDF_Expand mt ) = CB.concat [ "HKDF-Expand(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (KDF2 ht        ) = CB.concat [ "KDF2(" , hashTypeToCBytes ht, ")"]
+kdfTypeToCBytes (KDF1_18033 ht  ) = CB.concat [ "KDF1-18033(" , hashTypeToCBytes ht, ")"]
+kdfTypeToCBytes (KDF1 ht        ) = CB.concat [ "KDF1(" , hashTypeToCBytes ht, ")"]
+kdfTypeToCBytes (TLS_PRF        ) = "TLS-PRF"
+kdfTypeToCBytes (TLS_12_PRF mt  ) = CB.concat [ "TLS-12-PRF(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (SP800_108_Counter mt ) = CB.concat [ "SP800-108-Counter(" ,  macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (SP800_108_Feedback mt) = CB.concat [ "SP800-108-Feedback(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (SP800_108_Pipeline mt) = CB.concat [ "SP800-108-Pipeline(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (SP800_56AHash ht     ) = CB.concat [ "SP800-56A(" , hashTypeToCBytes ht, ")"]
+kdfTypeToCBytes (SP800_56AMAC mt      ) = CB.concat [ "SP800-56A(" , macTypeToCBytes mt, ")"]
+kdfTypeToCBytes (SP800_56C mt         ) = CB.concat [ "SP800-56C(" , macTypeToCBytes mt, ")"]
+
+-- | Derive a key using the given KDF algorithm.
+kdf
+  :: KDFType    -- ^ the name of the given PBKDF algorithm
+  -> Int        -- ^ length of output key
+  -> V.Bytes    -- ^ secret
+  -> V.Bytes    -- ^ salt
+  -> V.Bytes    -- ^ label
+  -> IO V.Bytes
+kdf algo siz secret salt label =
+    withCBytesUnsafe (kdfTypeToCBytes algo) $ \ algoBA ->
+        withPrimVectorUnsafe secret $ \ secretBA secretOff secretLen ->
+            withPrimVectorUnsafe salt $ \ saltBA saltOff saltLen ->
+                withPrimVectorUnsafe label $ \ labelBA labelOff labelLen ->
+                    fst <$> allocPrimVectorUnsafe siz (\ buf -> do
+                        -- some kdf needs xor output buffer, so we clear it first
+                        clearMBA buf siz
+                        throwBotanIfMinus_ $
+                            hs_botan_kdf algoBA buf (fromIntegral siz)
+                                secretBA secretOff secretLen
+                                saltBA saltOff saltLen
+                                labelBA labelOff labelLen)
+
+-- | Derive a key using the given KDF algorithm, with default empty salt and label.
+kdf'
+  :: KDFType    -- ^ the name of the given PBKDF algorithm
+  -> Int        -- ^ length of output key
+  -> V.Bytes    -- ^ secret
+  -> IO V.Bytes
+kdf' algo siz secret = kdf algo siz secret mempty mempty
+
+--------------------------------------------
+-- Password-Based Key Derivation Function --
+--------------------------------------------
+
+-- | Often one needs to convert a human readable password into a cryptographic key. It is useful to slow down the
+-- computation of these computations in order to reduce the speed of brute force search, thus they are parameterized
+-- in some way which allows their required computation to be tuned.
+data PBKDFType
+    = PBKDF2 MACType Int   -- ^ iterations
+    -- ^ PBKDF2 is the “standard” password derivation scheme,
+    -- widely implemented in many different libraries.
+    | Scrypt  Int Int Int   -- ^ N, r, p
+    -- ^ Scrypt is a relatively newer design which is “memory hard”,
+    -- in addition to requiring large amounts of CPU power it uses a large block of memory to compute the hash.
+    -- This makes brute force attacks using ASICs substantially more expensive.
+    | Argon2d Int Int Int   -- ^ iterations, memory, parallelism
+    -- ^ Argon2 is the winner of the PHC (Password Hashing Competition) and provides a tunable memory hard PBKDF.
+    | Argon2i Int Int Int   -- ^ iterations, memory, parallelism
+    | Argon2id Int Int Int  -- ^ iterations, memory, parallelism
+    | Bcrypt Int            -- ^ iterations
+    | OpenPGP_S2K HashType Int -- ^ iterations
+    -- ^ The OpenPGP algorithm is weak and strange, and should be avoided unless implementing OpenPGP.
+
+pbkdfTypeToParam :: PBKDFType -> (CBytes, Int, Int, Int)
+pbkdfTypeToParam (PBKDF2 mt i     ) = (CB.concat [ "PBKDF2(" , macTypeToCBytes mt, ")"], i, 0, 0)
+pbkdfTypeToParam (Scrypt n r p    ) = ("Scrypt", n, r, p)
+pbkdfTypeToParam (Argon2d i m p   ) = ("Argon2d", i, m, p)
+pbkdfTypeToParam (Argon2i i m p   ) = ("Argon2i", i, m, p)
+pbkdfTypeToParam (Argon2id i m p  ) = ("Argon2id", i, m, p)
+pbkdfTypeToParam (Bcrypt i        ) = ("Bcrypt-PBKDF", i, 0, 0)
+pbkdfTypeToParam (OpenPGP_S2K ht i) = (CB.concat [ "OpenPGP-S2K(" , hashTypeToCBytes ht, ")"], i, 0, 0)
+
+-- | Derive a key from a passphrase for a number of iterations using the given PBKDF algorithm and params.
+pbkdf :: PBKDFType  -- ^ PBKDF algorithm type
+      -> Int        -- ^ length of output key
+      -> CBytes     -- ^ passphrase
+      -> V.Bytes    -- ^ salt
+      -> IO V.Bytes
+pbkdf typ siz pwd salt = do
+    withCBytesUnsafe algo $ \ algoBA ->
+        withCBytesUnsafe pwd $ \ pwdBA ->
+            withPrimVectorUnsafe salt $ \ saltBA saltOff saltLen -> do
+                fst <$> allocPrimVectorUnsafe siz (\ buf -> do
+                    clearMBA buf siz
+                    throwBotanIfMinus_ $
+                        hs_botan_pwdhash algoBA
+                            i1 i2 i3
+                            buf (fromIntegral siz)
+                            pwdBA (CB.length pwd)
+                            saltBA saltOff saltLen)
+  where
+    (algo, i1, i2, i3) = pbkdfTypeToParam typ
+
+-- | Derive a key from a passphrase using the given PBKDF algorithm, the iteration params are
+-- ignored and PBKDF is run until given milliseconds have passed.
+pbkdfTimed :: PBKDFType  -- ^ the name of the given PBKDF algorithm
+           -> Int        -- ^ run until milliseconds have passwd
+           -> Int        -- ^ length of output key
+           -> CBytes     -- ^ passphrase
+           -> V.Bytes    -- ^ salt
+           -> IO V.Bytes
+pbkdfTimed typ msec siz pwd s = do
+    -- we want run it in new OS thread without stop GC from running
+    -- if the expected time is too long(>0.1s)
+    if msec > 100
+    then withCBytes algo $ \algo' ->
+        withCBytes pwd $ \ pwd' ->
+            withPrimVectorSafe s $ \s' sLen ->
+                fst <$> allocPrimVectorSafe siz (\ buf -> do
+                    clearPtr buf siz
+                    throwBotanIfMinus_ $
+                        hs_botan_pwdhash_timed_safe
+                            algo' msec buf (fromIntegral siz)
+                            pwd' (CB.length pwd) s' 0 sLen)
+    else withCBytesUnsafe algo $ \algo' ->
+        withCBytesUnsafe pwd $ \ pwd' ->
+            withPrimVectorUnsafe s $ \s' sOff sLen ->
+                fst <$> allocPrimVectorUnsafe siz (\ buf -> do
+                    clearMBA buf siz
+                    throwBotanIfMinus_ $
+                        hs_botan_pwdhash_timed
+                            algo' msec buf (fromIntegral siz)
+                            pwd' (CB.length pwd) s' sOff sLen)
+  where
+    (algo, _, _, _) = pbkdfTypeToParam typ
diff --git a/Z/Crypto/KeyWrap.hs b/Z/Crypto/KeyWrap.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/KeyWrap.hs
@@ -0,0 +1,39 @@
+{-|
+Module      : Z.Crypto.KeyWrap
+Description : AES Key Wrapping
+Copyright   : AnJie Dong, Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides RFC3394 key Wrapping. It uses a 128-bit, 192-bit, or 256-bit key to encrypt an input key. AES is always used. The input must be a multiple of 8 bytes; if not an exception is thrown.
+
+-}
+module Z.Crypto.KeyWrap where
+
+import           Z.Botan.FFI
+import qualified Z.Data.Vector as V
+import           Z.Foreign
+
+-- | Wrap the input key using kek (the key encryption key), and return the result. It will be 8 bytes longer than the input key.
+keyWrap :: V.Bytes -- ^ key
+        -> V.Bytes -- ^ kek
+        -> IO V.Bytes
+{-# INLINABLE keyWrap #-}
+keyWrap key kek =
+    withPrimVectorUnsafe key $ \ key' keyOff keyLen ->
+    withPrimVectorUnsafe kek $ \ kek' kekOff kekLen ->
+    allocBotanBufferUnsafe (V.length key + 8) $
+        hs_botan_key_wrap3394 key' keyOff keyLen kek' kekOff kekLen
+
+-- | Unwrap a key wrapped with rfc3394_keywrap.
+keyUnwrap :: V.Bytes -- ^ wrapped key
+          -> V.Bytes -- ^ kek
+          -> IO V.Bytes
+{-# INLINABLE keyUnwrap #-}
+keyUnwrap key kek =
+    withPrimVectorUnsafe key $ \ key' keyOff keyLen ->
+    withPrimVectorUnsafe kek $ \ kek' kekOff kekLen ->
+    allocBotanBufferUnsafe (V.length key - 8) $
+        hs_botan_key_unwrap3394 key' keyOff keyLen kek' kekOff kekLen
diff --git a/Z/Crypto/MAC.hs b/Z/Crypto/MAC.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/MAC.hs
@@ -0,0 +1,165 @@
+{-|
+Module      : Z.Crypto.MAC
+Description : Message Authentication Codes (MAC)
+Copyright   : YouShi, Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+A Message Authentication Code algorithm computes a tag over a message utilizing a shared secret key. Thus a valid tag confirms the authenticity and integrity of the message. Only entities in possession of the shared secret key are able to verify the tag.
+
+-}
+
+module Z.Crypto.MAC (
+    -- * MAC type
+    MACType(..)
+  , MAC, macName, macSize
+    -- * IUF interface
+  , newMAC
+  , setKeyMAC
+  , updateMAC
+  , finalMAC
+  , clearMAC
+  -- * function interface
+  , mac, macChunks
+  -- * BIO interface
+  , sinkToMAC
+  -- * Internal helper
+  , macTypeToCBytes
+  , withMAC
+  ) where
+
+import           GHC.Generics
+import           System.IO.Unsafe  (unsafePerformIO)
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.Cipher   (BlockCipherType, blockCipherTypeToCBytes)
+import           Z.Crypto.Hash     (HashType, hashTypeToCBytes)
+import           Z.Data.CBytes     as CB
+import qualified Z.Data.Text       as T
+import qualified Z.Data.Vector     as V
+import           Z.Foreign
+import           Z.IO.BIO
+
+
+data MACType = CMAC BlockCipherType
+                -- ^ A modern CBC-MAC variant that avoids the security problems of plain CBC-MAC.
+                -- Approved by NIST. Also sometimes called OMAC.
+             | OMAC BlockCipherType
+               -- ^
+             | GMAC BlockCipherType
+               -- ^ GMAC is related to the GCM authenticated cipher mode.
+               -- It is quite slow unless hardware support for carryless multiplications is available.
+               --  A new nonce must be used with each message authenticated, or otherwise all security is lost.
+             | CBC_MAC BlockCipherType
+              -- ^ An older authentication code based on a block cipher.
+              -- Serious security problems,
+              -- in particular insecure if messages of several different lengths are authenticated.
+              --  Avoid unless required for compatibility.
+             | HMAC HashType
+              -- ^ A message authentication code based on a hash function. Very commonly used.
+             | Poly1305
+             -- ^ A polynomial mac (similar to GMAC). Very fast, but tricky to use safely.
+             -- Forms part of the ChaCha20Poly1305 AEAD mode.
+             -- A new key must be used for each message, or all security is lost.
+             | SipHash Int Int
+             -- ^ A modern and very fast PRF. Produces only a 64-bit output.
+             -- Defaults to “SipHash(2,4)” which is the recommended configuration,
+             -- using 2 rounds for each input block and 4 rounds for finalization.
+             | X9'19_MAC
+             -- ^ A CBC-MAC variant sometimes used in finance. Always uses DES.
+             -- Sometimes called the “DES retail MAC”, also standardized in ISO 9797-1.
+             -- It is slow and has known attacks. Avoid unless required.
+
+macTypeToCBytes :: MACType -> CBytes
+macTypeToCBytes (CMAC bc   ) = CB.concat ["CMAC(", blockCipherTypeToCBytes bc, ")"]
+macTypeToCBytes (OMAC bc   ) = CB.concat ["OMAC(", blockCipherTypeToCBytes bc, ")"]
+macTypeToCBytes (GMAC bc   ) = CB.concat ["GMAC(", blockCipherTypeToCBytes bc, ")"]
+macTypeToCBytes (CBC_MAC bc) = CB.concat ["CBC-MAC(", blockCipherTypeToCBytes bc, ")"]
+macTypeToCBytes (HMAC ht)    = CB.concat ["HMAC(", hashTypeToCBytes ht, ")"]
+macTypeToCBytes Poly1305     = "Poly1305"
+macTypeToCBytes (SipHash r1 r2) = CB.concat ["SipHash(", sizeCBytes r1, ",", sizeCBytes r2, ")"]
+  where
+    sizeCBytes = CB.fromText . T.toText
+macTypeToCBytes X9'19_MAC = "X9.19-MAC"
+
+data MAC = MAC
+    { macStruct :: {-# UNPACK #-} !BotanStruct
+    , macName   :: {-# UNPACK #-} !CBytes             -- ^ mac algo name
+    , macSize   :: {-# UNPACK #-} !Int                -- ^ mac output size in bytes
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Pass MAC to FFI as 'botan_mac_t'.
+withMAC :: HasCallStack => MAC -> (BotanStructT -> IO r) -> IO r
+withMAC (MAC m _ _) = withBotanStruct m
+
+-- | Create a new 'MAC' object.
+newMAC :: MACType -> IO MAC
+newMAC typ = do
+    let name = macTypeToCBytes typ
+    bs <- newBotanStruct
+        (\ bts -> withCBytesUnsafe name $ \ pt ->
+            (botan_mac_init bts pt 0))
+        botan_mac_destroy
+    (osiz, _) <- withBotanStruct bs $ \ pbs ->
+        allocPrimUnsafe @CSize $ \ pl ->
+            botan_mac_output_length pbs pl
+    return (MAC bs name (fromIntegral osiz))
+
+-- | Set the random key.
+setKeyMAC :: HasCallStack => MAC -> V.Bytes -> IO ()
+setKeyMAC (MAC bts _ _) bs =
+    withBotanStruct bts $ \pbts->
+        withPrimVectorUnsafe bs $ \pbs off len ->
+            throwBotanIfMinus_ (hs_botan_mac_set_key pbts pbs off len)
+
+-- | Feed a chunk of input into a 'MAC' object.
+updateMAC :: HasCallStack => MAC -> V.Bytes -> IO ()
+updateMAC (MAC bts _ _) bs =
+    withBotanStruct bts $ \ pbts ->
+        withPrimVectorUnsafe bs $ \ pbs off len ->
+            throwBotanIfMinus_ (hs_botan_mac_update pbts pbs off len)
+
+finalMAC :: HasCallStack => MAC -> IO V.Bytes
+{-# INLINABLE finalMAC #-}
+finalMAC (MAC bts _ siz) =
+    withBotanStruct bts $ \ pbts -> do
+        fst <$> allocPrimVectorUnsafe siz (\ pout ->
+            throwBotanIfMinus_ (botan_mac_final pbts pout))
+
+-- | Reset the state of MAC object back to clean, as if no input has been supplied.
+clearMAC :: HasCallStack => MAC -> IO ()
+clearMAC (MAC bts _ _) =
+    throwBotanIfMinus_ (withBotanStruct bts hs_botan_mac_clear)
+
+-- | Trun 'MAC' to a 'V.Bytes' sink, update 'MAC' by write bytes to the sink.
+--
+sinkToMAC :: HasCallStack => MAC -> Sink V.Bytes
+{-# INLINABLE sinkToMAC #-}
+sinkToMAC h = \ k mbs -> case mbs of
+    Just bs -> updateMAC h bs
+    _       -> k EOF
+
+-- | Directly compute a message's mac
+mac :: HasCallStack => MACType
+                    -> V.Bytes  -- ^ key
+                    -> V.Bytes  -- ^ input
+                    -> V.Bytes
+{-# INLINABLE mac #-}
+mac mt key inp = unsafePerformIO $ do
+    m <- newMAC mt
+    setKeyMAC m key
+    updateMAC m inp
+    finalMAC m
+
+-- | Directly compute a chunked message's mac.
+macChunks :: HasCallStack => MACType -> V.Bytes -> [V.Bytes] -> V.Bytes
+{-# INLINABLE macChunks #-}
+macChunks mt key inps = unsafePerformIO $ do
+    m <- newMAC mt
+    setKeyMAC m key
+    mapM_ (updateMAC m) inps
+    finalMAC m
diff --git a/Z/Crypto/MPI.hs b/Z/Crypto/MPI.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/MPI.hs
@@ -0,0 +1,513 @@
+{-|
+Module      : Z.Crypto.MPI
+Description : Multiple Precision Integer
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide Botan's Multiple Precision Integer, featuring constant-time operations, which is suit for cryptograph usage.
+
+-}
+module Z.Crypto.MPI
+  ( -- * RNG
+    MPI, fromCInt, toWord32, byteSize, Z.Crypto.MPI.bitSize
+    -- * Builder & Parser
+  , toHex, toDecimal, fromHex, fromDecimal
+    -- * Predicator
+  , isNegative, isZero, isOdd, isEven, isPrim
+    -- * MPI specific
+  , mulMod, powMod, modInverse, Z.Crypto.MPI.gcd
+    -- * Random MPI
+  , randBits, randRange
+    -- * Internal
+  , copyMPI
+  , newMPI
+  , unsafeNewMPI
+  , newMPI'
+  , unsafeNewMPI'
+  , withMPI
+  , unsafeWithMPI
+  ) where
+
+import           Control.Monad
+import           Data.Bits
+import qualified Data.Scientific           as Scientific
+import           Data.Word
+import           GHC.Exts
+import           GHC.Generics
+import           GHC.Integer.GMP.Internals
+import           GHC.Real
+import           System.IO.Unsafe          (unsafeDupablePerformIO)
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.RNG
+import qualified Z.Data.Array              as A
+import           Z.Data.ASCII
+import qualified Z.Data.Builder            as B
+import           Z.Data.JSON               (JSON (..), Value (..), fail',
+                                            withBoundedScientific)
+import qualified Z.Data.Parser             as P
+import qualified Z.Data.Text               as T
+import qualified Z.Data.Vector.Base        as V
+import           Z.Foreign                 (CInt, CSize,
+                                            MutablePrimArray (MutablePrimArray),
+                                            PrimArray (..), allocPrimUnsafe,
+                                            newPrimArray, unsafeFreezePrimArray)
+
+-- | Opaque Botan Multiple Precision Integers.
+newtype MPI = MPI BotanStruct
+
+instance Eq MPI where
+    {-# INLINE (==) #-}
+    a == b =
+        unsafeWithMPI a $ \ btsa ->
+            withMPI b $ \ btsb -> do
+                r <- botan_mp_equal btsa btsb
+                return $! r == 1
+
+instance Ord MPI where
+    {-# INLINE compare #-}
+    a `compare` b =
+        unsafeWithMPI a $ \ btsa ->
+            withMPI b $ \ btsb -> do
+                (r, _) <- allocPrimUnsafe $ \ r -> botan_mp_cmp r btsa btsb
+                return $! case (r :: CInt) of
+                    1 -> GT
+                    0 -> EQ
+                    _ -> LT
+
+instance Num MPI where
+    {-# INLINE (+) #-}
+    a + b = unsafeDupablePerformIO $ do
+        withMPI a $ \ btsa ->
+            withMPI b $ \ btsb ->
+                newMPI $ \ btsr -> botan_mp_add btsr btsa btsb
+    {-# INLINE (-) #-}
+    a - b = unsafeDupablePerformIO $ do
+        withMPI a $ \ btsa ->
+            withMPI b $ \ btsb ->
+                newMPI $ \ btsr -> botan_mp_sub btsr btsa btsb
+    {-# INLINE (*) #-}
+    a * b = unsafeDupablePerformIO $ do
+        withMPI a $ \ btsa ->
+            withMPI b $ \ btsb ->
+                newMPI $ \ btsr -> botan_mp_mul btsr btsa btsb
+
+    {-# INLINE negate #-}
+
+    negate a = unsafeWithMPI a $ \ btsa ->
+        newMPI (\ bts -> do
+            throwBotanIfMinus_ (botan_mp_set_from_mp bts btsa)
+            botan_mp_flip_sign bts)
+
+    {-# INLINE abs #-}
+    abs mp | isNegative mp = negate mp
+           | otherwise = mp
+
+    {-# INLINE signum #-}
+    signum mp = case mp `compare` zero of
+        LT -> -1
+        EQ -> 0
+        _  -> 1
+
+    {-# INLINE fromInteger #-}
+    fromInteger c
+        | c == 0 = zero
+        | otherwise = unsafeDupablePerformIO $ do
+            mpa@(MutablePrimArray mba#)<- newPrimArray (I# (word2Int# siz#))
+            void (exportIntegerToMutableByteArray c mba# 0## 1#)
+            (PrimArray ba# :: PrimArray Word8) <- unsafeFreezePrimArray mpa
+            r <- newMPI $ \ bts -> hs_botan_mp_from_bin bts ba# 0 (I# (word2Int# siz#))
+            return $! if c < 0 then negate r else r
+      where
+        siz# = sizeInBaseInteger c 256#
+
+instance Real MPI where
+    {-# INLINE toRational #-}
+    toRational mp = toInteger mp :% 1
+
+instance Enum MPI where
+    succ x               = x + 1
+    pred x               = x - 1
+    toEnum               = fromIntegral
+    fromEnum             = fromIntegral
+    {-# INLINE enumFrom #-}
+    {-# INLINE enumFromThen #-}
+    {-# INLINE enumFromTo #-}
+    {-# INLINE enumFromThenTo #-}
+    enumFrom x             = enumDeltaMPI   x 1
+    enumFromThen x y       = enumDeltaMPI   x (y-x)
+    enumFromTo x lim       = enumDeltaToMPI x 1     lim
+    enumFromThenTo x y lim = enumDeltaToMPI x (y-x) lim
+
+instance JSON MPI where
+    {-# INLINE fromValue #-}
+    fromValue = withBoundedScientific "Z.Crypto.MPI.MPI" $ \ n ->
+        case Scientific.floatingOrInteger n :: Either Double Integer of
+            Right x -> pure (fromInteger x)
+            Left _  -> fail' . B.unsafeBuildText $ do
+                "converting Integer failed, unexpected floating number "
+                B.scientific n
+    {-# INLINE toValue #-}
+    toValue = Number . fromIntegral
+    {-# INLINE encodeJSON #-}
+    encodeJSON = toDecimal
+
+
+-- These RULES are copied from GHC.Enum
+{-# RULES
+"enumDeltaMPI"      [~1] forall x y.   enumDeltaMPI x y         = build (\c _ -> enumDeltaMPIFB c x y)
+"efdtMPI"           [~1] forall x d l. enumDeltaToMPI x d l     = build (\c n -> enumDeltaToMPIFB  c n x d l)
+"efdtMPI1"          [~1] forall x l.   enumDeltaToMPI x 1 l     = build (\c n -> enumDeltaToMPI1FB c n x l)
+
+"enumDeltaToMPI1FB" [1] forall c n x.  enumDeltaToMPIFB c n x 1 = enumDeltaToMPI1FB c n x
+
+"enumDeltaMPI"      [1] enumDeltaMPIFB    (:)     = enumDeltaMPI
+"enumDeltaToMPI"    [1] enumDeltaToMPIFB  (:) []  = enumDeltaToMPI
+"enumDeltaToMPI1"   [1] enumDeltaToMPI1FB (:) []  = enumDeltaToMPI1
+ #-}
+
+{-# INLINE [0] enumDeltaMPIFB #-}
+-- See Note [Inline FB functions] in GHC.List
+enumDeltaMPIFB :: (MPI -> b -> b) -> MPI -> MPI -> b
+enumDeltaMPIFB c x0 d = go x0
+  where go x = x `seq` (x `c` go (x+d))
+
+{-# NOINLINE [1] enumDeltaMPI #-}
+enumDeltaMPI :: MPI -> MPI -> [MPI]
+enumDeltaMPI x d = x `seq` (x : enumDeltaMPI (x+d) d)
+-- strict accumulator, so
+--     head (drop 1000000 [1 .. ]
+-- works
+
+{-# INLINE [0] enumDeltaToMPIFB #-}
+-- See Note [Inline FB functions] in GHC.List
+-- Don't inline this until RULE "enumDeltaToMPI" has had a chance to fire
+enumDeltaToMPIFB :: (MPI -> a -> a) -> a
+                     -> MPI -> MPI -> MPI -> a
+enumDeltaToMPIFB c n x delta lim
+  | delta >= 0 = up_fb c n x delta lim
+  | otherwise  = dn_fb c n x delta lim
+
+{-# INLINE [0] enumDeltaToMPI1FB #-}
+-- See Note [Inline FB functions] in GHC.List
+-- Don't inline this until RULE "enumDeltaToMPI" has had a chance to fire
+enumDeltaToMPI1FB :: (MPI -> a -> a) -> a
+                      -> MPI -> MPI -> a
+enumDeltaToMPI1FB c n x0 lim = go (x0 :: MPI)
+                      where
+                        go x | x > lim   = n
+                             | otherwise = x `c` go (x+1)
+
+{-# NOINLINE [1] enumDeltaToMPI #-}
+enumDeltaToMPI :: MPI -> MPI -> MPI -> [MPI]
+enumDeltaToMPI x delta lim
+  | delta >= 0 = up_list x delta lim
+  | otherwise  = dn_list x delta lim
+
+{-# NOINLINE [1] enumDeltaToMPI1 #-}
+enumDeltaToMPI1 :: MPI -> MPI -> [MPI]
+-- Special case for Delta = 1
+enumDeltaToMPI1 x0 lim = go (x0 :: MPI)
+                      where
+                        go x | x > lim   = []
+                             | otherwise = x : go (x+1)
+
+up_fb :: (MPI -> a -> a) -> a -> MPI -> MPI -> MPI -> a
+up_fb c n x0 delta lim = go (x0 :: MPI)
+                      where
+                        go x | x > lim   = n
+                             | otherwise = x `c` go (x+delta)
+dn_fb :: (MPI -> a -> a) -> a -> MPI -> MPI -> MPI -> a
+dn_fb c n x0 delta lim = go (x0 :: MPI)
+                      where
+                        go x | x < lim   = n
+                             | otherwise = x `c` go (x+delta)
+
+up_list :: MPI -> MPI -> MPI -> [MPI]
+up_list x0 delta lim = go (x0 :: MPI)
+                    where
+                        go x | x > lim   = []
+                             | otherwise = x : go (x+delta)
+dn_list :: MPI -> MPI -> MPI -> [MPI]
+dn_list x0 delta lim = go (x0 :: MPI)
+                    where
+                        go x | x < lim   = []
+                             | otherwise = x : go (x+delta)
+
+instance Integral MPI where
+    {-# INLINE quotRem #-}
+    a `quotRem` b =
+        unsafeWithMPI a $ \ btsa ->
+            withMPI b $ \ btsb ->
+                newMPI' $ \ q ->
+                    newMPI $ \ r ->
+                        botan_mp_div q r btsa btsb
+
+    {-# INLINE toInteger #-}
+    toInteger mp
+        | isZero mp = 0
+        | otherwise = unsafeWithMPI mp $ \ bts -> do
+            mpa@(MutablePrimArray mba#) <- newPrimArray siz
+            throwBotanIfMinus_ (hs_botan_mp_to_bin bts mba# 0)
+            (PrimArray ba# :: PrimArray Word8) <- unsafeFreezePrimArray mpa
+            let r = importIntegerFromByteArray ba# 0## (int2Word# siz#) 1#
+            return $! if mp < 0 then negate r else r
+      where
+        !siz@(I# siz#) = (byteSize mp)
+
+-- | The 'testBit' implementation ignore sign.
+instance Bits MPI where
+    x .&. y = fromInteger $ (toInteger x) .&. (toInteger y)
+    {-# INLINE (.&.) #-}
+    x .|. y = fromInteger $ (toInteger x) .|. (toInteger y)
+    {-# INLINE (.|.) #-}
+    x `xor` y = fromInteger $ (toInteger x) `xor` (toInteger y)
+    {-# INLINE xor #-}
+    complement = fromInteger . complement . toInteger
+    {-# INLINE complement #-}
+    shift x i
+        | i >= 0 =
+            unsafeWithMPI x $ \ btsx ->
+                newMPI $ \ btsr ->
+                    botan_mp_lshift btsr btsx (fromIntegral i)
+        | otherwise =
+            unsafeWithMPI x $ \ btsx ->
+                newMPI $ \ btsr ->
+                    botan_mp_rshift btsr btsx (fromIntegral (-i))
+    {-# INLINE shift #-}
+
+    testBit x i =
+        unsafeWithMPI x $ \ btsx -> do
+            r <- botan_mp_get_bit btsx (fromIntegral i)
+            return $! r == 1
+    {-# INLINE testBit #-}
+
+    zeroBits   = 0
+    bit = setBit 0
+    {-# INLINE bit #-}
+
+    setBit a i =
+        unsafeWithMPI a $ \ btsa ->
+            newMPI (\ btsr -> do
+                throwBotanIfMinus_ (botan_mp_set_from_mp btsr btsa)
+                botan_mp_set_bit btsr (fromIntegral i))
+    {-# INLINE setBit #-}
+
+    clearBit a i =
+        unsafeWithMPI a $ \ btsa ->
+            newMPI (\ btsr -> do
+                throwBotanIfMinus_ (botan_mp_set_from_mp btsr btsa)
+                botan_mp_clear_bit btsr (fromIntegral i))
+    {-# INLINE clearBit #-}
+
+    popCount = popCount . toInteger
+    {-# INLINE popCount #-}
+
+    rotate x i = shift x i   -- since an MPI never wraps around
+    {-# INLINE rotate #-}
+
+    bitSizeMaybe _ = Nothing
+    bitSize _  = error "Z.Crypto.MPI.bitSize(MPI)"
+    isSigned _ = True
+
+instance Show MPI where
+    show = T.toString
+
+instance T.Print MPI where
+    {-# INLINE toUTF8BuilderP #-}
+    toUTF8BuilderP _  = toDecimal
+
+zero :: MPI
+zero = unsafeNewMPI (\ _ -> return ())
+
+newMPI :: (BotanStructT -> IO a) -> IO MPI
+newMPI f = do
+    mp <- newBotanStruct (\ bts -> botan_mp_init bts) botan_mp_destroy
+    _ <- withBotanStruct mp f
+    return (MPI mp)
+
+newMPI' :: (BotanStructT -> IO a) -> IO (MPI, a)
+newMPI' f = do
+    mp <- newBotanStruct (\ bts -> botan_mp_init bts) botan_mp_destroy
+    r <- withBotanStruct mp f
+    return (MPI mp, r)
+
+copyMPI :: MPI -> IO MPI
+copyMPI (MPI a) = do
+    withBotanStruct a $ \ btsa -> do
+        newMPI (\ bts -> botan_mp_set_from_mp bts btsa)
+
+withMPI :: MPI -> (BotanStructT -> IO a) -> IO a
+withMPI (MPI bts) f = withBotanStruct bts f
+
+unsafeWithMPI :: MPI -> (BotanStructT -> IO a) -> a
+unsafeWithMPI (MPI bts) f = unsafeDupablePerformIO (withBotanStruct bts f)
+
+unsafeNewMPI :: (BotanStructT -> IO a) -> MPI
+unsafeNewMPI f = unsafeDupablePerformIO $ do
+    mp <- newBotanStruct (\ bts -> botan_mp_init bts) botan_mp_destroy
+    _ <- withBotanStruct mp f
+    return (MPI mp)
+
+unsafeNewMPI' :: (BotanStructT -> IO a) -> (MPI, a)
+unsafeNewMPI' f = unsafeDupablePerformIO $ do
+    mp <- newBotanStruct (\ bts -> botan_mp_init bts) botan_mp_destroy
+    r <- withBotanStruct mp f
+    return (MPI mp, r)
+
+-- | Get 'MPI' 's byte size.
+byteSize :: MPI -> Int
+byteSize mp = fromIntegral @CSize . fst . unsafeWithMPI mp $ \ bts ->
+    allocPrimUnsafe (botan_mp_num_bytes bts)
+
+-- | Get 'MPI' 's bit size.
+bitSize :: MPI -> Int
+bitSize mp = fromIntegral @CSize . fst . unsafeWithMPI mp $ \ bts ->
+    allocPrimUnsafe (botan_mp_num_bits bts)
+
+-- | Set 'MPI' from an integer value.
+fromCInt :: CInt -> MPI
+fromCInt x = unsafeNewMPI $ \ bts ->
+    botan_mp_set_from_int bts x
+
+-- | Convert a MPI to 'Word32', the sign is ignored.
+toWord32 :: HasCallStack => MPI -> Word32
+toWord32 mp = fst . unsafeWithMPI mp $ \ bts ->
+    allocPrimUnsafe (botan_mp_to_uint32 bts)
+
+-- | Write a 'MPI' in decimal format, with negative sign if < 0.
+toDecimal :: MPI -> B.Builder ()
+toDecimal mp = do
+    when (isNegative mp) (B.word8 MINUS)
+    -- botan write \NUL terminator
+    B.ensureN (byteSize mp * 3 + 1) $ \ (MutablePrimArray mba#) off ->
+        withMPI mp $ \ btst -> do
+            hs_botan_mp_to_dec btst mba# off
+
+-- | Parse a 'MPI' in decimal format, parse leading minus sign.
+fromDecimal :: P.Parser MPI
+fromDecimal = do
+    sign <- P.peek
+    let neg = sign == MINUS
+    when neg P.skipWord8
+    v@(V.PrimVector (A.PrimArray ba#) s l) <- P.takeWhile1 isDigit
+    let (x, r) = unsafeNewMPI' $ \ bts -> do
+            r' <- hs_botan_mp_set_from_dec bts ba# s l
+            when (r' >= 0 && neg) (void $ botan_mp_flip_sign bts)
+            return r'
+    if (r < 0)
+    then P.fail' $ "wrong decimal integer: " <> T.toText v
+    else return x
+
+-- | Write a 'MPI' in hexadecimal format(without '0x' prefix), the sign is ignored.
+toHex :: MPI -> B.Builder ()
+toHex mp =
+    -- botan write \NUL terminator
+    let !siz = byteSize mp `unsafeShiftL` 1
+    in B.ensureN (siz + 1) $ \ (MutablePrimArray mba#) off ->
+        withMPI mp $ \ btst -> do
+            _ <- hs_botan_mp_to_hex btst mba# off
+            return (off+siz)
+
+-- | Parse a 'MPI' in hexadecimal format(without '0x' prefix), no sign is allowed.
+fromHex :: P.Parser MPI
+fromHex = do
+    v@(V.PrimVector (A.PrimArray ba#) s l) <- P.takeWhile1 isHexDigit
+    let (x, r) = unsafeNewMPI' $ \ bts -> hs_botan_mp_set_from_hex bts ba# s l
+    if (r < 0)
+    then P.fail' $ "wrong hexadecimal integer: " <> T.toText v
+    else return x
+
+isNegative :: MPI -> Bool
+isNegative mp = unsafeWithMPI mp $ \ bts -> do
+    r <- botan_mp_is_negative bts
+    return $! r == 1
+
+isZero :: MPI -> Bool
+isZero mp = unsafeWithMPI mp $ \ bts -> do
+    r <- botan_mp_is_zero bts
+    return $! r == 1
+
+isOdd :: MPI -> Bool
+isOdd mp = unsafeWithMPI mp $ \ bts -> do
+    r <- botan_mp_is_odd bts
+    return $! r == 1
+
+isEven :: MPI -> Bool
+isEven mp = unsafeWithMPI mp $ \ bts -> do
+    r <- botan_mp_is_even bts
+    return $! r == 1
+
+--------------------------------------------------------------------------------
+
+-- | mulMod x y mod = x times y modulo mod
+mulMod :: MPI -> MPI -> MPI -> MPI
+mulMod x y m =
+    unsafeNewMPI $ \ btsr ->
+        withMPI x $ \ btsx ->
+            withMPI y $ \ btsy ->
+                withMPI m $ \ btsm ->
+                    botan_mp_mod_mul btsr btsx btsy btsm
+
+
+-- | Modular exponentiation. powMod base exp mod = base power exp module mod
+powMod :: MPI -> MPI -> MPI -> MPI
+powMod x y m =
+    unsafeNewMPI $ \ btsr ->
+        withMPI x $ \ btsx ->
+            withMPI y $ \ btsy ->
+                withMPI m $ \ btsm ->
+                    botan_mp_powmod btsr btsx btsy btsm
+
+-- | Modular inverse, find an integer x so that @a⋅x ≡ 1  mod m@
+--
+-- If no modular inverse exists (for instance because in and modulus are not relatively prime), return 0.
+modInverse :: MPI -> MPI -> MPI
+modInverse x y =
+    unsafeNewMPI $ \ btsr ->
+        withMPI x $ \ btsx ->
+            withMPI y $ \ btsy ->
+                    botan_mp_mod_inverse btsr btsx btsy
+
+-- | Create a random 'MPI' of the specified bit size.
+randBits :: RNG -> Int -> IO MPI
+randBits rng x = do
+    newMPI $ \ bts ->
+        withRNG rng $ \ bts_rng ->
+            throwBotanIfMinus_ (botan_mp_rand_bits bts bts_rng (fromIntegral (max x 0)))
+
+-- | Create a random 'MPI' within the provided range.
+randRange :: RNG
+          -> MPI     -- ^ lower bound
+          -> MPI     -- ^ upper bound
+          -> IO MPI
+randRange rng lower upper = do
+    newMPI $ \ bts ->
+        withRNG rng $ \ bts_rng ->
+            withMPI lower $ \ bts_lower ->
+                withMPI upper $ \ bts_upper ->
+                    throwBotanIfMinus_ (botan_mp_rand_range bts bts_rng bts_lower bts_upper)
+
+-- | Compute the greatest common divisor of x and y.
+gcd :: MPI -> MPI -> MPI
+gcd x y = unsafeNewMPI $ \ bts ->
+    withMPI x $ \ bts_x ->
+        withMPI y $ \ bts_y ->
+            throwBotanIfMinus_ (botan_mp_gcd bts bts_x bts_y)
+
+-- | Test if n is prime.
+--
+-- The algorithm used (Miller-Rabin) is probabilistic,
+-- set test_prob to the desired assurance level.
+-- For example if test_prob is 64, then sufficient Miller-Rabin iterations will run to
+-- assure there is at most a 1/2**64 chance that n is composite.
+isPrim :: RNG -> MPI -> Int -> IO Bool
+isPrim rng x prob = do
+    withRNG rng $ \ bts_rng ->
+        withMPI x $ \ bts_x -> do
+            r <- throwBotanIfMinus (botan_mp_is_prime bts_x bts_rng
+                (fromIntegral (max 0 prob)))
+            return $! r == 1
diff --git a/Z/Crypto/OTP.hs b/Z/Crypto/OTP.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/OTP.hs
@@ -0,0 +1,129 @@
+{-|
+Module      : Z.Crypto.OTP
+Description : One Time Passwords
+Copyright   : Dong Han, AnJie Dong, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+One time password schemes are a user authentication method that relies on a fixed secret key which is used to derive a sequence of short passwords, each of which is accepted only once. Commonly this is used to implement two-factor authentication (2FA), where the user authenticates using both a conventional password (or a public key signature) and an OTP generated by a small device such as a mobile phone.
+
+Botan implements the HOTP and TOTP schemes from RFC 4226 and 6238.
+
+Since the range of possible OTPs is quite small, applications must rate limit OTP authentication attempts to some small number per second. Otherwise an attacker could quickly try all 1000000 6-digit OTPs in a brief amount of time.
+
+-}
+
+module Z.Crypto.OTP (
+  -- * HOTP
+    HOTP, newHOTP, genHOTP, checkHOTP
+  -- * TOTP
+  , TOTP, newTOTP, genTOTP, checkTOTP
+  -- * constants
+  , OTPAlgo, pattern OTP_SHA1, pattern OTP_SHA256, pattern OTP_SHA512
+  , OTPDigitLen, pattern OTP_DIGIT_6, pattern OTP_DIGIT_7, pattern OTP_DIGIT_8
+  ) where
+
+import           Data.Word
+import           GHC.Generics
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Data.CBytes
+import qualified Z.Data.Vector  as V
+import qualified Z.Data.Text    as T
+import           Z.Crypto.Hash  (HashType(..), hashTypeToCBytes)
+import           Z.Foreign
+
+-- | HOTP generates OTPs that are a short numeric sequence, between 6 and 8 digits (most applications use 6 digits), created using the HMAC of a 64-bit counter value. If the counter ever repeats the OTP will also repeat, thus both parties must assure the counter only increments and is never repeated or decremented. Thus both client and server must keep track of the next counter expected.
+-- Anyone with access to the client-specific secret key can authenticate as that client, so it should be treated with the same security consideration as would be given to any other symmetric key or plaintext password.
+newtype HOTP = HOTP BotanStruct
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+type OTPAlgo = HashType
+pattern OTP_SHA1    :: OTPAlgo
+pattern OTP_SHA256  :: OTPAlgo
+pattern OTP_SHA512  :: OTPAlgo
+pattern OTP_SHA1    = SHA160
+pattern OTP_SHA256  = SHA256
+pattern OTP_SHA512  = SHA512
+
+type OTPDigitLen = Int
+pattern OTP_DIGIT_6 :: OTPDigitLen
+pattern OTP_DIGIT_7 :: OTPDigitLen
+pattern OTP_DIGIT_8 :: OTPDigitLen
+pattern OTP_DIGIT_6 = 6
+pattern OTP_DIGIT_7 = 7
+pattern OTP_DIGIT_8 = 8
+
+newHOTP :: V.Bytes -> OTPAlgo -> OTPDigitLen -> IO HOTP
+newHOTP key otpAlgo digits =
+    withPrimVectorUnsafe key $ \ key' keyOff keyLen ->
+    withCBytesUnsafe (hashTypeToCBytes otpAlgo) $ \ hashAlgo' ->
+        HOTP <$> newBotanStruct
+            (\ hotp -> hs_botan_hotp_init hotp key' keyOff keyLen hashAlgo' digits)
+            botan_hotp_destroy
+
+-- | Generate a HOTP code for the provided counter.
+genHOTP :: HOTP   -- ^ the HOTP object
+        -> Word64 -- ^ HOTP counter
+        -> IO Word32
+genHOTP (HOTP hotp) counter =
+    withBotanStruct hotp $ \ hotp' ->
+    fst <$> allocPrimUnsafe (\ code -> botan_hotp_generate hotp' code counter)
+
+-- | Verify a HOTP code.
+checkHOTP :: HOTP   -- ^ the HOTP object
+          -> Word32 -- ^ the presented HOTP code
+          -> Word64 -- ^ the HOTP counter
+          -> Int    -- ^ resync range
+          -> IO (Bool, Word64)
+checkHOTP (HOTP totp) code c range = do
+    withBotanStruct totp $ \ totp' -> do
+        (nc, ret) <- allocPrimUnsafe $ \ nc' ->
+            botan_hotp_check totp' nc' code c (fromIntegral range)
+        if ret == BOTAN_FFI_SUCCESS
+        then return (True, nc)
+        else if ret > 0
+            then return (False, nc)
+            else throwBotanError ret
+
+--------------------------------------------------------------------------------
+
+-- | TOTP is based on the same algorithm as HOTP, but instead of a counter a timestamp is used.
+newtype TOTP = TOTP BotanStruct
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+newTOTP :: V.Bytes -> OTPAlgo -> OTPDigitLen -> Int -> IO TOTP
+newTOTP key otpAlgo digits timeStep =
+    withPrimVectorUnsafe key $ \ key' keyOff keyLen ->
+    withCBytesUnsafe (hashTypeToCBytes otpAlgo) $ \ hashAlgo' ->
+        TOTP <$> newBotanStruct
+            (\ hotp -> hs_botan_totp_init hotp key' keyOff keyLen hashAlgo' digits timeStep)
+            botan_totp_destroy
+
+-- | Generate a TOTP code for the provided timestamp.
+genTOTP :: TOTP   -- ^ the TOTP object
+        -> Word64 -- ^ the current local timestamp
+        -> IO Word32
+genTOTP (TOTP totp) timestamp =
+    withBotanStruct totp $ \ totp' ->
+    fst <$> allocPrimUnsafe (\ code -> botan_totp_generate totp' code timestamp)
+
+-- | Verify a TOTP code.
+checkTOTP :: TOTP   -- ^ the TOTP object
+          -> Word32 -- ^ the presented OTP
+          -> Word64 -- ^ timestamp the current local timestamp
+          -> Int    -- ^ specifies the acceptable amount of clock drift
+                    --   (in terms of time steps) between the two hosts.
+          -> IO Bool
+checkTOTP (TOTP totp) code timestamp driftAmount = do
+    withBotanStruct totp $ \ totp' -> do
+        ret <- botan_totp_check totp' code timestamp (fromIntegral driftAmount)
+        if ret == BOTAN_FFI_SUCCESS
+        then return True
+        else if ret > 0
+            then return False
+            else throwBotanError ret
diff --git a/Z/Crypto/PubKey.hs b/Z/Crypto/PubKey.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/PubKey.hs
@@ -0,0 +1,1143 @@
+{-|
+Module      : Z.Crypto.PubKey
+Description : Public Key Cryptography
+Copyright   : Dong Han, 2021
+              AnJie Dong, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module is used for Public Key Cryptography.
+Public key cryptography (also called asymmetric cryptography) is a collection of techniques allowing for encryption, signatures, and key agreement.
+-}
+
+module Z.Crypto.PubKey (
+  -- * Asymmetric cryptography algorithms
+    KeyType(..), pattern RSADefault, pattern McElieceDefault, pattern XMSSDefault
+  , ECCType(..), DLType(..)
+  -- * Key generation and manipulation
+  , PrivKey(..), PubKey(..)
+  , newPrivKey, newKeyPair, privKeyToPubKey
+  , loadPrivKey
+  , privKeyAlgoName
+  , privKeyParam
+  , exportPrivKeyDER, exportPrivKeyPEM
+  , exportPrivKeyEncryptedDER, exportPrivKeyEncryptedPEM
+  , loadPubKey
+  , pubKeyAlgoName
+  , pubKeyParam
+  , exportPubKeyDER
+  , exportPubKeyPEM
+  , estStrength
+  , fingerPrintPubKey
+  -- * Encrypt & Decrypt
+  , pkEncrypt
+  , pkDecrypt
+  , EMEPadding(..)
+  -- * Sign & verify
+  , EMSA(..), SignFmt(..)
+  , newSigner, updateSigner, finalSigner, sinkToSigner, sign, signChunks
+  , newVerifier, updateVerifier, finalVerifier, sinkToVerifier, verify, verifyChunks
+  -- * Key agreement
+  , KeyAgreement(..)
+  , newKeyAgreement
+  , exportKeyAgreementPublic
+  , keyAgree
+  -- * RSA specific
+  , getRSAParams
+  , newRSAPrivKey
+  , getRSAPubParams
+  , newRSAPubKey
+  -- * DSA specific
+  , getDSAPrivParams
+  , newDSAPrivKey
+  , getDSAPubParams
+  , newDSAPubKey
+  -- * ElGamal specific
+  , getElGamalPrivParams
+  , newElGamalPrivKey
+  , getElGamalPubParams
+  , newElGamalPubKey
+  -- * Diffie-Hellman specific
+  , getDHPrivParams
+  , newDHPrivKey
+  , getDHPubParams
+  , newDHPubKey
+  -- * constants
+  , XMSSType
+  , 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
+  , ECGroup
+  , 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
+  , DLGroup
+  , 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
+  -- * re-exports
+  , HashType(..)
+  , KDFType(..)
+  -- * internal
+  , withPrivKey
+  , withPubKey
+  ) where
+
+import           Data.Word
+import           GHC.Generics
+import           System.IO.Unsafe    (unsafePerformIO)
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.Hash       (HashType(..), hashTypeToCBytes)
+import           Z.Crypto.KDF        (KDFType(..), kdfTypeToCBytes)
+import           Z.Crypto.MPI
+import           Z.Crypto.RNG        (RNG, getRNG, withRNG)
+import qualified Z.Data.Builder      as B
+import           Z.Data.CBytes       (CBytes)
+
+import qualified Z.Data.CBytes       as CB
+import qualified Z.Data.Text         as T
+import qualified Z.Data.Vector       as V
+import           Z.Foreign
+import           Z.IO.BIO
+
+---------------
+-- Key Types --
+---------------
+
+-- | Public Key Cryptography Algorithms.
+data KeyType
+    = Curve25519
+    | -- | RSA key of the given size, namely n bits
+        RSA Word32
+     -- | eXtended Merkle Signature Scheme, see <xmss https://botan.randombit.net/handbook/api_ref/pubkey.html#extended-merkle-signature-scheme-xmss>
+    | XMSS XMSSType
+      -- | Ed25519 high-speed high-security signatures
+    | Ed25519
+      -- | Elliptic-curve cryptography, see 'ECCType'
+    | ECC ECCType ECGroup
+      -- | Asymmetric algorithm based on the discrete logarithm problem, see 'DLType'
+    | DL DLType DLGroup
+      -- | McEliece is a cryptographic scheme based on error correcting codes which is thought to be resistant to quantum computers. See <mceliece https://botan.randombit.net/handbook/api_ref/pubkey.html#mceliece>.
+    | McEliece
+        Word32 -- ^ n
+        Word32 -- ^ t
+
+
+keyTypeToCBytes :: KeyType -> (CBytes, CBytes)
+keyTypeToCBytes keyType' = case keyType' of
+    Curve25519 -> ("Curve25519", "")
+    RSA bits -> ("RSA", CB.buildCBytes $ B.int bits)
+    McEliece n t -> ("McEliece", CB.buildCBytes $ B.int n >> B.char8 ',' >> B.int t)
+    XMSS xmss -> ("XMSS", xmss)
+    Ed25519 -> ("Ed25519", "")
+    ECC ecc grp -> (eccToCBytes ecc, grp)
+    DL dl grp -> (dlToCBytes dl, grp)
+
+-- | Default RSA Key type(3072 bits).
+pattern RSADefault :: KeyType
+pattern RSADefault = RSA 3072
+
+-- | Default McEliece key type.
+pattern McElieceDefault :: KeyType
+pattern McElieceDefault = McEliece 2960 57
+
+-- | A type wrapper.
+type XMSSType = CBytes
+
+pattern XMSSDefault :: KeyType
+pattern XMSSDefault = XMSS XMSS_SHA2_10_512
+
+pattern XMSS_SHA2_10_256 :: XMSSType
+pattern XMSS_SHA2_10_256 = "XMSS-SHA2_10_256"
+
+pattern XMSS_SHA2_16_256 :: XMSSType
+pattern XMSS_SHA2_16_256 = "XMSS-SHA2_16_256"
+
+pattern XMSS_SHA2_20_256 :: XMSSType
+pattern XMSS_SHA2_20_256 = "XMSS-SHA2_20_256"
+
+pattern XMSS_SHA2_10_512 :: XMSSType
+pattern XMSS_SHA2_10_512 = "XMSS-SHA2_10_512"
+
+pattern XMSS_SHA2_16_512 :: XMSSType
+pattern XMSS_SHA2_16_512 = "XMSS-SHA2_16_512"
+
+pattern XMSS_SHA2_20_512 :: XMSSType
+pattern XMSS_SHA2_20_512 = "XMSS-SHA2_20_512"
+
+pattern XMSS_SHAKE_10_256 :: XMSSType
+pattern XMSS_SHAKE_10_256 = "XMSS-SHAKE_10_256"
+
+pattern XMSS_SHAKE_16_256 :: XMSSType
+pattern XMSS_SHAKE_16_256 = "XMSS-SHAKE_16_256"
+
+pattern XMSS_SHAKE_20_256 :: XMSSType
+pattern XMSS_SHAKE_20_256 = "XMSS-SHAKE_20_256"
+
+pattern XMSS_SHAKE_10_512 :: XMSSType
+pattern XMSS_SHAKE_10_512 = "XMSS-SHAKE_10_512"
+
+pattern XMSS_SHAKE_16_512 :: XMSSType
+pattern XMSS_SHAKE_16_512 = "XMSS-SHAKE_16_512"
+
+pattern XMSS_SHAKE_20_512 :: XMSSType
+pattern XMSS_SHAKE_20_512 = "XMSS-SHAKE_20_512"
+
+-- | Algorithms based on elliptic curve.
+data ECCType
+    = ECDSA | ECDH | ECKCDSA | ECGDSA | SM2 | SM2_Sig
+    | SM2_Enc | GOST_34_10 | GOST_34_10_2012_256 | GOST_34_10_2012_512
+
+eccToCBytes :: ECCType -> CBytes
+eccToCBytes = \ case
+    ECDSA -> "ECDSA"
+    ECDH -> "ECDH"
+    ECKCDSA -> "ECKCDSA"
+    ECGDSA -> "ECGDSA"
+    SM2 -> "SM2"
+    SM2_Sig -> "SM2_Sig"
+    SM2_Enc -> "SM2_Enc"
+    GOST_34_10 -> "GOST-34.10"
+    GOST_34_10_2012_256 -> "GOST-34.10-2012-256"
+    GOST_34_10_2012_512 -> "GOST-34.10-2012-512"
+
+-- | An elliptic curve.
+type ECGroup = CBytes
+
+pattern Secp160k1 :: ECGroup
+pattern Secp160k1 = "secp160k1"
+
+pattern Secp160r1 :: ECGroup
+pattern Secp160r1 = "secp160r1"
+
+pattern Secp160r2 :: ECGroup
+pattern Secp160r2 = "secp160r2"
+
+pattern Secp192k1 :: ECGroup
+pattern Secp192k1 = "secp192k1"
+
+pattern Secp192r1 :: ECGroup
+pattern Secp192r1 = "secp192r1"
+
+pattern Secp224k1 :: ECGroup
+pattern Secp224k1 = "secp224k1"
+
+pattern Secp224r1 :: ECGroup
+pattern Secp224r1 = "secp224r1"
+
+pattern Secp256k1 :: ECGroup
+pattern Secp256k1 = "secp256k1"
+
+pattern Secp256r1 :: ECGroup
+pattern Secp256r1 = "secp256r1"
+
+pattern Secp384r1 :: ECGroup
+pattern Secp384r1 = "secp384r1"
+
+pattern Secp521r1 :: ECGroup
+pattern Secp521r1 = "secp521r1"
+
+pattern Brainpool160r1 :: ECGroup
+pattern Brainpool160r1 = "brainpool160r1"
+
+pattern Brainpool192r1 :: ECGroup
+pattern Brainpool192r1 = "brainpool192r1"
+
+pattern Brainpool224r1 :: ECGroup
+pattern Brainpool224r1 = "brainpool224r1"
+
+pattern Brainpool256r1 :: ECGroup
+pattern Brainpool256r1 = "brainpool256r1"
+
+pattern Brainpool320r1 :: ECGroup
+pattern Brainpool320r1 = "brainpool320r1"
+
+pattern Brainpool384r1 :: ECGroup
+pattern Brainpool384r1 = "brainpool384r1"
+
+pattern Brainpool512r1 :: ECGroup
+pattern Brainpool512r1 = "brainpool512r1"
+
+pattern X962_p192v2 :: ECGroup
+pattern X962_p192v2 = "x962_p192v2"
+
+pattern X962_p192v3 :: ECGroup
+pattern X962_p192v3 = "x962_p192v3"
+
+pattern X962_p239v1 :: ECGroup
+pattern X962_p239v1 = "x962_p239v1"
+
+pattern X962_p239v2 :: ECGroup
+pattern X962_p239v2 = "x962_p239v2"
+
+pattern X962_p239v3 :: ECGroup
+pattern X962_p239v3 = "x962_p239v3"
+
+pattern Gost_256A :: ECGroup
+pattern Gost_256A = "gost_256A"
+
+pattern Gost_512A :: ECGroup
+pattern Gost_512A = "gost_512A"
+
+pattern Frp256v1 :: ECGroup
+pattern Frp256v1 = "frp256v1"
+
+pattern Sm2p256v1 :: ECGroup
+pattern Sm2p256v1 = "sm2p256v1"
+
+-- | Discrete Logarithm
+data DLType
+    = DH        -- ^ Diffie-Hellman key exchange
+    | DSA       -- ^ Digital Signature Algorithm
+    | ElGamal
+
+dlToCBytes :: DLType -> CBytes
+dlToCBytes = \ case
+    DH -> "DH"
+    DSA -> "DSA"
+    ElGamal -> "ElGamal"
+
+-- | Discrete Logarithm Group
+type DLGroup = CBytes
+
+pattern FFDHE_IETF_2048 :: DLGroup
+pattern FFDHE_IETF_2048 = "ffdhe/ietf/2048"
+
+pattern FFDHE_IETF_3072 :: DLGroup
+pattern FFDHE_IETF_3072 = "ffdhe/ietf/3072"
+
+pattern FFDHE_IETF_4096 :: DLGroup
+pattern FFDHE_IETF_4096 = "ffdhe/ietf/4096"
+
+pattern FFDHE_IETF_6144 :: DLGroup
+pattern FFDHE_IETF_6144 = "ffdhe/ietf/6144"
+
+pattern FFDHE_IETF_8192 :: DLGroup
+pattern FFDHE_IETF_8192 = "ffdhe/ietf/8192"
+
+pattern MODP_IETF_1024 :: DLGroup
+pattern MODP_IETF_1024 = "modp/ietf/1024"
+
+pattern MODP_IETF_1536 :: DLGroup
+pattern MODP_IETF_1536 = "modp/ietf/1536"
+
+pattern MODP_IETF_2048 :: DLGroup
+pattern MODP_IETF_2048 = "modp/ietf/2048"
+
+pattern MODP_IETF_3072 :: DLGroup
+pattern MODP_IETF_3072 = "modp/ietf/3072"
+
+pattern MODP_IETF_4096 :: DLGroup
+pattern MODP_IETF_4096 = "modp/ietf/4096"
+
+pattern MODP_IETF_6144 :: DLGroup
+pattern MODP_IETF_6144 = "modp/ietf/6144"
+
+pattern MODP_IETF_8192 :: DLGroup
+pattern MODP_IETF_8192 = "modp/ietf/8192"
+
+pattern MODP_SRP_1024 :: DLGroup
+pattern MODP_SRP_1024 = "modp/srp/1024"
+
+pattern MODP_SRP_1536 :: DLGroup
+pattern MODP_SRP_1536 = "modp/srp/1536"
+
+pattern MODP_SRP_2048 :: DLGroup
+pattern MODP_SRP_2048 = "modp/srp/2048"
+
+pattern MODP_SRP_3072 :: DLGroup
+pattern MODP_SRP_3072 = "modp/srp/3072"
+
+pattern MODP_SRP_4096 :: DLGroup
+pattern MODP_SRP_4096 = "modp/srp/4096"
+
+pattern MODP_SRP_6144 :: DLGroup
+pattern MODP_SRP_6144 = "modp/srp/6144"
+
+pattern MODP_SRP_8192 :: DLGroup
+pattern MODP_SRP_8192 = "modp/srp/8192"
+
+pattern DSA_JCE_1024 :: DLGroup
+pattern DSA_JCE_1024 = "dsa/jce/1024"
+
+pattern DSA_BOTAN_2048 :: DLGroup
+pattern DSA_BOTAN_2048 = "dsa/botan/2048"
+
+pattern DSA_BOTAN_3072 :: DLGroup
+pattern DSA_BOTAN_3072 = "dsa/botan/3072"
+
+---------------------------
+-- Private Key Functions --
+---------------------------
+
+-- | An opaque data type for a private-public key pair.
+newtype PrivKey = PrivKey BotanStruct
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Pass 'PrivKey' to FFI.
+withPrivKey :: HasCallStack
+            => PrivKey -> (BotanStructT -> IO r) -> IO r
+withPrivKey (PrivKey key) = withBotanStruct key
+
+-- | Creating a new key pair.
+--
+newKeyPair ::
+    -- | Algorithm name and some algorithm specific arguments.
+    KeyType ->
+    RNG ->
+    IO (PrivKey, PubKey)
+newKeyPair kt rng = do
+    priv <- newPrivKey kt rng
+    let !pub = privKeyToPubKey priv
+    return (priv, pub)
+
+-- | Creating a private key.
+--
+-- Creating a private key requires two things:
+--
+-- * a source of random numbers
+-- * some algorithm specific arguments that define the security level of the resulting key.
+--
+newPrivKey ::
+    -- | Algorithm name and some algorithm specific arguments.
+    KeyType ->
+    RNG ->
+    IO PrivKey
+newPrivKey keyTyp rng =
+    withRNG rng $ \ rng' ->
+    CB.withCBytes algo $ \ algo' ->
+    CB.withCBytes args $ \ args' ->
+        PrivKey <$> newBotanStruct'
+            (\ key -> botan_privkey_create key algo' args' rng')
+            botan_privkey_destroy
+    where
+        (algo, args) = keyTypeToCBytes keyTyp
+
+-- | Load a private key. If the key is encrypted, password will be used to attempt decryption.
+loadPrivKey ::
+    RNG ->
+    V.Bytes ->
+    -- | Password.
+    CBytes ->
+    IO PrivKey
+loadPrivKey rng buf passwd =
+    withRNG rng $ \ rng' ->
+    withPrimVectorUnsafe buf $ \ buf' off len ->
+    CB.withCBytesUnsafe passwd $ \ passwd' ->
+        PrivKey <$> newBotanStruct
+            (\ key -> hs_botan_privkey_load key rng' buf' off len passwd')
+            botan_privkey_destroy
+
+-- | Get the algorithm name of a private key.
+privKeyAlgoName :: PrivKey -> IO V.Bytes
+privKeyAlgoName key =
+    withPrivKey key $ allocBotanBufferUnsafe 16 . botan_privkey_algo_name
+
+-- | Export a private key in DER binary format.
+exportPrivKeyDER :: HasCallStack => PrivKey -> V.Bytes
+{-# INLINE exportPrivKeyDER #-}
+exportPrivKeyDER key = unsafePerformIO $
+    withPrivKey key $ \ key' ->
+    allocBotanBufferUnsafe V.smallChunkSize $ \ buf siz ->
+    botan_privkey_export key' buf siz 0
+
+-- | Export a private key in PEM textual format.
+exportPrivKeyPEM :: HasCallStack => PrivKey -> T.Text
+{-# INLINE exportPrivKeyPEM #-}
+exportPrivKeyPEM key = unsafePerformIO $
+    withPrivKey key $ \ key' ->
+    allocBotanBufferUTF8Unsafe V.smallChunkSize $ \ buf siz ->
+    botan_privkey_export key' buf siz 1
+
+-- | Export a private key with password.
+exportPrivKeyEncryptedDER :: PrivKey -> RNG
+                          -> CBytes        -- ^ password
+                          -> IO V.Bytes
+exportPrivKeyEncryptedDER key rng pwd =
+    withPrivKey key $ \ key' ->
+    withRNG rng $ \ rng' ->
+    CB.withCBytesUnsafe pwd $ \ pwd' ->
+    CB.withCBytesUnsafe "" $ \ pbe' ->   -- currently ignored
+    allocBotanBufferUnsafe V.smallChunkSize $ \ buf siz ->
+    botan_privkey_export_encrypted key' buf siz rng' pwd' pbe' 0
+
+-- | Export a private key with password in PEM textual format.
+exportPrivKeyEncryptedPEM :: PrivKey -> RNG
+                          -> CBytes        -- ^ password
+                          -> IO T.Text
+exportPrivKeyEncryptedPEM key rng pwd =
+    withPrivKey key $ \ key' ->
+    withRNG rng $ \ rng' ->
+    CB.withCBytesUnsafe pwd $ \ pwd' ->
+    CB.withCBytesUnsafe "" $ \ pbe' ->   -- currently ignored
+    allocBotanBufferUTF8Unsafe V.smallChunkSize $ \ buf siz ->
+    botan_privkey_export_encrypted key' buf siz rng' pwd' pbe' 1
+
+-- | Export a public key from a given key pair.
+privKeyToPubKey :: PrivKey -> PubKey
+{-# INLINE privKeyToPubKey #-}
+privKeyToPubKey (PrivKey priv) = unsafePerformIO $ do
+    withBotanStruct priv $ \ priv' ->
+        PubKey <$> newBotanStruct (`botan_privkey_export_pubkey` priv') botan_privkey_destroy
+
+-- | Read an algorithm specific field from the key pair object.
+privKeyParam :: HasCallStack
+             => PrivKey      -- ^ key
+             -> CBytes       -- ^ field name
+             -> MPI
+{-# INLINE privKeyParam #-}
+privKeyParam key name =
+    unsafeNewMPI $ \ mp ->
+    withPrivKey key $ \ key' -> do
+    throwBotanIfMinus_ (CB.withCBytesUnsafe name (botan_privkey_get_field mp key'))
+
+-- | A newtype wrapper.
+newtype PubKey = PubKey BotanStruct
+    deriving (Show, Eq, Ord, Generic)
+    deriving anyclass T.Print
+
+-- | Pass 'PubKey' to FFI.
+withPubKey :: HasCallStack
+            => PubKey -> (BotanStructT -> IO r) -> IO r
+withPubKey (PubKey key) = withBotanStruct key
+
+-- | Load a publickey.
+loadPubKey :: HasCallStack
+           => V.Bytes -> IO PubKey
+{-# INLINE loadPubKey #-}
+loadPubKey buf = do
+    withPrimVectorUnsafe buf $ \ buf' off len ->
+        PubKey <$> newBotanStruct (\ pubKey -> hs_botan_pubkey_load pubKey buf' off len) botan_pubkey_destroy
+
+-- | Export a public key in DER binary format..
+exportPubKeyDER :: HasCallStack => PubKey -> V.Bytes
+{-# INLINE exportPubKeyDER #-}
+exportPubKeyDER pubKey = unsafePerformIO $
+    withPubKey pubKey $ \ pubKey' ->
+    allocBotanBufferUnsafe V.smallChunkSize $ \ buf siz ->
+    botan_pubkey_export pubKey' buf siz 0
+
+-- | Export a public key in PEM textual format.
+exportPubKeyPEM :: HasCallStack => PubKey -> T.Text
+{-# INLINE exportPubKeyPEM #-}
+exportPubKeyPEM pubKey = unsafePerformIO $
+    withPubKey pubKey $ \ pubKey' ->
+    allocBotanBufferUTF8Unsafe V.smallChunkSize $ \ buf siz ->
+    botan_pubkey_export pubKey' buf siz 1
+
+-- | Get the algorithm name of a public key.
+pubKeyAlgoName :: PubKey -> CBytes
+{-# INLINE pubKeyAlgoName #-}
+pubKeyAlgoName pubKey = unsafePerformIO $
+    withPubKey pubKey $ \ pubKey' ->
+        CB.fromBytes <$> allocBotanBufferUnsafe 16
+            (botan_pubkey_algo_name pubKey')
+
+-- | Estimate the strength of a public key.
+estStrength :: PubKey -> Int
+{-# INLINE estStrength #-}
+estStrength pubKey = unsafePerformIO $
+    withPubKey pubKey $ \ pubKey' -> do
+        (a, _) <- allocPrimUnsafe @CSize $ \ est ->
+            throwBotanIfMinus_ (botan_pubkey_estimated_strength pubKey' est)
+        return (fromIntegral a)
+
+-- | Fingerprint a given publickey.
+fingerPrintPubKey :: PubKey -> HashType -> V.Bytes
+{-# INLINE fingerPrintPubKey #-}
+fingerPrintPubKey pubKey ht = unsafePerformIO $
+    withPubKey pubKey $ \ pubKey' ->
+    CB.withCBytesUnsafe (hashTypeToCBytes ht) $ \ hash' ->
+    allocBotanBufferUnsafe V.smallChunkSize $ \ buf siz ->
+    botan_pubkey_fingerprint pubKey' hash' buf siz
+
+-- | Read an algorithm specific field from the public key object.
+pubKeyParam :: HasCallStack
+                    => PubKey       -- ^ key
+                    -> CBytes       -- ^ field name
+                    -> MPI
+pubKeyParam pubKey name =
+    unsafeNewMPI $ \ mp ->
+    withPubKey pubKey $ \ pubKey' -> do
+    throwBotanIfMinus_ (CB.withCBytesUnsafe name (botan_pubkey_get_field mp pubKey'))
+
+----------------------------
+-- RSA specific functions --
+----------------------------
+
+
+-- | Get RSA parameters
+--
+-- * Set p to the first RSA prime.
+-- * Set q to the second RSA prime.
+-- * Set n to the RSA modulus.
+-- * Set d to the RSA private exponent.
+-- * Set e to the RSA public exponent.
+--
+getRSAParams :: PrivKey
+             -> (MPI, MPI, MPI, MPI, MPI)   -- ^ (p, q, n, d, e)
+getRSAParams key = (p, q, n, d, e)
+  where
+    !p = privKeyParam key "p"
+    !q = privKeyParam key "q"
+    !n = privKeyParam key "n"
+    !d = privKeyParam key "d"
+    !e = privKeyParam key "e"
+
+-- | Get RSA Public parameters
+--
+-- * Set n to the RSA modulus.
+-- * Set e to the RSA public exponent.
+--
+getRSAPubParams :: PubKey
+                -> (MPI, MPI)   -- ^ (n, e)
+getRSAPubParams key = (n, e)
+  where
+    !n = pubKeyParam key "n"
+    !e = pubKeyParam key "e"
+
+-- | Initialize a RSA key pair using arguments p, q, and e.
+newRSAPrivKey :: MPI -> MPI -> MPI -> PrivKey
+newRSAPrivKey p q e =
+    unsafeWithMPI p $ \ p' ->
+    withMPI q $ \ q' ->
+    withMPI e $ \ e' ->
+        PrivKey <$> newBotanStruct
+            (\ key -> botan_privkey_load_rsa key p' q' e')
+            botan_privkey_destroy
+
+-- | Initialize a public RSA key using arguments n and e.
+newRSAPubKey :: MPI -> MPI -> PubKey
+newRSAPubKey n e = do
+    unsafeWithMPI n $ \ n' ->
+        withMPI e $ \ e' ->
+            PubKey <$> newBotanStruct (\ pubKey -> botan_pubkey_load_rsa pubKey n' e') botan_pubkey_destroy
+
+----------------------------
+-- DSA specific functions --
+----------------------------
+
+-- | Get DSA parameters
+--
+-- * Set p, q, g to group parameters
+-- * Set x to the private key
+--
+getDSAPrivParams :: PrivKey
+             -> (MPI, MPI, MPI, MPI)   -- ^ (p, q, g, x)
+getDSAPrivParams key = (p, q, g, x)
+  where
+    !p = privKeyParam key "p"
+    !q = privKeyParam key "q"
+    !g = privKeyParam key "g"
+    !x = privKeyParam key "x"
+
+-- | Initialize a DSA key pair using arguments p, q, g and x.
+newDSAPrivKey :: MPI -> MPI -> MPI -> MPI -> PrivKey
+newDSAPrivKey p q g x =
+    unsafeWithMPI p $ \ p' ->
+    withMPI q $ \ q' ->
+    withMPI g $ \ g' ->
+    withMPI x $ \ x' ->
+        PrivKey <$> newBotanStruct
+            (\ key -> botan_privkey_load_dsa key p' q' g' x')
+            botan_privkey_destroy
+
+-- | Get DSA parameters
+--
+-- * Set p, q, g to group parameters
+-- * Set y to the public key
+--
+getDSAPubParams :: PubKey
+                -> (MPI, MPI, MPI, MPI)   -- ^ (p, q, g, y)
+getDSAPubParams key = (p, q, g, y)
+  where
+    !p = pubKeyParam key "p"
+    !q = pubKeyParam key "q"
+    !g = pubKeyParam key "g"
+    !y = pubKeyParam key "y"
+
+-- | Initialize a DSA public key using arguments p, q, g and y.
+newDSAPubKey :: MPI -> MPI -> MPI -> MPI -> PubKey
+newDSAPubKey p q g y =
+    unsafeWithMPI p $ \ p' ->
+    withMPI q $ \ q' ->
+    withMPI g $ \ g' ->
+    withMPI y $ \ y' ->
+        PubKey <$> newBotanStruct
+            (\ key -> botan_pubkey_load_dsa key p' q' g' y')
+            botan_pubkey_destroy
+
+--------------------------------
+-- ElGamal specific functions --
+--------------------------------
+
+-- | Get ElGamal parameters
+--
+-- * Set p, g to group parameters
+-- * Set x to the private key
+--
+getElGamalPrivParams :: PrivKey
+                 -> (MPI, MPI, MPI)   -- ^ (p, g, x)
+getElGamalPrivParams key = (p, g, x)
+  where
+    !p = privKeyParam key "p"
+    !g = privKeyParam key "g"
+    !x = privKeyParam key "x"
+
+newElGamalPrivKey :: MPI -> MPI -> MPI -> PrivKey
+newElGamalPrivKey p g x =
+    unsafeWithMPI p $ \ p' ->
+    withMPI g $ \ g' ->
+    withMPI x $ \ x' ->
+        PrivKey <$> newBotanStruct
+            (\ key -> botan_privkey_load_elgamal key p' g' x')
+            botan_privkey_destroy
+
+-- | Get ElGamal parameters
+--
+-- * Set p, g to group parameters
+-- * Set y to the public key
+--
+getElGamalPubParams :: PubKey
+                    -> (MPI, MPI, MPI)   -- ^ (p, g, y)
+getElGamalPubParams key = (p, g, y)
+  where
+    !p = pubKeyParam key "p"
+    !g = pubKeyParam key "g"
+    !y = pubKeyParam key "y"
+
+newElGamalPubKey :: MPI -> MPI -> MPI -> PubKey
+newElGamalPubKey p g y =
+    unsafeWithMPI p $ \ p' ->
+    withMPI g $ \ g' ->
+    withMPI y $ \ y' ->
+        PubKey <$> newBotanStruct
+                (\ key -> botan_pubkey_load_elgamal key p' g' y')
+                botan_pubkey_destroy
+
+---------------------------------------
+-- Diffie-Hellman specific functions --
+---------------------------------------
+
+-- | Get Diffie-Hellman parameters
+--
+-- * Set p, g to group parameters
+-- * Set x to the private key
+--
+getDHPrivParams :: PrivKey
+                 -> (MPI, MPI, MPI)   -- ^ (p, g, x)
+getDHPrivParams key = (p, g, x)
+  where
+    !p = privKeyParam key "p"
+    !g = privKeyParam key "g"
+    !x = privKeyParam key "x"
+
+newDHPrivKey :: MPI -> MPI -> MPI -> PrivKey
+newDHPrivKey p g x = do
+    unsafeWithMPI p $ \ p' -> withMPI g $ \ g' -> withMPI x $ \ x' ->
+        PrivKey <$> newBotanStruct (\ key -> botan_privkey_load_dh key p' g' x') botan_privkey_destroy
+
+-- | Get Diffie-Hellman parameters
+--
+-- * Set p, g to group parameters
+-- * Set y to the public key
+--
+getDHPubParams :: PubKey
+               -> (MPI, MPI, MPI)   -- ^ (p, g, y)
+getDHPubParams key = (p, g, y)
+  where
+    !p = pubKeyParam key "p"
+    !g = pubKeyParam key "g"
+    !y = pubKeyParam key "y"
+
+newDHPubKey :: MPI -> MPI -> MPI -> PubKey
+newDHPubKey p g y = do
+    unsafeWithMPI p $ \ p' ->
+        withMPI g $ \ g' ->
+            withMPI y $ \ y' ->
+                PubKey <$> newBotanStruct (\ pubKey -> botan_pubkey_load_dh pubKey p' g' y') botan_pubkey_destroy
+
+----------------------------------------
+-- Public Key Encryption / Decryption --
+----------------------------------------
+
+-- | Sets of allowed padding schemes for public key types.
+--
+-- The recommended values for eme is 'EME1_SHA1' or 'EME1_SHA256'.
+-- If you need compatibility with protocols using the PKCS #1 v1.5 standard, you can also use 'EME_PKCS1_v1'5'.
+data EMEPadding
+    = EME_RAW
+    | EME_PKCS1_v1'5
+    | EME_OAEP HashType CBytes              -- ^ hash, label
+    | EME_OAEP' HashType HashType CBytes    -- ^ hash, mask gen hash, labal
+
+emeToCBytes :: EMEPadding -> CBytes
+emeToCBytes EME_RAW              = "Raw"
+emeToCBytes EME_PKCS1_v1'5       = "PKCS1v15"
+emeToCBytes (EME_OAEP  ht label)
+    | CB.null label = CB.concat ["OAEP(", hashTypeToCBytes ht, ",MGF1)" ]
+    | otherwise = CB.concat ["OAEP(", hashTypeToCBytes ht, ",MGF1,", label, ")"]
+
+emeToCBytes (EME_OAEP' ht ht' label)
+    | CB.null label =
+        CB.concat ["OAEP(", hashTypeToCBytes ht, ",MGF1(", hashTypeToCBytes ht', "))"]
+    | otherwise =
+        CB.concat ["OAEP(", hashTypeToCBytes ht, ",MGF1(", hashTypeToCBytes ht', "),", label, ")"]
+
+-- |  Encrypt a message, returning the ciphertext.
+--
+-- Though botan support DLIES and ECIES but only EME are exported via FFI, please use an algorithm that directly
+-- support encryption such as RSA and ElGamal.
+--
+pkEncrypt :: PubKey -> EMEPadding -> RNG
+          -> V.Bytes        -- ^ plaintext
+          -> IO V.Bytes     -- ^ ciphertext
+pkEncrypt pubKey padding rng ptext = do
+    let paddingStr = emeToCBytes padding
+    encryptor <-
+        withPubKey pubKey $ \ pubKey' ->
+        CB.withCBytesUnsafe paddingStr $ \ padding' ->
+            newBotanStruct
+                (\ op -> botan_pk_op_encrypt_create op pubKey' padding' 0) -- Flags should be 0 in this version.
+                botan_pk_op_encrypt_destroy
+
+    withBotanStruct encryptor $ \ op -> do
+        let ptextLen = fromIntegral (V.length ptext)
+        (len, _) <- allocPrimUnsafe @CSize $ \ ret ->
+            throwBotanIfMinus_ (botan_pk_op_encrypt_output_length op ptextLen ret)
+        withRNG rng $ \ rng' ->
+            withPrimVectorUnsafe ptext $ \ ptext' ptextOff' ptextLen' ->
+            allocBotanBufferUnsafe (fromIntegral len) $ \ out len' ->
+                hs_botan_pk_op_encrypt op rng' out len' ptext' ptextOff' ptextLen'
+
+-- |  Decrypt a message, returning the ciphertext.
+--
+-- Though botan support DLIES and ECIES but only EME are exported via FFI, please use an algorithm that directly
+-- support decryption such as RSA and ElGamal.
+--
+pkDecrypt :: PrivKey -> EMEPadding
+          -> V.Bytes        -- ^ ciphertext
+          -> V.Bytes        -- ^ plaintext
+pkDecrypt key padding ctext = unsafePerformIO $ do
+    let paddingStr = emeToCBytes padding
+    decryptor <-
+        withPrivKey key $ \ key' ->
+        CB.withCBytesUnsafe paddingStr $ \ padding' ->
+            newBotanStruct
+                (\ op -> botan_pk_op_decrypt_create op key' padding' 0) -- Flags should be 0 in this version.
+                botan_pk_op_decrypt_destroy
+
+    withBotanStruct decryptor $ \ op -> do
+        let ctextLen = fromIntegral (V.length ctext)
+        (len, _) <- allocPrimUnsafe @CSize $ \ ret ->
+            throwBotanIfMinus_ (botan_pk_op_decrypt_output_length op ctextLen ret)
+        withPrimVectorUnsafe ctext $ \ ctext' ctextOff' ctextLen' ->
+            allocBotanBufferUnsafe (fromIntegral len) $ \ out len' ->
+                hs_botan_pk_op_decrypt op out len' ctext' ctextOff' ctextLen'
+
+--------------------------
+-- Signature Generation --
+--------------------------
+
+-- |  Currently available values for 'EMSA', examples are “EMSA1(SHA-1)” and “EMSA4(SHA-256)”.
+--
+-- Currently available values for 'EMSA' include EMSA1, EMSA2, EMSA3, EMSA4, and Raw. All of them, except Raw, take a parameter naming a message digest function to hash the message with. The Raw encoding signs the input directly; if the message is too big, the signing operation will fail. Raw is not useful except in very specialized applications.
+-- For RSA, use EMSA4 (also called PSS) unless you need compatibility with software that uses the older PKCS #1 v1.5 standard, in which case use EMSA3 (also called “EMSA-PKCS1-v1_5”). For DSA, ECDSA, ECKCDSA, ECGDSA and GOST 34.10-2001 you should use EMSA1.
+--
+data EMSA
+    = EMSA1 HashType
+    | EMSA2 HashType
+    | EMSA3_RAW (Maybe HashType)
+    | EMSA3 HashType
+    | EMSA4_Raw HashType (Maybe Int)            -- ^ hash, salt size
+    | EMSA4 HashType (Maybe Int)                -- ^ hash, salt size
+    | ISO_9796_DS2 HashType Bool (Maybe Int)    -- ^ hash, implicit, salt size
+    | ISO_9796_DS3 HashType Bool                -- ^ hash, implicit
+    | EMSA_Raw
+
+emsaToCBytes :: EMSA -> CBytes
+emsaToCBytes (EMSA1 ht) = CB.concat ["EMSA1(", hashTypeToCBytes ht, ")"]
+emsaToCBytes (EMSA3_RAW (Just ht)) =
+    CB.concat ["EMSA3(Raw,", hashTypeToCBytes ht, ")"]
+emsaToCBytes (EMSA3_RAW _) = "EMSA3(Raw)"
+emsaToCBytes (EMSA3 ht) = CB.concat ["EMSA3(", hashTypeToCBytes ht, ")"]
+emsaToCBytes (EMSA4_Raw ht (Just siz)) =
+    CB.concat ["EMSA4_Raw(", hashTypeToCBytes ht, ",MGF1,", CB.fromText (T.toText siz) , ")"]
+emsaToCBytes (EMSA4_Raw ht _) =
+    CB.concat ["EMSA4_Raw(", hashTypeToCBytes ht, ")"]
+emsaToCBytes (EMSA4 ht (Just siz)) =
+    CB.concat ["EMSA4(", hashTypeToCBytes ht, ",MGF1,", CB.fromText (T.toText siz) , ")"]
+emsaToCBytes (EMSA4 ht _) =
+    CB.concat ["EMSA4(", hashTypeToCBytes ht, ")"]
+emsaToCBytes (ISO_9796_DS2 ht imp (Just siz)) =
+    CB.concat [ "ISO_9796_DS2(", hashTypeToCBytes ht
+              , if imp then ",imp," else ",exp,"
+              , CB.fromText (T.toText siz) , ")"]
+emsaToCBytes (ISO_9796_DS2 ht imp _) =
+    CB.concat [ "ISO_9796_DS2(", hashTypeToCBytes ht
+              , if imp then ",imp)" else ",exp)"
+              ]
+emsaToCBytes (ISO_9796_DS3 ht imp) =
+    CB.concat [ "ISO_9796_DS3(", hashTypeToCBytes ht
+              , if imp then ",imp)" else ",exp)"
+              ]
+emsaToCBytes (EMSA2 ht) = CB.concat ["EMSA2(", hashTypeToCBytes ht, ")"]
+emsaToCBytes EMSA_Raw = "Raw"
+
+-- The format defaults to IEEE_1363 which is the only available format for RSA. For DSA, ECDSA, ECGDSA and ECKCDSA you can also use DER_SEQUENCE, which will format the signature as an ASN.1 SEQUENCE value.
+data SignFmt = DER_SEQUENCE | IEEE_1363
+    deriving (Eq, Ord, Show, Generic)
+    deriving anyclass T.Print
+
+signFmtToFlag :: SignFmt -> Word32
+signFmtToFlag DER_SEQUENCE = 1
+signFmtToFlag IEEE_1363    = 0
+
+data Signer = Signer
+    { signerStruct :: {-# UNPACK #-} !BotanStruct
+    , signerName   :: {-# UNPACK #-} !CBytes
+    , signerFmt    :: !SignFmt
+    , signerSiz    :: {-# UNPACK #-} !Int           -- ^ output length
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+newSigner :: PrivKey -> EMSA -> SignFmt -> IO Signer
+newSigner key emsa fmt = do
+    let name = emsaToCBytes emsa
+    withPrivKey key $ \ key' ->
+        CB.withCBytesUnsafe name $ \ arg -> do
+            op <- newBotanStruct
+                (\ ret -> botan_pk_op_sign_create ret key' arg (signFmtToFlag fmt))
+                botan_pk_op_sign_destroy
+            siz <- withBotanStruct op $ \ op' ->
+                fst <$> allocPrimUnsafe @CSize (\ siz' ->
+                    throwBotanIfMinus_ $ botan_pk_op_sign_output_length op' siz')
+            return (Signer op name fmt (fromIntegral siz))
+
+updateSigner :: Signer -> V.Bytes -> IO ()
+updateSigner (Signer op _ _ _) msg =
+    withBotanStruct op $ \ op' ->
+    withPrimVectorUnsafe msg $ \ m moff mlen ->
+        throwBotanIfMinus_ (hs_botan_pk_op_sign_update op' m moff mlen)
+
+-- | Produce a signature over all of the bytes passed to 'Signer'.
+-- Afterwards, the sign operator is reset and may be used to sign a new message.
+finalSigner :: Signer -> RNG -> IO V.Bytes
+finalSigner (Signer op _ _ siz) rng =
+    withBotanStruct op $ \ op' ->
+    withRNG rng $ \ rng' ->
+    allocBotanBufferUnsafe siz $ botan_pk_op_sign_finish op' rng'
+
+-- | Trun 'Signer' to a 'V.Bytes' sink, update 'Signer' by write bytes to the sink.
+--
+sinkToSigner :: HasCallStack => Signer -> Sink V.Bytes
+{-# INLINABLE sinkToSigner #-}
+sinkToSigner h = \ k mbs -> case mbs of
+    Just bs -> updateSigner h bs
+    _       -> k EOF
+
+-- | Directly sign a message, with system RNG.
+sign :: HasCallStack
+     => PrivKey -> EMSA -> SignFmt
+     -> V.Bytes         -- ^ input
+     -> IO V.Bytes      -- ^ signature
+{-# INLINABLE sign #-}
+sign key emsa fmt inp = do
+    m <- newSigner key emsa fmt
+    updateSigner m inp
+    finalSigner m =<< getRNG
+
+-- | Directly compute a chunked message's mac with system RNG.
+signChunks :: HasCallStack
+           => PrivKey -> EMSA -> SignFmt
+           -> [V.Bytes]
+           -> IO V.Bytes
+{-# INLINABLE signChunks #-}
+signChunks key emsa fmt inps = do
+    m <- newSigner key emsa fmt
+    mapM_ (updateSigner m) inps
+    finalSigner m =<< getRNG
+
+----------------------------
+-- Signature Verification --
+----------------------------
+
+data Verifier = Verifier
+    { verifierStruct :: {-# UNPACK #-} !BotanStruct
+    , verifierName   :: {-# UNPACK #-} !CBytes
+    , verifierFmt    :: !SignFmt
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+newVerifier :: PubKey -> EMSA -> SignFmt -> IO Verifier
+newVerifier pubKey emsa fmt = do
+    let name = emsaToCBytes emsa
+    withPubKey pubKey $ \ pubKey' ->
+        CB.withCBytesUnsafe name $ \ arg -> do
+            op <- newBotanStruct
+                (\ ret -> botan_pk_op_verify_create ret pubKey' arg (signFmtToFlag fmt))
+                botan_pk_op_verify_destroy
+            return (Verifier op name fmt)
+
+updateVerifier :: Verifier -> V.Bytes -> IO ()
+updateVerifier (Verifier op _ _) msg = do
+    withBotanStruct op $ \ op' ->
+        withPrimVectorUnsafe msg $ \ msg' off len ->
+            throwBotanIfMinus_ $ hs_botan_pk_op_verify_update op' msg' off len
+
+finalVerifier :: Verifier
+              -> V.Bytes
+              -> IO Bool
+finalVerifier (Verifier op _ _) msg =
+    withBotanStruct op $ \ op' ->
+    withPrimVectorUnsafe msg $ \ msg' off len -> do
+        r <- throwBotanIfMinus $ hs_botan_pk_op_verify_finish op' msg' off len
+        print r
+        return $ r == BOTAN_FFI_SUCCESS
+--    BOTAN_FFI_SUCCESS = 0,
+--    BOTAN_FFI_INVALID_VERIFIER = 1
+
+-- | Trun 'Verifier' to a 'V.Bytes' sink, update 'Verifier' by write bytes to the sink.
+--
+sinkToVerifier :: HasCallStack => Verifier -> Sink V.Bytes
+{-# INLINABLE sinkToVerifier #-}
+sinkToVerifier h = \ k mbs -> case mbs of
+    Just bs -> updateVerifier h bs
+    _       -> k EOF
+
+-- | Directly sign a message.
+verify :: HasCallStack
+       => PubKey -> EMSA -> SignFmt
+       -> V.Bytes  -- ^ input
+       -> V.Bytes  -- ^ signature
+       -> Bool
+{-# INLINABLE verify #-}
+verify key emsa fmt inp sig = unsafePerformIO $ do
+    m <- newVerifier key emsa fmt
+    updateVerifier m inp
+    finalVerifier m sig
+
+-- | Directly compute a chunked message's mac.
+verifyChunks :: HasCallStack
+           => PubKey -> EMSA -> SignFmt
+           -> [V.Bytes]
+           -> V.Bytes           -- ^ signature
+           -> Bool
+{-# INLINABLE verifyChunks #-}
+verifyChunks key emsa fmt inps sig = unsafePerformIO $ do
+    m <- newVerifier key emsa fmt
+    mapM_ (updateVerifier m) inps
+    finalVerifier m sig
+
+-------------------
+-- Key Agreement --
+-------------------
+
+-- | Key agreement object.
+data KeyAgreement = KeyAgreement
+    { keyAgreementStruct :: {-# UNPACK #-} !BotanStruct
+    , keyAgreementSize   :: {-# UNPACK #-} !Int
+    }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Create a new key agreement operation with a given key pair and KDF algorithm.
+--
+-- Use a key type that support key agreement, such as 'DH' or 'ECDH',
+-- Botan implements the following key agreement methods:
+-- * ECDH over GF(p) Weierstrass curves
+-- * ECDH over x25519
+-- * DH over prime fields
+-- * McEliece
+-- * NewHope
+--
+newKeyAgreement :: PrivKey -> KDFType -> IO KeyAgreement
+newKeyAgreement key kdf =
+    withPrivKey key $ \ key' ->
+    CB.withCBytesUnsafe (kdfTypeToCBytes kdf) $ \ kdf' -> do
+        op <- newBotanStruct
+            (\ op -> botan_pk_op_key_agreement_create op key' kdf' 0) -- Flags should be 0 in this version.
+            botan_pk_op_key_agreement_destroy
+        siz <- withBotanStruct op $ \ op' ->
+            fst <$> allocPrimUnsafe @CSize (\ siz' ->
+                throwBotanIfMinus_ $ botan_pk_op_key_agreement_size op' siz')
+        return (KeyAgreement op (fromIntegral siz))
+
+exportKeyAgreementPublic :: PrivKey -> IO V.Bytes
+exportKeyAgreementPublic key =
+    withPrivKey key $ \ key' ->
+    allocBotanBufferUnsafe 128 $ botan_pk_op_key_agreement_export_public key'
+
+-- | How key agreement works is that you trade public values with some other party, and then each of you runs a computation with the other’s value and your key (this should return the same result to both parties).
+keyAgree ::
+    KeyAgreement ->
+    -- | other key
+    V.Bytes ->
+    -- | salt
+    V.Bytes ->
+    IO V.Bytes
+keyAgree (KeyAgreement op siz) others salt =
+    withBotanStruct op $ \ op' ->
+    withPrimVectorUnsafe others $ \ others' others_off others_len ->
+    withPrimVectorUnsafe salt $ \ salt' salt_off salt_len ->
+    allocBotanBufferUnsafe siz $ \ ret len ->
+        hs_botan_pk_op_key_agreement op' ret len others'
+            others_off others_len salt' salt_off salt_len
diff --git a/Z/Crypto/PwdHash.hs b/Z/Crypto/PwdHash.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/PwdHash.hs
@@ -0,0 +1,71 @@
+{-|
+Module      : Z.Crypto.PwdHash
+Description : Password Hashing
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Storing passwords for user authentication purposes in plaintext is the simplest but least secure method; when an attacker compromises the database in which the passwords are stored, they immediately gain access to all of them. Often passwords are reused among multiple services or machines, meaning once a password to a single service is known an attacker has a substantial head start on attacking other machines.
+
+The general approach is to store, instead of the password, the output of a one way function of the password. Upon receiving an authentication request, the authenticating party can recompute the one way function and compare the value just computed with the one that was stored. If they match, then the authentication request succeeds. But when an attacker gains access to the database, they only have the output of the one way function, not the original password.
+
+Common hash functions such as SHA-256 are one way, but used alone they have problems for this purpose. What an attacker can do, upon gaining access to such a stored password database, is hash common dictionary words and other possible passwords, storing them in a list. Then he can search through his list; if a stored hash and an entry in his list match, then he has found the password. Even worse, this can happen offline: an attacker can begin hashing common passwords days, months, or years before ever gaining access to the database. In addition, if two users choose the same password, the one way function output will be the same for both of them, which will be visible upon inspection of the database.
+
+There are two solutions to these problems: salting and iteration. Salting refers to including, along with the password, a randomly chosen value which perturbs the one way function. Salting can reduce the effectiveness of offline dictionary generation, because for each potential password, an attacker would have to compute the one way function output for all possible salts. It also prevents the same password from producing the same output, as long as the salts do not collide. Choosing n-bit salts randomly, salt collisions become likely only after about 2:sup:(n/2) salts have been generated. Choosing a large salt (say 80 to 128 bits) ensures this is very unlikely. Note that in password hashing salt collisions are unfortunate, but not fatal - it simply allows the attacker to attack those two passwords in parallel easier than they would otherwise be able to.
+
+The other approach, iteration, refers to the general technique of forcing multiple one way function evaluations when computing the output, to slow down the operation. For instance if hashing a single password requires running SHA-256 100,000 times instead of just once, that will slow down user authentication by a factor of 100,000, but user authentication happens quite rarely, and usually there are more expensive operations that need to occur anyway (network and database I/O, etc). On the other hand, an attacker who is attempting to break a database full of stolen password hashes will be seriously inconvenienced by a factor of 100,000 slowdown; they will be able to only test at a rate of .0001% of what they would without iterations (or, equivalently, will require 100,000 times as many zombie botnet hosts).
+
+Memory usage while checking a password is also a consideration; if the computation requires using a certain minimum amount of memory, then an attacker can become memory-bound, which may in particular make customized cracking hardware more expensive. Some password hashing designs, such as scrypt, explicitly attempt to provide this. The bcrypt approach requires over 4 KiB of RAM (for the Blowfish key schedule) and may also make some hardware attacks more expensive.
+-}
+
+module Z.Crypto.PwdHash where
+
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.RNG       (RNG, withRNG)
+import qualified Z.Data.Vector.Base as V
+import           Z.Foreign
+
+-- | Create a password hash using Bcrypt.
+--
+-- Bcrypt is a password hashing scheme originally designed for use in OpenBSD, but numerous other implementations exist. It has the advantage that it requires a small amount (4K) of fast RAM to compute, which can make hardware password cracking somewhat more expensive.
+--
+-- Bcrypt provides outputs that look like this:
+--
+-- >>> "$2a$12$7KIYdyv8Bp32WAvc.7YvI.wvRlyVn0HP/EhPmmOyMQA4YKxINO0p2"
+--
+-- Higher work factors increase the amount of time the algorithm runs, increasing the cost of cracking attempts. The increase is exponential, so a work factor of 12 takes roughly twice as long as work factor 11. The default work factor was set to 10 up until the 2.8.0 release.
+--
+-- It is recommended to set the work factor as high as your system can tolerate (from a performance and latency perspective) since higher work factors greatly improve the security against GPU-based attacks. For example, for protecting high value administrator passwords, consider using work factor 15 or 16; at these work factors each bcrypt computation takes several seconds. Since admin logins will be relatively uncommon, it might be acceptable for each login attempt to take some time. As of 2018, a good password cracking rig (with 8 NVIDIA 1080 cards) can attempt about 1 billion bcrypt computations per month for work factor 13. For work factor 12, it can do twice as many. For work factor 15, it can do only one quarter as many attempts.
+--
+-- The bcrypt work factor must be at least 4 (though at this work factor bcrypt is not very secure). The bcrypt format allows up to 31, but Botan currently rejects all work factors greater than 18 since even that work factor requires roughly 15 seconds of computation on a fast machine.
+--
+genBcrypt :: V.Bytes    -- ^ password.
+          -> RNG
+          -> Int        -- ^ work factors (4 <= n <= 18).
+          -> IO V.Bytes
+genBcrypt pwd rng n = do
+    withPrimVectorUnsafe pwd $ \ pwd_p pwd_off pwd_len ->
+        withRNG rng $ \ rng_p -> do
+            (pa, r) <- allocPrimArrayUnsafe 64 $ \ out -> do
+                throwBotanIfMinus $
+                    hs_botan_bcrypt_generate out
+                        pwd_p pwd_off pwd_len rng_p n 0
+            let !r' = r - 1
+            mpa <- unsafeThawPrimArray pa
+            shrinkMutablePrimArray mpa r'
+            pa' <- unsafeFreezePrimArray mpa
+            return (V.PrimVector pa' 0 r')
+
+-- | Takes a password and a bcrypt output and returns true if the password is the same as the one that was used to generate the bcrypt hash.
+--
+validBcrypt :: V.Bytes -- ^ password.
+            -> V.Bytes -- ^ hash generated by 'genBcrypt'.
+            -> IO Bool
+validBcrypt pwd hash = do
+    withPrimVectorUnsafe pwd $ \ pwd_p pwd_off pwd_l ->
+        withPrimVectorUnsafe hash $ \ hash_p hash_off hash_l -> do
+            ret <- hs_botan_bcrypt_is_valid  pwd_p pwd_off pwd_l hash_p hash_off hash_l
+            return $! ret == BOTAN_FFI_SUCCESS
diff --git a/Z/Crypto/RNG.hs b/Z/Crypto/RNG.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/RNG.hs
@@ -0,0 +1,109 @@
+{-|
+Module      : Z.Crypto.RNG
+Description : Random Number Generators
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Several different RNG types are implemented. Some access hardware RNGs, which are only available on certain platforms. Others are mostly useful in specific situations.
+
+-}
+
+module Z.Crypto.RNG
+  ( -- * RNG
+    RNGType(..), RNG
+  , newRNG, getRNG, getRandom
+  , reseedRNG, reseedRNGFromRNG, addEntropyRNG
+  -- * Internal
+  , withRNG
+  ) where
+
+import           Control.Monad
+import           Data.IORef
+import           GHC.Conc
+import           GHC.Generics
+import           System.IO.Unsafe
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import qualified Z.Data.Array      as A
+import           Z.Data.CBytes
+import           Z.Data.JSON       (JSON)
+import qualified Z.Data.Text       as T
+import qualified Z.Data.Vector     as V
+import           Z.Foreign
+
+-- | RNG types.
+data RNGType = SystemRNG | AutoSeededRNG | ProcessorRNG
+    deriving (Show, Eq, Ord, Generic)
+    deriving anyclass (T.Print, JSON)
+
+-- | Opaque botan RNG type.
+newtype RNG = RNG BotanStruct
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Initialize a random number generator object from the given 'RNGType'
+newRNG :: RNGType -> IO RNG
+newRNG typ = RNG <$> newBotanStruct
+    (\ bts -> withCBytesUnsafe (rngTypeCBytes typ) (botan_rng_init bts))
+    botan_rng_destroy
+  where
+    rngTypeCBytes SystemRNG     = "system"
+    rngTypeCBytes AutoSeededRNG = "user"
+    rngTypeCBytes ProcessorRNG  = "hwrng"
+
+-- | Use RNG as a `botan_rng_t` object.
+withRNG :: RNG -> (BotanStructT -> IO a) -> IO a
+withRNG (RNG rng) f = withBotanStruct rng f
+
+-- | Get an autoseeded RNG from a global RNG pool divide by haskell capability.
+--
+-- Botan internal use a lock to protect user-space RNG, which may cause contention if shared.
+-- This function will fetch an autoseeded RNG from a global RNG pool, which is recommended under
+-- concurrent settings.
+getRNG :: IO RNG
+getRNG = do
+    (cap, _) <- threadCapability =<< myThreadId
+    rngArray <- readIORef rngArrayRef
+    A.indexArrM rngArray (cap `rem` A.sizeofArr rngArray)
+  where
+    rngArrayRef :: IORef (A.SmallArray RNG)
+    {-# NOINLINE rngArrayRef #-}
+    rngArrayRef = unsafePerformIO $ do
+        numCaps <- getNumCapabilities
+        rngArray <- A.newArr numCaps
+        forM_ [0..numCaps-1] $ \ i -> do
+            A.writeArr rngArray i =<< newRNG AutoSeededRNG
+        irngArray <- A.unsafeFreezeArr rngArray
+        newIORef irngArray
+
+-- | Get random bytes from a random number generator.
+getRandom :: RNG -> Int -> IO V.Bytes
+getRandom r siz =  withRNG r $ \ rng -> do
+    (b, _) <- allocPrimVectorUnsafe siz $ \ buf ->
+        throwBotanIfMinus_ (botan_rng_get rng buf (fromIntegral siz))
+    return b
+
+-- | Reseeds the random number generator with bits number of bits from the 'SystemRNG'.
+reseedRNG :: RNG -> Int -> IO ()
+reseedRNG r siz = withRNG r $ \ rng -> do
+    throwBotanIfMinus_ (botan_rng_reseed rng (fromIntegral siz))
+
+-- | Reseeds the random number generator with bits number of bits from the given source RNG.
+reseedRNGFromRNG :: RNG -> RNG -> Int -> IO ()
+reseedRNGFromRNG r1 r2 siz =
+    withRNG r1 $ \ rng1 -> do
+        withRNG r2 $ \ rng2 -> do
+            throwBotanIfMinus_ (botan_rng_reseed_from_rng rng1 rng2 (fromIntegral siz))
+
+-- | Adds the provided seed material to the internal RNG state.
+--
+-- This call may be ignored by certain RNG instances (such as 'ProcessorRNG' or, on some systems, the 'SystemRNG').
+addEntropyRNG :: RNG -> V.Bytes -> IO ()
+addEntropyRNG r seed =
+    withRNG r $ \ rng -> do
+        withPrimVectorUnsafe seed $ \ pseed offseed lseed -> do
+            throwBotanIfMinus_ (hs_botan_rng_add_entropy rng pseed
+                    (fromIntegral offseed) (fromIntegral lseed))
diff --git a/Z/Crypto/X509.hs b/Z/Crypto/X509.hs
new file mode 100644
--- /dev/null
+++ b/Z/Crypto/X509.hs
@@ -0,0 +1,432 @@
+{-|
+Module      : Z.Crypto.X509
+Description : X.509 Certificates and CRLs
+Copyright   : Dong Han, 2021
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+X.509 Certificates read, write and verification.
+
+-}
+module Z.Crypto.X509 (
+  -- * X509 Certificates
+    Cert, withCert
+  , loadCert, loadCertFile, dupCert
+  -- * read X509 field
+  , certStart, certExpire
+  , certStart', certExpire'
+  , certStartText, certExpireText
+  , certFingerPrint
+  , certSerial
+  , certIDAuthority
+  , certIDSubject
+  , certPubBits
+  , certPubKey
+  , certDNIssuer
+  , certDNSubject
+  , certToText
+  , certUsage
+  -- * verify certificate
+  , verifyCert
+  , verifyCertCRL
+  , verifyCertCRL'
+  -- * CRL
+  , CRL
+  , withCRL, loadCRL, loadCRLFile, isRevokedX509
+  -- * CertStore
+  , CertStore
+  , withCertStore
+  , loadCertStoreFile
+  -- * constants
+  , KeyUsageConstraint
+  , pattern NO_CONSTRAINTS
+  , pattern DIGITAL_SIGNATURE
+  , pattern NON_REPUDIATION
+  , pattern KEY_ENCIPHERMENT
+  , pattern DATA_ENCIPHERMENT
+  , pattern KEY_AGREEMENT
+  , pattern KEY_CERT_SIGN
+  , pattern CRL_SIGN
+  , pattern ENCIPHER_ONLY
+  , pattern DECIPHER_ONLY
+  ) where
+
+import           Data.Time.Clock.System (SystemTime (..))
+import           Data.Word
+import           GHC.Generics
+import           Z.Botan.Exception
+import           Z.Botan.FFI
+import           Z.Crypto.Hash          (HashType, hashTypeToCBytes)
+import           Z.Crypto.PubKey        (PubKey (..))
+import qualified Z.Data.Text            as T
+import qualified Z.Data.Text.Base       as T
+import qualified Z.Data.Vector          as V
+import qualified Z.Data.Vector.Extra    as V
+import           Z.Data.CBytes          (CBytes)
+import qualified Z.Data.CBytes          as CB
+import           Z.Foreign
+import           Z.Foreign.CPtr
+import           System.IO.Unsafe
+
+------------------------
+-- X.509 Certificates --
+------------------------
+
+-- | An opaque newtype wrapper for an X.509 certificate.
+--
+-- A certificate is a binding between some identifying information (called a subject) and a public key.
+-- This binding is asserted by a signature on the certificate, which is placed there by some authority (the issuer) that at least claims that it knows the subject named in the certificate really “owns” the private key corresponding to the public key in the certificate.
+--
+-- The major certificate format in use today is X.509v3, used for instance in the Transport Layer Security (TLS) protocol. A X.509 certificate is represented by the type 'Cert'.
+newtype Cert = Cert { certStruct :: BotanStruct }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+withCert :: HasCallStack => Cert -> (BotanStructT -> IO r) -> IO r
+withCert (Cert cert) = withBotanStruct cert
+
+-- | Load a certificate from the DER or PEM representation.
+loadCert :: HasCallStack => V.Bytes -> IO Cert
+loadCert cert = do
+    withPrimVectorUnsafe cert $ \ cert' off len ->
+        Cert <$> newBotanStruct
+            (\ ret -> hs_botan_x509_cert_load ret cert' off len)
+            botan_x509_cert_destroy
+
+-- | Load a certificate from a file.
+loadCertFile :: HasCallStack => CBytes -> IO Cert
+loadCertFile name = do
+    CB.withCBytesUnsafe name $ \ name' ->
+        Cert <$> newBotanStruct
+            (`botan_x509_cert_load_file` name')
+            botan_x509_cert_destroy
+
+-- | Create a new object that refers to the same certificate.
+dupCert :: HasCallStack => Cert -> IO Cert
+dupCert cert = do
+    withCert cert $ \ cert' ->
+        Cert <$> newBotanStruct
+            (`botan_x509_cert_dup` cert')
+            botan_x509_cert_destroy
+
+{- NO IMPLEMENTED IN BOTAN
+-- | Create a new self-signed X.509 certificate.
+-- Generating a new self-signed certificate can often be useful, for example when setting up a new root CA, or for use in specialized protocols.
+newCertSelfsigned ::
+  -- | the private key you wish to use (the public key, used in the certificate itself is extracted from the private key)
+  PrivKey ->
+  RNG ->
+  -- | common name
+  CBytes ->
+  -- | org name
+  CBytes ->
+  IO Cert
+newCertSelfsigned (PrivKey key) rng common org =
+    withBotanStruct key $ \ key' ->
+    withRNG rng $ \ rng' ->
+    CB.withCBytesUnsafe common $ \ common' ->
+    CB.withCBytesUnsafe org $ \ org' ->
+        Cert <$> newBotanStruct
+            (\ ret -> botan_x509_cert_gen_selfsigned ret key' rng' common' org')
+            botan_x509_cert_destroy
+-}
+
+-- | Return the time the certificate becomes valid, as a 'T.Text' in form “YYYYMMDDHHMMSSZ” where Z is a literal character reflecting that this time is relative to UTC.
+certStartText :: Cert -> IO T.Text
+certStartText cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUTF8Unsafe 16 (botan_x509_cert_get_time_starts cert')
+
+-- | Return the time the certificate expires, as a 'T.Text' in form “YYYYMMDDHHMMSSZ” where Z is a literal character reflecting that this time is relative to UTC.
+certExpireText :: Cert -> IO T.Text
+certExpireText cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUTF8Unsafe 16 (botan_x509_cert_get_time_expires cert')
+
+-- | Return the time the certificate becomes valid, as seconds since epoch.
+certStart :: Cert -> IO Word64
+certStart cert =
+    withCert cert $ \ cert' -> do
+        (a, _) <- allocPrimUnsafe @Word64 $ botan_x509_cert_not_before cert'
+        return a
+
+-- | Return the time the certificate becomes valid.
+certStart' :: Cert -> IO SystemTime
+certStart' cert = do
+    !r <- fromIntegral <$> certStart cert
+    return (MkSystemTime r 0)
+
+-- | Return the time the certificate expires, as 'SystemTime'.
+certExpire :: Cert -> IO Word64
+certExpire cert =
+    withCert cert $ \ cert' -> do
+        (a, _) <- allocPrimUnsafe @Word64 $ botan_x509_cert_not_after cert'
+        return a
+
+-- | Return the time the certificate expires, as 'SystemTime'.
+certExpire' :: Cert -> IO SystemTime
+certExpire' cert = do
+    !r <- fromIntegral <$> certExpire cert
+    return (MkSystemTime r 0)
+
+-- | Return the finger print of the certificate.
+certFingerPrint :: Cert -> HashType -> IO T.Text
+certFingerPrint cert ht =
+    withCert cert $ \ cert' ->
+    CB.withCBytesUnsafe (hashTypeToCBytes ht) $ \ ht' ->
+    allocBotanBufferUTF8Unsafe V.smallChunkSize $
+        botan_x509_cert_get_fingerprint cert' ht'
+
+-- | Return the serial number of the certificate.
+certSerial :: Cert -> IO V.Bytes
+certSerial cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUnsafe 64 $
+        botan_x509_cert_get_serial_number cert'
+
+-- | Return the authority key ID set in the certificate, which may be empty.
+certIDAuthority :: Cert -> IO V.Bytes
+certIDAuthority cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUnsafe 64 $
+        botan_x509_cert_get_authority_key_id cert'
+
+-- | Return the subject key ID set in the certificate, which may be empty.
+certIDSubject :: Cert -> IO V.Bytes
+certIDSubject cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUnsafe 64 $
+        botan_x509_cert_get_subject_key_id cert'
+
+-- | Get the serialized representation of the public key included in this certificate.
+certPubBits :: Cert -> IO V.Bytes
+certPubBits cert =
+    withCert cert $ \ cert' ->
+    allocBotanBufferUnsafe V.smallChunkSize $
+        botan_x509_cert_get_public_key_bits cert'
+
+-- | Get the public key included in this certificate.
+certPubKey :: Cert -> IO PubKey
+certPubKey cert = do
+    withCert cert $ \ cert' ->
+        PubKey <$> newBotanStruct
+            (cert' `botan_x509_cert_get_public_key`)
+            botan_pubkey_destroy
+
+-- | Get a value from the issuer DN field.
+certDNIssuer ::
+    Cert ->
+    -- | key
+    CBytes ->
+    -- | index
+    Int ->
+    IO T.Text
+certDNIssuer cert key ix =
+    withCert cert $ \ cert' ->
+    CB.withCBytesUnsafe key $ \ key' -> do
+    allocBotanBufferUTF8Unsafe 64 $
+        botan_x509_cert_get_issuer_dn cert' key' ix
+
+-- | Get a value from the subject DN field.
+certDNSubject ::
+  Cert ->
+  -- | key
+  CBytes ->
+  -- | index
+  Int ->
+  IO T.Text
+certDNSubject cert key ix =
+    withCert cert $ \ cert' ->
+    CB.withCBytesUnsafe key $ \ key' ->
+    allocBotanBufferUTF8Unsafe 64 $
+        botan_x509_cert_get_subject_dn cert' key' ix
+
+-- | Format the certificate as a free-form string.
+certToText :: Cert -> IO T.Text
+certToText cert =
+    withCert cert $ \ cert' ->
+    T.Text . V.unsafeInit <$>
+        allocBotanBufferUnsafe V.smallChunkSize (botan_x509_cert_to_string cert')
+
+-- | Change cert's 'KeyUsageConstraint'.
+certUsage :: Cert -> KeyUsageConstraint -> IO ()
+certUsage cert usage =
+    withCert cert $ \ cert' ->
+    throwBotanIfMinus_ $ botan_x509_cert_allowed_usage cert' usage
+
+-- Verify a certificate. Returns 'Nothing' if validation was successful, 'Just reason' if unsuccessful.
+--
+verifyCert ::
+    -- | Intermediate certificates, set to @[]@ if not needed.
+    [Cert] ->
+    -- | Trusted certificates, set to @[]@ if not needed.
+    [Cert] ->
+    -- | Set required strength to indicate the minimum key and hash strength that is allowed, set to zero to use default(110).
+    Int ->
+    -- | Hostname.
+    CBytes ->
+    -- | Set reference time(seconds since epoch) to be the time which the certificate chain is validated against. Use zero to use the current system clock.
+    Word64 ->
+    -- | The certificate to be verified.
+    Cert ->
+    IO (Maybe CBytes)
+verifyCert intermediates trusted strength hostname refTime cert =
+    withCert cert $ \ cert' ->
+    withCPtrsUnsafe (map certStruct intermediates) $ \ intermediates' intermediatesLen ->
+    withCPtrsUnsafe (map certStruct trusted) $ \ trusted' trustedLen ->
+    CB.withCBytesUnsafe hostname $ \ hostname' -> do
+        a <- throwBotanIfMinus $
+            hs_botan_x509_cert_verify cert'
+                intermediates' intermediatesLen
+                trusted' trustedLen
+                strength hostname' refTime
+        if a == 0
+        then return Nothing
+        else let !reason = certValidateStatus a in return (Just reason)
+
+-- | Return a (statically allocated) CString associated with the verification result.
+certValidateStatus :: CInt -> CBytes
+certValidateStatus r =
+    unsafeDupablePerformIO $ CB.fromCString =<< botan_x509_cert_validation_status r
+
+-- | Certificate path validation supporting Certificate Revocation Lists.
+--
+-- Verify a certificate. Returns 'Nothing' if validation was successful, 'Just reason' if unsuccessful.
+--
+verifyCertCRL ::
+    -- | Intermediate certificates, set to @[]@ if not needed.
+    [Cert] ->
+    -- | Trusted certificates, set to @[]@ if not needed.
+    [Cert] ->
+    -- | Certificate Revocation Lists, set to @[]@ if not needed.
+    [CRL] ->
+    -- | Set required strength to indicate the minimum key and hash strength that is allowed, set to zero to use default(110).
+    Int ->
+    -- | Hostname.
+    CBytes ->
+    -- | Set reference time(seconds since epoch) to be the time which the certificate chain is validated against. Use zero to use the current system clock.
+    Word64 ->
+    -- | The certificate to be verified.
+    Cert ->
+    IO (Maybe CBytes)
+verifyCertCRL intermediates trusted crls strength hostname refTime cert =
+    withCert cert $ \ cert' ->
+    withCPtrsUnsafe (map certStruct intermediates) $ \ intermediates' intermediatesLen ->
+    withCPtrsUnsafe (map certStruct trusted) $ \ trusted' trustedLen ->
+    withCPtrsUnsafe (map crlStruct crls) $ \ crls' crlsLen ->
+    CB.withCBytesUnsafe hostname $ \ hostname' -> do
+        a <- throwBotanIfMinus $
+            hs_botan_x509_cert_verify_with_crl cert'
+                intermediates' intermediatesLen
+                trusted' trustedLen
+                crls' crlsLen
+                strength hostname' refTime
+        if a == 0
+        then return Nothing
+        else let !reason = certValidateStatus a in return (Just reason)
+
+-- | Certificate path validation supporting Certificate Revocation Lists with a 'CertStore'.
+--
+-- Verify a certificate. Returns 'Nothing' if validation was successful, 'Just reason' if unsuccessful.
+--
+verifyCertCRL' ::
+    -- | Intermediate certificates, set to @[]@ if not needed.
+    [Cert] ->
+    -- | Trusted certificates in 'CertStore'
+    CertStore ->
+    -- | Certificate Revocation Lists, set to @[]@ if not needed.
+    [CRL] ->
+    -- | Set required strength to indicate the minimum key and hash strength that is allowed, set to zero to use default(110).
+    Int ->
+    -- | Hostname.
+    CBytes ->
+    -- | Set reference time(seconds since epoch) to be the time which the certificate chain is validated against. Use zero to use the current system clock.
+    Word64 ->
+    -- | The certificate to be verified.
+    Cert ->
+    IO (Maybe CBytes)
+verifyCertCRL' intermediates store crls strength hostname refTime cert =
+    withCert cert $ \ cert' ->
+    withCertStore store $ \ store' ->
+    withCPtrsUnsafe (map certStruct intermediates) $ \ intermediates' intermediatesLen ->
+    withCPtrsUnsafe (map crlStruct crls) $ \ crls' crlsLen ->
+    CB.withCBytesUnsafe hostname $ \ hostname' -> do
+        a <- throwBotanIfMinus $
+            hs_botan_x509_cert_verify_with_certstore_crl cert'
+                intermediates' intermediatesLen
+                store'
+                crls' crlsLen
+                strength hostname' refTime
+        if a == 0
+        then return Nothing
+        else let !reason = certValidateStatus a in return (Just reason)
+
+----------------------------------------
+-- X.509 Certificate Revocation Lists --
+----------------------------------------
+
+-- | An opaque newtype wrapper for an X.509 Certificate Revocation Lists.
+--
+-- It will occasionally happen that a certificate must be revoked before its expiration date.
+-- Examples of this happening include the private key being compromised, or the user to which it has been assigned leaving an organization.
+-- Certificate revocation lists are an answer to this problem (though online certificate validation techniques are starting to become somewhat more popular).
+-- Every once in a while the CA will release a new CRL, listing all certificates that have been revoked.
+-- Also included is various pieces of information like what time a particular certificate was revoked, and for what reason.
+-- In most systems, it is wise to support some form of certificate revocation, and CRLs handle this easily.
+newtype CRL = CRL { crlStruct :: BotanStruct }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+withCRL :: HasCallStack => CRL -> (BotanStructT -> IO r) -> IO r
+withCRL (CRL crl) = withBotanStruct crl
+
+-- | Load a CRL from the DER or PEM representation.
+loadCRL :: V.Bytes -> IO CRL
+loadCRL src =
+    withPrimVectorUnsafe src $ \ src' off len ->
+    CRL <$> newBotanStruct
+        (\ ret -> hs_botan_x509_crl_load ret src' off len)
+        botan_x509_crl_destroy
+
+-- | Load a CRL from a file.
+loadCRLFile :: CBytes -> IO CRL
+loadCRLFile src =
+    CB.withCBytesUnsafe src $ \ src' ->
+    CRL <$> newBotanStruct
+        (`botan_x509_crl_load_file` src')
+        botan_x509_crl_destroy
+
+-- | Check whether a given crl contains a given cert. Return True when the certificate is revoked, False otherwise.
+isRevokedX509 :: HasCallStack => CRL -> Cert -> IO Bool
+isRevokedX509 crl cert =
+    withCRL crl $ \ crl' ->
+    withCert cert $ \ cert' -> do
+        ret <- botan_x509_is_revoked crl' cert'
+        if ret == 0
+        then return True
+        else if ret == -1
+            then return False
+            else throwBotanError ret
+
+----------------------------------------
+-- X.509 Certificate Store            --
+----------------------------------------
+
+-- | An opaque newtype wrapper for an X.509 Certificate Store based on botan's 'FlatFile_Certificate_Store'.
+newtype CertStore = CertStore { certStoreStruct :: BotanStruct }
+    deriving (Show, Generic)
+    deriving anyclass T.Print
+
+-- | Use 'CertStore' as a 'botan_x509_certstore_t'.
+withCertStore :: HasCallStack => CertStore -> (BotanStructT -> IO r) -> IO r
+withCertStore (CertStore c) = withBotanStruct c
+
+-- | Load a CertStore from a file.
+loadCertStoreFile :: CBytes -> IO CertStore
+loadCertStoreFile src =
+    CB.withCBytesUnsafe src $ \ src' ->
+    CertStore <$> newBotanStruct
+        (`botan_x509_certstore_load_file` src')
+        botan_x509_certstore_destroy
diff --git a/cbits/hs_botan.c b/cbits/hs_botan.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_botan.c
@@ -0,0 +1,376 @@
+#include <hs_botan.h>
+#include <string.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// FFI Helper
+// Codec
+
+int hs_botan_hex_encode(const uint8_t *x, HsInt x_off, HsInt x_len, char *out){
+    return botan_hex_encode(x+x_off, x_len, out, 0);
+}
+int hs_botan_hex_encode_lower(const uint8_t *x, HsInt x_off, HsInt x_len, char *out){
+    return botan_hex_encode(x+x_off, x_len, out, BOTAN_FFI_HEX_LOWER_CASE);
+}
+HsInt hs_botan_hex_decode(const char* hex_str, HsInt in_off, HsInt in_len, uint8_t* out){
+    size_t* out_size;
+    int r = botan_hex_decode(hex_str+in_off, in_len, out, out_size);
+    if (r < 0) return (HsInt)r;
+    return (HsInt)out_size;
+}
+
+// RNG
+
+int hs_botan_rng_add_entropy(botan_rng_t rng, const uint8_t *seed, HsInt off, HsInt len){
+    return botan_rng_add_entropy(rng, seed+off, len);
+}
+
+// Block cipher
+int hs_botan_block_cipher_set_key(botan_block_cipher_t bc, const uint8_t *key, HsInt key_off, HsInt key_len){
+    return botan_block_cipher_set_key(bc, key+key_off, key_len);
+}
+int hs_botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc
+    , const uint8_t in[], HsInt off, uint8_t out[], HsInt blocks){
+    return botan_block_cipher_encrypt_blocks(bc, in+off, out, blocks);
+}
+int hs_botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc
+    , const uint8_t in[], HsInt off, uint8_t out[], HsInt blocks){
+    return botan_block_cipher_decrypt_blocks(bc, in+off, out, blocks);
+}
+
+// Hash
+int hs_botan_hash_update(botan_hash_t hash, const uint8_t *input, HsInt off, HsInt len){
+    return botan_hash_update(hash, input+off, len);
+}
+
+// Cipher Mode
+int hs_botan_cipher_set_associated_data(botan_cipher_t cipher, const uint8_t* ad, HsInt ad_off, HsInt ad_len){
+    return botan_cipher_set_associated_data(cipher, ad+ad_off, ad_len);
+}
+
+int hs_botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, HsInt key_off, HsInt key_len){
+    return botan_cipher_set_key(cipher, key+key_off, key_len);
+}
+
+int hs_botan_cipher_start(botan_cipher_t cipher, const uint8_t* nonce, HsInt nonce_off, HsInt nonce_len){
+    return botan_cipher_start(cipher, nonce+nonce_off, nonce_len);
+}
+
+HsInt hs_botan_cipher_update(botan_cipher_t cipher,
+                           uint8_t* output,
+                           const uint8_t* input,
+                           HsInt input_off,
+                           HsInt input_len){
+    size_t input_consumed, output_written;
+    int r = botan_cipher_update(cipher, 0, output, input_len
+        , &output_written, input+input_off, input_len, &input_consumed);
+    if (r < 0){
+        return (HsInt)r;
+    } else {
+        return (HsInt)output_written;
+    }
+}
+
+HsInt hs_botan_cipher_finish(botan_cipher_t cipher,
+                           uint8_t* output,
+                           HsInt output_len,
+                           const uint8_t* input,
+                           HsInt input_off,
+                           HsInt input_len){
+    size_t input_consumed, output_written;
+    int r = botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, output, output_len
+        , &output_written, input+input_off, input_len, &input_consumed);
+    if (r < 0){
+        return (HsInt)r;
+    } else {
+        return (HsInt)output_written;
+    }
+}
+
+// Multiple Precision Integers
+int hs_botan_mp_to_hex(botan_mp_t mp, char *out, HsInt off){
+    return botan_mp_to_hex(mp, out+off);
+}
+HsInt hs_botan_mp_to_dec(botan_mp_t mp, char *out, HsInt off){
+    size_t len;
+    int r = botan_mp_to_str(mp, 10, out+off, &len);
+    if (r >= 0) {
+        return (HsInt)len+off-1;
+    } else {
+        return (HsInt)r;
+    }
+}
+int hs_botan_mp_set_from_hex(botan_mp_t dest, const char *str, HsInt off, HsInt len){
+    char temp[len+3];
+    temp[0] = '0';
+    temp[1] = 'x';
+    memcpy(temp+2, str+off, len);
+    temp[len+2] = 0;
+    return botan_mp_set_from_str(dest, temp);
+
+}
+int hs_botan_mp_set_from_dec(botan_mp_t dest, const char *str, HsInt off, HsInt len){
+    char temp[len+1];
+    memcpy(temp, str+off, len);
+    temp[len] = 0;
+    return botan_mp_set_from_str(dest, temp);
+}
+int hs_botan_mp_from_bin(botan_mp_t mp, const uint8_t* vec, HsInt off, HsInt len){
+    return botan_mp_from_bin(mp, vec+off, len);
+}
+int hs_botan_mp_to_bin(botan_mp_t mp, uint8_t* vec, HsInt off){
+    return botan_mp_to_bin(mp, vec+off);
+}
+
+// KDF & PBKDF
+
+int hs_botan_kdf(const char* algo
+                ,uint8_t out[], HsInt out_len
+                ,const uint8_t passwd[], HsInt passwd_off, HsInt passwd_len
+                ,const uint8_t salt[], HsInt salt_off, HsInt salt_len
+                ,const uint8_t label[], HsInt label_off, HsInt label_len){
+    return botan_kdf(algo, out, out_len, passwd+passwd_off, passwd_len, salt+salt_off, salt_len, label+label_off, label_len);
+}
+
+int hs_botan_pwdhash(const char* algo
+                    ,HsInt p1, HsInt p2, HsInt p3
+                    ,uint8_t out[], HsInt out_len
+                    ,const char* passwd, HsInt passwd_len
+                    ,const uint8_t salt[], HsInt salt_off, HsInt salt_len){
+    return botan_pwdhash(algo, p1, p2, p3, out, out_len, passwd, passwd_len, salt+salt_off, salt_len);
+}
+
+int hs_botan_pwdhash_timed(const char* algo
+                          ,uint32_t msec
+                          ,uint8_t out[], HsInt out_len
+                          ,const char* passwd, HsInt passwd_len
+                          ,const uint8_t salt[], HsInt salt_off, HsInt salt_len){
+    return botan_pwdhash_timed(algo, msec, NULL, NULL, NULL, out, out_len, passwd, passwd_len, salt+salt_off, salt_len);
+}
+
+HsInt hs_botan_bcrypt_generate(uint8_t *out, const char *pwd, HsInt pwd_off, HsInt pwd_len
+    , botan_rng_t rng, HsInt work_factor, uint32_t flags){
+    size_t out_len = 64;
+    char pwd0[pwd_len+1];
+
+    memcpy(pwd0, pwd+pwd_off, pwd_len);
+    pwd0[pwd_len] = 0;
+
+    int r = botan_bcrypt_generate(out, &out_len, pwd0, rng, work_factor, flags);
+    if (r < 0) {
+        return (HsInt)r;
+    } else {
+        return (HsInt)out_len;
+    }
+}
+
+int hs_botan_bcrypt_is_valid(const char* pwd, HsInt pwd_off, HsInt pwd_len
+    , const char* hash, HsInt hash_off, HsInt hash_len){
+    char pwd0[pwd_len+1];
+    char hash0[hash_len+1];
+
+    memcpy(pwd0, pwd+pwd_off, pwd_len);
+    pwd0[pwd_len] = 0;
+    memcpy(hash0, hash+hash_off, hash_len);
+    hash0[hash_len] = 0;
+
+    return botan_bcrypt_is_valid(pwd0, hash0);
+}
+
+// MAC
+
+int hs_botan_mac_set_key(botan_mac_t mac, const uint8_t* key, HsInt key_off, HsInt key_len){
+    return botan_mac_set_key(mac, key+key_off, key_len);
+}
+int hs_botan_mac_update(botan_mac_t mac, const uint8_t* buf, HsInt off ,HsInt len){
+    return botan_mac_update(mac, buf + off, len);
+}
+
+int hs_botan_mac_final(botan_mac_t mac, uint8_t out[]){
+    return botan_mac_final(mac, out);
+}
+int hs_botan_mac_clear(botan_mac_t mac){
+    return botan_mac_clear(mac);
+}
+
+int hs_botan_mac_name(botan_mac_t mac, char* name, size_t* name_len){
+    return botan_mac_name(mac, name, name_len);
+}
+int hs_botan_mac_get_keyspec(botan_mac_t mac,
+    size_t* out_minimum_keylength,
+    size_t* out_maximum_keylength,
+    size_t* out_keylength_modulo){
+        return botan_mac_get_keyspec(mac, out_minimum_keylength, out_maximum_keylength, out_keylength_modulo);
+}
+
+// Public Key Creation, Import and Export
+
+int hs_botan_privkey_load (botan_privkey_t* key, botan_rng_t rng
+                          ,const uint8_t bits[], HsInt off, HsInt len
+                          ,const char* passwd){
+    if (*passwd == '\0') passwd = NULL;
+    return botan_privkey_load(key, rng, bits+off, len, passwd);
+}
+
+int hs_botan_pubkey_load (botan_pubkey_t* key
+                         ,const uint8_t bits[], HsInt off, HsInt len){
+    return botan_pubkey_load(key, bits+off, len);
+}
+
+// Public Key Encryption / Decryption
+
+int hs_botan_pk_op_encrypt(botan_pk_op_encrypt_t op, botan_rng_t rng, uint8_t out[], HsInt *out_len, const uint8_t plaintext[], HsInt plaintext_off, HsInt plaintext_len){
+    return botan_pk_op_encrypt(op, rng, out, out_len, plaintext+plaintext_off, plaintext_len);
+}
+
+int hs_botan_pk_op_decrypt(botan_pk_op_decrypt_t op, uint8_t out[], HsInt *out_len, uint8_t ciphertext[], HsInt ciphertext_off, HsInt ciphertext_len){
+    return botan_pk_op_decrypt(op, out, out_len, ciphertext+ciphertext_off, ciphertext_len);
+}
+
+// Signature Generation & Signature Verification
+
+int hs_botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], HsInt off, HsInt len){
+    return botan_pk_op_sign_update(op, in+off, len);
+}
+
+int hs_botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], HsInt off, HsInt len){
+    return botan_pk_op_verify_update(op, in+off, len);
+}
+
+int hs_botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], HsInt off, HsInt len){
+    return botan_pk_op_verify_finish(op, sig+off, len);
+}
+
+// Key Agreement
+
+int hs_botan_pk_op_key_agreement(botan_pk_op_ka_t op, uint8_t out[], HsInt *out_len, const uint8_t other_key[], HsInt other_key_off, HsInt other_key_len, const uint8_t salt[], HsInt salt_off, HsInt salt_len){
+    return botan_pk_op_key_agreement(op, out, out_len, other_key+other_key_off, other_key_len, salt+salt_off, salt_len);
+}
+
+/*
+int hs_botan_mceies_encrypt(botan_pubkey_t mce_key, botan_rng_t rng, const char *aead, const uint8_t pt[], HsInt pt_off, HsInt pt_len, const uint8_t ad[], HsInt ad_off, HsInt ad_len, uint8_t ct[], HsInt *ct_len){
+    return botan_mceies_encrypt(mce_key, rng, aead, pt+pt_off, pt_len, ad+ad_off, ad_len, ct, ct_len);
+}
+*/
+
+// X.509 Certificates & Certificate Revocation Lists
+
+int hs_botan_x509_cert_load(botan_x509_cert_t *cert_obj, const uint8_t cert[], HsInt cert_off, HsInt cert_len){
+    return botan_x509_cert_load(cert_obj, cert+cert_off, cert_len);
+}
+
+int hs_botan_x509_cert_verify(botan_x509_cert_t cert
+        , const botan_x509_cert_t *intermediates, HsInt intermediates_len
+        , const botan_x509_cert_t *trusted, HsInt trusted_len
+        , HsInt required_strength, const char *hostname, uint64_t reference_time){
+    int r1;
+    int r2 = botan_x509_cert_verify(&r1, cert
+            , intermediates, intermediates_len
+            , trusted, trusted_len
+            , NULL, required_strength, hostname, reference_time);
+    if (r2 < 0){
+        return r2;
+    } else { 
+        return r1; 
+    }
+}
+
+int hs_botan_x509_cert_verify_with_crl(botan_x509_cert_t cert
+        , const botan_x509_cert_t *intermediates, HsInt intermediates_len
+        , const botan_x509_cert_t *trusted, HsInt trusted_len
+        , const botan_x509_crl_t *crls, HsInt crls_len
+        , HsInt required_strength, const char *hostname, uint64_t reference_time){
+    int r1;
+    int r2 = botan_x509_cert_verify_with_crl(&r1, cert
+            , intermediates, intermediates_len
+            , trusted, trusted_len
+            , crls, crls_len
+            , NULL, required_strength, hostname, reference_time);
+    if (r2 < 0){
+        return r2;
+    } else { 
+        return r1; 
+    }
+}
+
+
+int hs_botan_x509_cert_verify_with_certstore_crl(
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates, HsInt intermediates_len,
+   const botan_x509_certstore_t store,
+   const botan_x509_crl_t* crls, HsInt crls_len,
+   size_t required_strength,
+   const char* hostname,
+   uint64_t reference_time) {
+    int r1;
+    int r2 = botan_x509_cert_verify_with_certstore_crl(&r1, cert
+            , intermediates, intermediates_len
+            , store
+            , crls, crls_len
+            , required_strength, hostname, reference_time);
+    if (r2 < 0){
+        return r2;
+    } else { 
+        return r1; 
+    }
+
+}
+
+int hs_botan_x509_crl_load(botan_x509_crl_t *crl_obj, const uint8_t crl[], HsInt crl_off, HsInt crl_len){
+    return botan_x509_crl_load(crl_obj, crl+crl_off, crl_len);
+}
+
+// Key wrap
+
+/**
+  * Advanced Encryption Standard (AES) Key Wrap Algorithm
+  * See `https://tools.ietf.org/html/rfc3394`.
+  * Key wrapping as per RFC 3394
+*/
+
+int hs_botan_key_wrap3394(const uint8_t key[], HsInt key_off, HsInt key_len
+                         ,const uint8_t kek[], HsInt kek_off, HsInt kek_len
+                         ,uint8_t wrapped_key[], size_t *wrapped_key_len)
+{
+    return botan_key_wrap3394(key+key_off, key_len
+                             ,kek+kek_off, kek_len
+                             ,wrapped_key, wrapped_key_len);
+}
+
+int hs_botan_key_unwrap3394(const uint8_t wrapped_key[], HsInt wrapped_key_off, HsInt wrapped_key_len
+                           ,const uint8_t kek[], HsInt kek_off, HsInt kek_len
+                           ,uint8_t key[], size_t *key_len){
+    return botan_key_unwrap3394(wrapped_key+wrapped_key_off, wrapped_key_len
+                               ,kek+kek_off, kek_len
+                               ,key, key_len);}
+
+// OTP
+
+int hs_botan_hotp_init(botan_hotp_t* hotp
+                      ,const uint8_t key[], HsInt key_off, HsInt key_len
+                      ,const char* hash_algo
+                      ,HsInt digits){
+    return botan_hotp_init(hotp
+                          ,key+key_off, key_len
+                          ,hash_algo, digits);}
+
+int hs_botan_totp_init(botan_totp_t* totp
+                      ,const uint8_t key[], HsInt key_off, HsInt key_len
+                      ,const char* hash_algo
+                      ,HsInt digits
+                      ,HsInt time_step){
+    return botan_totp_init(totp, key+key_off, key_len, hash_algo, digits, time_step);}
+
+// FPE
+
+int hs_botan_fpe_fe1_init(botan_fpe_t* fpe, botan_mp_t n,
+                       const uint8_t key[], HsInt key_off, HsInt key_len,
+                       size_t rounds, uint32_t flags){
+return botan_fpe_fe1_init(fpe, n,
+                       key+key_off, key_len,
+                       rounds, flags);}
+
+int hs_botan_fpe_encrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], HsInt tweak_off, HsInt tweak_len)
+{return botan_fpe_encrypt(fpe, x, tweak+tweak_off, tweak_len);}
+
+int hs_botan_fpe_decrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], HsInt tweak_off, HsInt tweak_len)
+{return botan_fpe_decrypt(fpe, x, tweak+tweak_off, tweak_len);}
diff --git a/include/hs_botan.h b/include/hs_botan.h
new file mode 100644
--- /dev/null
+++ b/include/hs_botan.h
@@ -0,0 +1,149 @@
+#include <HsFFI.h>
+#include <botan/ffi.h>
+
+// Utility Functions
+int hs_botan_hex_encode(const uint8_t *x, HsInt x_off, HsInt x_len, char *out);
+int hs_botan_hex_encode_lower(const uint8_t *x, HsInt x_off, HsInt x_len, char *out);
+HsInt hs_botan_hex_decode(const char* hex_str, HsInt in_off, HsInt in_len, uint8_t* out);
+
+// RNG
+int hs_botan_rng_add_entropy(botan_rng_t rng, const uint8_t *seed, HsInt off, HsInt len);
+
+// Block Cipher
+int hs_botan_block_cipher_set_key(botan_block_cipher_t bc, const uint8_t *key, HsInt key_off, HsInt key_len);
+int hs_botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc
+    , const uint8_t in[], HsInt off, uint8_t out[], HsInt blocks);
+int hs_botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc
+    , const uint8_t in[], HsInt off, uint8_t out[], HsInt blocks);
+
+// Hash
+int hs_botan_hash_update(botan_hash_t hash, const uint8_t *input, HsInt off, HsInt len);
+
+// Cipher Mode
+int hs_botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, HsInt key_off, HsInt key_len);
+int hs_botan_cipher_set_associated_data(botan_cipher_t cipher, const uint8_t* ad, HsInt ad_off, HsInt ad_len);
+int hs_botan_cipher_start(botan_cipher_t cipher, const uint8_t* nonce, HsInt nonce_off, HsInt nonce_len);
+// output buffer length should be at least equal to input length, 
+// which must be larger than botan_cipher_get_update_granularity
+// input_consumed == output_written
+HsInt hs_botan_cipher_update(botan_cipher_t cipher_obj,
+                               uint8_t* output,
+                               const uint8_t* input,
+                               HsInt input_off,
+                               HsInt input_len);
+// output buffer length should be at least equal to following call's result
+// botan_cipher_output_length(cipher, input_length, &output_length)
+// output_written may differ
+HsInt hs_botan_cipher_finish(botan_cipher_t cipher_obj,
+                           uint8_t* output,
+                           HsInt output_len,
+                           const uint8_t* input,
+                           HsInt input_off,
+                           HsInt input_len);
+
+// Multiple Precision Integers
+
+int hs_botan_mp_to_hex(botan_mp_t mp, char *out, HsInt off);
+HsInt hs_botan_mp_to_dec(botan_mp_t mp, char *out, HsInt off);
+int hs_botan_mp_set_from_hex(botan_mp_t dest, const char *str, HsInt off, HsInt len);
+int hs_botan_mp_set_from_dec(botan_mp_t dest, const char *str, HsInt off, HsInt len);
+int hs_botan_mp_from_bin(botan_mp_t mp, const uint8_t* vec, HsInt off, HsInt len);
+int hs_botan_mp_to_bin(botan_mp_t mp, uint8_t* vec, HsInt off);
+
+// KDF & PBKDF
+int hs_botan_kdf(const char* algo
+                ,uint8_t out[], HsInt out_len
+                ,const uint8_t passwd[], HsInt passwd_off, HsInt passwd_len
+                ,const uint8_t salt[], HsInt salt_off, HsInt salt_len
+                ,const uint8_t label[], HsInt label_off, HsInt label_len);
+
+int hs_botan_pwdhash(const char* algo
+                    ,HsInt p1, HsInt p2, HsInt p3
+                    ,uint8_t out[], HsInt out_len
+                    ,const char* passwd, HsInt passwd_len
+                    ,const uint8_t salt[], HsInt salt_off, HsInt salt_len);
+int hs_botan_pwdhash_timed(const char* algo
+                          ,uint32_t msec
+                          ,uint8_t out[], HsInt out_len
+                          ,const char* passwd, HsInt passwd_len
+                          ,const uint8_t salt[], HsInt salt_off, HsInt salt_len);
+
+HsInt hs_botan_bcrypt_generate(uint8_t *out, const char *pwd, HsInt pwd_off, HsInt pwd_len
+    , botan_rng_t rng, HsInt work_factor, uint32_t flags);
+int hs_botan_bcrypt_is_valid(const char* pwd, HsInt pwd_off, HsInt pwd_len
+    , const char* hash, HsInt hash_off, HsInt hash_len);
+
+// MAC
+
+int hs_botan_mac_set_key(botan_mac_t mac, const uint8_t* key, HsInt key_off, HsInt key_len);
+int hs_botan_mac_update(botan_mac_t mac, const uint8_t* buf, HsInt off, HsInt len);
+int hs_botan_mac_final(botan_mac_t mac, uint8_t out[]);
+int hs_botan_mac_clear(botan_mac_t mac);
+int hs_botan_mac_name(botan_mac_t mac, char* name, size_t* name_len);
+int hs_botan_mac_get_keyspec(botan_mac_t mac,
+    size_t* out_minimum_keylength,
+    size_t* out_maximum_keylength,
+    size_t* out_keylength_modulo);
+
+// Public Key Creation, Import and Export
+int hs_botan_privkey_load (botan_privkey_t* key, botan_rng_t rng
+                          ,const uint8_t bits[], HsInt off, HsInt len
+                          ,const char* passwd);
+int hs_botan_pubkey_load (botan_pubkey_t* key
+                         ,const uint8_t bits[], HsInt off, HsInt len);
+// Public Key Encryption/Decryption
+//
+int hs_botan_pk_op_encrypt(botan_pk_op_encrypt_t op, botan_rng_t rng, uint8_t out[], HsInt *out_len, const uint8_t plaintext[], HsInt plaintext_off, HsInt plaintext_len);
+int hs_botan_pk_op_decrypt(botan_pk_op_decrypt_t op, uint8_t out[], HsInt *out_len, uint8_t ciphertext[], HsInt ciphertext_off, HsInt ciphertext_len);
+
+// Signature Generation & Signature Verification
+//
+int hs_botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t * in, HsInt off , HsInt len);
+int hs_botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t * in, HsInt off, HsInt in_len);
+int hs_botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t * sig, HsInt off, HsInt sig_len);
+
+// Key Agreement
+
+int hs_botan_pk_op_key_agreement(botan_pk_op_ka_t op, uint8_t out[], HsInt *out_len, const uint8_t other_key[], HsInt other_key_off, HsInt other_key_len, const uint8_t salt[], HsInt salt_off, HsInt salt_len);
+
+// X.509 Certificates & X.509 Certificate Revocation Lists
+
+int hs_botan_x509_cert_load(botan_x509_cert_t *cert_obj, const uint8_t cert[], HsInt cert_off, HsInt cert_len);
+int hs_botan_x509_cert_verify(botan_x509_cert_t cert
+        , const botan_x509_cert_t *intermediates, HsInt intermediates_len
+        , const botan_x509_cert_t *trusted, HsInt trusted_len
+        , HsInt required_strength, const char *hostname, uint64_t reference_time);
+int hs_botan_x509_cert_verify_with_crl(botan_x509_cert_t cert
+        , const botan_x509_cert_t *intermediates, HsInt intermediates_len
+        , const botan_x509_cert_t *trusted, HsInt trusted_len
+        , const botan_x509_crl_t *crls, HsInt crls_len
+        , HsInt required_strength, const char *hostname, uint64_t reference_time);
+int hs_botan_x509_cert_verify_with_certstore_crl(
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates, HsInt intermediates_len,
+   const botan_x509_certstore_t store,
+   const botan_x509_crl_t* crls, HsInt crls_len,
+   size_t required_strength,
+   const char* hostname_cstr,
+   uint64_t reference_time);
+
+// Key wrap
+
+int hs_botan_key_wrap3394(const uint8_t key[], HsInt key_off, HsInt key_len
+                         ,const uint8_t kek[], HsInt kek_off, HsInt kek_len
+                         ,uint8_t wrapped_key[], size_t *wrapped_key_len);
+
+int hs_botan_key_unwrap3394(const uint8_t wrapped_key[], HsInt wrapped_key_off, HsInt wrapped_key_len
+                           ,const uint8_t kek[], HsInt kek_off, HsInt kek_len
+                           ,uint8_t key[], size_t *key_len);
+// OTP
+int hs_botan_hotp_init(botan_hotp_t* hotp
+                      ,const uint8_t key[], HsInt key_off, HsInt key_len
+                      ,const char* hash_algo
+                      ,HsInt digits);
+
+int hs_botan_totp_init(botan_totp_t* totp
+                      ,const uint8_t key[], HsInt key_off, HsInt key_len
+                      ,const char* hash_algo
+                      ,HsInt digits
+                      ,HsInt time_step);
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+-- file test/Spec.hs
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/Utils.hs b/test/Utils.hs
new file mode 100644
--- /dev/null
+++ b/test/Utils.hs
@@ -0,0 +1,444 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Utils where
+
+import           Control.Applicative
+import           Control.Monad
+import           Z.IO
+import qualified Z.IO.FileSystem    as FS
+import           Z.Data.ASCII
+import           Z.Data.Vector.Hex
+import qualified Z.Data.Parser      as P
+import           Z.Data.Parser.Numeric (decLoopIntegerFast)
+import           Z.Data.CBytes      (CBytes)
+import qualified Z.Data.CBytes      as CB
+import qualified Z.Data.Vector      as V
+import           Prelude            hiding (lines, mod)
+-- import           Data.IORef
+
+-- | Parse test data vector files.
+-- See `./third_party/botan/src/tests/data/`.
+parseNamedTestVector :: HasCallStack => P.Parser (x, Bool) -> CBytes -> IO [(V.Bytes, x)]
+parseNamedTestVector p path  = do
+    withResource (FS.initFile path FS.O_RDONLY FS.DEFAULT_FILE_MODE) $ \ f -> do
+        bi <- newBufferedInput f
+        let loopRead = do
+                name <- readParser algoName bi
+                (lines, eof) <- readParser p bi
+                if eof
+                then return [(name, lines)]
+                else do
+                    rest <- loopRead
+                    return ((name, lines):rest)
+        loopRead
+  where
+    algoName = do
+        P.skipWhile (/= BRACKET_LEFT)
+        P.skipWord8
+        n <- P.takeWhile1 (/= BRACKET_RIGHT)
+        P.skipWord8
+        return n
+
+-- | Parse @key = value@ or @key  = value@ or @key =@ line
+parseKeyValueLine :: V.Bytes -> P.Parser V.Bytes
+parseKeyValueLine k1 = do
+    P.bytes k1
+    P.skipWhile (== SPACE)
+    P.word8 EQUAL
+    P.skipWhile (== SPACE)
+    -- some lines contains trailing spaces, e.g., tls_prf.vec
+    V.dropWhileR isSpace <$> P.takeWhile (/= NEWLINE)
+
+parseHashTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes)])]
+parseHashTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_I && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          _ -> do
+              i <- parseKeyValueLine "In"
+              P.skipWord8
+              o <- parseKeyValueLine "Out"
+              go ((hexDecode' i, hexDecode' o):acc)
+
+parseBlockCipherTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes)])]
+parseBlockCipherTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_K && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          _ -> do
+              key <- parseKeyValueLine "Key"
+              P.skipWord8
+              i <- parseKeyValueLine "In"
+              P.skipWord8
+              o <- parseKeyValueLine "Out"
+              go ((hexDecode' key, hexDecode' i, hexDecode' o):acc)
+
+parseCipherModeTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
+parseCipherModeTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_K && w /= LETTER_N && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          Just w' -> do
+              (key, nonce) <- if w' == LETTER_K
+                  then do
+                    key <- parseKeyValueLine "Key"
+                    P.skipWord8
+                    nonce <- parseKeyValueLine "Nonce"
+                    P.skipWord8
+                    return (key, nonce)
+                  else do
+                    nonce <- parseKeyValueLine "Nonce"
+                    P.skipWord8
+                    key <- parseKeyValueLine "Key"
+                    P.skipWord8
+                    return (key, nonce)
+              i <- parseKeyValueLine "In"
+              P.skipWord8
+              o <- parseKeyValueLine "Out"
+              go ((hexDecode' key, hexDecode' nonce, hexDecode' i, hexDecode' o):acc)
+
+parseCipherAEADTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
+parseCipherAEADTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_K && w /= LETTER_N && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          Just w' -> do
+              (key, nonce) <- if w' == LETTER_K
+                  then do
+                    key <- parseKeyValueLine "Key"
+                    P.skipWord8
+                    nonce <- parseKeyValueLine "Nonce"
+                    P.skipWord8
+                    return (key, nonce)
+                  else do
+                    nonce <- parseKeyValueLine "Nonce"
+                    P.skipWord8
+                    key <- parseKeyValueLine "Key"
+                    P.skipWord8
+                    return (key, nonce)
+              i <- parseKeyValueLine "In"
+              P.skipWord8
+              ad <- parseKeyValueLine "AD"
+              P.skipWord8
+              o <- parseKeyValueLine "Out"
+              go ((hexDecode' key, hexDecode' nonce, hexDecode' i, hexDecode' ad, hexDecode' o):acc)
+
+
+-- | Parse test data vector files.
+-- See `./third_party/botan/src/tests/data/`.
+parseTestVector ::
+  HasCallStack =>
+  -- | (txt, eof)
+  P.Parser ([x], Bool) ->
+  -- | path
+  CBytes ->
+  IO [x]
+parseTestVector p path = do
+  withResource (FS.initFile path FS.O_RDONLY FS.DEFAULT_FILE_MODE) $ \f -> do
+    bi <- newBufferedInput f
+    let loopRead = do
+          (txt, eof) <- readParser p bi
+          if eof
+            then return txt
+            else do
+              rest <- loopRead
+              return $ txt ++ rest
+    loopRead
+
+-- | Parse test data vectors of the form:
+--    -- @Password = @
+--    -- @Passhash = @
+-- See `./third_party/botan/src/tests/data/passhash/bcrypt.vec`.
+parsePasswdHashTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes)]
+parsePasswdHashTestVector = parseTestVector $ h []
+  where
+    h acc = do
+      P.skipWhile $ \c ->
+        c /= LETTER_P -- goto @Password = @ or @Passhash = @
+          && c /= HASH -- deal with comment
+      c <- P.peekMaybe
+      case c of
+        Nothing -> pure (acc, True) -- end of file
+        Just HASH -> do
+          P.skipWhile (/= NEWLINE) -- line comment
+          P.skipWord8 -- skip @\n@
+          h acc
+        _ -> do
+          passwd <- parseKeyValueLine "Password"
+          P.skipWord8
+          passhash <- parseKeyValueLine "Passhash"
+          h $ (hexDecode' passwd, passhash) : acc
+
+-- | Parse test data vectors of the form
+--
+--    -- @Salt == @
+--    -- @Label == @
+--    -- @Secret == @
+--    -- @Output == @
+--
+-- and (unused) possible
+--
+--    -- @IKM == @
+--    -- @XTS == @
+--
+-- See `./third_party/botan/src/tests/data/kdf/hkdf.vec`.
+parseKDFTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
+parseKDFTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_S
+                       && w /= LETTER_L
+                       && w /= LETTER_O
+                       && w /= LETTER_I
+                       && w /= LETTER_X
+                       && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          _ -> do
+              -- skip possible IKM and XTS
+              ikm <- parseKeyValueLine "IKM" <|> pure mempty
+              unless (V.null ikm)
+                  P.skipWord8
+              xts <- parseKeyValueLine "XTS" <|> pure mempty
+              unless (V.null xts)
+                  P.skipWord8
+              salt <- parseKeyValueLine "Salt" <|> pure mempty
+              -- and here the salt's value may be empty...: in the format: Salt = <\n>.
+              P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                    _            -> return ()
+              label <- parseKeyValueLine "Label" <|> pure mempty
+              unless (V.null label)
+                  P.skipWord8
+              secret <- parseKeyValueLine "Secret"
+              P.skipWord8
+              -- in some test data files the "salt" is placed after "secret", e.g., "kdf1.vec"
+              salt <- if V.null salt
+                         then do salt <- parseKeyValueLine "Salt" <|> pure mempty
+                                 -- and here the salt's value may be empty...: in the format: Salt = <\n>.
+                                 P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                                       _            -> return ()
+                                 return salt
+                         else return salt
+              -- in some test data files the "label" is placed after "secret", e.g., "sp800_56a.vec"
+              label <- if V.null label
+                         then do label <- parseKeyValueLine "Label" <|> pure mempty
+                                 unless (V.null label)
+                                     P.skipWord8
+                                 return label
+                         else return label
+              o <- parseKeyValueLine "Output"
+              go ((hexDecode' salt, hexDecode' label, hexDecode' secret, hexDecode' o):acc)
+
+-- | Parse test data vectors of the form
+--
+--    -- @Salt == @
+--    -- @Iterations == @
+--    -- @Passphrase == @
+--    -- @Output == @
+--
+-- See `./third_party/botan/src/tests/data/pbkdf/pbkdf1.vec`.
+parsePBKDFTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, Int, CBytes, V.Bytes)])]
+parsePBKDFTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_S
+                       && w /= LETTER_I
+                       && w /= LETTER_P
+                       && w /= LETTER_O
+                       && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          _ -> do
+              -- in some cases the "Salt" and "Iterations" is after "Passphrase"
+              salt <- parseKeyValueLine "Salt" <|> pure mempty
+              P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                    _            -> return ()
+              iter <- parseKeyValueLine "Iterations" <|> pure mempty
+              P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                    _            -> return ()
+              passphrase <- parseKeyValueLine "Passphrase"
+              P.skipWord8
+              salt <- if V.null salt
+                         then do salt <- parseKeyValueLine "Salt" <|> pure mempty
+                                 P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                                       _            -> return ()
+                                 return salt
+                         else return salt
+              iter <- if V.null iter
+                         then do iter <- parseKeyValueLine "Iterations" <|> pure mempty
+                                 P.peekMaybe >>= \case Just NEWLINE -> P.skipWord8
+                                                       _            -> return ()
+                                 return iter
+                         else return iter
+              o <- parseKeyValueLine "Output"
+              go ( (hexDecode' salt
+                 , (fromIntegral . decLoopIntegerFast) iter
+                 , CB.fromBytes passphrase
+                 , hexDecode' o):acc)
+
+
+-- | Parse test data vectors of the form
+--
+--    -- @IV == @ (possible)
+--    -- @Key == @
+--    -- @In == @
+--    -- @Out == @
+--
+-- See `./third_party/botan/src/tests/data/mac/cbcmac.vec`.
+parseMACTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(Maybe V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
+parseMACTestVector = parseNamedTestVector (go [])
+  where
+    go acc = do
+      P.skipWhile (\ w -> w /= LETTER_I
+                       && w /= LETTER_K
+                       && w /= LETTER_O
+                       && w /= BRACKET_LEFT && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> do
+              P.skipWhile (/= NEWLINE)
+              P.skipWord8
+              go acc
+          Just BRACKET_LEFT -> return (acc, False)
+          _ -> do
+              -- in some cases the "Salt" and "Iterations" is after "Passphrase"
+              iv <- optional $ parseKeyValueLine "IV"
+              case iv of
+                  Just iv' -> unless (V.null iv') P.skipWord8
+                  Nothing  -> return ()
+              key <- parseKeyValueLine "Key"
+              P.skipWord8
+              in_ <- parseKeyValueLine "In"
+              P.skipWord8
+              o <- parseKeyValueLine "Out"
+              go ((fmap hexDecode' iv, hexDecode' key, hexDecode' in_, hexDecode' o):acc)
+
+skipComment :: (t -> P.Parser b) -> t -> P.Parser b
+{-# INLINE skipComment #-}
+skipComment h acc = do
+    P.skipWhile (/= NEWLINE)
+    P.skipWord8
+    h acc
+
+-- | Parse test data vectors of the form:
+--
+--    -- @Key == @
+--    -- @KEK == @
+--    -- @Output == @
+--
+-- See `./third_party/botan/src/tests/data/keywrap/rfc3394.vec`.
+parseKeyWrapVec :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes, V.Bytes)]
+parseKeyWrapVec = parseTestVector (h [])
+  where
+    h acc = do
+      P.skipWhile (\ w -> w /= LETTER_K && w /= LETTER_O && w /= HASH)
+      w <- P.peekMaybe
+      case w of
+          Nothing -> return (acc, True)
+          Just HASH -> skipComment h acc
+          _ -> do
+            key <- parseKeyValueLine "Key"
+            P.skipWord8
+            kek <- parseKeyValueLine "KEK"
+            P.skipWord8
+            o <- parseKeyValueLine "Output"
+            h ((hexDecode' key, hexDecode' kek, hexDecode' o) : acc)
+
+-- | Parse test data vectors of the form:
+--
+--    -- [label]         -- [label]
+--    -- @Key == @       -- @Key == @
+--    -- @Input == @     -- @Output == @
+--    -- @Output == @    -- @Input == @
+--
+-- See `./third_party/botan/src/tests/data/keywrap/`.
+-- parseNistKeyWrapVec :: HasCallStack => CBytes -> IO [(V.Bytes ,[(V.Bytes, V.Bytes, V.Bytes)])]
+-- parseNistKeyWrapVec = parseNamedTestVector (h [])
+--     where
+--       h acc = do
+--         P.skipWhile $ \ w -> w /= LETTER_K
+--                           && w /= LETTER_O
+--                           && w /= BRACKET_LEFT && w /= HASH
+--         w <- P.peekMaybe
+--         case w of
+--           Nothing -> return (acc, True)
+--           Just HASH -> skipComment h acc
+--           Just BRACE_LEFT -> return (acc, False)
+--           _ -> do
+--             key <- parseKeyValueLine "Key"
+--             P.skipWord8
+--             resTag <- liftIO $ newIORef False
+--             res0 <- parseKeyValueLine "Input" <* (liftIO $ writeIORef resTag True) <|> parseKeyValueLine "Output"
+--             P.skipWord8
+--             res1 <- parseKeyValueLine "Input" <|> parseKeyValueLine "Output"
+--             resTag' <- liftIO $ readIORef resTag
+--             let (i, o) = if resTag' then (res0, res1) else (res1, res0)
+--             h ((hexDecode' key, hexDecode' i, hexDecode' o) : acc)
+
+parseFPEVec :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes, V.Bytes, V.Bytes, V.Bytes)]
+parseFPEVec = parseTestVector (h [])
+    where
+        h acc = do
+            P.skipWhile $ \ w -> w /= LETTER_M -- Mod
+                              && w /= LETTER_I -- In
+                              && w /= LETTER_O -- Out
+                              && w /= LETTER_K -- Key
+                              && w /= LETTER_T -- Tweak
+                              && w /= HASH
+            w <- P.peekMaybe
+            case w of
+                Nothing -> return (acc, True)
+                Just HASH -> skipComment h acc
+                _ -> do
+                    mod   <- parseKeyValueLine "Mod"   <* P.skipWord8
+                    i     <- parseKeyValueLine "In"    <* P.skipWord8
+                    o     <- parseKeyValueLine "Out"   <* P.skipWord8
+                    key   <- parseKeyValueLine "Key"   <* P.skipWord8
+                    tweak <- parseKeyValueLine "Tweak" <* P.skipWord8
+                    h ((mod, i, o, hexDecode' key, hexDecode' tweak) : acc)
diff --git a/test/Z/Crypto/CipherSpec.hs b/test/Z/Crypto/CipherSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/CipherSpec.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+module Z.Crypto.CipherSpec where
+import           Control.Monad
+import           Test.Hspec
+import           Test.HUnit
+import           Z.IO
+import qualified Z.IO.FileSystem as FS
+import           Z.Data.ASCII
+import           Z.Data.Vector.Hex
+import qualified Z.Data.Parser      as P
+import qualified Z.Data.Vector      as V
+import qualified Z.Data.Text        as T
+import           Z.Crypto.Cipher
+import           Z.Crypto.Hash
+import           Utils
+
+spec :: Spec
+spec = do
+    describe "Crypto.Cipher.BlockCipher" $ do
+        forM_
+            [ ("aes.vec", "AES-128", AES128)
+            , ("aes.vec", "AES-192", AES192)
+            , ("aes.vec", "AES-256", AES256)
+            , ("aria.vec", "ARIA-128", ARIA128)
+            , ("aria.vec", "ARIA-192", ARIA192)
+            , ("aria.vec", "ARIA-256", ARIA256)
+            , ("blowfish.vec", "Blowfish", Blowfish)
+            , ("camellia.vec", "Camellia-128", Camellia128)
+            , ("camellia.vec", "Camellia-192", Camellia192)
+            , ("camellia.vec", "Camellia-256", Camellia256)
+            , ("cascade.vec", "Cascade(Serpent,Twofish)", Cascade Serpent Twofish)
+            , ("cascade.vec", "Cascade(Serpent,AES-256)", Cascade Serpent AES256)
+            , ("cascade.vec", "Cascade(Serpent,CAST-128)", Cascade Serpent CAST128)
+            , ("cast128.vec", "CAST-128", CAST128)
+            , ("cast256.vec", "CAST-256", CAST256)
+            , ("des.vec", "DES", DES)
+            , ("idea.vec", "IDEA", IDEA)
+            , ("kasumi.vec", "KASUMI", KASUMI)
+            , ("lion.vec", "Lion(SHA-160,RC4,64)", Lion SHA160 RC4 64)
+            , ("misty.vec", "MISTY1", MISTY1)
+            , ("noekeon.vec", "Noekeon", Noekeon)
+            , ("seed.vec", "SEED", SEED)
+            , ("serpent.vec", "Serpent", Serpent)
+            , ("shacal2.vec", "SHACAL2", SHACAL2)
+            -- dont support iterations
+            -- , ("sm4.vec", "SM4", SM4)
+            -- dont support tweak
+            -- , ("threefish.vec", "Threefish-512", Threefish512)
+            , ("twofish.vec", "Twofish", Twofish)
+            , ("xtea.vec", "XTEA", XTEA)
+            ] $ \ (file, algoName, cipherType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parseBlockCipherTestVector =<< "./third_party/botan/src/tests/data/block/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \ (key, i, o) -> do
+                        c <- newBlockCipher cipherType
+                        setBlockCipherKey c key
+                        o' <- encryptBlocks c i (V.length i `quot` blockCipherSize c)
+                        o' @=? o
+                        i' <- decryptBlocks c o (V.length o `quot` blockCipherSize c)
+                        i' @=? i
+
+    describe "Crypto.Cipher.CipherMode" $ do
+        forM_
+            [ ("cbc.vec", "DES/CBC/NoPadding", CBC_NoPadding DES)
+            , ("cbc.vec", "CAST-128/CBC/PKCS7", CBC_PKCS7 CAST128)
+            , ("cbc.vec", "DES/CBC/PKCS7", CBC_PKCS7 DES)
+            , ("cbc.vec", "TripleDES/CBC/NoPadding", CBC_NoPadding TripleDES)
+            , ("cbc.vec", "TripleDES/CBC/PKCS7", CBC_PKCS7 TripleDES)
+            , ("cbc.vec", "Blowfish/CBC/NoPadding", CBC_NoPadding Blowfish)
+            , ("cbc.vec", "Noekeon/CBC/PKCS7", CBC_PKCS7 Noekeon)
+            , ("cbc.vec", "DES/CBC/OneAndZeros", CBC_OneAndZeros DES)
+            , ("cbc.vec", "DES/CBC/CTS", CBC_CTS DES)
+            , ("cbc.vec", "AES-128/CBC/PKCS7", CBC_PKCS7 AES128)
+            , ("cbc.vec", "AES-128/CBC/NoPadding", CBC_NoPadding AES128)
+            , ("cbc.vec", "AES-192/CBC/NoPadding", CBC_NoPadding AES192)
+            , ("cbc.vec", "AES-256/CBC/NoPadding", CBC_NoPadding AES256)
+            , ("cbc.vec", "AES-128/CBC/CTS", CBC_CTS AES128)
+            , ("cbc.vec", "ARIA-256/CBC/NoPadding", CBC_NoPadding ARIA256)
+            , ("cfb.vec", "DES/CFB", CFB DES 0)
+            , ("cfb.vec", "DES/CFB(32)", CFB DES 32)
+            , ("cfb.vec", "DES/CFB(16)", CFB DES 16)
+            , ("cfb.vec", "DES/CFB(8)", CFB DES 8)
+            , ("cfb.vec", "TripleDES/CFB", CFB TripleDES 0)
+            , ("cfb.vec", "TripleDES/CFB(8)", CFB TripleDES 8)
+            , ("cfb.vec", "AES-128/CFB(8)", CFB AES128 8)
+            , ("cfb.vec", "AES-192/CFB(8)", CFB AES192 8)
+            , ("cfb.vec", "AES-256/CFB(8)", CFB AES256 8)
+            , ("cfb.vec", "AES-128/CFB", CFB AES128 0)
+            , ("cfb.vec", "AES-192/CFB", CFB AES192 0)
+            , ("cfb.vec", "AES-256/CFB", CFB AES256 0)
+            , ("xts.vec", "AES-128/XTS", XTS AES128)
+            , ("xts.vec", "AES-256/XTS", XTS AES256)
+            , ("xts.vec", "Twofish/XTS", XTS Twofish)
+            , ("xts.vec", "Serpent/XTS", XTS Serpent)
+            , ("xts.vec", "TripleDES/XTS", XTS TripleDES)
+            , ("xts.vec", "SHACAL2/XTS", XTS SHACAL2)
+            , ("xts.vec", "Threefish-512/XTS", XTS Threefish512)
+            ] $ \ (file, algoName, cipherType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parseCipherModeTestVector =<< "./third_party/botan/src/tests/data/modes/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \ (key, nonce, i, o) -> do
+
+                        c <- newCipher cipherType CipherEncrypt
+                        setCipherKey c key
+                        startCipher c nonce
+
+                        d <- newCipher cipherType CipherDecrypt
+                        setCipherKey d key
+                        startCipher d nonce
+
+                        o' <- finishCipher c i
+                        o' @=? o
+                        i' <- finishCipher d o
+                        i' @=? i
+
+    describe "Crypto.Cipher.StreamCipher(I)" $ do
+        forM_
+            [ ("ctr.vec", "CTR-BE(DES)", CTR_BE DES)
+            , ("ctr.vec", "CTR-BE(AES-128)", CTR_BE AES128)
+            ] $ \ (file, algoName, cipherType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parseCipherModeTestVector =<< "./third_party/botan/src/tests/data/modes/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \ (key, nonce, i, o) -> do
+
+                        c <- newStreamCipher cipherType CipherEncrypt
+                        setCipherKey c key
+                        startCipher c nonce
+
+                        d <- newStreamCipher cipherType CipherDecrypt
+                        setCipherKey d key
+                        startCipher d nonce
+
+                        o' <- finishCipher c i
+                        o' @?= o
+                        i' <- finishCipher d o
+                        i' @?= i
diff --git a/test/Z/Crypto/HashSpec.hs b/test/Z/Crypto/HashSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/HashSpec.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+module Z.Crypto.HashSpec where
+
+import           Control.Monad
+import           Test.Hspec
+import           Test.HUnit
+import           Z.IO
+import qualified Z.IO.FileSystem    as FS
+import qualified Z.Data.Text        as T
+import           Z.Crypto.Hash
+import           Utils
+
+spec :: Spec
+spec = describe "Crypto.Hash" $ do
+    forM_
+        [ ("adler32.vec", "Adler32", Adler32)
+        , ("blake2b.vec", "BLAKE2b(224)", BLAKE2b 224)
+        , ("blake2b.vec", "BLAKE2b(256)", BLAKE2b 256)
+        , ("blake2b.vec", "BLAKE2b(384)", BLAKE2b 384)
+        , ("blake2b.vec", "BLAKE2b(512)", BLAKE2b 512)
+        , ("comp4p.vec", "Comb4P(MD4,MD5)", Comb4P MD4 MD5)
+        , ("crc24.vec", "CRC24", CRC24)
+        , ("crc32.vec", "CRC32", CRC32)
+        , ("keccak.vec", "Keccak-1600(224)", Keccak1600_224)
+        , ("keccak.vec", "Keccak-1600(256)", Keccak1600_256)
+        , ("keccak.vec", "Keccak-1600(384)", Keccak1600_384)
+        , ("keccak.vec", "Keccak-1600(512)", Keccak1600_512)
+        , ("md4.vec", "MD4", MD4)
+        , ("md5.vec", "MD5", MD5)
+        , ("parallel.vec", "Parallel(MD5,SHA-160)", Parallel MD5 SHA160)
+        , ("parallel.vec", "Parallel(SHA-256,SHA-512)", Parallel SHA256 SHA512)
+        , ("ripemd160.vec", "RIPEMD-160", RIPEMD160)
+        , ("sha1.vec", "SHA-160", SHA160)
+        , ("sha2_32.vec", "SHA-224", SHA224)
+        , ("sha2_32.vec", "SHA-256", SHA256)
+        , ("sha2_64.vec", "SHA-384", SHA384)
+        , ("sha2_64.vec", "SHA-512", SHA512)
+        , ("sha2_64.vec", "SHA-512-256", SHA512_256)
+        , ("sha3.vec", "SHA-3(224)", SHA3_224)
+        , ("sha3.vec", "SHA-3(256)", SHA3_256)
+        , ("sha3.vec", "SHA-3(384)", SHA3_384)
+        , ("sha3.vec", "SHA-3(512)", SHA3_512)
+        , ("shake.vec", "SHAKE-128(128)", SHAKE128 128)
+        , ("shake.vec", "SHAKE-256(256)", SHAKE256 256)
+        , ("shake.vec", "SHAKE-128(1120)", SHAKE128 1120)
+        , ("shake.vec", "SHAKE-256(2000)", SHAKE256 2000)
+        , ("skein.vec", "Skein-512(224)", Skein512 224 "")
+        , ("skein.vec", "Skein-512(256)", Skein512 256 "")
+        , ("skein.vec", "Skein-512(384)", Skein512 384 "")
+        , ("skein.vec", "Skein-512(512)", Skein512 512 "")
+        , ("skein.vec", "Skein-512(512,Test)", Skein512 512 "Test")
+        , ("streebog.vec", "Streebog-256", Streebog256)
+        , ("streebog.vec", "Streebog-512", Streebog512)
+        , ("sm3.vec", "SM3", SM3)
+        , ("whirlpool.vec", "Whirlpool", Whirlpool)
+        ] $ \ (file, algoName, hashType) ->
+            it (T.unpack $ T.validate algoName) $ do
+                tvMap <- parseHashTestVector =<< "./third_party/botan/src/tests/data/hash/" `FS.join` file
+                tvs <- unwrap' "ENOTFOUND" "no algo found" $ lookup algoName tvMap
+                forM_ tvs $ \ (i, o) -> hash hashType i @?= o
diff --git a/test/Z/Crypto/KDFSpec.hs b/test/Z/Crypto/KDFSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/KDFSpec.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+module Z.Crypto.KDFSpec where
+import           Control.Monad
+import           Test.Hspec
+import           Test.HUnit
+import           Z.IO
+import qualified Z.IO.FileSystem as FS
+import           Z.Data.ASCII
+import           Z.Data.Vector.Hex
+import qualified Z.Data.Parser      as P
+import qualified Z.Data.Vector      as V
+import qualified Z.Data.Text        as T
+import qualified Z.Data.Vector.FlatMap as Map
+import           Z.Crypto.Cipher
+import           Z.Crypto.Hash
+import           Z.Crypto.KDF
+import           Utils
+
+spec :: Spec
+spec = do
+    describe "Crypto.KDF.KDF" $ do
+        forM_
+            [ ("hkdf.vec", "HKDF(HMAC(SHA-160))", HKDF (HMAC SHA160))
+            , ("hkdf.vec", "HKDF(HMAC(SHA-256))", HKDF (HMAC SHA256))
+            , ("hkdf.vec", "HKDF(HMAC(SHA-512))", HKDF (HMAC SHA512))
+            , ("hkdf.vec", "HKDF-Extract(HMAC(SHA-160))", HKDF_Extract (HMAC SHA160))
+            , ("hkdf.vec", "HKDF-Extract(HMAC(SHA-256))", HKDF_Extract (HMAC SHA256))
+            , ("hkdf.vec", "HKDF-Extract(HMAC(SHA-512))", HKDF_Extract (HMAC SHA512))
+            , ("hkdf.vec", "HKDF-Expand(HMAC(SHA-160))", HKDF_Expand (HMAC SHA160))
+            , ("hkdf.vec", "HKDF-Expand(HMAC(SHA-256))", HKDF_Expand (HMAC SHA256))
+            , ("hkdf.vec", "HKDF-Expand(HMAC(SHA-512))", HKDF_Expand (HMAC SHA512))
+            , ("kdf1_iso18033.vec", "KDF1-18033(SHA-160)", KDF1_18033 SHA160)
+            , ("kdf1_iso18033.vec", "KDF1-18033(SHA-256)", KDF1_18033 SHA256)
+            , ("kdf1.vec", "KDF1(SHA-160)", KDF1 SHA160)
+            , ("kdf2.vec", "KDF2(SHA-160)", KDF2 SHA160)
+            , ("kdf2.vec", "KDF2(SHA-256)", KDF2 SHA256)
+            , ("sp800_56a.vec", "SP800-56A(SHA-160)", SP800_56AHash SHA160)
+            , ("sp800_56a.vec", "SP800-56A(SHA-224)", SP800_56AHash SHA224)
+            , ("sp800_56a.vec", "SP800-56A(SHA-256)", SP800_56AHash SHA256)
+            , ("sp800_56a.vec", "SP800-56A(SHA-384)", SP800_56AHash SHA384)
+            , ("sp800_56a.vec", "SP800-56A(SHA-512)", SP800_56AHash SHA512)
+            , ("sp800_56a.vec", "SP800-56A(HMAC(SHA-160))", SP800_56AMAC (HMAC SHA160))
+            , ("sp800_56a.vec", "SP800-56A(HMAC(SHA-224))", SP800_56AMAC (HMAC SHA224))
+            , ("sp800_56a.vec", "SP800-56A(HMAC(SHA-256))", SP800_56AMAC (HMAC SHA256))
+            , ("sp800_56a.vec", "SP800-56A(HMAC(SHA-384))", SP800_56AMAC (HMAC SHA384))
+            , ("sp800_56a.vec", "SP800-56A(HMAC(SHA-512))", SP800_56AMAC (HMAC SHA512))
+            , ("sp800_56c.vec", "SP800-56C(HMAC(SHA-160))", SP800_56C (HMAC SHA160))
+            , ("sp800_56c.vec", "SP800-56C(HMAC(SHA-256))", SP800_56C (HMAC SHA256))
+            , ("sp800_56c.vec", "SP800-56C(HMAC(SHA-384))", SP800_56C (HMAC SHA384))
+            , ("sp800_56c.vec", "SP800-56C(HMAC(SHA-512))", SP800_56C (HMAC SHA512))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(HMAC(SHA-160))", SP800_108_Counter (HMAC SHA160))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(HMAC(SHA-256))", SP800_108_Counter (HMAC SHA256))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(HMAC(SHA-384))", SP800_108_Counter (HMAC SHA384))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(HMAC(SHA-512))", SP800_108_Counter (HMAC SHA512))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(CMAC(AES-128))", SP800_108_Counter (CMAC AES128))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(CMAC(AES-192))", SP800_108_Counter (CMAC AES192))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(CMAC(AES-256))", SP800_108_Counter (CMAC AES256))
+            , ("sp800_108_ctr.vec", "SP800-108-Counter(CMAC(TripleDES))", SP800_108_Counter (CMAC TripleDES))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(HMAC(SHA-160))", SP800_108_Feedback (HMAC SHA160))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(HMAC(SHA-256))", SP800_108_Feedback (HMAC SHA256))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(HMAC(SHA-384))", SP800_108_Feedback (HMAC SHA384))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(HMAC(SHA-512))", SP800_108_Feedback (HMAC SHA512))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(CMAC(AES-128))", SP800_108_Feedback (CMAC AES128))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(CMAC(AES-192))", SP800_108_Feedback (CMAC AES192))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(CMAC(AES-256))", SP800_108_Feedback (CMAC AES256))
+            , ("sp800_108_fb.vec", "SP800-108-Feedback(CMAC(TripleDES))", SP800_108_Feedback (CMAC TripleDES))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(HMAC(SHA-160))", SP800_108_Pipeline (HMAC SHA160))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(HMAC(SHA-256))", SP800_108_Pipeline (HMAC SHA256))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(HMAC(SHA-384))", SP800_108_Pipeline (HMAC SHA384))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(HMAC(SHA-512))", SP800_108_Pipeline (HMAC SHA512))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(CMAC(AES-128))", SP800_108_Pipeline (CMAC AES128))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(CMAC(AES-192))", SP800_108_Pipeline (CMAC AES192))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(CMAC(AES-256))", SP800_108_Pipeline (CMAC AES256))
+            , ("sp800_108_pipe.vec", "SP800-108-Pipeline(CMAC(TripleDES))", SP800_108_Pipeline (CMAC TripleDES))
+            , ("tls_prf.vec", "TLS-PRF", TLS_PRF)
+            , ("tls_prf.vec", "TLS-12-PRF(HMAC(SHA-224))", TLS_12_PRF (HMAC SHA224))
+            , ("tls_prf.vec", "TLS-12-PRF(HMAC(SHA-256))", TLS_12_PRF (HMAC SHA256))
+            , ("tls_prf.vec", "TLS-12-PRF(HMAC(SHA-384))", TLS_12_PRF (HMAC SHA384))
+            , ("tls_prf.vec", "TLS-12-PRF(HMAC(SHA-512))", TLS_12_PRF (HMAC SHA512))
+            -- dont support iterations
+            -- , ("x942", "X9.42-PRF(KeyWrap.TripleDES)", ...)
+            -- , ("x942", "X9.42-PRF(1.2.840.113549.1.9.16.3.7)", ...)
+            ] $ \ (file, algoName, kdfType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parseKDFTestVector =<< "./third_party/botan/src/tests/data/kdf/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \ (salt, label, secret, o) -> do
+                        o' <- kdf kdfType (V.length o) secret salt label
+                        o' @?= o
+
+    describe "Crypto.KDF.PBKDF" $ do
+        forM_
+            [ -- PBKDF1 is not recommanded and not supoorted
+              -- ("pbkdf1.vec", "PBKDF1(SHA-160)", PBKDF1 SHA160)
+              ("pbkdf2.vec", "PBKDF2(HMAC(SHA-160))", PBKDF2 (HMAC SHA160))
+            , ("pbkdf2.vec", "PBKDF2(HMAC(SHA-256))", PBKDF2 (HMAC SHA256))
+            , ("pbkdf2.vec", "PBKDF2(HMAC(SHA-384))", PBKDF2 (HMAC SHA384))
+            , ("pbkdf2.vec", "PBKDF2(HMAC(SHA-512))", PBKDF2 (HMAC SHA512))
+            , ("pbkdf2.vec", "PBKDF2(CMAC(Blowfish))", PBKDF2 (CMAC Blowfish))
+            , ("pgp_s2k.vec", "OpenPGP-S2K(SHA-160)", OpenPGP_S2K SHA160)
+            ] $ \ (file, algoName, pbkdfType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parsePBKDFTestVector =<< "./third_party/botan/src/tests/data/pbkdf/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \ (salt, iterations, passphrase, o) -> do
+                        o' <- pbkdf (pbkdfType iterations) (V.length o) passphrase salt
+                        o' @?= o
diff --git a/test/Z/Crypto/KeyWrapSpec.hs b/test/Z/Crypto/KeyWrapSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/KeyWrapSpec.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Z.Crypto.KeyWrapSpec where
+
+import           Control.Monad
+import           Test.HUnit
+import           Test.Hspec
+import           Utils
+import           Z.Crypto.KeyWrap
+import qualified Z.Data.Vector     as V
+import qualified Z.Data.Vector.Hex as H
+import qualified Z.IO.FileSystem   as FS
+
+spec :: Spec
+spec = describe "Crypto.KeyWrap" $ do
+    it "RFC 3394" $ do
+        tvMap <- parseKeyWrapVec "./third_party/botan/src/tests/data/keywrap/rfc3394.vec"
+        forM_ tvMap $ \ (key, kek, o) -> do
+            o'   <- keyWrap key kek
+            key' <- keyUnwrap o kek
+            (o', key') @=? (o, key)
diff --git a/test/Z/Crypto/MACSpec.hs b/test/Z/Crypto/MACSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/MACSpec.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+module Z.Crypto.MACSpec where
+import           Control.Monad
+import           Test.Hspec
+import           Test.HUnit
+import           Z.IO
+import qualified Z.IO.FileSystem as FS
+import           Z.Data.ASCII
+import           Z.Data.Vector.Hex
+import qualified Z.Data.Parser      as P
+import qualified Z.Data.Vector      as V
+import qualified Z.Data.Text        as T
+import qualified Z.Data.Vector.FlatMap as Map
+import           Z.Crypto.Cipher
+import           Z.Crypto.Hash
+import           Z.Crypto.MAC
+import           Utils
+
+spec :: Spec
+spec = do
+    describe "Crypto.MAC" $ do
+        forM_
+            [ ("cbcmac.vec", "CBC-MAC(DES)", CBC_MAC DES)
+            , ("cbcmac.vec", "CBC-MAC(AES-128)", CBC_MAC AES128)
+            , ("cmac.vec", "CMAC(AES-128)", CMAC AES128)
+            , ("cmac.vec", "CMAC(AES-192)", CMAC AES192)
+            , ("cmac.vec", "CMAC(AES-256)", CMAC AES256)
+            , ("cmac.vec", "CMAC(Blowfish)", CMAC Blowfish)
+            , ("cmac.vec", "CMAC(Threefish-512)", CMAC Threefish512)
+            , ("gmac.vec", "GMAC(AES-128)", GMAC AES128)
+            , ("gmac.vec", "GMAC(AES-192)", GMAC AES192)
+            , ("gmac.vec", "GMAC(AES-256)", GMAC AES256)
+            , ("hmac.vec", "HMAC(MD5)", HMAC MD5)
+            , ("hmac.vec", "HMAC(SHA-160)", HMAC SHA160)
+            , ("hmac.vec", "HMAC(RIPEMD-160)", HMAC RIPEMD160)
+            , ("hmac.vec", "HMAC(SHA-224)", HMAC SHA224)
+            , ("hmac.vec", "HMAC(SHA-256)", HMAC SHA256)
+            , ("hmac.vec", "HMAC(SHA-384)", HMAC SHA384)
+            , ("hmac.vec", "HMAC(SHA-512)", HMAC SHA512)
+            , ("hmac.vec", "HMAC(SHA-512-256)", HMAC SHA512_256)
+            , ("hmac.vec", "HMAC(SHA-3(224))", HMAC SHA3_224)
+            , ("hmac.vec", "HMAC(SHA-3(256))", HMAC SHA3_256)
+            , ("hmac.vec", "HMAC(SHA-3(384))", HMAC SHA3_384)
+            , ("hmac.vec", "HMAC(SHA-3(512))", HMAC SHA3_512)
+            , ("poly1305.vec", "Poly1305", Poly1305)
+            , ("siphash.vec", "SipHash(2,4)", SipHash 2 4)
+            , ("x919_mac.vec", "X9.19-MAC", X9'19_MAC)
+            ] $ \ (file, algoName, macType) ->
+                it (T.unpack $ T.validate algoName) $ do
+                    tvMap <- parseMACTestVector =<< "./third_party/botan/src/tests/data/mac/" `FS.join` file
+                    tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap
+                    forM_ tvs $ \(miv, key, i, o) -> case miv of
+                        Just _  -> return ()  -- mac->start(nonce) is not supported yet.
+                        Nothing -> do
+                            m <- newMAC macType
+                            setKeyMAC m key
+                            updateMAC m i
+                            o' <- finalMAC m
+                            o' @?= o
+
+                            -- test clear
+                            clearMAC m
+                            setKeyMAC m key
+                            updateMAC m "some discarded input"
+                            clearMAC m
+                            setKeyMAC m key
+                            updateMAC m i
+                            o'' <- finalMAC m
+                            o'' @=? o
+
+                            -- test multiple update
+                            let ilen = V.length i
+                            when (ilen > 2) $ do
+                                clearMAC m
+                                setKeyMAC m key
+                                updateMAC m $ V.slice 0 1 i
+                                updateMAC m $ V.slice 1 (ilen - 2) i
+                                updateMAC m $ V.slice (ilen - 1) 1 i
+                                o''' <- finalMAC m
+                                o''' @=? o
diff --git a/test/Z/Crypto/PwdHashSpec.hs b/test/Z/Crypto/PwdHashSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/Crypto/PwdHashSpec.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+module Z.Crypto.PwdHashSpec where
+
+import Control.Monad (forM_)
+import Test.HUnit
+import Test.Hspec (Spec, describe, it)
+import Utils (parsePasswdHashTestVector)
+import Z.Crypto.PwdHash
+import Z.Crypto.RNG (RNGType (SystemRNG), newRNG)
+
+spec :: Spec
+spec = describe "Crypto.PwdHash" $ do
+    it "Bcrypt" $ do
+        tvMap <- parsePasswdHashTestVector "./third_party/botan/src/tests/data/passhash/bcrypt.vec"
+        forM_ tvMap $ \ (passwd, passhash) -> do
+            rng <- newRNG SystemRNG
+            ret <- genBcrypt passwd rng 8
+            b <- validBcrypt passwd passhash
+            b @?= True
diff --git a/third_party/botan/botan_all.cpp b/third_party/botan/botan_all.cpp
new file mode 100644
# file too large to diff: third_party/botan/botan_all.cpp
diff --git a/third_party/botan/build/include/external/cabal.placeholder b/third_party/botan/build/include/external/cabal.placeholder
new file mode 100644
--- /dev/null
+++ b/third_party/botan/build/include/external/cabal.placeholder
diff --git a/third_party/botan/configure.py b/third_party/botan/configure.py
new file mode 100644
--- /dev/null
+++ b/third_party/botan/configure.py
@@ -0,0 +1,3403 @@
+#!/usr/bin/env python
+
+"""
+Configuration program for botan
+
+(C) 2009-2020 Jack Lloyd
+(C) 2015,2016,2017 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+
+This script is regularly tested with CPython 2.7 and 3.5, and
+occasionally tested with CPython 2.6 and PyPy 4.
+
+Support for CPython 2.6 will be dropped eventually, but is kept up for as
+long as reasonably convenient.
+
+CPython 2.5 and earlier are not supported.
+
+On Jython target detection does not work (use --os and --cpu).
+"""
+
+import collections
+import copy
+import json
+import sys
+import os
+import os.path
+import platform
+import re
+import shlex
+import shutil
+import subprocess
+import traceback
+import logging
+import time
+import errno
+import optparse # pylint: disable=deprecated-module
+
+# An error caused by and to be fixed by the user, e.g. invalid command line argument
+class UserError(Exception):
+    pass
+
+
+# An error caused by bugs in this script or when reading/parsing build data files
+# Those are not expected to be fixed by the user of this script
+class InternalError(Exception):
+    pass
+
+
+def flatten(l):
+    return sum(l, [])
+
+def normalize_source_path(source):
+    """
+    cmake needs this, and nothing else minds
+    """
+    return os.path.normpath(source).replace('\\', '/')
+
+def parse_version_file(version_path):
+    version_file = open(version_path)
+    key_and_val = re.compile(r"([a-z_]+) = ([a-zA-Z0-9:\-\']+)")
+
+    results = {}
+    for line in version_file.readlines():
+        if not line or line[0] == '#':
+            continue
+        match = key_and_val.match(line)
+        if match:
+            key = match.group(1)
+            val = match.group(2)
+
+            if val == 'None':
+                val = None
+            elif val.startswith("'") and val.endswith("'"):
+                val = val[1:len(val)-1]
+            else:
+                val = int(val)
+
+            results[key] = val
+    return results
+
+class Version(object):
+    """
+    Version information are all static members
+    """
+    data = {}
+
+    @staticmethod
+    def get_data():
+        if not Version.data:
+            root_dir = os.path.dirname(os.path.realpath(__file__))
+            Version.data = parse_version_file(os.path.join(root_dir, 'src/build-data/version.txt'))
+
+            suffix = Version.data["release_suffix"]
+            if suffix != "":
+                suffix_re = re.compile('-(alpha|beta|rc)[0-9]+')
+
+                if not suffix_re.match(suffix):
+                    raise Exception("Unexpected version suffix '%s'" % (suffix))
+        return Version.data
+
+    @staticmethod
+    def major():
+        return Version.get_data()["release_major"]
+
+    @staticmethod
+    def minor():
+        return Version.get_data()["release_minor"]
+
+    @staticmethod
+    def patch():
+        return Version.get_data()["release_patch"]
+
+    @staticmethod
+    def suffix():
+        return Version.get_data()["release_suffix"]
+
+    @staticmethod
+    def packed():
+         # Used on macOS for dylib versioning
+        return Version.major() * 1000 + Version.minor()
+
+    @staticmethod
+    def so_rev():
+        return Version.get_data()["release_so_abi_rev"]
+
+    @staticmethod
+    def release_type():
+        return Version.get_data()["release_type"]
+
+    @staticmethod
+    def datestamp():
+        return Version.get_data()["release_datestamp"]
+
+    @staticmethod
+    def as_string():
+        return '%d.%d.%d%s' % (Version.major(), Version.minor(), Version.patch(), Version.suffix())
+
+    @staticmethod
+    def vc_rev():
+        # Lazy load to ensure _local_repo_vc_revision() does not run before logger is set up
+        if Version.get_data()["release_vc_rev"] is None:
+            Version.data["release_vc_rev"] = Version._local_repo_vc_revision()
+        return Version.get_data()["release_vc_rev"]
+
+    @staticmethod
+    def _local_repo_vc_revision():
+        vc_command = ['git', 'rev-parse', 'HEAD']
+        cmdname = vc_command[0]
+
+        try:
+            vc = subprocess.Popen(
+                vc_command,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+                universal_newlines=True)
+            (stdout, stderr) = vc.communicate()
+
+            if vc.returncode != 0:
+                logging.debug('Error getting rev from %s - %d (%s)',
+                              cmdname, vc.returncode, stderr)
+                return 'unknown'
+
+            rev = str(stdout).strip()
+            logging.debug('%s reported revision %s', cmdname, rev)
+
+            return '%s:%s' % (cmdname, rev)
+        except OSError as e:
+            logging.debug('Error getting rev from %s - %s' % (cmdname, e.strerror))
+            return 'unknown'
+
+
+
+class SourcePaths(object):
+    """
+    A collection of paths defined by the project structure and
+    independent of user configurations.
+    All paths are relative to the base_dir, which may be relative as well (e.g. ".")
+    """
+
+    def __init__(self, base_dir):
+        self.base_dir = base_dir
+        self.doc_dir = os.path.join(self.base_dir, 'doc')
+        self.src_dir = os.path.join(self.base_dir, 'src')
+
+        # dirs in src/
+        self.build_data_dir = os.path.join(self.src_dir, 'build-data')
+        self.configs_dir = os.path.join(self.src_dir, 'configs')
+        self.lib_dir = os.path.join(self.src_dir, 'lib')
+        self.python_dir = os.path.join(self.src_dir, 'python')
+        self.scripts_dir = os.path.join(self.src_dir, 'scripts')
+
+        # subdirs of src/
+        self.test_data_dir = os.path.join(self.src_dir, 'tests/data')
+        self.sphinx_config_dir = os.path.join(self.configs_dir, 'sphinx')
+
+
+class BuildPaths(object): # pylint: disable=too-many-instance-attributes
+    """
+    Constructor
+    """
+    def __init__(self, source_paths, options, modules):
+        self.build_dir = os.path.join(options.with_build_dir, 'build')
+
+        self.libobj_dir = os.path.join(self.build_dir, 'obj', 'lib')
+        self.cliobj_dir = os.path.join(self.build_dir, 'obj', 'cli')
+        self.testobj_dir = os.path.join(self.build_dir, 'obj', 'test')
+
+        self.doc_output_dir = os.path.join(self.build_dir, 'docs')
+        self.handbook_output_dir = os.path.join(self.doc_output_dir, 'handbook')
+        self.doc_output_dir_doxygen = os.path.join(self.doc_output_dir, 'doxygen') if options.with_doxygen else None
+
+        self.include_dir = os.path.join(self.build_dir, 'include')
+        self.botan_include_dir = os.path.join(self.include_dir, 'botan')
+        self.internal_include_dir = os.path.join(self.botan_include_dir, 'internal')
+        self.external_include_dir = os.path.join(self.include_dir, 'external')
+
+        self.internal_headers = sorted(flatten([m.internal_headers() for m in modules]))
+        self.external_headers = sorted(flatten([m.external_headers() for m in modules]))
+
+        # this is overwritten if amalgamation is used
+        self.lib_sources = [normalize_source_path(s) for s in sorted(flatten([mod.sources() for mod in modules]))]
+
+        self.public_headers = sorted(flatten([m.public_headers() for m in modules]))
+
+        def find_sources_in(basedir, srcdir):
+            for (dirpath, _, filenames) in os.walk(os.path.join(basedir, srcdir)):
+                for filename in filenames:
+                    if filename.endswith('.cpp') and not filename.startswith('.'):
+                        yield os.path.join(dirpath, filename)
+
+        def find_headers_in(basedir, srcdir):
+            for (dirpath, _, filenames) in os.walk(os.path.join(basedir, srcdir)):
+                for filename in filenames:
+                    if filename.endswith('.h') and not filename.startswith('.'):
+                        yield os.path.join(dirpath, filename)
+
+        self.cli_sources = [normalize_source_path(s) for s in find_sources_in(source_paths.src_dir, 'cli')]
+        self.cli_headers = [normalize_source_path(s) for s in find_headers_in(source_paths.src_dir, 'cli')]
+        self.test_sources = [normalize_source_path(s) for s in find_sources_in(source_paths.src_dir, 'tests')]
+
+        if options.build_fuzzers:
+            self.fuzzer_sources = list(find_sources_in(source_paths.src_dir, 'fuzzer'))
+            self.fuzzer_output_dir = os.path.join(self.build_dir, 'fuzzer')
+            self.fuzzobj_dir = os.path.join(self.build_dir, 'obj', 'fuzzer')
+        else:
+            self.fuzzer_sources = None
+            self.fuzzer_output_dir = None
+            self.fuzzobj_dir = None
+
+    def build_dirs(self):
+        out = [
+            self.libobj_dir,
+            self.cliobj_dir,
+            self.testobj_dir,
+            self.botan_include_dir,
+            self.internal_include_dir,
+            self.external_include_dir,
+            self.handbook_output_dir,
+        ]
+        if self.doc_output_dir_doxygen:
+            out += [self.doc_output_dir_doxygen]
+        if self.fuzzer_output_dir:
+            out += [self.fuzzobj_dir]
+            out += [self.fuzzer_output_dir]
+        return out
+
+    def format_include_paths(self, cc, external_includes):
+        dash_i = cc.add_include_dir_option
+        output = dash_i + self.include_dir
+        if self.external_headers:
+            output += ' ' + dash_i + self.external_include_dir
+        for external_include in external_includes:
+            output += ' ' + dash_i + external_include
+        return output
+
+    def src_info(self, typ):
+        if typ == 'lib':
+            return (self.lib_sources, self.libobj_dir)
+        elif typ == 'cli':
+            return (self.cli_sources, self.cliobj_dir)
+        elif typ == 'test':
+            return (self.test_sources, self.testobj_dir)
+        elif typ == 'fuzzer':
+            return (self.fuzzer_sources, self.fuzzobj_dir)
+        else:
+            raise InternalError("Unknown src info type '%s'" % (typ))
+
+ACCEPTABLE_BUILD_TARGETS = ["static", "shared", "cli", "tests", "bogo_shim"]
+
+def process_command_line(args): # pylint: disable=too-many-locals,too-many-statements
+    """
+    Handle command line options
+    Do not use logging in this method as command line options need to be
+    available before logging is setup.
+    """
+
+    parser = optparse.OptionParser(
+        formatter=optparse.IndentedHelpFormatter(max_help_position=50),
+        version=Version.as_string())
+
+    parser.add_option('--verbose', action='store_true', default=False,
+                      help='Show debug messages')
+    parser.add_option('--quiet', action='store_true', default=False,
+                      help='Show only warnings and errors')
+
+    target_group = optparse.OptionGroup(parser, 'Target options')
+
+    target_group.add_option('--cpu', help='set the target CPU architecture')
+
+    target_group.add_option('--os', help='set the target operating system')
+
+    target_group.add_option('--cc', dest='compiler', help='set the desired build compiler')
+
+    target_group.add_option('--cc-min-version', dest='cc_min_version', default=None,
+                            metavar='MAJOR.MINOR',
+                            help='Set the minimal version of the target compiler. ' \
+                                 'Use --cc-min-version=0.0 to support all compiler versions. ' \
+                                 'Default is auto detection.')
+
+    target_group.add_option('--cc-bin', dest='compiler_binary', metavar='BINARY',
+                            help='set path to compiler binary')
+
+    target_group.add_option('--cc-abi-flags', metavar='FLAGS', default='',
+                            help='set compiler ABI flags')
+
+    target_group.add_option('--cxxflags', metavar='FLAGS', default=None,
+                            help='override all compiler flags')
+
+    target_group.add_option('--extra-cxxflags', metavar='FLAGS', default=[], action='append',
+                            help='set extra compiler flags')
+
+    target_group.add_option('--ldflags', metavar='FLAGS',
+                            help='set linker flags', default=None)
+
+    target_group.add_option('--ar-command', dest='ar_command', metavar='AR', default=None,
+                            help='set path to static archive creator')
+
+    target_group.add_option('--ar-options', dest='ar_options', metavar='AR_OPTIONS', default=None,
+                            help='set options for ar')
+
+    target_group.add_option('--msvc-runtime', metavar='RT', default=None,
+                            help='specify MSVC runtime (MT, MD, MTd, MDd)')
+
+    target_group.add_option('--with-endian', metavar='ORDER', default=None,
+                            help='override byte order guess')
+
+    target_group.add_option('--with-os-features', action='append', metavar='FEAT',
+                            help='specify OS features to use')
+    target_group.add_option('--without-os-features', action='append', metavar='FEAT',
+                            help='specify OS features to disable')
+
+    isa_extensions = [
+        'SSE2', 'SSSE3', 'SSE4.1', 'SSE4.2', 'AVX2', 'BMI2', 'RDRAND', 'RDSEED',
+        'AES-NI', 'SHA-NI',
+        'AltiVec', 'NEON', 'ARMv8 Crypto', 'POWER Crypto']
+
+    for isa_extn_name in isa_extensions:
+        isa_extn = isa_extn_name.lower().replace(' ', '')
+
+        target_group.add_option('--disable-%s' % (isa_extn),
+                                help='disable %s intrinsics' % (isa_extn_name),
+                                action='append_const',
+                                const=isa_extn.replace('-', '').replace('.', '').replace(' ', ''),
+                                dest='disable_intrinsics')
+
+    build_group = optparse.OptionGroup(parser, 'Build options')
+
+    build_group.add_option('--system-cert-bundle', metavar='PATH', default=None,
+                           help='set path to trusted CA bundle')
+
+    build_group.add_option('--with-debug-info', action='store_true', default=False, dest='with_debug_info',
+                           help='include debug symbols')
+
+    build_group.add_option('--with-sanitizers', action='store_true', default=False, dest='with_sanitizers',
+                           help='enable ASan/UBSan checks')
+
+    build_group.add_option('--enable-sanitizers', metavar='SAN', default='',
+                           help='enable specific sanitizers')
+
+    build_group.add_option('--with-stack-protector', dest='with_stack_protector',
+                           action='store_false', default=None, help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--without-stack-protector', dest='with_stack_protector',
+                           action='store_false', help='disable stack smashing protections')
+
+    build_group.add_option('--with-coverage', action='store_true', default=False, dest='with_coverage',
+                           help='add coverage info and disable opts')
+
+    build_group.add_option('--with-coverage-info', action='store_true', default=False, dest='with_coverage_info',
+                           help='add coverage info')
+
+    build_group.add_option('--enable-shared-library', dest='build_shared_lib',
+                           action='store_true', default=None,
+                           help=optparse.SUPPRESS_HELP)
+    build_group.add_option('--disable-shared-library', dest='build_shared_lib',
+                           action='store_false',
+                           help='disable building shared library')
+
+    build_group.add_option('--enable-static-library', dest='build_static_lib',
+                           action='store_true', default=None,
+                           help=optparse.SUPPRESS_HELP)
+    build_group.add_option('--disable-static-library', dest='build_static_lib',
+                           action='store_false',
+                           help='disable building static library')
+
+    build_group.add_option('--optimize-for-size', dest='optimize_for_size',
+                           action='store_true', default=False,
+                           help='optimize for code size')
+
+    build_group.add_option('--no-optimizations', dest='no_optimizations',
+                           action='store_true', default=False,
+                           help='disable all optimizations (for debugging)')
+
+    build_group.add_option('--debug-mode', action='store_true', default=False, dest='debug_mode',
+                           help='enable debug info, disable optimizations')
+
+    build_group.add_option('--amalgamation', dest='amalgamation',
+                           default=False, action='store_true',
+                           help='use amalgamation to build')
+
+    build_group.add_option('--name-amalgamation', metavar='NAME', default='botan_all',
+                           help='specify alternate name for amalgamation files')
+
+    build_group.add_option('--with-build-dir', metavar='DIR', default='',
+                           help='setup the build in DIR')
+
+    build_group.add_option('--with-external-includedir', metavar='DIR', default=[],
+                           help='use DIR for external includes', action='append')
+
+    build_group.add_option('--with-external-libdir', metavar='DIR', default=[],
+                           help='use DIR for external libs', action='append')
+
+    build_group.add_option('--define-build-macro', metavar='DEFINE', default=[],
+                           help='set compile-time pre-processor definition like KEY[=VALUE]', action='append')
+
+    build_group.add_option('--with-sysroot-dir', metavar='DIR', default='',
+                           help='use DIR for system root while cross-compiling')
+
+    build_group.add_option('--with-openmp', default=False, action='store_true',
+                           help='enable use of OpenMP')
+
+    link_methods = ['symlink', 'hardlink', 'copy']
+    build_group.add_option('--link-method', default=None, metavar='METHOD',
+                           choices=link_methods,
+                           help='choose how links to include headers are created (%s)' % ', '.join(link_methods))
+
+    build_group.add_option('--with-local-config',
+                           dest='local_config', metavar='FILE',
+                           help='include the contents of FILE into build.h')
+
+    build_group.add_option('--distribution-info', metavar='STRING',
+                           help='distribution specific version',
+                           default='unspecified')
+
+    build_group.add_option('--maintainer-mode', dest='maintainer_mode',
+                           action='store_true', default=False,
+                           help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--werror-mode', dest='werror_mode',
+                           action='store_true', default=False,
+                           help="Prohibit compiler warnings")
+
+    build_group.add_option('--no-store-vc-rev', action='store_true', default=False,
+                           help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--no-install-python-module', action='store_true', default=False,
+                           help='skip installing Python module')
+
+    build_group.add_option('--with-python-versions', dest='python_version',
+                           metavar='N.M',
+                           default='%d.%d' % (sys.version_info[0], sys.version_info[1]),
+                           help='where to install botan2.py (def %default)')
+
+    build_group.add_option('--disable-cc-tests', dest='enable_cc_tests',
+                           default=True, action='store_false',
+                           help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--with-valgrind', help='use valgrind API',
+                           dest='with_valgrind', action='store_true', default=False)
+
+    # Cmake and bakefile options are hidden as they should not be used by end users
+    build_group.add_option('--with-cmake', action='store_true',
+                           default=False, help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--with-bakefile', action='store_true',
+                           default=False, help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--unsafe-fuzzer-mode', action='store_true', default=False,
+                           help='Disable essential checks for testing')
+
+    build_group.add_option('--build-fuzzers', dest='build_fuzzers',
+                           metavar='TYPE', default=None,
+                           help='Build fuzzers (afl, libfuzzer, klee, test)')
+
+    build_group.add_option('--with-fuzzer-lib', metavar='LIB', default=None, dest='fuzzer_lib',
+                           help='additionally link in LIB')
+
+    build_group.add_option('--test-mode', action='store_true', default=False,
+                           help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--with-debug-asserts', action='store_true', default=False,
+                           help=optparse.SUPPRESS_HELP)
+
+    build_group.add_option('--build-targets', default=None, dest="build_targets", action='append',
+                           help="build specific targets and tools (%s)" % ', '.join(ACCEPTABLE_BUILD_TARGETS))
+
+    build_group.add_option('--with-pkg-config', action='store_true', default=None,
+                           help=optparse.SUPPRESS_HELP)
+    build_group.add_option('--without-pkg-config', dest='with_pkg_config', action='store_false',
+                           help=optparse.SUPPRESS_HELP)
+    build_group.add_option('--boost-library-name', dest='boost_libnames', default=[],
+                           help="file name of some boost library to link", action='append')
+
+    docs_group = optparse.OptionGroup(parser, 'Documentation Options')
+
+    docs_group.add_option('--with-documentation', action='store_true',
+                          help=optparse.SUPPRESS_HELP)
+
+    docs_group.add_option('--without-documentation', action='store_false',
+                          default=True, dest='with_documentation',
+                          help='Skip building/installing documentation')
+
+    docs_group.add_option('--with-sphinx', action='store_true',
+                          default=None, help='Use Sphinx')
+
+    docs_group.add_option('--without-sphinx', action='store_false',
+                          dest='with_sphinx', help=optparse.SUPPRESS_HELP)
+
+    docs_group.add_option('--with-pdf', action='store_true',
+                          default=False, help='Use Sphinx to generate PDF doc')
+
+    docs_group.add_option('--without-pdf', action='store_false',
+                          dest='with_pdf', help=optparse.SUPPRESS_HELP)
+
+    docs_group.add_option('--with-rst2man', action='store_true',
+                          default=None, help='Use rst2man to generate man page')
+
+    docs_group.add_option('--without-rst2man', action='store_false',
+                          dest='with_rst2man', help=optparse.SUPPRESS_HELP)
+
+    docs_group.add_option('--with-doxygen', action='store_true',
+                          default=False, help='Use Doxygen')
+
+    docs_group.add_option('--without-doxygen', action='store_false',
+                          dest='with_doxygen', help=optparse.SUPPRESS_HELP)
+
+    mods_group = optparse.OptionGroup(parser, 'Module selection')
+
+    mods_group.add_option('--module-policy', dest='module_policy',
+                          help="module policy file (see src/build-data/policy)",
+                          metavar='POL', default=None)
+
+    mods_group.add_option('--enable-modules', dest='enabled_modules',
+                          metavar='MODS', action='append',
+                          help='enable specific modules')
+    mods_group.add_option('--disable-modules', dest='disabled_modules',
+                          metavar='MODS', action='append',
+                          help='disable specific modules')
+    mods_group.add_option('--no-autoload', action='store_true', default=False,
+                          help=optparse.SUPPRESS_HELP)
+    mods_group.add_option('--minimized-build', action='store_true', dest='no_autoload',
+                          help='minimize build')
+
+    # Should be derived from info.txt but this runs too early
+    third_party = ['boost', 'bzip2', 'lzma', 'openssl', 'commoncrypto', 'sqlite3', 'zlib', 'tpm']
+
+    for mod in third_party:
+        mods_group.add_option('--with-%s' % (mod),
+                              help=('use %s' % (mod)) if mod in third_party else optparse.SUPPRESS_HELP,
+                              action='append_const',
+                              const=mod,
+                              dest='enabled_modules')
+
+        mods_group.add_option('--without-%s' % (mod),
+                              help=optparse.SUPPRESS_HELP,
+                              action='append_const',
+                              const=mod,
+                              dest='disabled_modules')
+
+    mods_group.add_option('--with-everything', help=optparse.SUPPRESS_HELP,
+                          action='store_true', default=False)
+
+    install_group = optparse.OptionGroup(parser, 'Installation options')
+
+    install_group.add_option('--program-suffix', metavar='SUFFIX',
+                             help='append string to program names')
+    install_group.add_option('--library-suffix', metavar='SUFFIX', default='',
+                             help='append string to library names')
+
+    install_group.add_option('--prefix', metavar='DIR',
+                             help='set the install prefix')
+    install_group.add_option('--docdir', metavar='DIR',
+                             help='set the doc install dir')
+    install_group.add_option('--bindir', metavar='DIR',
+                             help='set the binary install dir')
+    install_group.add_option('--libdir', metavar='DIR',
+                             help='set the library install dir')
+    install_group.add_option('--mandir', metavar='DIR',
+                             help='set the install dir for man pages')
+    install_group.add_option('--includedir', metavar='DIR',
+                             help='set the include file install dir')
+
+    info_group = optparse.OptionGroup(parser, 'Informational')
+
+    info_group.add_option('--list-modules', dest='list_modules',
+                          action='store_true',
+                          help='list available modules and exit')
+
+    info_group.add_option('--list-os-features', dest='list_os_features',
+                          action='store_true',
+                          help='list available OS features and exit')
+
+    parser.add_option_group(target_group)
+    parser.add_option_group(build_group)
+    parser.add_option_group(docs_group)
+    parser.add_option_group(mods_group)
+    parser.add_option_group(install_group)
+    parser.add_option_group(info_group)
+
+    # These exist only for autoconf compatibility (requested by zw for mtn)
+    compat_with_autoconf_options = [
+        'datadir',
+        'datarootdir',
+        'dvidir',
+        'exec-prefix',
+        'htmldir',
+        'infodir',
+        'libexecdir',
+        'localedir',
+        'localstatedir',
+        'oldincludedir',
+        'pdfdir',
+        'psdir',
+        'sbindir',
+        'sharedstatedir',
+        'sysconfdir'
+        ]
+
+    for opt in compat_with_autoconf_options:
+        parser.add_option('--' + opt, help=optparse.SUPPRESS_HELP)
+
+    (options, args) = parser.parse_args(args)
+
+    if args != []:
+        raise UserError('Unhandled option(s): ' + ' '.join(args))
+
+    if options.with_endian not in [None, 'little', 'big']:
+        raise UserError('Bad value to --with-endian "%s"' % (options.with_endian))
+
+    if options.debug_mode:
+        options.no_optimizations = True
+        options.with_debug_info = True
+
+    if options.with_coverage:
+        options.with_coverage_info = True
+        options.no_optimizations = True
+
+    def parse_multiple_enable(modules):
+        if modules is None:
+            return []
+
+        return sorted({m for m in flatten([s.split(',') for s in modules]) if m != ''})
+
+    options.enabled_modules = parse_multiple_enable(options.enabled_modules)
+    options.disabled_modules = parse_multiple_enable(options.disabled_modules)
+
+    options.with_os_features = parse_multiple_enable(options.with_os_features)
+    options.without_os_features = parse_multiple_enable(options.without_os_features)
+
+    options.disable_intrinsics = parse_multiple_enable(options.disable_intrinsics)
+
+    return options
+
+def take_options_from_env(options):
+    # Take some values from environment, if not set on command line
+
+    def update_from_env(val, var, name):
+        if val is None:
+            val = os.getenv(var)
+            if val is not None:
+                logging.info('Implicit --%s=%s due to environment variable %s', name, val, var)
+
+        return val
+
+    if os.getenv('CXX') and options.compiler_binary is None and options.compiler is not None:
+        logging.info('CXX environment variable is set which will override compiler path')
+
+    options.ar_command = update_from_env(options.ar_command, 'AR', 'ar-command')
+    options.ar_options = update_from_env(options.ar_options, 'AR_OPTIONS', 'ar-options')
+    options.compiler_binary = update_from_env(options.compiler_binary, 'CXX', 'cc-bin')
+    options.cxxflags = update_from_env(options.cxxflags, 'CXXFLAGS', 'cxxflags')
+    options.ldflags = update_from_env(options.ldflags, 'LDFLAGS', 'ldflags')
+
+class LexResult(object):
+    pass
+
+
+class LexerError(InternalError):
+    def __init__(self, msg, lexfile, line):
+        super(LexerError, self).__init__(msg)
+        self.msg = msg
+        self.lexfile = lexfile
+        self.line = line
+
+    def __str__(self):
+        return '%s at %s:%d' % (self.msg, self.lexfile, self.line)
+
+def parse_lex_dict(as_list, map_name, infofile):
+    if len(as_list) % 3 != 0:
+        raise InternalError("Lex dictionary has invalid format (input not divisible by 3): %s" % as_list)
+
+    result = {}
+    for key, sep, value in [as_list[3*i:3*i+3] for i in range(0, len(as_list)//3)]:
+        if sep != '->':
+            raise InternalError("Map %s in %s has invalid format" % (map_name, infofile))
+        if key in result:
+            raise InternalError("Duplicate map entry %s in map %s file %s" % (key, map_name, infofile))
+        result[key] = value
+    return result
+
+def lex_me_harder(infofile, allowed_groups, allowed_maps, name_val_pairs):
+    """
+    Generic lexer function for info.txt and src/build-data files
+    """
+    out = LexResult()
+
+    # Format as a nameable Python variable
+    def py_var(group):
+        return group.replace(':', '_')
+
+    lexer = shlex.shlex(open(infofile), infofile, posix=True)
+    lexer.wordchars += '=:.<>/,-!?+*' # handle various funky chars in info.txt
+
+    groups = allowed_groups + allowed_maps
+    for group in groups:
+        out.__dict__[py_var(group)] = []
+    for (key, val) in name_val_pairs.items():
+        out.__dict__[key] = val
+
+    def lexed_tokens(): # Convert to an iterator
+        while True:
+            token = lexer.get_token()
+            if token != lexer.eof:
+                yield token
+            else:
+                return
+
+    for token in lexed_tokens():
+        match = re.match('<(.*)>', token)
+
+        # Check for a grouping
+        if match is not None:
+            group = match.group(1)
+
+            if group not in groups:
+                raise LexerError('Unknown group "%s"' % (group),
+                                 infofile, lexer.lineno)
+
+            end_marker = '</' + group + '>'
+
+            token = lexer.get_token()
+            while token != end_marker:
+                out.__dict__[py_var(group)].append(token)
+                token = lexer.get_token()
+                if token is None:
+                    raise LexerError('Group "%s" not terminated' % (group),
+                                     infofile, lexer.lineno)
+
+        elif token in name_val_pairs.keys():
+            if isinstance(out.__dict__[token], list):
+                out.__dict__[token].append(lexer.get_token())
+            else:
+                out.__dict__[token] = lexer.get_token()
+
+        else: # No match -> error
+            raise LexerError('Bad token "%s"' % (token), infofile, lexer.lineno)
+
+    for group in allowed_maps:
+        out.__dict__[group] = parse_lex_dict(out.__dict__[group], group, infofile)
+
+    return out
+
+class InfoObject(object):
+    def __init__(self, infofile):
+        """
+        Constructor sets members `infofile`, `lives_in`, `parent_module` and `basename`
+        """
+
+        self.infofile = infofile
+        (dirname, basename) = os.path.split(infofile)
+        self.lives_in = dirname
+        if basename == 'info.txt':
+            (obj_dir, self.basename) = os.path.split(dirname)
+            if os.access(os.path.join(obj_dir, 'info.txt'), os.R_OK):
+                self.parent_module = os.path.basename(obj_dir)
+            else:
+                self.parent_module = None
+        else:
+            self.basename = basename.replace('.txt', '')
+
+
+class ModuleInfo(InfoObject):
+    """
+    Represents the information about a particular module
+    """
+
+    def __init__(self, infofile):
+        # pylint: disable=too-many-statements
+        super(ModuleInfo, self).__init__(infofile)
+        lex = lex_me_harder(
+            infofile,
+            ['header:internal', 'header:public', 'header:external', 'requires',
+             'os_features', 'arch', 'isa', 'cc', 'comment', 'warning'],
+            ['defines', 'libs', 'frameworks'],
+            {
+                'load_on': 'auto',
+                'endian': 'any',
+            })
+
+        def check_header_duplicates(header_list_public, header_list_internal):
+            pub_header = set(header_list_public)
+            int_header = set(header_list_internal)
+            if not pub_header.isdisjoint(int_header):
+                logging.error("Module %s header contains same header in public and internal sections" % self.infofile)
+
+        check_header_duplicates(lex.header_public, lex.header_internal)
+
+        all_source_files = []
+        all_header_files = []
+
+        for fspath in os.listdir(self.lives_in):
+            if fspath.endswith('.cpp'):
+                all_source_files.append(fspath)
+            elif fspath.endswith('.h'):
+                all_header_files.append(fspath)
+
+        self.source = all_source_files
+
+        # If not entry for the headers, all are assumed public
+        if lex.header_internal == [] and lex.header_public == []:
+            self.header_public = list(all_header_files)
+            self.header_internal = []
+        else:
+            self.header_public = lex.header_public
+            self.header_internal = lex.header_internal
+        self.header_external = lex.header_external
+
+        def convert_lib_list(libs):
+            out = {}
+            for (os_name, lib_list) in libs.items():
+                out[os_name] = lib_list.split(',')
+            return out
+
+        def combine_lines(c):
+            return ' '.join(c) if c else None
+
+        # Convert remaining lex result to members
+        self.arch = lex.arch
+        self.cc = lex.cc
+        self.comment = combine_lines(lex.comment)
+        self._defines = lex.defines
+        self._validate_defines_content(self._defines)
+        self.frameworks = convert_lib_list(lex.frameworks)
+        self.libs = convert_lib_list(lex.libs)
+        self.load_on = lex.load_on
+        self.isa = lex.isa
+        self.os_features = lex.os_features
+        self.requires = lex.requires
+        self.warning = combine_lines(lex.warning)
+        self.endian = lex.endian
+
+        # Modify members
+        self.source = [normalize_source_path(os.path.join(self.lives_in, s)) for s in self.source]
+        self.header_internal = [os.path.join(self.lives_in, s) for s in self.header_internal]
+        self.header_public = [os.path.join(self.lives_in, s) for s in self.header_public]
+        self.header_external = [os.path.join(self.lives_in, s) for s in self.header_external]
+
+        # Filesystem read access check
+        for src in self.source + self.header_internal + self.header_public + self.header_external:
+            if not os.access(src, os.R_OK):
+                logging.error("Missing file %s in %s" % (src, infofile))
+
+        # Check for duplicates
+        def intersect_check(type_a, list_a, type_b, list_b):
+            intersection = set.intersection(set(list_a), set(list_b))
+            if intersection:
+                logging.error('Headers %s marked both %s and %s' % (' '.join(intersection), type_a, type_b))
+
+        intersect_check('public', self.header_public, 'internal', self.header_internal)
+        intersect_check('public', self.header_public, 'external', self.header_external)
+        intersect_check('external', self.header_external, 'internal', self.header_internal)
+
+    @staticmethod
+    def _validate_defines_content(defines):
+        for key, value in defines.items():
+            if not re.match('^[0-9A-Za-z_]{3,30}$', key):
+                raise InternalError('Module defines key has invalid format: "%s"' % key)
+            if not re.match('^20[0-9]{6}$', value):
+                raise InternalError('Module defines value has invalid format: "%s"' % value)
+
+    def cross_check(self, arch_info, cc_info, all_os_features, all_isa_extn):
+
+        for feat in set(flatten([o.split(',') for o in self.os_features])):
+            if feat not in all_os_features:
+                logging.error("Module %s uses an OS feature (%s) which no OS supports", self.infofile, feat)
+
+        for supp_cc in self.cc:
+            if supp_cc not in cc_info:
+                colon_idx = supp_cc.find(':')
+                # a versioned compiler dependency
+                if colon_idx > 0 and supp_cc[0:colon_idx] in cc_info:
+                    pass
+                else:
+                    raise InternalError('Module %s mentions unknown compiler %s' % (self.infofile, supp_cc))
+
+        for supp_arch in self.arch:
+            if supp_arch not in arch_info:
+                raise InternalError('Module %s mentions unknown arch %s' % (self.infofile, supp_arch))
+
+        def known_isa(isa):
+            if isa in all_isa_extn:
+                return True
+
+            compound_isa = isa.split(':')
+            if len(compound_isa) == 2 and compound_isa[0] in arch_info and compound_isa[1] in all_isa_extn:
+                return True
+            return False
+
+        for isa in self.isa:
+            if not known_isa(isa):
+                raise InternalError('Module %s uses unknown ISA extension %s' % (self.infofile, isa))
+
+    def sources(self):
+        return self.source
+
+    def public_headers(self):
+        return self.header_public
+
+    def internal_headers(self):
+        return self.header_internal
+
+    def external_headers(self):
+        return self.header_external
+
+    def isas_needed(self, arch):
+        isas = []
+
+        for isa in self.isa:
+            if isa.find(':') == -1:
+                isas.append(isa)
+            elif isa.startswith(arch + ':'):
+                isas.append(isa[len(arch)+1:])
+
+        return isas
+
+    def defines(self):
+        return [(key + ' ' + value) for key, value in self._defines.items()]
+
+    def compatible_cpu(self, archinfo, options):
+        arch_name = archinfo.basename
+        cpu_name = options.cpu
+
+        if self.endian != 'any':
+            if self.endian != options.with_endian:
+                return False
+
+        for isa in self.isa:
+            if isa.find(':') > 0:
+                (arch, isa) = isa.split(':')
+
+                if arch != arch_name:
+                    continue
+
+            if isa in options.disable_intrinsics:
+                return False # explicitly disabled
+
+            if isa not in archinfo.isa_extensions:
+                return False
+
+        if self.arch != []:
+            if arch_name not in self.arch and cpu_name not in self.arch:
+                return False
+
+        return True
+
+    def compatible_os(self, os_data, options):
+        if not self.os_features:
+            return True
+
+        def has_all(needed, provided):
+            for n in needed:
+                if n not in provided:
+                    return False
+            return True
+
+        provided_features = os_data.enabled_features(options)
+
+        for feature_set in self.os_features:
+            if has_all(feature_set.split(','), provided_features):
+                return True
+
+        return False
+
+    def compatible_compiler(self, ccinfo, cc_min_version, arch):
+        # Check if this compiler supports the flags we need
+        def supported_isa_flags(ccinfo, arch):
+            for isa in self.isa:
+                if ccinfo.isa_flags_for(isa, arch) is None:
+                    return False
+            return True
+
+        # Check if module gives explicit compiler dependencies
+        def supported_compiler(ccinfo, cc_min_version):
+            if self.cc == []:
+                # no compiler restriction
+                return True
+
+            if ccinfo.basename in self.cc:
+                # compiler is supported, independent of version
+                return True
+
+            # Maybe a versioned compiler dep
+            for cc in self.cc:
+                try:
+                    name, version = cc.split(":")
+                    if name == ccinfo.basename:
+                        min_cc_version = [int(v) for v in version.split('.')]
+                        cur_cc_version = [int(v) for v in cc_min_version.split('.')]
+                        # With lists of ints, this does what we want
+                        return cur_cc_version >= min_cc_version
+                except ValueError:
+                    # No version part specified
+                    pass
+
+            return False # compiler not listed
+
+        return supported_isa_flags(ccinfo, arch) and supported_compiler(ccinfo, cc_min_version)
+
+    def dependencies(self, osinfo):
+        # base is an implicit dep for all submodules
+        deps = ['base']
+        if self.parent_module is not None:
+            deps.append(self.parent_module)
+
+        for req in self.requires:
+            if req.find('?') != -1:
+                (cond, dep) = req.split('?')
+                if osinfo is None or cond in osinfo.target_features:
+                    deps.append(dep)
+            else:
+                deps.append(req)
+
+        return deps
+
+    def dependencies_exist(self, modules):
+        """
+        Ensure that all dependencies of this module actually exist, warning
+        about any that do not
+        """
+
+        missing = [s for s in self.dependencies(None) if s not in modules]
+
+        if missing:
+            logging.error("Module '%s', dep of '%s', does not exist" % (
+                missing, self.basename))
+
+
+class ModulePolicyInfo(InfoObject):
+    def __init__(self, infofile):
+        super(ModulePolicyInfo, self).__init__(infofile)
+        lex = lex_me_harder(
+            infofile,
+            ['required', 'if_available', 'prohibited'],
+            [],
+            {})
+
+        self.if_available = lex.if_available
+        self.required = lex.required
+        self.prohibited = lex.prohibited
+
+    def cross_check(self, modules):
+        def check(tp, lst):
+            for mod in lst:
+                if mod not in modules:
+                    logging.error("Module policy %s includes non-existent module %s in <%s>" % (
+                        self.infofile, mod, tp))
+
+        check('required', self.required)
+        check('if_available', self.if_available)
+        check('prohibited', self.prohibited)
+
+
+class ArchInfo(InfoObject):
+    def __init__(self, infofile):
+        super(ArchInfo, self).__init__(infofile)
+        lex = lex_me_harder(
+            infofile,
+            ['aliases', 'isa_extensions'],
+            [],
+            {
+                'endian': None,
+                'family': None,
+                'wordsize': 32
+            })
+
+        self.aliases = lex.aliases
+        self.endian = lex.endian
+        self.family = lex.family
+        self.isa_extensions = lex.isa_extensions
+        self.wordsize = int(lex.wordsize)
+
+        if self.wordsize not in [32, 64]:
+            logging.error('Unexpected wordsize %d for arch %s', self.wordsize, infofile)
+
+        alphanumeric = re.compile('^[a-z0-9]+$')
+        for isa in self.isa_extensions:
+            if alphanumeric.match(isa) is None:
+                logging.error('Invalid name for ISA extension "%s"', isa)
+
+    def supported_isa_extensions(self, cc, options):
+        isas = []
+
+        for isa in self.isa_extensions:
+            if isa not in options.disable_intrinsics:
+                if cc.isa_flags_for(isa, self.basename) is not None:
+                    isas.append(isa)
+
+        return sorted(isas)
+
+
+class CompilerInfo(InfoObject): # pylint: disable=too-many-instance-attributes
+    def __init__(self, infofile):
+        super(CompilerInfo, self).__init__(infofile)
+        lex = lex_me_harder(
+            infofile,
+            [],
+            ['cpu_flags', 'cpu_flags_no_debug', 'so_link_commands', 'binary_link_commands',
+             'mach_abi_linking', 'isa_flags', 'sanitizers', 'lib_flags'],
+            {
+                'binary_name': None,
+                'linker_name': None,
+                'macro_name': None,
+                'output_to_object': '-o ',
+                'output_to_exe': '-o ',
+                'add_include_dir_option': '-I',
+                'add_lib_dir_option': '-L',
+                'add_compile_definition_option': '-D',
+                'add_sysroot_option': '',
+                'add_lib_option': '-l%s',
+                'add_framework_option': '-framework ',
+                'preproc_flags': '-E',
+                'compile_flags': '-c',
+                'debug_info_flags': '-g',
+                'optimization_flags': '',
+                'size_optimization_flags': '',
+                'sanitizer_optimization_flags': '',
+                'coverage_flags': '',
+                'stack_protector_flags': '',
+                'shared_flags': '',
+                'lang_flags': '',
+                'warning_flags': '',
+                'maintainer_warning_flags': '',
+                'visibility_build_flags': '',
+                'visibility_attribute': '',
+                'ar_command': '',
+                'ar_options': '',
+                'ar_output_to': '',
+                'werror_flags': '',
+            })
+
+        self.add_framework_option = lex.add_framework_option
+        self.add_include_dir_option = lex.add_include_dir_option
+        self.add_lib_dir_option = lex.add_lib_dir_option
+        self.add_lib_option = lex.add_lib_option
+        self.add_compile_definition_option = lex.add_compile_definition_option
+        self.add_sysroot_option = lex.add_sysroot_option
+        self.ar_command = lex.ar_command
+        self.ar_options = lex.ar_options
+        self.ar_output_to = lex.ar_output_to
+        self.binary_link_commands = lex.binary_link_commands
+        self.binary_name = lex.binary_name
+        self.cpu_flags = lex.cpu_flags
+        self.cpu_flags_no_debug = lex.cpu_flags_no_debug
+        self.compile_flags = lex.compile_flags
+        self.coverage_flags = lex.coverage_flags
+        self.debug_info_flags = lex.debug_info_flags
+        self.isa_flags = lex.isa_flags
+        self.lang_flags = lex.lang_flags
+        self.lib_flags = lex.lib_flags
+        self.linker_name = lex.linker_name
+        self.mach_abi_linking = lex.mach_abi_linking
+        self.macro_name = lex.macro_name
+        self.maintainer_warning_flags = lex.maintainer_warning_flags
+        self.optimization_flags = lex.optimization_flags
+        self.output_to_exe = lex.output_to_exe
+        self.output_to_object = lex.output_to_object
+        self.preproc_flags = lex.preproc_flags
+        self.sanitizers = lex.sanitizers
+        self.sanitizer_types = []
+        self.sanitizer_optimization_flags = lex.sanitizer_optimization_flags
+        self.shared_flags = lex.shared_flags
+        self.size_optimization_flags = lex.size_optimization_flags
+        self.so_link_commands = lex.so_link_commands
+        self.stack_protector_flags = lex.stack_protector_flags
+        self.visibility_attribute = lex.visibility_attribute
+        self.visibility_build_flags = lex.visibility_build_flags
+        self.warning_flags = lex.warning_flags
+        self.werror_flags = lex.werror_flags
+
+    def cross_check(self, os_info, arch_info, all_isas):
+
+        for isa in self.isa_flags:
+            if ":" in isa:
+                (arch, isa) = isa.split(":")
+                if isa not in all_isas:
+                    raise InternalError('Compiler %s has flags for unknown ISA %s' % (self.infofile, isa))
+                if arch not in arch_info:
+                    raise InternalError('Compiler %s has flags for unknown arch/ISA %s:%s' % (self.infofile, arch, isa))
+
+        for os_name in self.binary_link_commands:
+            if os_name in ["default", "default-debug"]:
+                continue
+            if os_name not in os_info:
+                raise InternalError("Compiler %s has binary_link_command for unknown OS %s" % (self.infofile, os_name))
+
+        for os_name in self.so_link_commands:
+            if os_name in ["default", "default-debug"]:
+                continue
+            if os_name not in os_info:
+                raise InternalError("Compiler %s has so_link_command for unknown OS %s" % (self.infofile, os_name))
+
+    def isa_flags_for(self, isa, arch):
+        if isa.find(':') > 0:
+            (isa_arch, isa) = isa.split(':')
+            if isa_arch != arch:
+                return ''
+            if isa in self.isa_flags:
+                return self.isa_flags[isa]
+
+        if isa in self.isa_flags:
+            return self.isa_flags[isa]
+        arch_isa = '%s:%s' % (arch, isa)
+        if arch_isa in self.isa_flags:
+            return self.isa_flags[arch_isa]
+
+        return None
+
+    def get_isa_specific_flags(self, isas, arch, options):
+        flags = set()
+
+        def simd32_impl():
+            for simd_isa in ['sse2', 'altivec', 'neon']:
+                if simd_isa in arch.isa_extensions and \
+                   simd_isa not in options.disable_intrinsics and \
+                   self.isa_flags_for(simd_isa, arch.basename):
+                    return simd_isa
+            return None
+
+        for isa in isas:
+
+            if isa == 'simd':
+                isa = simd32_impl()
+
+                if isa is None:
+                    continue
+
+            flagset = self.isa_flags_for(isa, arch.basename)
+            if flagset is None:
+                raise UserError('Compiler %s does not support %s' % (self.basename, isa))
+            flags.add(flagset)
+
+        return " ".join(sorted(flags))
+
+    def gen_lib_flags(self, options, variables):
+        """
+        Return any flags specific to building the library
+        (vs the cli or tests)
+        """
+
+        def flag_builder():
+            if options.build_shared_lib:
+                yield self.shared_flags
+                yield self.visibility_build_flags
+
+            if 'debug' in self.lib_flags and options.with_debug_info:
+                yield process_template_string(self.lib_flags['debug'], variables, self.infofile)
+
+
+        return ' '.join(list(flag_builder()))
+
+    def gen_visibility_attribute(self, options):
+        if options.build_shared_lib:
+            return self.visibility_attribute
+        return ''
+
+    def mach_abi_link_flags(self, options, debug_mode=None):
+        #pylint: disable=too-many-branches
+
+        """
+        Return the machine specific ABI flags
+        """
+
+        if debug_mode is None:
+            debug_mode = options.debug_mode
+
+        def mach_abi_groups():
+
+            yield 'all'
+
+            if options.msvc_runtime is None:
+                if debug_mode:
+                    yield 'rt-debug'
+                else:
+                    yield 'rt'
+
+            for all_except in [s for s in self.mach_abi_linking.keys() if s.startswith('all!')]:
+                exceptions = all_except[4:].split(',')
+                if options.os not in exceptions and options.arch not in exceptions:
+                    yield all_except
+
+            yield options.os
+            yield options.cpu
+
+        abi_link = set()
+        for what in mach_abi_groups():
+            if what in self.mach_abi_linking:
+                flag = self.mach_abi_linking.get(what)
+                if flag is not None and flag != '' and flag not in abi_link:
+                    abi_link.add(flag)
+
+        if options.msvc_runtime:
+            abi_link.add("/" + options.msvc_runtime)
+
+        if options.with_stack_protector and self.stack_protector_flags != '':
+            abi_link.add(self.stack_protector_flags)
+
+        if options.with_coverage_info:
+            if self.coverage_flags == '':
+                raise UserError('No coverage handling for %s' % (self.basename))
+            abi_link.add(self.coverage_flags)
+
+        if options.with_sanitizers or options.enable_sanitizers != '':
+            if not self.sanitizers:
+                raise UserError('No sanitizer handling for %s' % (self.basename))
+
+            default_san = self.sanitizers['default'].split(',')
+
+            if options.enable_sanitizers:
+                san = options.enable_sanitizers.split(',')
+            else:
+                san = default_san
+
+            for s in san:
+                if s not in self.sanitizers:
+                    raise UserError('No flags defined for sanitizer %s in %s' % (s, self.basename))
+
+                if s == 'default':
+                    abi_link.update([self.sanitizers[x] for x in default_san])
+                else:
+                    abi_link.add(self.sanitizers[s])
+
+            self.sanitizer_types = san
+
+        if options.with_openmp:
+            if 'openmp' not in self.mach_abi_linking:
+                raise UserError('No support for OpenMP for %s' % (self.basename))
+            abi_link.add(self.mach_abi_linking['openmp'])
+
+        abi_flags = ' '.join(sorted(abi_link))
+
+        if options.cc_abi_flags != '':
+            abi_flags += ' ' + options.cc_abi_flags
+
+        return abi_flags
+
+    def cc_warning_flags(self, options):
+        def gen_flags():
+            yield self.warning_flags
+            if options.werror_mode or options.maintainer_mode:
+                yield self.werror_flags
+            if options.maintainer_mode:
+                yield self.maintainer_warning_flags
+
+        return (' '.join(gen_flags())).strip()
+
+    def cc_lang_flags(self):
+        return self.lang_flags
+
+    def cc_compile_flags(self, options, with_debug_info=None, enable_optimizations=None):
+        #pylint: disable=too-many-branches
+
+        def gen_flags(with_debug_info, enable_optimizations):
+
+            sanitizers_enabled = options.with_sanitizers or (len(options.enable_sanitizers) > 0)
+
+            if with_debug_info is None:
+                with_debug_info = options.with_debug_info
+            if enable_optimizations is None:
+                enable_optimizations = not options.no_optimizations
+
+            if with_debug_info:
+                yield self.debug_info_flags
+
+            if enable_optimizations:
+                if options.optimize_for_size:
+                    if self.size_optimization_flags != '':
+                        yield self.size_optimization_flags
+                    else:
+                        logging.warning("No size optimization flags set for current compiler")
+                        yield self.optimization_flags
+                elif sanitizers_enabled and self.sanitizer_optimization_flags != '':
+                    yield self.sanitizer_optimization_flags
+                else:
+                    yield self.optimization_flags
+
+            if options.arch in self.cpu_flags:
+                yield self.cpu_flags[options.arch]
+
+            if options.arch in self.cpu_flags_no_debug:
+
+                # Only enable these if no debug/sanitizer options enabled
+
+                if not (options.debug_mode or sanitizers_enabled):
+                    yield self.cpu_flags_no_debug[options.arch]
+
+            for flag in options.extra_cxxflags:
+                yield flag
+
+            for definition in options.define_build_macro:
+                yield self.add_compile_definition_option + definition
+
+        return (' '.join(gen_flags(with_debug_info, enable_optimizations))).strip()
+
+    @staticmethod
+    def _so_link_search(osname, debug_info):
+        so_link_typ = [osname, 'default']
+        if debug_info:
+            so_link_typ = [l + '-debug' for l in so_link_typ] + so_link_typ
+        return so_link_typ
+
+    def so_link_command_for(self, osname, options):
+        """
+        Return the command needed to link a shared object
+        """
+
+        for s in self._so_link_search(osname, options.with_debug_info):
+            if s in self.so_link_commands:
+                return self.so_link_commands[s]
+
+        raise InternalError(
+            "No shared library link command found for target '%s' in compiler settings '%s'" %
+            (osname, self.infofile))
+
+    def binary_link_command_for(self, osname, options):
+        """
+        Return the command needed to link an app/test object
+        """
+
+        for s in self._so_link_search(osname, options.with_debug_info):
+            if s in self.binary_link_commands:
+                return self.binary_link_commands[s]
+
+        return '$(LINKER)'
+
+class OsInfo(InfoObject): # pylint: disable=too-many-instance-attributes
+    def __init__(self, infofile):
+        super(OsInfo, self).__init__(infofile)
+        lex = lex_me_harder(
+            infofile,
+            ['aliases', 'target_features', 'feature_macros'],
+            [],
+            {
+                'program_suffix': '',
+                'obj_suffix': 'o',
+                'soname_suffix': '',
+                'soname_pattern_patch': '',
+                'soname_pattern_abi': '',
+                'soname_pattern_base': '',
+                'static_suffix': 'a',
+                'ar_command': 'ar',
+                'ar_options': '',
+                'ar_output_to': '',
+                'install_root': '/usr/local',
+                'header_dir': 'include',
+                'bin_dir': 'bin',
+                'lib_dir': 'lib',
+                'doc_dir': 'share/doc',
+                'man_dir': 'share/man',
+                'use_stack_protector': 'true',
+                'cli_exe_name': 'botan',
+                'lib_prefix': 'lib',
+                'library_name': 'botan{suffix}-{major}',
+                'shared_lib_symlinks': 'yes',
+                'default_compiler': 'gcc',
+                'uses_pkg_config': 'yes',
+            })
+
+        if lex.ar_command == 'ar' and lex.ar_options == '':
+            lex.ar_options = 'crs'
+
+        if lex.soname_pattern_base:
+            self.soname_pattern_base = lex.soname_pattern_base
+            if lex.soname_pattern_patch == '' and lex.soname_pattern_abi == '':
+                self.soname_pattern_patch = lex.soname_pattern_base
+                self.soname_pattern_abi = lex.soname_pattern_base
+            elif lex.soname_pattern_patch != '' and lex.soname_pattern_abi != '':
+                self.soname_pattern_patch = lex.soname_pattern_patch
+                self.soname_pattern_abi = lex.soname_pattern_abi
+            else:
+                # base set, only one of patch/abi set
+                raise InternalError("Invalid soname_patterns in %s" % (self.infofile))
+        else:
+            if lex.soname_suffix:
+                self.soname_pattern_base = "libbotan{lib_suffix}-{version_major}.%s" % (lex.soname_suffix)
+                self.soname_pattern_abi = self.soname_pattern_base + ".{abi_rev}"
+                self.soname_pattern_patch = self.soname_pattern_abi + ".{version_minor}.{version_patch}"
+            else:
+                # Could not calculate soname_pattern_*
+                # This happens for OSs without shared library support (e.g. nacl, mingw, includeos, cygwin)
+                self.soname_pattern_base = None
+                self.soname_pattern_abi = None
+                self.soname_pattern_patch = None
+
+        self._aliases = lex.aliases
+        self.ar_command = lex.ar_command
+        self.ar_options = lex.ar_options
+        self.bin_dir = lex.bin_dir
+        self.cli_exe_name = lex.cli_exe_name
+        self.doc_dir = lex.doc_dir
+        self.header_dir = lex.header_dir
+        self.install_root = lex.install_root
+        self.lib_dir = lex.lib_dir
+        self.lib_prefix = lex.lib_prefix
+        self.library_name = lex.library_name
+        self.man_dir = lex.man_dir
+        self.obj_suffix = lex.obj_suffix
+        self.program_suffix = lex.program_suffix
+        self.static_suffix = lex.static_suffix
+        self.target_features = lex.target_features
+        self.use_stack_protector = (lex.use_stack_protector == "true")
+        self.shared_lib_uses_symlinks = (lex.shared_lib_symlinks == 'yes')
+        self.default_compiler = lex.default_compiler
+        self.uses_pkg_config = (lex.uses_pkg_config == 'yes')
+        self.feature_macros = lex.feature_macros
+
+    def matches_name(self, nm):
+        if nm in self._aliases:
+            return True
+
+        for alias in self._aliases:
+            if re.match(alias, nm):
+                return True
+        return False
+
+    def building_shared_supported(self):
+        return self.soname_pattern_base is not None
+
+    def enabled_features(self, options):
+        feats = []
+        for feat in self.target_features:
+            if feat not in options.without_os_features:
+                feats.append(feat)
+        for feat in options.with_os_features:
+            if feat not in self.target_features:
+                feats.append(feat)
+
+        return sorted(feats)
+
+    def macros(self, cc):
+        value = [cc.add_compile_definition_option + define
+                 for define in self.feature_macros]
+
+        return ' '.join(value)
+
+def fixup_proc_name(proc):
+    proc = proc.lower().replace(' ', '')
+    for junk in ['(tm)', '(r)']:
+        proc = proc.replace(junk, '')
+    return proc
+
+def canon_processor(archinfo, proc):
+    proc = fixup_proc_name(proc)
+
+    # First, try to search for an exact match
+    for ainfo in archinfo.values():
+        if ainfo.basename == proc or proc in ainfo.aliases:
+            return ainfo.basename
+
+    return None
+
+def system_cpu_info():
+
+    cpu_info = []
+
+    if platform.machine() != '':
+        cpu_info.append(platform.machine())
+
+    if platform.processor() != '':
+        cpu_info.append(platform.processor())
+
+    if 'uname' in os.__dict__:
+        cpu_info.append(os.uname()[4])
+
+    return cpu_info
+
+def guess_processor(archinfo):
+    for info_part in system_cpu_info():
+        if info_part:
+            match = canon_processor(archinfo, info_part)
+            if match is not None:
+                logging.debug("Matched '%s' to processor '%s'" % (info_part, match))
+                return match, info_part
+            else:
+                logging.debug("Failed to deduce CPU from '%s'" % info_part)
+
+    raise UserError('Could not determine target CPU; set with --cpu')
+
+
+def read_textfile(filepath):
+    """
+    Read a whole file into memory as a string
+    """
+    if filepath is None:
+        return ''
+
+    with open(filepath) as f:
+        return ''.join(f.readlines())
+
+
+def process_template_string(template_text, variables, template_source):
+    # pylint: disable=too-many-branches,too-many-statements
+
+    """
+    Perform template substitution
+
+    The template language supports (un-nested) conditionals.
+    """
+    class SimpleTemplate(object):
+
+        def __init__(self, vals):
+            self.vals = vals
+            self.value_pattern = re.compile(r'%{([a-z][a-z_0-9\|]+)}')
+            self.cond_pattern = re.compile('%{(if|unless) ([a-z][a-z_0-9]+)}')
+            self.for_pattern = re.compile('(.*)%{for ([a-z][a-z_0-9]+)}')
+            self.join_pattern = re.compile('(.*)%{join ([a-z][a-z_0-9]+)}')
+
+        def substitute(self, template):
+            # pylint: disable=too-many-locals
+            def insert_value(match):
+                v = match.group(1)
+                if v in self.vals:
+                    return str(self.vals.get(v))
+                if v.endswith('|upper'):
+                    v = v.replace('|upper', '')
+                    if v in self.vals:
+                        return str(self.vals.get(v)).upper()
+
+                raise KeyError(v)
+
+            lines = template.splitlines()
+
+            output = ""
+            idx = 0
+
+            while idx < len(lines):
+                cond_match = self.cond_pattern.match(lines[idx])
+                join_match = self.join_pattern.match(lines[idx])
+                for_match = self.for_pattern.match(lines[idx])
+
+                if cond_match:
+                    cond_type = cond_match.group(1)
+                    cond_var = cond_match.group(2)
+
+                    include_cond = False
+
+                    if cond_type == 'if' and cond_var in self.vals and self.vals.get(cond_var):
+                        include_cond = True
+                    elif cond_type == 'unless' and (cond_var not in self.vals or (not self.vals.get(cond_var))):
+                        include_cond = True
+
+                    idx += 1
+                    while idx < len(lines):
+                        if lines[idx] == '%{endif}':
+                            break
+                        if include_cond:
+                            output += lines[idx] + "\n"
+                        idx += 1
+                elif join_match:
+                    join_var = join_match.group(2)
+                    join_str = ' '
+                    join_line = '%%{join %s}' % (join_var)
+                    output += lines[idx].replace(join_line, join_str.join(self.vals[join_var])) + "\n"
+                elif for_match:
+                    for_prefix = for_match.group(1)
+                    output += for_prefix
+                    for_var = for_match.group(2)
+
+                    if for_var not in self.vals:
+                        raise InternalError("Unknown for loop iteration variable '%s'" % (for_var))
+
+                    var = self.vals[for_var]
+                    if not isinstance(var, list):
+                        raise InternalError("For loop iteration variable '%s' is not a list" % (for_var))
+                    idx += 1
+
+                    for_body = ""
+                    while idx < len(lines):
+                        if lines[idx] == '%{endfor}':
+                            break
+                        for_body += lines[idx] + "\n"
+                        idx += 1
+
+                    for v in var:
+                        if isinstance(v, dict):
+                            for_val = for_body
+                            for ik, iv in v.items():
+                                for_val = for_val.replace('%{' + ik + '}', iv)
+                            output += for_val + "\n"
+                        else:
+                            output += for_body.replace('%{i}', v).replace('%{i|upper}', v.upper())
+                    output += "\n"
+                else:
+                    output += lines[idx] + "\n"
+                idx += 1
+
+            return self.value_pattern.sub(insert_value, output) + '\n'
+
+    try:
+        return SimpleTemplate(variables).substitute(template_text)
+    except KeyError as e:
+        logging.error('Unbound var %s in template %s' % (e, template_source))
+    except Exception as e: # pylint: disable=broad-except
+        logging.error('Exception %s during template processing file %s' % (e, template_source))
+
+def process_template(template_file, variables):
+    return process_template_string(read_textfile(template_file), variables, template_file)
+
+def yield_objectfile_list(sources, obj_dir, obj_suffix, options):
+    obj_suffix = '.' + obj_suffix
+
+    for src in sources:
+        (directory, filename) = os.path.split(os.path.normpath(src))
+        parts = directory.split(os.sep)
+
+        if 'src' in parts:
+            parts = parts[parts.index('src')+2:]
+        elif options.amalgamation and filename.find(options.name_amalgamation) != -1:
+            parts = []
+        else:
+            raise InternalError("Unexpected file '%s/%s'" % (directory, filename))
+
+        if parts != []:
+            # Handle src/X/X.cpp -> X.o
+            if filename == parts[-1] + '.cpp':
+                name = '_'.join(parts) + '.cpp'
+            else:
+                name = '_'.join(parts) + '_' + filename
+
+            def fixup_obj_name(name):
+                def remove_dups(parts):
+                    last = None
+                    for part in parts:
+                        if last is None or part != last:
+                            last = part
+                            yield part
+
+                return '_'.join(remove_dups(name.split('_')))
+
+            name = fixup_obj_name(name)
+        else:
+            name = filename
+
+        name = name.replace('.cpp', obj_suffix)
+        yield os.path.join(obj_dir, name)
+
+def generate_build_info(build_paths, modules, cc, arch, osinfo, options):
+    # pylint: disable=too-many-locals
+
+    # first create a map of src_file->owning module
+
+    module_that_owns = {}
+
+    for mod in modules:
+        for src in mod.sources():
+            module_that_owns[src] = mod
+
+    def _isa_specific_flags(src):
+        if os.path.basename(src) == 'test_simd.cpp':
+            return cc.get_isa_specific_flags(['simd'], arch, options)
+
+        if src in module_that_owns:
+            module = module_that_owns[src]
+            isas = module.isas_needed(arch.basename)
+            if 'simd' in module.dependencies(osinfo):
+                isas.append('simd')
+
+            return cc.get_isa_specific_flags(isas, arch, options)
+
+        return ''
+
+    def _build_info(sources, objects, target_type):
+        output = []
+        for (obj_file, src) in zip(objects, sources):
+            info = {
+                'src': src,
+                'obj': obj_file,
+                'isa_flags': _isa_specific_flags(src)
+                }
+
+            if target_type == 'fuzzer':
+                fuzz_basename = os.path.basename(obj_file).replace('.' + osinfo.obj_suffix, '')
+                info['exe'] = os.path.join(build_paths.fuzzer_output_dir, fuzz_basename)
+
+            output.append(info)
+
+        return output
+
+    out = {}
+
+    targets = ['lib', 'cli', 'test', 'fuzzer']
+
+    out['isa_build_info'] = []
+
+    fuzzer_bin = []
+    for t in targets:
+        src_list, src_dir = build_paths.src_info(t)
+
+        src_key = '%s_srcs' % (t)
+        obj_key = '%s_objs' % (t)
+        build_key = '%s_build_info' % (t)
+
+        objects = []
+        build_info = []
+
+        if src_list is not None:
+            src_list.sort()
+            objects = list(yield_objectfile_list(src_list, src_dir, osinfo.obj_suffix, options))
+            build_info = _build_info(src_list, objects, t)
+
+            for b in build_info:
+                if b['isa_flags'] != '':
+                    out['isa_build_info'].append(b)
+
+            if t == 'fuzzer':
+                fuzzer_bin = [b['exe'] for b in build_info]
+
+        out[src_key] = src_list if src_list else []
+        out[obj_key] = objects
+        out[build_key] = build_info
+
+    out['fuzzer_bin'] = ' '.join(fuzzer_bin)
+    out['cli_headers'] = build_paths.cli_headers
+
+    return out
+
+def create_template_vars(source_paths, build_paths, options, modules, cc, arch, osinfo):
+    #pylint: disable=too-many-locals,too-many-branches,too-many-statements
+
+    """
+    Create the template variables needed to process the makefile, build.h, etc
+    """
+
+    def external_link_cmd():
+        return ' '.join([cc.add_lib_dir_option + libdir for libdir in options.with_external_libdir])
+
+    def adjust_library_name(info_txt_libname):
+        """
+        Apply custom library name mappings where necessary
+        """
+
+        # potentially map boost library names to the associated name provided
+        # via ./configure.py --boost-library-name <build/platform specific name>
+        #
+        # We assume that info.txt contains the library name's "stem", i.e.
+        # 'boost_system'. While the user-provided (actual) library will contain
+        # the same stem plus a set of prefixes and/or suffixes, e.g.
+        # libboost_system-vc140-mt-x64-1_69.lib. We use the stem for selecting
+        # the correct user-provided library name override.
+        if options.boost_libnames and 'boost_' in info_txt_libname:
+            adjusted_libnames = [chosen_libname for chosen_libname in options.boost_libnames \
+                                 if info_txt_libname in chosen_libname]
+
+            if len(adjusted_libnames) > 1:
+                logging.warning('Ambiguous boost library names: %s' % ', '.join(adjusted_libnames))
+            if len(adjusted_libnames) == 1:
+                logging.debug('Replacing boost library name %s -> %s' % (info_txt_libname, adjusted_libnames[0]))
+                return adjusted_libnames[0]
+
+        return info_txt_libname
+
+    def link_to(module_member_name):
+        """
+        Figure out what external libraries/frameworks are needed based on selected modules
+        """
+        if module_member_name not in ['libs', 'frameworks']:
+            raise InternalError("Invalid argument")
+
+        libs = set()
+        for module in modules:
+            for (osname, module_link_to) in getattr(module, module_member_name).items():
+                if osname in ['all', osinfo.basename]:
+                    libs |= set(module_link_to)
+                else:
+                    match = re.match('^all!(.*)', osname)
+                    if match is not None:
+                        exceptions = match.group(1).split(',')
+                        if osinfo.basename not in exceptions:
+                            libs |= set(module_link_to)
+
+        return sorted([adjust_library_name(lib) for lib in libs])
+
+    def choose_mp_bits():
+        mp_bits = arch.wordsize # allow command line override?
+        logging.debug('Using MP bits %d' % (mp_bits))
+        return mp_bits
+
+    def innosetup_arch(os_name, arch):
+        if os_name == 'windows':
+            inno_arch = {'x86_32': '',
+                         'x86_64': 'x64',
+                         'ia64': 'ia64'}
+            if arch in inno_arch:
+                return inno_arch[arch]
+            else:
+                logging.warning('Unknown arch %s in innosetup_arch' % (arch))
+        return None
+
+    def configure_command_line():
+        # Cut absolute path from main executable (e.g. configure.py or python interpreter)
+        # to get the same result when configuring the same thing on different machines
+        main_executable = os.path.basename(sys.argv[0])
+        return ' '.join([main_executable] + sys.argv[1:])
+
+    def cmake_escape(s):
+        return s.replace('(', '\\(').replace(')', '\\)')
+
+    def sysroot_option():
+        if options.with_sysroot_dir == '':
+            return ''
+        if cc.add_sysroot_option == '':
+            logging.error("This compiler doesn't support --sysroot option")
+        return cc.add_sysroot_option + options.with_sysroot_dir
+
+    def ar_command():
+        if options.ar_command:
+            return options.ar_command
+
+        if cc.ar_command:
+            if cc.ar_command == cc.binary_name:
+                return options.compiler_binary or cc.binary_name
+            else:
+                return cc.ar_command
+
+        return osinfo.ar_command
+
+    build_dir = options.with_build_dir or os.path.curdir
+    program_suffix = options.program_suffix or osinfo.program_suffix
+
+    def join_with_build_dir(path):
+        # For some unknown reason MinGW doesn't like ./foo
+        if build_dir == os.path.curdir and options.os == 'mingw':
+            return path
+        return os.path.join(build_dir, path)
+
+    def all_targets(options):
+        yield 'libs'
+        if 'cli' in options.build_targets:
+            yield 'cli'
+        if 'tests' in options.build_targets:
+            yield 'tests'
+        if options.build_fuzzers:
+            yield 'fuzzers'
+        if 'bogo_shim' in options.build_targets:
+            yield 'bogo_shim'
+        if options.with_documentation:
+            yield 'docs'
+
+    def install_targets(options):
+        yield 'libs'
+        if 'cli' in options.build_targets:
+            yield 'cli'
+        if options.with_documentation:
+            yield 'docs'
+
+    def absolute_install_dir(p):
+        if os.path.isabs(p):
+            return p
+        return os.path.join(options.prefix or osinfo.install_root, p)
+
+    variables = {
+        'version_major':  Version.major(),
+        'version_minor':  Version.minor(),
+        'version_patch':  Version.patch(),
+        'version_suffix': Version.suffix(),
+        'version_vc_rev': 'unknown' if options.no_store_vc_rev else Version.vc_rev(),
+        'abi_rev':        Version.so_rev(),
+
+        'version':        Version.as_string(),
+        'release_type':   Version.release_type(),
+        'version_datestamp': Version.datestamp(),
+
+        'distribution_info': options.distribution_info,
+
+        'macos_so_compat_ver': '%s.%s.0' % (Version.packed(), Version.so_rev()),
+        'macos_so_current_ver': '%s.%s.%s' % (Version.packed(), Version.so_rev(), Version.patch()),
+
+        'all_targets': ' '.join(all_targets(options)),
+        'install_targets': ' '.join(install_targets(options)),
+
+        'base_dir': source_paths.base_dir,
+        'src_dir': source_paths.src_dir,
+        'test_data_dir': source_paths.test_data_dir,
+        'doc_dir': source_paths.doc_dir,
+        'scripts_dir': normalize_source_path(source_paths.scripts_dir),
+        'python_dir': source_paths.python_dir,
+
+        'cli_exe_name': osinfo.cli_exe_name + program_suffix,
+        'cli_exe': join_with_build_dir(osinfo.cli_exe_name + program_suffix),
+        'build_cli_exe': bool('cli' in options.build_targets),
+        'test_exe': join_with_build_dir('botan-test' + program_suffix),
+
+        'lib_prefix': osinfo.lib_prefix,
+        'static_suffix': osinfo.static_suffix,
+        'lib_suffix': options.library_suffix,
+        'libname': osinfo.library_name.format(major=Version.major(),
+                                              minor=Version.minor(),
+                                              suffix=options.library_suffix),
+
+        'command_line': configure_command_line(),
+        'local_config': read_textfile(options.local_config),
+
+        'program_suffix': program_suffix,
+
+        'prefix': options.prefix or osinfo.install_root,
+        'bindir': absolute_install_dir(options.bindir or osinfo.bin_dir),
+        'libdir': absolute_install_dir(options.libdir or osinfo.lib_dir),
+        'mandir': options.mandir or osinfo.man_dir,
+        'includedir': options.includedir or osinfo.header_dir,
+        'docdir': options.docdir or osinfo.doc_dir,
+
+        'with_documentation': options.with_documentation,
+        'with_sphinx': options.with_sphinx,
+        'with_pdf': options.with_pdf,
+        'with_rst2man': options.with_rst2man,
+        'sphinx_config_dir': source_paths.sphinx_config_dir,
+        'with_doxygen': options.with_doxygen,
+        'maintainer_mode': options.maintainer_mode,
+
+        'out_dir': build_dir,
+        'build_dir': build_paths.build_dir,
+
+        'doc_stamp_file': os.path.join(build_paths.build_dir, 'doc.stamp'),
+        'makefile_path': os.path.join(build_paths.build_dir, '..', 'Makefile'),
+
+        'build_static_lib': options.build_static_lib,
+        'build_shared_lib': options.build_shared_lib,
+
+        'build_fuzzers': options.build_fuzzers,
+
+        'build_coverage' : options.with_coverage_info or options.with_coverage,
+
+        'symlink_shared_lib': options.build_shared_lib and osinfo.shared_lib_uses_symlinks,
+
+        'libobj_dir': build_paths.libobj_dir,
+        'cliobj_dir': build_paths.cliobj_dir,
+        'testobj_dir': build_paths.testobj_dir,
+        'fuzzobj_dir': build_paths.fuzzobj_dir,
+
+        'fuzzer_output_dir': build_paths.fuzzer_output_dir if build_paths.fuzzer_output_dir else '',
+        'doc_output_dir': build_paths.doc_output_dir,
+        'handbook_output_dir': build_paths.handbook_output_dir,
+        'doc_output_dir_doxygen': build_paths.doc_output_dir_doxygen,
+
+        'compiler_include_dirs': '%s %s' % (build_paths.include_dir, build_paths.external_include_dir),
+
+        'os': options.os,
+        'arch': options.arch,
+        'cpu_family': arch.family,
+        'endian': options.with_endian,
+        'cpu_is_64bit': arch.wordsize == 64,
+
+        'bakefile_arch': 'x86' if options.arch == 'x86_32' else 'x86_64',
+
+        'innosetup_arch': innosetup_arch(options.os, options.arch),
+
+        'mp_bits': choose_mp_bits(),
+
+        'python_exe': os.path.basename(sys.executable),
+        'python_version': options.python_version,
+        'install_python_module': not options.no_install_python_module,
+
+        'cxx': (options.compiler_binary or cc.binary_name),
+        'cxx_abi_flags': cc.mach_abi_link_flags(options),
+        'linker': cc.linker_name or '$(CXX)',
+        'make_supports_phony': osinfo.basename != 'windows',
+
+        'sanitizer_types' : sorted(cc.sanitizer_types),
+
+        'cc_compile_opt_flags': cc.cc_compile_flags(options, False, True),
+        'cc_compile_debug_flags': cc.cc_compile_flags(options, True, False),
+
+        # These are for CMake
+        'cxx_abi_opt_flags': cc.mach_abi_link_flags(options, False),
+        'cxx_abi_debug_flags': cc.mach_abi_link_flags(options, True),
+
+        'dash_o': cc.output_to_object,
+        'dash_c': cc.compile_flags,
+
+        'cc_lang_flags': cc.cc_lang_flags(),
+        'os_feature_macros': osinfo.macros(cc),
+        'cc_sysroot': sysroot_option(),
+        'cc_compile_flags': options.cxxflags or cc.cc_compile_flags(options),
+        'ldflags': options.ldflags or '',
+        'cc_warning_flags': cc.cc_warning_flags(options),
+        'output_to_exe': cc.output_to_exe,
+        'cc_macro': cc.macro_name,
+
+        'visibility_attribute': cc.gen_visibility_attribute(options),
+
+        'lib_link_cmd': cc.so_link_command_for(osinfo.basename, options) + ' ' + external_link_cmd(),
+        'exe_link_cmd': cc.binary_link_command_for(osinfo.basename, options) + ' ' + external_link_cmd(),
+        'post_link_cmd': '',
+
+        'ar_command': ar_command(),
+        'ar_options': options.ar_options or cc.ar_options or osinfo.ar_options,
+        'ar_output_to': cc.ar_output_to,
+
+        'link_to': ' '.join(
+            [(cc.add_lib_option % lib) for lib in link_to('libs')] +
+            [cc.add_framework_option + fw for fw in link_to('frameworks')]
+        ),
+
+        'cmake_link_to': ' '.join(
+            link_to('libs') +
+            [('"' + cc.add_framework_option + fw + '"') for fw in link_to('frameworks')]
+        ),
+
+        'fuzzer_lib': (cc.add_lib_option % options.fuzzer_lib) if options.fuzzer_lib else '',
+        'libs_used': [lib.replace('.lib', '') for lib in link_to('libs')],
+
+        'include_paths': build_paths.format_include_paths(cc, options.with_external_includedir),
+        'module_defines': sorted(flatten([m.defines() for m in modules])),
+
+        'build_bogo_shim': bool('bogo_shim' in options.build_targets),
+        'bogo_shim_src': os.path.join(source_paths.src_dir, 'bogo_shim', 'bogo_shim.cpp'),
+
+        'os_features': osinfo.enabled_features(options),
+        'os_name': osinfo.basename,
+        'cpu_features': arch.supported_isa_extensions(cc, options),
+        'system_cert_bundle': options.system_cert_bundle,
+
+        'fuzzer_mode': options.unsafe_fuzzer_mode,
+        'fuzzer_type': options.build_fuzzers.upper() if options.build_fuzzers else '',
+
+        'with_valgrind': options.with_valgrind,
+        'with_openmp': options.with_openmp,
+        'with_debug_asserts': options.with_debug_asserts,
+        'test_mode': options.test_mode,
+        'optimize_for_size': options.optimize_for_size,
+
+        'mod_list': sorted([m.basename for m in modules])
+    }
+
+    if cc.basename == 'msvc' and variables['cxx_abi_flags'] != '':
+        # MSVC linker doesn't support/need the ABI options,
+        # just transfer them over to just the compiler invocations
+        variables['cc_compile_flags'] = '%s %s' % (variables['cxx_abi_flags'], variables['cc_compile_flags'])
+        variables['cxx_abi_flags'] = ''
+
+    variables['lib_flags'] = cc.gen_lib_flags(options, variables)
+    variables['cmake_lib_flags'] = cmake_escape(variables['lib_flags'])
+
+    if options.with_pkg_config:
+        variables['botan_pkgconfig'] = os.path.join(build_paths.build_dir, 'botan-%d.pc' % (Version.major()))
+
+    # The name is always set because Windows build needs it
+    variables['static_lib_name'] = '%s%s.%s' % (variables['lib_prefix'], variables['libname'],
+                                                variables['static_suffix'])
+
+    if options.build_shared_lib:
+        if osinfo.soname_pattern_base is not None:
+            variables['soname_base'] = osinfo.soname_pattern_base.format(**variables)
+            variables['shared_lib_name'] = variables['soname_base']
+
+        if osinfo.soname_pattern_abi is not None:
+            variables['soname_abi'] = osinfo.soname_pattern_abi.format(**variables)
+            variables['shared_lib_name'] = variables['soname_abi']
+
+        if osinfo.soname_pattern_patch is not None:
+            variables['soname_patch'] = osinfo.soname_pattern_patch.format(**variables)
+
+        variables['lib_link_cmd'] = variables['lib_link_cmd'].format(**variables)
+
+    lib_targets = []
+    if options.build_static_lib:
+        lib_targets.append('static_lib_name')
+    if options.build_shared_lib:
+        lib_targets.append('shared_lib_name')
+
+    variables['library_targets'] = ' '.join([join_with_build_dir(variables[t]) for t in lib_targets])
+
+    if options.os == 'llvm' or options.compiler == 'msvc':
+        # llvm-link and msvc require just naming the file directly
+        variables['link_to_botan'] = os.path.join(build_dir, variables['static_lib_name'])
+    else:
+        variables['link_to_botan'] = '%s%s %s' % (cc.add_lib_dir_option, build_dir,
+                                                  (cc.add_lib_option % variables['libname']))
+
+    return variables
+
+class ModulesChooser(object):
+    """
+    Determine which modules to load based on options, target, etc
+    """
+
+    def __init__(self, modules, module_policy, archinfo, osinfo, ccinfo, cc_min_version, options):
+        self._modules = modules
+        self._module_policy = module_policy
+        self._archinfo = archinfo
+        self._osinfo = osinfo
+        self._ccinfo = ccinfo
+        self._cc_min_version = cc_min_version
+        self._options = options
+
+        self._maybe_dep = set()
+        self._to_load = set()
+        # string to set mapping with reasons as key and modules as value
+        self._not_using_because = collections.defaultdict(set)
+
+        ModulesChooser._validate_dependencies_exist(self._modules)
+        ModulesChooser._validate_user_selection(
+            self._modules, self._options.enabled_modules, self._options.disabled_modules)
+
+    def _check_usable(self, module, modname):
+        if not module.compatible_cpu(self._archinfo, self._options):
+            self._not_using_because['incompatible CPU'].add(modname)
+            return False
+        elif not module.compatible_os(self._osinfo, self._options):
+            self._not_using_because['incompatible OS'].add(modname)
+            return False
+        elif not module.compatible_compiler(self._ccinfo, self._cc_min_version, self._archinfo.basename):
+            self._not_using_because['incompatible compiler'].add(modname)
+            return False
+        return True
+
+    @staticmethod
+    def _display_module_information_unused(skipped_modules):
+        for reason in sorted(skipped_modules.keys()):
+            disabled_mods = sorted(skipped_modules[reason])
+            if disabled_mods:
+                logging.info('Skipping (%s): %s' % (reason, ' '.join(disabled_mods)))
+
+    @staticmethod
+    def _display_module_information_to_load(all_modules, modules_to_load):
+        sorted_modules_to_load = sorted(modules_to_load)
+
+        for modname in sorted_modules_to_load:
+            if all_modules[modname].comment:
+                logging.info('%s: %s' % (modname, all_modules[modname].comment))
+            if all_modules[modname].warning:
+                logging.warning('%s: %s' % (modname, all_modules[modname].warning))
+            if all_modules[modname].load_on == 'vendor':
+                logging.info('Enabling use of external dependency %s' % modname)
+
+        if sorted_modules_to_load:
+            logging.info('Loading modules: %s', ' '.join(sorted_modules_to_load))
+        else:
+            logging.error('This configuration disables every submodule and is invalid')
+
+    @staticmethod
+    def _validate_state(used_modules, unused_modules):
+        for reason, unused_for_reason in unused_modules.items():
+            intersection = unused_for_reason & used_modules
+            if intersection:
+                raise InternalError(
+                    "Disabled modules (%s) and modules to load have common elements: %s"
+                    % (reason, intersection))
+
+    @staticmethod
+    def _validate_dependencies_exist(modules):
+        for module in modules.values():
+            module.dependencies_exist(modules)
+
+    @staticmethod
+    def _validate_user_selection(modules, enabled_modules, disabled_modules):
+        for modname in enabled_modules:
+            if modname not in modules:
+                logging.error("Module not found: %s" % modname)
+
+        for modname in disabled_modules:
+            if modname not in modules:
+                logging.warning("Disabled module not found: %s" % modname)
+
+    def _handle_by_module_policy(self, modname, usable):
+        if self._module_policy is not None:
+            if modname in self._module_policy.required:
+                if not usable:
+                    logging.error('Module policy requires module %s not usable on this platform' % (modname))
+                elif modname in self._options.disabled_modules:
+                    logging.error('Module %s was disabled but is required by policy' % (modname))
+                self._to_load.add(modname)
+                return True
+            elif modname in self._module_policy.if_available:
+                if modname in self._options.disabled_modules:
+                    self._not_using_because['disabled by user'].add(modname)
+                elif usable:
+                    logging.debug('Enabling optional module %s' % (modname))
+                    self._to_load.add(modname)
+                return True
+            elif modname in self._module_policy.prohibited:
+                if modname in self._options.enabled_modules:
+                    logging.error('Module %s was requested but is prohibited by policy' % (modname))
+                self._not_using_because['prohibited by module policy'].add(modname)
+                return True
+
+        return False
+
+    @staticmethod
+    def resolve_dependencies(available_modules, dependency_table, module, loaded_modules=None):
+        """
+        Parameters
+        - available_modules: modules to choose from. Constant.
+        - dependency_table: module to dependencies map. Constant.
+        - module: name of the module to resolve dependencies. Constant.
+        - loaded_modules: modules already loaded. Defensive copy in order to not change value for caller.
+        """
+        if loaded_modules is None:
+            loaded_modules = set([])
+        else:
+            loaded_modules = copy.copy(loaded_modules)
+
+        if module not in available_modules:
+            return False, None
+
+        loaded_modules.add(module)
+        for dependency in dependency_table[module]:
+            dependency_choices = set(dependency.split('|'))
+
+            dependency_met = False
+
+            if not set(dependency_choices).isdisjoint(loaded_modules):
+                dependency_met = True
+            else:
+                possible_mods = dependency_choices.intersection(available_modules)
+
+                for mod in possible_mods:
+                    ok, dependency_modules = ModulesChooser.resolve_dependencies(
+                        available_modules, dependency_table, mod, loaded_modules)
+                    if ok:
+                        dependency_met = True
+                        loaded_modules.add(mod)
+                        loaded_modules.update(dependency_modules)
+                        break
+
+            if not dependency_met:
+                return False, None
+
+        return True, loaded_modules
+
+    def _modules_dependency_table(self):
+        out = {}
+        for modname in self._modules:
+            out[modname] = self._modules[modname].dependencies(self._osinfo)
+        return out
+
+    def _resolve_dependencies_for_all_modules(self):
+        available_modules = set(self._to_load) | set(self._maybe_dep)
+        dependency_table = self._modules_dependency_table()
+
+        successfully_loaded = set()
+
+        for modname in self._to_load:
+            # This will try to recursively load all dependencies of modname
+            ok, modules = self.resolve_dependencies(available_modules, dependency_table, modname)
+            if ok:
+                successfully_loaded.add(modname)
+                successfully_loaded.update(modules)
+            else:
+                # Skip this module
+                pass
+
+        self._not_using_because['dependency failure'].update(self._to_load - successfully_loaded)
+        self._to_load = successfully_loaded
+        self._maybe_dep -= successfully_loaded
+
+    def _handle_by_load_on(self, module): # pylint: disable=too-many-branches
+        modname = module.basename
+        if module.load_on == 'never':
+            self._not_using_because['disabled as buggy'].add(modname)
+        elif module.load_on == 'request':
+            if self._options.with_everything:
+                self._to_load.add(modname)
+            else:
+                self._not_using_because['by request only'].add(modname)
+        elif module.load_on == 'vendor':
+            if self._options.with_everything:
+                self._to_load.add(modname)
+            else:
+                self._not_using_because['requires external dependency'].add(modname)
+        elif module.load_on == 'dep':
+            self._maybe_dep.add(modname)
+
+        elif module.load_on == 'always':
+            self._to_load.add(modname)
+
+        elif module.load_on == 'auto':
+            if self._options.no_autoload or self._module_policy is not None:
+                self._maybe_dep.add(modname)
+            else:
+                self._to_load.add(modname)
+        else:
+            logging.error('Unknown load_on %s in %s' % (
+                module.load_on, modname))
+
+    def choose(self):
+        for (modname, module) in self._modules.items():
+            usable = self._check_usable(module, modname)
+
+            module_handled = self._handle_by_module_policy(modname, usable)
+            if module_handled:
+                continue
+
+            if modname in self._options.disabled_modules:
+                self._not_using_because['disabled by user'].add(modname)
+            elif usable:
+                if modname in self._options.enabled_modules:
+                    self._to_load.add(modname) # trust the user
+                else:
+                    self._handle_by_load_on(module)
+
+        if 'compression' in self._to_load:
+            # Confirm that we have at least one compression library enabled
+            # Otherwise we leave a lot of useless support code compiled in, plus a
+            # make_compressor call that always fails
+            if 'zlib' not in self._to_load and 'bzip2' not in self._to_load and 'lzma' not in self._to_load:
+                self._to_load.remove('compression')
+                self._not_using_because['no enabled compression schemes'].add('compression')
+
+        self._resolve_dependencies_for_all_modules()
+
+        for not_a_dep in self._maybe_dep:
+            self._not_using_because['not requested'].add(not_a_dep)
+
+        ModulesChooser._validate_state(self._to_load, self._not_using_because)
+        ModulesChooser._display_module_information_unused(self._not_using_because)
+        ModulesChooser._display_module_information_to_load(self._modules, self._to_load)
+
+        return self._to_load
+
+def choose_link_method(options):
+    """
+    Choose the link method based on system availability and user request
+    """
+
+    req = options.link_method
+
+    def useable_methods():
+
+        # Symbolic link support on Windows was introduced in Windows 6.0 (Vista)
+        # and Python 3.2. Furthermore, the SeCreateSymbolicLinkPrivilege is
+        # required in order to successfully create symlinks. So only try to use
+        # symlinks on Windows if explicitly requested.
+
+        # MinGW declares itself as 'Windows'
+        host_is_windows = python_platform_identifier() in ['windows', 'cygwin']
+
+        if 'symlink' in os.__dict__:
+            if host_is_windows:
+                if req == 'symlink':
+                    yield 'symlink'
+            else:
+                yield 'symlink'
+
+        if 'link' in os.__dict__:
+            yield 'hardlink'
+
+        yield 'copy'
+
+    for method in useable_methods():
+        if req is None or req == method:
+            logging.info('Using %s to link files into build dir ' \
+                         '(use --link-method to change)' % (method))
+            return method
+
+    logging.warning('Could not use link method "%s", will copy instead' % (req))
+    return 'copy'
+
+def portable_symlink(file_path, target_dir, method):
+    """
+    Copy or link the file, depending on what the platform offers
+    """
+
+    if not os.access(file_path, os.R_OK):
+        logging.warning('Missing file %s' % (file_path))
+        return
+
+    if method == 'symlink':
+        rel_file_path = os.path.relpath(file_path, start=target_dir)
+        os.symlink(rel_file_path, os.path.join(target_dir, os.path.basename(file_path)))
+    elif method == 'hardlink':
+        os.link(file_path, os.path.join(target_dir, os.path.basename(file_path)))
+    elif method == 'copy':
+        shutil.copy(file_path, target_dir)
+    else:
+        raise UserError('Unknown link method %s' % (method))
+
+
+class AmalgamationHelper(object):
+    # All include types may have trailing comment like e.g. '#include <vector> // IWYU pragma: export'
+    _any_include = re.compile(r'#include <(.*)>')
+    _botan_include = re.compile(r'#include <botan/(.*)>')
+
+    # Only matches at the beginning of the line. By convention, this means that the include
+    # is not wrapped by condition macros
+    _unconditional_any_include = re.compile(r'^#include <(.*)>')
+    # stddef.h is included in ffi.h
+    _unconditional_std_include = re.compile(r'^#include <([^/\.]+|stddef.h)>')
+
+    @staticmethod
+    def is_any_include(cpp_source_line):
+        match = AmalgamationHelper._any_include.search(cpp_source_line)
+        if match:
+            return match.group(1)
+        else:
+            return None
+
+    @staticmethod
+    def is_botan_include(cpp_source_line):
+        match = AmalgamationHelper._botan_include.search(cpp_source_line)
+        if match:
+            return match.group(1)
+        else:
+            return None
+
+    @staticmethod
+    def is_unconditional_any_include(cpp_source_line):
+        match = AmalgamationHelper._unconditional_any_include.search(cpp_source_line)
+        if match:
+            return match.group(1)
+        else:
+            return None
+
+    @staticmethod
+    def is_unconditional_std_include(cpp_source_line):
+        match = AmalgamationHelper._unconditional_std_include.search(cpp_source_line)
+        if match:
+            return match.group(1)
+        else:
+            return None
+
+    @staticmethod
+    def write_banner(fd):
+        fd.write("""/*
+* Botan %s Amalgamation
+* (C) 1999-2020 The Botan Authors
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+""" % (Version.as_string()))
+
+
+class AmalgamationHeader(object):
+    def __init__(self, input_filepaths):
+
+        self.included_already = set()
+        self.all_std_includes = set()
+
+        self.file_contents = {}
+        for filepath in sorted(input_filepaths):
+            try:
+                contents = AmalgamationGenerator.read_header(filepath)
+                self.file_contents[os.path.basename(filepath)] = contents
+            except IOError as e:
+                logging.error('Error processing file %s for amalgamation: %s' % (filepath, e))
+
+        self.contents = ''
+        for name in sorted(self.file_contents):
+            self.contents += ''.join(list(self.header_contents(name)))
+
+        self.header_includes = ''
+        for std_header in sorted(self.all_std_includes):
+            self.header_includes += '#include <%s>\n' % (std_header)
+        self.header_includes += '\n'
+
+    def header_contents(self, name):
+        name = name.replace('internal/', '')
+
+        if name in self.included_already:
+            return
+
+        self.included_already.add(name)
+
+        if name not in self.file_contents:
+            return
+
+        depr_marker = 'BOTAN_DEPRECATED_HEADER(%s)\n' % (name)
+        if depr_marker in self.file_contents[name]:
+            logging.debug("Ignoring deprecated header %s", name)
+            return
+
+        for line in self.file_contents[name]:
+            header = AmalgamationHelper.is_botan_include(line)
+            if header:
+                for c in self.header_contents(header):
+                    yield c
+            else:
+                std_header = AmalgamationHelper.is_unconditional_std_include(line)
+
+                if std_header:
+                    self.all_std_includes.add(std_header)
+                else:
+                    yield line
+
+    def write_to_file(self, filepath, include_guard):
+        with open(filepath, 'w') as f:
+            AmalgamationHelper.write_banner(f)
+            f.write("\n#ifndef %s\n#define %s\n\n" % (include_guard, include_guard))
+            f.write(self.header_includes)
+            f.write(self.contents)
+            f.write("\n#endif // %s\n" % (include_guard))
+
+
+class AmalgamationGenerator(object):
+    _header_guard_pattern = re.compile(r'^#define BOTAN_.*_H_\s*$')
+    _header_endif_pattern = re.compile(r'^#endif.*$')
+
+    @staticmethod
+    def read_header(filepath):
+        encoding_kwords = {}
+        if sys.version_info[0] == 3:
+            encoding_kwords['encoding'] = 'utf8'
+        with open(filepath, **encoding_kwords) as f:
+            raw_content = f.readlines()
+            return AmalgamationGenerator.strip_header_goop(filepath, raw_content)
+
+    @staticmethod
+    def strip_header_goop(header_name, header_lines):
+        lines = copy.copy(header_lines) # defensive copy
+
+        start_header_guard_index = None
+        for index, line in enumerate(lines):
+            if AmalgamationGenerator._header_guard_pattern.match(line):
+                start_header_guard_index = index
+                break
+        if start_header_guard_index is None:
+            raise InternalError("No header guard start found in " + header_name)
+
+        end_header_guard_index = None
+        for index, line in enumerate(lines):
+            if AmalgamationGenerator._header_endif_pattern.match(line):
+                end_header_guard_index = index # override with last found
+        if end_header_guard_index is None:
+            raise InternalError("No header guard end found in " + header_name)
+
+        lines = lines[start_header_guard_index+1 : end_header_guard_index]
+
+        # Strip leading and trailing empty lines
+        while lines[0].strip() == "":
+            lines = lines[1:]
+        while lines[-1].strip() == "":
+            lines = lines[0:-1]
+
+        return lines
+
+    def __init__(self, prefix, build_paths, modules, options):
+        self._filename_prefix = prefix
+        self._build_paths = build_paths
+        self._modules = modules
+        self._options = options
+
+    def generate(self):
+        encoding_kwords = {}
+        if sys.version_info[0] == 3:
+            encoding_kwords['encoding'] = 'utf8'
+
+        pub_header_amalag = AmalgamationHeader(self._build_paths.public_headers)
+        amalgamation_header_fsname = '%s.h' % (self._filename_prefix)
+        logging.info('Writing amalgamation header to %s' % (amalgamation_header_fsname))
+        pub_header_amalag.write_to_file(amalgamation_header_fsname, "BOTAN_AMALGAMATION_H_")
+
+        internal_headers_list = []
+
+        for hdr in self._build_paths.internal_headers:
+            internal_headers_list.append(hdr)
+
+        # file descriptors for all `amalgamation_sources`
+        amalgamation_fsname = '%s.cpp' % (self._filename_prefix)
+        logging.info('Writing amalgamation source to %s' % (amalgamation_fsname))
+
+        amalgamation_file = open(amalgamation_fsname, 'w', **encoding_kwords)
+
+        AmalgamationHelper.write_banner(amalgamation_file)
+        amalgamation_file.write('\n#include "%s"\n\n' % (amalgamation_header_fsname))
+
+        internal_headers = AmalgamationHeader(internal_headers_list)
+        amalgamation_file.write(internal_headers.header_includes)
+        amalgamation_file.write(internal_headers.contents)
+
+        unconditional_headers = set([])
+
+        for mod in sorted(self._modules, key=lambda module: module.basename):
+            for src in sorted(mod.source):
+                with open(src, 'r', **encoding_kwords) as f:
+                    for line in f:
+                        if AmalgamationHelper.is_botan_include(line):
+                            # Botan headers are inlined in amalgamation headers
+                            continue
+
+                        if AmalgamationHelper.is_any_include(line) in unconditional_headers:
+                            # This include (conditional or unconditional) was unconditionally added before
+                            continue
+
+                        amalgamation_file.write(line)
+                        unconditional_header = AmalgamationHelper.is_unconditional_any_include(line)
+                        if unconditional_header:
+                            unconditional_headers.add(unconditional_header)
+
+        amalgamation_file.close()
+
+        return ([amalgamation_fsname], [amalgamation_header_fsname])
+
+
+def have_program(program):
+    """
+    Test for the existence of a program
+    """
+
+    def exe_test(path, program):
+        exe_file = os.path.join(path, program)
+
+        if os.path.exists(exe_file) and os.access(exe_file, os.X_OK):
+            logging.debug('Found program %s in %s' % (program, path))
+            return True
+        else:
+            return False
+
+    exe_suffixes = ['', '.exe']
+
+    for path in os.environ['PATH'].split(os.pathsep):
+        for suffix in exe_suffixes:
+            if exe_test(path, program + suffix):
+                return True
+
+    logging.debug('Program %s not found' % (program))
+    return False
+
+
+class BotanConfigureLogHandler(logging.StreamHandler, object):
+    def emit(self, record):
+        # Do the default stuff first
+        super(BotanConfigureLogHandler, self).emit(record)
+        # Exit script if and ERROR or worse occurred
+        if record.levelno >= logging.ERROR:
+            sys.exit(1)
+
+
+def setup_logging(options):
+    if options.verbose:
+        log_level = logging.DEBUG
+    elif options.quiet:
+        log_level = logging.WARNING
+    else:
+        log_level = logging.INFO
+
+    lh = BotanConfigureLogHandler(sys.stdout)
+    lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+    logging.getLogger().addHandler(lh)
+    logging.getLogger().setLevel(log_level)
+
+
+def load_info_files(search_dir, descr, filename_matcher, class_t):
+    info = {}
+
+    def filename_matches(filename):
+        if isinstance(filename_matcher, str):
+            return filename == filename_matcher
+        else:
+            return filename_matcher.match(filename) is not None
+
+    for (dirpath, _, filenames) in os.walk(search_dir):
+        for filename in filenames:
+            filepath = os.path.join(dirpath, filename)
+            if filename_matches(filename):
+                info_obj = class_t(filepath)
+                info[info_obj.basename] = info_obj
+
+    if info:
+        infotxt_basenames = ' '.join(sorted(info.keys()))
+        logging.debug('Loaded %d %s files: %s' % (len(info), descr, infotxt_basenames))
+    else:
+        logging.warning('Failed to load any %s files' % (descr))
+
+    return info
+
+
+def load_build_data_info_files(source_paths, descr, subdir, class_t):
+    matcher = re.compile(r'[_a-z0-9]+\.txt$')
+    return load_info_files(os.path.join(source_paths.build_data_dir, subdir), descr, matcher, class_t)
+
+
+# Workaround for Windows systems where antivirus is enabled GH #353
+def robust_rmtree(path, max_retries=5):
+    for _ in range(max_retries):
+        try:
+            shutil.rmtree(path)
+            return
+        except OSError:
+            time.sleep(0.1)
+
+    # Final attempt, pass any exceptions up to caller.
+    shutil.rmtree(path)
+
+
+# Workaround for Windows systems where antivirus is enabled GH #353
+def robust_makedirs(directory, max_retries=5):
+    for _ in range(max_retries):
+        try:
+            os.makedirs(directory)
+            return
+        except OSError as e:
+            if e.errno == errno.EEXIST:
+                raise
+
+        time.sleep(0.1)
+
+    # Final attempt, pass any exceptions up to caller.
+    os.makedirs(directory)
+
+def python_platform_identifier():
+    system_from_python = platform.system().lower()
+    if re.match('^cygwin_.*', system_from_python):
+        return 'cygwin'
+    else:
+        return system_from_python
+
+# This is for otions that have --with-XYZ and --without-XYZ. If user does not
+# set any of those, we choose a default here.
+# Mutates `options`
+def set_defaults_for_unset_options(options, info_arch, info_cc, info_os): # pylint: disable=too-many-branches
+    if options.os is None:
+        options.os = python_platform_identifier()
+        logging.info('Guessing target OS is %s (use --os to set)' % (options.os))
+
+    if options.os not in info_os:
+        def find_canonical_os_name(os_name_variant):
+            for (canonical_os_name, os_info) in info_os.items():
+                if os_info.matches_name(os_name_variant):
+                    return canonical_os_name
+            return os_name_variant # not found
+        options.os = find_canonical_os_name(options.os)
+
+    def deduce_compiler_type_from_cc_bin(cc_bin):
+        if cc_bin.find('clang') != -1 or cc_bin in ['emcc', 'em++']:
+            return 'clang'
+        if cc_bin.find('-g++') != -1 or cc_bin.find('g++') != -1:
+            return 'gcc'
+        return None
+
+    if options.compiler is None and options.compiler_binary is not None:
+        options.compiler = deduce_compiler_type_from_cc_bin(options.compiler_binary)
+
+        if options.compiler is None:
+            logging.error("Could not figure out what compiler type '%s' is, use --cc to set" % (
+                options.compiler_binary))
+
+    if options.compiler is None and options.os in info_os:
+        options.compiler = info_os[options.os].default_compiler
+
+        if not have_program(info_cc[options.compiler].binary_name):
+            logging.error("Default compiler for system is %s but could not find binary '%s'; use --cc to set" % (
+                options.compiler, info_cc[options.compiler].binary_name))
+
+        logging.info('Guessing to use compiler %s (use --cc or CXX to set)' % (options.compiler))
+
+    if options.cpu is None:
+        (arch, cpu) = guess_processor(info_arch)
+        options.arch = arch
+        options.cpu = cpu
+        logging.info('Guessing target processor is a %s (use --cpu to set)' % (options.arch))
+
+    # OpenBSD uses an old binutils that does not support AVX2
+    if options.os == 'openbsd':
+        del info_cc['gcc'].isa_flags['avx2']
+
+    if options.with_documentation is True:
+        if options.with_sphinx is None and have_program('sphinx-build'):
+            logging.info('Found sphinx-build (use --without-sphinx to disable)')
+            options.with_sphinx = True
+        if options.with_rst2man is None and have_program('rst2man'):
+            logging.info('Found rst2man (use --without-rst2man to disable)')
+            options.with_rst2man = True
+
+    if options.with_pkg_config is None and options.os in info_os:
+        options.with_pkg_config = info_os[options.os].uses_pkg_config
+
+    if options.system_cert_bundle is None:
+        default_paths = [
+            '/etc/ssl/certs/ca-certificates.crt', # Ubuntu, Debian, Arch, Gentoo
+            '/etc/pki/tls/certs/ca-bundle.crt', # RHEL
+            '/etc/ssl/ca-bundle.pem', # SuSE
+            '/etc/ssl/cert.pem', # OpenBSD, FreeBSD, Alpine
+            '/etc/certs/ca-certificates.crt', # Solaris
+        ]
+
+        for path in default_paths:
+            if os.access(path, os.R_OK):
+                logging.info('Using %s as system certificate store', path)
+                options.system_cert_bundle = path
+                break
+    else:
+        if not os.access(options.system_cert_bundle, os.R_OK):
+            logging.warning('Provided system cert bundle path %s not found, ignoring', options.system_cert_bundle)
+            options.system_cert_bundle = None
+
+# Mutates `options`
+def canonicalize_options(options, info_os, info_arch):
+    # pylint: disable=too-many-branches
+
+    # canonical ARCH/CPU
+    options.arch = canon_processor(info_arch, options.cpu)
+    if options.arch is None:
+        raise UserError('Unknown or unidentifiable processor "%s"' % (options.cpu))
+
+    if options.cpu != options.arch:
+        logging.info('Canonicalized CPU target %s to %s', options.cpu, options.arch)
+
+    # select and sanity check build targets
+    def canonicalize_build_targets(options):
+        # --build-targets was not provided: build default targets
+        if options.build_targets is None:
+            return ["cli", "tests"]
+
+        # flatten the list of multiple --build-targets="" and comma separation
+        build_targets = [t.strip().lower() for ts in options.build_targets for t in ts.split(",")]
+
+        # validate that all requested build targets are available
+        for build_target in build_targets:
+            if build_target not in ACCEPTABLE_BUILD_TARGETS:
+                raise UserError("unknown build target: %s" % build_target)
+
+        # building the shared lib desired and without contradiction?
+        if options.build_shared_lib is None:
+            options.build_shared_lib = "shared" in build_targets
+        elif bool(options.build_shared_lib) != bool("shared" in build_targets):
+            raise UserError("inconsistent usage of --enable/disable-shared-library and --build-targets")
+
+        # building the static lib desired and without contradiction?
+        if options.build_static_lib is None:
+            options.build_static_lib = "static" in build_targets
+        elif bool(options.build_static_lib) != bool("static" in build_targets):
+            raise UserError("inconsistent usage of --enable/disable-static-library and --build-targets")
+
+        return build_targets
+
+    options.build_targets = canonicalize_build_targets(options)
+
+    shared_libs_supported = options.os in info_os and info_os[options.os].building_shared_supported()
+
+    if not shared_libs_supported:
+        if options.build_shared_lib is True:
+            logging.warning('Shared libs not supported on %s, disabling shared lib support' % (options.os))
+            options.build_shared_lib = False
+        elif options.build_shared_lib is None:
+            logging.info('Shared libs not supported on %s, disabling shared lib support' % (options.os))
+
+    if options.os == 'windows' and options.build_shared_lib is None and options.build_static_lib is None:
+        options.build_shared_lib = True
+
+    if options.with_stack_protector is None:
+        if options.os in info_os:
+            options.with_stack_protector = info_os[options.os].use_stack_protector
+
+    if options.build_shared_lib is None:
+        if options.os == 'windows' and options.build_static_lib:
+            pass
+        else:
+            options.build_shared_lib = shared_libs_supported
+
+    if options.build_static_lib is None:
+        if options.os == 'windows' and options.build_shared_lib:
+            pass
+        else:
+            options.build_static_lib = True
+
+    # Set default fuzzing lib
+    if options.build_fuzzers == 'libfuzzer' and options.fuzzer_lib is None:
+        options.fuzzer_lib = 'Fuzzer'
+
+# Checks user options for consistency
+# This method DOES NOT change options on behalf of the user but explains
+# why the given configuration does not work.
+def validate_options(options, info_os, info_cc, available_module_policies):
+    # pylint: disable=too-many-branches,too-many-statements
+
+    if options.name_amalgamation != 'botan_all':
+        if options.name_amalgamation == '':
+            raise UserError('Amalgamation basename must be non-empty')
+
+        acceptable_name_re = re.compile('^[a-zA-Z0-9_]+$')
+        if acceptable_name_re.match(options.name_amalgamation) is None:
+            raise UserError("Amalgamation basename must match [a-zA-Z0-9_]+")
+
+    if options.os == "java":
+        raise UserError("Jython detected: need --os and --cpu to set target")
+
+    if options.os not in info_os:
+        raise UserError('Unknown OS "%s"; available options: %s' % (
+            options.os, ' '.join(sorted(info_os.keys()))))
+
+    if options.compiler not in info_cc:
+        raise UserError('Unknown compiler "%s"; available options: %s' % (
+            options.compiler, ' '.join(sorted(info_cc.keys()))))
+
+    if options.cc_min_version is not None and not re.match(r'^[0-9]+\.[0-9]+$', options.cc_min_version):
+        raise UserError("--cc-min-version must have the format MAJOR.MINOR")
+
+    if options.module_policy and options.module_policy not in available_module_policies:
+        raise UserError("Unknown module set %s" % options.module_policy)
+
+    if options.cpu == 'llvm' or options.os in ['llvm', 'emscripten']:
+        if options.compiler != 'clang':
+            raise UserError('LLVM target requires using Clang')
+
+        if options.cpu != 'llvm':
+            raise UserError('LLVM target requires CPU target set to LLVM bitcode (llvm)')
+
+        if options.os not in ['llvm', 'emscripten']:
+            raise UserError('Target OS is not an LLVM bitcode target')
+
+    if options.build_fuzzers is not None:
+        if options.build_fuzzers not in ['libfuzzer', 'afl', 'klee', 'test']:
+            raise UserError('Bad value to --build-fuzzers')
+
+        if options.build_fuzzers == 'klee' and options.os != 'llvm':
+            raise UserError('Building for KLEE requires targeting LLVM')
+
+    if options.build_static_lib is False and options.build_shared_lib is False:
+        raise UserError('With both --disable-static-library and --disable-shared-library, nothing to do')
+
+    if options.os == 'windows' and options.build_static_lib is True and options.build_shared_lib is True:
+        raise UserError('On Windows only one of static lib and DLL can be selected')
+
+    if options.with_documentation is False:
+        if options.with_doxygen:
+            raise UserError('Using --with-doxygen plus --without-documentation makes no sense')
+        if options.with_sphinx:
+            raise UserError('Using --with-sphinx plus --without-documentation makes no sense')
+        if options.with_pdf:
+            raise UserError('Using --with-pdf plus --without-documentation makes no sense')
+
+    if options.with_pdf and not options.with_sphinx:
+        raise UserError('Option --with-pdf requires --with-sphinx')
+
+    if options.with_bakefile:
+        if options.os != 'windows' or options.compiler != 'msvc' or options.build_shared_lib is False:
+            raise UserError("Building via bakefile is only supported for MSVC DLL build")
+
+        if options.arch not in ['x86_64', 'x86_32']:
+            raise UserError("Bakefile only supports x86 targets")
+
+    # Warnings
+    if options.os == 'windows' and options.compiler != 'msvc':
+        logging.warning('The windows target is oriented towards MSVC; maybe you want --os=cygwin or --os=mingw')
+
+    if options.msvc_runtime:
+        if options.compiler != 'msvc':
+            raise UserError("Makes no sense to specify MSVC runtime for %s" % (options.compiler))
+
+        if options.msvc_runtime not in ['MT', 'MD', 'MTd', 'MDd']:
+            logging.warning("MSVC runtime option '%s' not known", (options.msvc_runtime))
+
+def run_compiler_preproc(options, ccinfo, source_file, default_return, extra_flags=None):
+    if extra_flags is None:
+        extra_flags = []
+
+    cc_bin = options.compiler_binary or ccinfo.binary_name
+
+    cmd = cc_bin.split(' ') + ccinfo.preproc_flags.split(' ') + extra_flags + [source_file]
+
+    try:
+        logging.debug("Running '%s'", ' '.join(cmd))
+        stdout, _ = subprocess.Popen(
+            cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            universal_newlines=True).communicate()
+        cc_output = stdout
+    except OSError as e:
+        logging.warning('Could not execute %s: %s' % (cmd, e))
+        return default_return
+
+    def cleanup_output(output):
+        return ('\n'.join([l for l in output.splitlines() if l.startswith('#') is False])).strip()
+
+    return cleanup_output(cc_output)
+
+def calculate_cc_min_version(options, ccinfo, source_paths):
+    version_patterns = {
+        'msvc': r'^ *MSVC ([0-9]{2})([0-9]{2})$',
+        'gcc': r'^ *GCC ([0-9]+) ([0-9]+)$',
+        'clang': r'^ *CLANG ([0-9]+) ([0-9]+)$',
+        'xlc': r'^ *XLC ([0-9]+) ([0-9]+)$',
+    }
+
+    unknown_pattern = r'UNKNOWN 0 0'
+
+    if ccinfo.basename not in version_patterns:
+        logging.info("No compiler version detection available for %s" % (ccinfo.basename))
+        return "0.0"
+
+    detect_version_source = os.path.join(source_paths.build_data_dir, "detect_version.cpp")
+
+    cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, "0.0")
+
+    if re.search(unknown_pattern, cc_output) is not None:
+        logging.warning('Failed to get version for %s from macro check' % (ccinfo.basename))
+        return "0.0"
+
+    match = re.search(version_patterns[ccinfo.basename], cc_output, flags=re.MULTILINE)
+    if match is None:
+        logging.warning("Tried to get %s version, but output '%s' does not match expected version format" % (
+            ccinfo.basename, cc_output))
+        return "0.0"
+
+    major_version = int(match.group(1), 0)
+    minor_version = int(match.group(2), 0)
+    cc_version = "%d.%d" % (major_version, minor_version)
+    logging.info('Auto-detected compiler version %s' % (cc_version))
+
+    return cc_version
+
+def check_compiler_arch(options, ccinfo, archinfo, source_paths):
+    detect_version_source = os.path.join(source_paths.build_data_dir, 'detect_arch.cpp')
+
+    abi_flags = ccinfo.mach_abi_link_flags(options).split(' ')
+    cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, 'UNKNOWN', abi_flags).lower()
+
+    if cc_output == '':
+        cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, 'UNKNOWN').lower()
+
+    if cc_output == 'unknown':
+        logging.warning('Unable to detect target architecture via compiler macro checks')
+        return None
+
+    if cc_output not in archinfo:
+        # Should not happen
+        logging.warning("Error detecting compiler target arch: '%s'", cc_output)
+        return None
+
+    logging.info('Auto-detected compiler arch %s' % (cc_output))
+    return cc_output
+
+def do_io_for_build(cc, arch, osinfo, using_mods, build_paths, source_paths, template_vars, options):
+    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+
+    try:
+        robust_rmtree(build_paths.build_dir)
+    except OSError as e:
+        if e.errno != errno.ENOENT:
+            logging.error('Problem while removing build dir: %s' % (e))
+
+    for build_dir in build_paths.build_dirs():
+        try:
+            robust_makedirs(build_dir)
+        except OSError as e:
+            if e.errno != errno.EEXIST:
+                logging.error('Error while creating "%s": %s' % (build_dir, e))
+
+    def write_template(sink, template):
+        with open(sink, 'w') as f:
+            f.write(process_template(template, template_vars))
+
+    def in_build_dir(p):
+        return os.path.join(build_paths.build_dir, p)
+    def in_build_data(p):
+        return os.path.join(source_paths.build_data_dir, p)
+
+    write_template(in_build_dir('build.h'), in_build_data('buildh.in'))
+    write_template(in_build_dir('botan.doxy'), in_build_data('botan.doxy.in'))
+
+    if 'botan_pkgconfig' in template_vars:
+        write_template(template_vars['botan_pkgconfig'], in_build_data('botan.pc.in'))
+
+    if options.os == 'windows':
+        write_template(in_build_dir('botan.iss'), in_build_data('innosetup.in'))
+
+    link_method = choose_link_method(options)
+
+    def link_headers(headers, visibility, directory):
+        logging.debug('Linking %d %s header files in %s' % (len(headers), visibility, directory))
+
+        for header_file in headers:
+            try:
+                portable_symlink(header_file, directory, link_method)
+            except OSError as e:
+                if e.errno != errno.EEXIST:
+                    raise UserError('Error linking %s into %s: %s' % (header_file, directory, e))
+
+    link_headers(build_paths.public_headers, 'public',
+                 build_paths.botan_include_dir)
+
+    link_headers(build_paths.internal_headers, 'internal',
+                 build_paths.internal_include_dir)
+
+    link_headers(build_paths.external_headers, 'external',
+                 build_paths.external_include_dir)
+
+    if options.amalgamation:
+        (amalg_cpp_files, amalg_headers) = AmalgamationGenerator(
+            options.name_amalgamation, build_paths, using_mods, options).generate()
+        build_paths.lib_sources = amalg_cpp_files
+        template_vars['generated_files'] = ' '.join(amalg_cpp_files + amalg_headers)
+
+        # Inserting an amalgamation generated using DLL visibility flags into a
+        # binary project will either cause errors (on Windows) or unnecessary overhead.
+        # Provide a hint
+        if options.build_shared_lib:
+            logging.warning('Unless you are building a DLL or .so from the amalgamation, use --disable-shared as well')
+
+    template_vars.update(generate_build_info(build_paths, using_mods, cc, arch, osinfo, options))
+
+    with open(os.path.join(build_paths.build_dir, 'build_config.json'), 'w') as f:
+        json.dump(template_vars, f, sort_keys=True, indent=2)
+
+    if options.with_cmake:
+        logging.warning("CMake build is only for development: use make for production builds")
+        cmake_template = os.path.join(source_paths.build_data_dir, 'cmake.in')
+        write_template('CMakeLists.txt', cmake_template)
+    elif options.with_bakefile:
+        logging.warning("Bakefile build is only for development: use make for production builds")
+        bakefile_template = os.path.join(source_paths.build_data_dir, 'bakefile.in')
+        write_template('botan.bkl', bakefile_template)
+    else:
+        makefile_template = os.path.join(source_paths.build_data_dir, 'makefile.in')
+        write_template(template_vars['makefile_path'], makefile_template)
+
+    if options.with_rst2man:
+        rst2man_file = os.path.join(build_paths.build_dir, 'botan.rst')
+        cli_doc = os.path.join(source_paths.doc_dir, 'cli.rst')
+
+        cli_doc_contents = open(cli_doc).readlines()
+
+        while cli_doc_contents[0] != "\n":
+            cli_doc_contents.pop(0)
+
+        rst2man_header = """
+botan
+=============================
+
+:Subtitle: Botan command line util
+:Manual section: 1
+
+        """.strip()
+
+        with open(rst2man_file, 'w') as f:
+            f.write(rst2man_header)
+            f.write("\n")
+            for line in cli_doc_contents:
+                f.write(line)
+
+    logging.info('Botan %s (revision %s) (%s %s) build setup is complete' % (
+        Version.as_string(),
+        Version.vc_rev(),
+        Version.release_type(),
+        ('dated %d' % (Version.datestamp())) if Version.datestamp() != 0 else 'undated'))
+
+    if options.unsafe_fuzzer_mode:
+        logging.warning("The fuzzer mode flag is labeled unsafe for a reason, this version is for testing only")
+
+def list_os_features(all_os_features, info_os):
+    for feat in all_os_features:
+        os_with_feat = [o for o in info_os.keys() if feat in info_os[o].target_features]
+        os_without_feat = [o for o in info_os.keys() if feat not in info_os[o].target_features]
+
+        if len(os_with_feat) < len(os_without_feat):
+            print("%s: %s" % (feat, ' '.join(sorted(os_with_feat))))
+        else:
+            print("%s: %s" % (feat, '!' + ' !'.join(sorted(os_without_feat))))
+    return 0
+
+
+def main(argv):
+    """
+    Main driver
+    """
+
+    # pylint: disable=too-many-locals,too-many-statements
+
+    options = process_command_line(argv[1:])
+
+    setup_logging(options)
+
+    source_paths = SourcePaths(os.path.dirname(argv[0]))
+
+    info_modules = load_info_files(source_paths.lib_dir, 'Modules', "info.txt", ModuleInfo)
+
+    if options.list_modules:
+        for mod in sorted(info_modules.keys()):
+            print(mod)
+        return 0
+
+    info_arch = load_build_data_info_files(source_paths, 'CPU info', 'arch', ArchInfo)
+    info_os = load_build_data_info_files(source_paths, 'OS info', 'os', OsInfo)
+    info_cc = load_build_data_info_files(source_paths, 'compiler info', 'cc', CompilerInfo)
+    info_module_policies = load_build_data_info_files(source_paths, 'module policy', 'policy', ModulePolicyInfo)
+
+    all_os_features = sorted(set(flatten([o.target_features for o in info_os.values()])))
+    all_defined_isas = set(flatten([a.isa_extensions for a in info_arch.values()]))
+
+    if options.list_os_features:
+        return list_os_features(all_os_features, info_os)
+
+    for mod in info_modules.values():
+        mod.cross_check(info_arch, info_cc, all_os_features, all_defined_isas)
+
+    for cc in info_cc.values():
+        cc.cross_check(info_os, info_arch, all_defined_isas)
+
+    for policy in info_module_policies.values():
+        policy.cross_check(info_modules)
+
+    logging.info('%s invoked with options "%s"', argv[0], ' '.join(argv[1:]))
+    logging.info('Configuring to build Botan %s (revision %s)' % (
+        Version.as_string(), Version.vc_rev()))
+    logging.info('Running under %s', sys.version.replace('\n', ''))
+
+    take_options_from_env(options)
+
+    logging.info('Autodetected platform information: OS="%s" machine="%s" proc="%s"',
+                 platform.system(), platform.machine(), platform.processor())
+
+    logging.debug('Known CPU names: ' + ' '.join(
+        sorted(flatten([[ainfo.basename] + ainfo.aliases for ainfo in info_arch.values()]))))
+
+    set_defaults_for_unset_options(options, info_arch, info_cc, info_os)
+    canonicalize_options(options, info_os, info_arch)
+    validate_options(options, info_os, info_cc, info_module_policies)
+
+    cc = info_cc[options.compiler]
+    arch = info_arch[options.arch]
+    osinfo = info_os[options.os]
+    module_policy = info_module_policies[options.module_policy] if options.module_policy else None
+
+    if options.enable_cc_tests:
+        cc_min_version = options.cc_min_version or calculate_cc_min_version(options, cc, source_paths)
+        cc_arch = check_compiler_arch(options, cc, info_arch, source_paths)
+
+        if options.arch != 'generic':
+            if cc_arch is not None and cc_arch != options.arch:
+                logging.error("Configured target is %s but compiler probe indicates %s", options.arch, cc_arch)
+    else:
+        cc_min_version = options.cc_min_version or "0.0"
+
+    logging.info('Target is %s:%s-%s-%s' % (
+        options.compiler, cc_min_version, options.os, options.arch))
+
+    def choose_endian(arch_info, options):
+        if options.with_endian is not None:
+            return options.with_endian
+
+        if options.cpu.endswith('eb') or options.cpu.endswith('be'):
+            return 'big'
+        elif options.cpu.endswith('el') or options.cpu.endswith('le'):
+            return 'little'
+
+        if arch_info.endian:
+            logging.info('Assuming target %s is %s endian', arch_info.basename, arch_info.endian)
+        return arch_info.endian
+
+    options.with_endian = choose_endian(arch, options)
+
+    chooser = ModulesChooser(info_modules, module_policy, arch, osinfo, cc, cc_min_version, options)
+    loaded_module_names = chooser.choose()
+    using_mods = [info_modules[modname] for modname in loaded_module_names]
+
+    build_paths = BuildPaths(source_paths, options, using_mods)
+    build_paths.public_headers.append(os.path.join(build_paths.build_dir, 'build.h'))
+
+    template_vars = create_template_vars(source_paths, build_paths, options, using_mods, cc, arch, osinfo)
+
+    # Now we start writing to disk
+    do_io_for_build(cc, arch, osinfo, using_mods, build_paths, source_paths, template_vars, options)
+
+    return 0
+
+if __name__ == '__main__':
+    try:
+        sys.exit(main(argv=sys.argv))
+    except UserError as e:
+        logging.debug(traceback.format_exc())
+        logging.error(e)
+    except Exception as e: # pylint: disable=broad-except
+        # error() will stop script, so wrap all information into one call
+        logging.error("""%s
+An internal error occurred.
+
+Don't panic, this is probably not your fault! Please open an issue
+with the entire output at https://github.com/randombit/botan
+
+You'll meet friendly people happy to help!""" % traceback.format_exc())
+
+    sys.exit(0)
diff --git a/third_party/botan/src/bogo_shim/bogo_shim.cpp b/third_party/botan/src/bogo_shim/bogo_shim.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/bogo_shim/bogo_shim.cpp
@@ -0,0 +1,1679 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/*
+* This is a shim for testing Botan against BoringSSL's test TLS stack (BoGo).
+*
+* Instructions on use should go here.
+*/
+
+#include <botan/tls_client.h>
+#include <botan/tls_server.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_algos.h>
+#include <botan/data_src.h>
+#include <botan/pkcs8.h>
+#include <botan/loadstor.h>
+#include <botan/oids.h>
+#include <botan/chacha_rng.h>
+#include <botan/base64.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+#include <botan/mem_ops.h>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <ctime>
+#include <unordered_map>
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+  #include <sys/socket.h>
+  #include <sys/time.h>
+  #include <netinet/in.h>
+  #include <netdb.h>
+  #include <string.h>
+  #include <unistd.h>
+  #include <errno.h>
+  #include <fcntl.h>
+#endif
+
+namespace {
+
+int shim_output(const std::string& s, int rc = 0)
+   {
+   std::cout << s << "\n";
+   return rc;
+   }
+
+void shim_log(const std::string& s)
+   {
+   if(::getenv("BOTAN_BOGO_SHIM_LOG"))
+      {
+      static FILE* log = std::fopen("/tmp/bogo_shim.log", "w");
+      struct timeval tv;
+      ::gettimeofday(&tv, nullptr);
+      std::fprintf(log, "%lld.%lu: %s\n", static_cast<unsigned long long>(tv.tv_sec), tv.tv_usec, s.c_str());
+      std::fflush(log);
+      }
+   }
+
+void BOTAN_NORETURN shim_exit_with_error(const std::string& s, int rc = 1)
+   {
+   shim_log("Exiting with " + s);
+   std::cerr << s << "\n";
+   std::exit(rc);
+   }
+
+std::string map_to_bogo_error(const std::string& e)
+   {
+   shim_log("Original error " + e);
+
+   static const std::unordered_map<std::string, std::string> err_map
+      {
+         { "Application data before handshake done", ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:" },
+         { "Bad Hello_Request, has non-zero size", ":BAD_HELLO_REQUEST:" },
+         { "Bad code for TLS alert level", ":UNKNOWN_ALERT_TYPE:" },
+         { "Bad extension size", ":DECODE_ERROR:" },
+         { "Bad length in hello verify request", ":DECODE_ERROR:" },
+         { "Bad lengths in DTLS header", ":BAD_HANDSHAKE_RECORD:" },
+         { "Bad signature on server key exchange", ":BAD_SIGNATURE:" },
+         { "Bad size (1) for TLS alert message", ":BAD_ALERT:" },
+         { "Bad size (4) for TLS alert message", ":BAD_ALERT:" },
+         { "CERTIFICATE decoding failed with PEM: No PEM header found", ":CANNOT_PARSE_LEAF_CERT:" },
+         { "Can't agree on a ciphersuite with client", ":NO_SHARED_CIPHER:" },
+         { "Can't interleave application and handshake data", ":UNEXPECTED_RECORD:" },
+         { "Certificate chain exceeds policy specified maximum size", ":EXCESSIVE_MESSAGE_SIZE:" },
+         { "Certificate key type did not match ciphersuite", ":WRONG_CERTIFICATE_TYPE:" },
+         { "Certificate usage constraints do not allow this ciphersuite", ":KEY_USAGE_BIT_INCORRECT:" },
+         { "Certificate: Message malformed", ":DECODE_ERROR:" },
+         { "Channel::key_material_export cannot export during renegotiation", "failed to export keying material" },
+         { "Client cert verify failed", ":BAD_SIGNATURE:" },
+         { "Client did not offer NULL compression", ":INVALID_COMPRESSION_LIST:" },
+         { "Client offered TLS version with major version under 3", ":UNSUPPORTED_PROTOCOL:" },
+         { "Client offered DTLS version with major version 0xFF",  ":UNSUPPORTED_PROTOCOL:" },
+         { "Client policy prohibits insecure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+         { "Client policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
+         { "Client resumed extended ms session without sending extension", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" },
+         { "Client signalled fallback SCSV, possible attack", ":INAPPROPRIATE_FALLBACK:" },
+         { "Client version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Client version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Client version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Client: No certificates sent by server", ":DECODE_ERROR:" },
+         { "Counterparty sent inconsistent key and sig types", ":WRONG_SIGNATURE_TYPE:" },
+         { "Downgrade attack detected", ":TLS13_DOWNGRADE:" },
+         { "Empty ALPN protocol not allowed", ":PARSE_TLSEXT:" },
+         { "Encoding error: Cannot encode PSS string, output length too small", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
+         { "Expected TLS but got a record with DTLS version", ":WRONG_VERSION_NUMBER:" },
+         { "Finished message didn't verify", ":DIGEST_CHECK_FAILED:" },
+         { "Inconsistent length in certificate request", ":DECODE_ERROR:" },
+         { "Inconsistent values in fragmented DTLS handshake header", ":FRAGMENT_MISMATCH:" },
+         { "Invalid CertificateRequest: Length field outside parameters", ":DECODE_ERROR:" },
+         { "Invalid CertificateVerify: Extra bytes at end of message", ":DECODE_ERROR:" },
+         { "Invalid Certificate_Status: invalid length field", ":DECODE_ERROR:" },
+         { "Invalid ChangeCipherSpec", ":BAD_CHANGE_CIPHER_SPEC:" },
+         { "Invalid ClientHello: Length field outside parameters", ":DECODE_ERROR:" },
+         { "Invalid ClientKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
+         { "Invalid ServerKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
+         { "Invalid SessionTicket: Extra bytes at end of message", ":DECODE_ERROR:" },
+         { "Invalid authentication tag: ChaCha20Poly1305 tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
+         { "Invalid authentication tag: GCM tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
+         { "Message authentication failure", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
+         { "No shared TLS version", ":UNSUPPORTED_PROTOCOL:" },
+         { "No shared DTLS version", ":UNSUPPORTED_PROTOCOL:" },
+         { "OS2ECP: Unknown format type 251", ":BAD_ECPOINT:" },
+         { "Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
+         { "Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
+         { "Policy refuses to accept signing with any hash supported by peer", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
+         { "Policy requires client send a certificate, but it did not", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:" },
+         { "Received a record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:" },
+         { "Received unexpected record version in initial record", ":WRONG_VERSION_NUMBER:" },
+         { "Received unexpected record version", ":WRONG_VERSION_NUMBER:" },
+         { "Received application data after connection closure", ":APPLICATION_DATA_ON_SHUTDOWN:" },
+         { "Received handshake data after connection closure", ":NO_RENEGOTIATION:" },
+         { "Server certificate changed during renegotiation", ":SERVER_CERT_CHANGED:" },
+         { "Server changed its mind about extended master secret", ":RENEGOTIATION_EMS_MISMATCH:" },
+         { "Server changed its mind about secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+         { "Server changed version after renegotiation", ":WRONG_SSL_VERSION:" },
+         { "Server downgraded version after renegotiation", ":WRONG_SSL_VERSION:" },
+         { "Server policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
+         { "Server replied using a ciphersuite not allowed in version it offered", ":WRONG_CIPHER_RETURNED:" },
+         { "Server replied with DTLS-SRTP alg we did not send", ":BAD_SRTP_PROTECTION_PROFILE_LIST:" },
+         { "Server replied with ciphersuite we didn't send", ":WRONG_CIPHER_RETURNED:" },
+         { "Server replied with later version than client offered", ":UNSUPPORTED_PROTOCOL:" },
+         { "Server replied with non-null compression method", ":UNSUPPORTED_COMPRESSION_ALGORITHM:" },
+         { "Server replied with some unknown ciphersuite", ":UNKNOWN_CIPHER_RETURNED:" },
+         { "Server replied with unsupported extensions: 0", ":UNEXPECTED_EXTENSION:" },
+         { "Server replied with unsupported extensions: 1234", ":UNEXPECTED_EXTENSION:" },
+         { "Server replied with unsupported extensions: 16", ":UNEXPECTED_EXTENSION:" },
+         { "Server replied with unsupported extensions: 43", ":UNEXPECTED_EXTENSION:" },
+         { "Server replied with unsupported extensions: 5", ":UNEXPECTED_EXTENSION:" },
+         { "Server resumed session and removed extended master secret", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" },
+         { "Server resumed session but added extended master secret", ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:" },
+         { "Server resumed session but with wrong version", ":OLD_SESSION_VERSION_NOT_RETURNED:" },
+         { "Server sent ECC curve prohibited by policy", ":WRONG_CURVE:" },
+         { "Server sent an unsupported extension", ":UNEXPECTED_EXTENSION:" },
+         { "Server sent bad values for secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+         { "Server version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Server version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Server version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+         { "Server_Hello_Done: Must be empty, and is not", ":DECODE_ERROR:" },
+         { "Simulated OCSP callback failure", ":OCSP_CB_ERROR:" },
+         { "Simulating cert verify callback failure", ":CERT_CB_ERROR:" },
+         { "Simulating failure from OCSP response callback", ":OCSP_CB_ERROR:" },
+         { "TLS plaintext record is larger than allowed maximum", ":DATA_LENGTH_TOO_LONG:" },
+         { "TLS signature extension did not allow for RSA/SHA-256 signature", ":WRONG_SIGNATURE_TYPE:", },
+         { "Test requires rejecting cert", ":CERTIFICATE_VERIFY_FAILED:" },
+         { "Unexpected ALPN protocol", ":INVALID_ALPN_PROTOCOL:" },
+         { "Unexpected record type 42 from counterparty", ":UNEXPECTED_RECORD:" },
+
+         { "Unexpected state transition in handshake got a certificate_request expected server_hello_done seen server_hello+server_key_exchange", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a certificate_request expected server_key_exchange|server_hello_done seen server_hello", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a certificate_status expected certificate seen server_hello", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a change_cipher_spec expected certificate_verify seen client_hello+certificate+client_key_exchange", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a change_cipher_spec expected client_key_exchange seen client_hello", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a change_cipher_spec expected new_session_ticket seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a client_key_exchange expected certificate seen client_hello", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello+client_key_exchange", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done+new_session_ticket", ":UNEXPECTED_RECORD:" },
+         { "Unexpected state transition in handshake got a hello_request expected server_hello", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a server_hello_done expected server_key_exchange seen server_hello+certificate+certificate_status", ":UNEXPECTED_MESSAGE:" },
+         { "Unexpected state transition in handshake got a server_key_exchange not expecting messages", ":BAD_HELLO_REQUEST:" },
+         { "Unexpected state transition in handshake got a server_key_exchange expected certificate_request|server_hello_done seen server_hello+certificate+certificate_status", ":UNEXPECTED_MESSAGE:" },
+
+         { "Unknown TLS handshake message type 43", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 44", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 45", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 46", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 53", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 54", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 55", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 56", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 57", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 58", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 6", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 62", ":UNEXPECTED_MESSAGE:" },
+         { "Unknown TLS handshake message type 64", ":UNEXPECTED_MESSAGE:" },
+         { "signature_algorithm_of_scheme: Unknown signature algorithm enum", ":WRONG_SIGNATURE_TYPE:" },
+      };
+
+   auto err_map_i = err_map.find(e);
+   if(err_map_i != err_map.end())
+      return err_map_i->second;
+
+   return "Unmapped error: '" + e + "'";
+   }
+
+class Shim_Exception final : public std::exception
+   {
+   public:
+      Shim_Exception(const std::string& msg, int rc = 1) :
+         m_msg(msg), m_rc(rc) {}
+
+      const char* what() const noexcept override { return m_msg.c_str(); }
+
+      int rc() const { return m_rc; }
+   private:
+      const std::string m_msg;
+      int m_rc;
+   };
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+class Shim_Socket final
+   {
+   private:
+      typedef int socket_type;
+      typedef ssize_t socket_op_ret_type;
+      static void close_socket(socket_type s) { ::close(s); }
+      static std::string get_last_socket_error() { return ::strerror(errno); }
+
+   public:
+      Shim_Socket(const std::string& hostname, int port) : m_socket(-1)
+         {
+         addrinfo hints;
+         std::memset(&hints, 0, sizeof(hints));
+         hints.ai_family = AF_UNSPEC;
+         hints.ai_socktype = SOCK_STREAM;
+         hints.ai_flags = AI_NUMERICSERV;
+         addrinfo* res;
+
+         const std::string service = std::to_string(port);
+         int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+         shim_log("Connecting " + hostname + ":" + service);
+
+         if(rc != 0)
+            {
+            throw Shim_Exception("Name resolution failed for " + hostname);
+            }
+
+         for(addrinfo* rp = res; (m_socket == -1) && (rp != nullptr); rp = rp->ai_next)
+            {
+            if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+               continue;
+
+            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+            if(m_socket == -1)
+               {
+               // unsupported socket type?
+               continue;
+               }
+
+            int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen);
+
+            if(err != 0)
+               {
+               ::close(m_socket);
+               m_socket = -1;
+               }
+            }
+
+         if(m_socket < 0)
+            throw Shim_Exception("Failed to connect to host");
+         }
+
+      ~Shim_Socket()
+         {
+         ::close(m_socket);
+         m_socket = -1;
+         }
+
+      void write(const uint8_t buf[], size_t len)
+         {
+         if(m_socket < 0)
+            throw Shim_Exception("Socket was bad on write");
+         size_t sent_so_far = 0;
+         while(sent_so_far != len)
+            {
+            const size_t left = len - sent_so_far;
+            socket_op_ret_type sent = ::send(m_socket, Botan::cast_uint8_ptr_to_char(&buf[sent_so_far]), left, MSG_NOSIGNAL);
+            if(sent < 0)
+               {
+               if(errno == EPIPE)
+                  return;
+               else
+                  throw Shim_Exception("Socket write failed", errno);
+               }
+            else
+               sent_so_far += static_cast<size_t>(sent);
+            }
+         }
+
+      size_t read(uint8_t buf[], size_t len)
+         {
+         if(m_socket < 0)
+            throw Shim_Exception("Socket was bad on read");
+         socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
+
+         if(got < 0)
+            {
+            if(errno == ECONNRESET)
+               return 0;
+            throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
+            }
+
+         return static_cast<size_t>(got);
+         }
+
+      void read_exactly(uint8_t buf[], size_t len)
+         {
+         if(m_socket < 0)
+            throw Shim_Exception("Socket was bad on read");
+
+         while(len > 0)
+            {
+            socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len);
+
+            if(got == 0)
+               throw Shim_Exception("Socket read EOF");
+            else if(got < 0)
+               throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
+
+            buf += static_cast<size_t>(got);
+            len -= static_cast<size_t>(got);
+            }
+         }
+
+   private:
+      socket_type m_socket;
+   };
+
+#endif
+
+std::set<std::string> combine_options(
+   const std::set<std::string>& a,
+   const std::set<std::string>& b,
+   const std::set<std::string>& c,
+   const std::set<std::string>& d)
+   {
+   std::set<std::string> combined;
+
+   for(auto i : a)
+      combined.insert(i);
+   for(auto i : b)
+      combined.insert(i);
+   for(auto i : c)
+      combined.insert(i);
+   for(auto i : d)
+      combined.insert(i);
+
+   return combined;
+   }
+
+class Shim_Arguments final
+   {
+   public:
+      Shim_Arguments(const std::set<std::string>& flags,
+                     const std::set<std::string>& string_opts,
+                     const std::set<std::string>& base64_opts,
+                     const std::set<std::string>& int_opts,
+                     const std::set<std::string>& int_vec_opts) :
+         m_flags(flags),
+         m_string_opts(string_opts),
+         m_base64_opts(base64_opts),
+         m_int_opts(int_opts),
+         m_int_vec_opts(int_vec_opts),
+         m_all_options(combine_options(string_opts, base64_opts, int_opts, int_vec_opts))
+         {}
+
+      void parse_args(char* argv[]);
+
+      bool flag_set(const std::string& flag) const
+         {
+         if(m_flags.count(flag) == 0)
+            throw Shim_Exception("Unknown bool flag " + flag);
+
+         return m_parsed_flags.count(flag);
+         }
+
+      std::string test_name() const
+         {
+         return get_string_opt("test-name");
+         }
+
+      std::string get_string_opt(const std::string& key) const
+         {
+         if(m_string_opts.count(key) == 0)
+            throw Shim_Exception("Unknown string key " + key);
+         return get_opt(key);
+         }
+
+      std::string get_string_opt_or_else(const std::string& key, const std::string& def) const
+         {
+         if(m_string_opts.count(key) == 0)
+            throw Shim_Exception("Unknown string key " + key);
+         if(!option_used(key))
+            return def;
+         return get_opt(key);
+         }
+
+      std::vector<uint8_t> get_b64_opt(const std::string& key) const
+         {
+         if(m_base64_opts.count(key) == 0)
+            throw Shim_Exception("Unknown base64 key " + key);
+         return Botan::unlock(Botan::base64_decode(get_opt(key)));
+         }
+
+      size_t get_int_opt(const std::string& key) const
+         {
+         if(m_int_opts.count(key) == 0)
+            throw Shim_Exception("Unknown int key " + key);
+         return Botan::to_u32bit(get_opt(key));
+         }
+
+      size_t get_int_opt_or_else(const std::string& key, size_t def) const
+         {
+         if(m_int_opts.count(key) == 0)
+            throw Shim_Exception("Unknown int key " + key);
+         if(!option_used(key))
+            return def;
+
+         return Botan::to_u32bit(get_opt(key));
+         }
+
+      std::vector<size_t> get_int_vec_opt(const std::string& key) const
+         {
+         if(m_int_vec_opts.count(key) == 0)
+            throw Shim_Exception("Unknown int vec key " + key);
+
+         auto i = m_parsed_int_vec_opts.find(key);
+         if(i == m_parsed_int_vec_opts.end())
+            return std::vector<size_t>();
+         else
+            return i->second;
+         }
+
+      std::vector<std::string> get_alpn_string_vec_opt(const std::string& option) const
+         {
+         // hack used for alpn list (relies on all ALPNs being 3 chars long...)
+         char delim = 0x03;
+
+         if(option_used(option))
+            return Botan::split_on(get_string_opt(option), delim);
+         else
+            return std::vector<std::string>();
+         }
+
+      bool option_used(const std::string& key) const
+         {
+         if(m_all_options.count(key) == 0)
+            throw Shim_Exception("Invalid option " + key);
+         if(m_parsed_opts.find(key) != m_parsed_opts.end())
+            return true;
+         if(m_parsed_int_vec_opts.find(key) != m_parsed_int_vec_opts.end())
+            return true;
+         return false;
+         }
+
+   private:
+      std::string get_opt(const std::string& key) const
+         {
+         auto i = m_parsed_opts.find(key);
+         if(i == m_parsed_opts.end())
+            throw Shim_Exception("Option " + key + " was not provided");
+         return i->second;
+         }
+
+      const std::set<std::string> m_flags;
+      const std::set<std::string> m_string_opts;
+      const std::set<std::string> m_base64_opts;
+      const std::set<std::string> m_int_opts;
+      const std::set<std::string> m_int_vec_opts;
+      const std::set<std::string> m_all_options;
+
+      std::set<std::string> m_parsed_flags;
+      std::map<std::string, std::string> m_parsed_opts;
+      std::map<std::string, std::vector<size_t>> m_parsed_int_vec_opts;
+   };
+
+void Shim_Arguments::parse_args(char* argv[])
+   {
+   int i = 1; // skip argv[0]
+
+   while(argv[i] != nullptr)
+      {
+      const std::string param(argv[i]);
+
+      if(param.find("-") == 0)
+         {
+         const std::string flag_name = param.substr(1, std::string::npos);
+
+         if(m_flags.count(flag_name))
+            {
+            shim_log("flag " + flag_name);
+            m_parsed_flags.insert(flag_name);
+            i += 1;
+            }
+         else if(m_all_options.count(flag_name))
+            {
+            if(argv[i+1] == nullptr)
+               throw Shim_Exception("Expected argument following " + param);
+            std::string val(argv[i+1]);
+            shim_log("param " + flag_name + "=" + val);
+
+            if(m_int_vec_opts.count(flag_name))
+               {
+               const size_t v = Botan::to_u32bit(val);
+               m_parsed_int_vec_opts[flag_name].push_back(v);
+               }
+            else
+               {
+               m_parsed_opts[flag_name] = val;
+               }
+            i += 2;
+            }
+         else
+            {
+            shim_log("Unknown option " + param);
+            throw Shim_Exception("Unknown option " + param, 89);
+            }
+         }
+      else
+         {
+         shim_log("Unknown option " + param);
+         throw Shim_Exception("Unknown option " + param, 89);
+         }
+      }
+   }
+
+std::unique_ptr<Shim_Arguments> parse_options(char* argv[])
+   {
+   const std::set<std::string> bogo_shim_flags = {
+      "allow-false-start-without-alpn",
+      "allow-unknown-alpn-protos",
+      "async",
+      "cbc-record-splitting",
+      "check-close-notify",
+      "decline-alpn",
+      "decline-ocsp-callback",
+      "dtls",
+      "enable-all-curves",
+      "enable-channel-id",
+      "enable-early-data",
+      "enable-ed25519",
+      "enable-grease",
+      "enable-ocsp-stapling",
+      "enable-signed-cert-timestamps",
+      "enforce-rsa-key-usage",
+      //"expect-accept-early-data",
+      "expect-extended-master-secret",
+      "expect-no-offer-early-data",
+      "expect-no-secure-renegotiation",
+      "expect-no-session",
+      "expect-no-session-id",
+      //"expect-reject-early-data",
+      "expect-secure-renegotiation",
+      "expect-session-id",
+      "expect-session-miss",
+      "expect-sha256-client-cert",
+      "expect-ticket-renewal",
+      "expect-ticket-supports-early-data",
+      //"expect-tls13-downgrade",
+      "expect-verify-result",
+      "expect-no-hrr",
+      //"export-traffic-secrets",
+      "fail-cert-callback",
+      //"fail-ddos-callback",
+      //"fail-early-callback",
+      "fail-ocsp-callback",
+      "fallback-scsv",
+      //"false-start",
+      "forbid-renegotiation-after-handshake",
+      "handoff",
+      "handshake-never-done",
+      "handshake-twice",
+      "handshaker-resume",
+      //"ignore-tls13-downgrade",
+      "implicit-handshake",
+      "install-cert-compression-algs",
+      "install-ddos-callback",
+      "is-handshaker-supported",
+      //"jdk11-workaround",
+      //"key-update",
+      "no-op-extra-handshake",
+      "no-rsa-pss-rsae-certs",
+      "no-ticket",
+      "no-tls1",
+      "no-tls11",
+      "no-tls12",
+      "no-tls13", // implict due to 1.3 not being implemented
+      "on-resume-no-ticket",
+      //"on-resume-verify-fail",
+      //"partial-write",
+      //"peek-then-read",
+      //"read-with-unfinished-write",
+      "renegotiate-freely",
+      "renegotiate-ignore",
+      "renegotiate-once",
+      //"renew-ticket",
+      "require-any-client-certificate",
+      "retain-only-sha256-client-cert",
+      //"reverify-on-resume",
+      "select-empty-alpn",
+      "send-alert",
+      "server",
+      "server-preference",
+      "set-ocsp-in-callback",
+      "shim-shuts-down",
+      "shim-writes-first",
+      //"tls-unique",
+      "use-custom-verify-callback",
+      "use-early-callback",
+      "use-export-context",
+      "use-exporter-between-reads",
+      "use-ocsp-callback",
+      //"use-old-client-cert-callback",
+      //"use-ticket-callback",
+      "verify-fail",
+      "verify-peer",
+      //"verify-peer-if-no-obc",
+      "write-different-record-sizes",
+   };
+
+   const std::set<std::string> bogo_shim_string_opts = {
+      "advertise-alpn",
+      //"advertise-npn",
+      "cert-file",
+      "cipher",
+      //"delegated-credential",
+      "expect-advertised-alpn",
+      "expect-alpn",
+      "expect-client-ca-list",
+      "expect-late-alpn",
+      "expect-msg-callback",
+      //"expect-next-proto",
+      "expect-peer-cert-file",
+      "expect-server-name",
+      "export-context",
+      "export-label",
+      "handshaker-path",
+      "host-name",
+      "key-file",
+      "psk",
+      "psk-identity",
+      "select-alpn",
+      "select-next-proto",
+      "srtp-profiles",
+      "test-name",
+      "use-client-ca-list",
+      //"send-channel-id",
+      //"write-settings",
+   };
+
+   const std::set<std::string> bogo_shim_base64_opts = {
+      "expect-certificate-types",
+      //"expect-channel-id",
+      "expect-ocsp-response",
+      //"expect-quic-transport-params",
+      //"expect-signed-cert-timestamps",
+      "ocsp-response",
+      //"quic-transport-params",
+      //"signed-cert-timestamps",
+      //"ticket-key", /* we use a different ticket format from Boring */
+      //"token-binding-params",
+   };
+
+   const std::set<std::string> bogo_shim_int_opts {
+      "expect-cipher-aes",
+      "expect-cipher-no-aes",
+      "expect-curve-id",
+      "expect-peer-signature-algorithm",
+      "expect-ticket-age-skew",
+      "expect-token-binding-param",
+      "expect-total-renegotiations",
+      "expect-version",
+      //"export-early-keying-material",
+      "export-keying-material",
+      "initial-timeout-duration-ms",
+      "max-cert-list",
+      //"max-send-fragment",
+      "max-version",
+      "min-version",
+      "mtu",
+      "port",
+      "read-size",
+      "resume-count",
+      "resumption-delay",
+   };
+
+   const std::set<std::string> bogo_shim_int_vec_opts {
+      "curves",
+      "expect-peer-verify-pref",
+      "signing-prefs",
+      "verify-prefs",
+   };
+
+   std::unique_ptr<Shim_Arguments> args(
+      new Shim_Arguments(bogo_shim_flags,
+                         bogo_shim_string_opts,
+                         bogo_shim_base64_opts,
+                         bogo_shim_int_opts,
+                         bogo_shim_int_vec_opts));
+
+   // may throw:
+   args->parse_args(argv);
+
+   return args;
+   }
+
+class Shim_Policy final : public Botan::TLS::Policy
+   {
+   public:
+      Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {}
+
+      void incr_session_established() { m_sessions += 1; }
+
+      std::vector<std::string> allowed_ciphers() const override
+         {
+         return {
+            "AES-256/OCB(12)",
+            "AES-128/OCB(12)",
+            "ChaCha20Poly1305",
+            "AES-256/GCM",
+            "AES-128/GCM",
+            "AES-256/CCM",
+            "AES-128/CCM",
+            "AES-256/CCM(8)",
+            "AES-128/CCM(8)",
+            "Camellia-256/GCM",
+            "Camellia-128/GCM",
+            "ARIA-256/GCM",
+            "ARIA-128/GCM",
+            "AES-256",
+            "AES-128",
+            "Camellia-256",
+            "Camellia-128",
+            "SEED",
+            "3DES",
+         };
+
+         }
+
+      std::vector<std::string> allowed_signature_hashes() const override
+         {
+         if(m_args.option_used("signing-prefs"))
+            {
+            std::vector<std::string> pref_hash;
+            for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
+               {
+               const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(pref);
+               if(Botan::TLS::signature_scheme_is_known(scheme) == false)
+                  continue;
+               pref_hash.push_back(Botan::TLS::hash_function_of_scheme(scheme));
+               }
+
+            if(m_args.flag_set("server"))
+               pref_hash.push_back("SHA-256");
+            return pref_hash;
+            }
+         else
+            {
+            return { "SHA-512", "SHA-384", "SHA-256", "SHA-1" };
+            }
+         }
+
+      //std::vector<std::string> allowed_macs() const override;
+
+      std::vector<std::string> allowed_key_exchange_methods() const override
+         {
+         return {
+            "ECDHE_PSK",
+            "DHE_PSK",
+            "PSK",
+            "CECPQ1",
+            "ECDH",
+            "DH",
+            "RSA",
+         };
+         }
+
+      std::vector<std::string> allowed_signature_methods() const override
+         {
+         return {
+            "ECDSA",
+            "RSA",
+            "IMPLICIT",
+         };
+
+         }
+
+      std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override
+         {
+         if(m_args.option_used("signing-prefs"))
+            {
+            std::vector<Botan::TLS::Signature_Scheme> schemes;
+            for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
+               {
+               schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
+               }
+
+            // BoGo gets sad if these are not included in our signature_algorithms extension
+            if(!m_args.flag_set("server"))
+               {
+               schemes.push_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
+               schemes.push_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
+               }
+
+            return schemes;
+            }
+
+         if(m_args.option_used("verify-prefs"))
+            {
+            std::vector<Botan::TLS::Signature_Scheme> schemes;
+            for(size_t pref : m_args.get_int_vec_opt("verify-prefs"))
+               {
+               schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
+               }
+
+            return schemes;
+            }
+
+         return Botan::TLS::Policy::allowed_signature_schemes();
+         }
+
+      //size_t minimum_signature_strength() const override;
+
+      //bool require_cert_revocation_info() const override;
+
+      std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+         {
+         if(m_args.option_used("curves"))
+            {
+            std::vector<Botan::TLS::Group_Params> groups;
+            for(size_t pref : m_args.get_int_vec_opt("curves"))
+               {
+               groups.push_back(static_cast<Botan::TLS::Group_Params>(pref));
+               }
+
+            return groups;
+            }
+
+         return Botan::TLS::Policy::key_exchange_groups();
+         }
+
+      bool use_ecc_point_compression() const override { return false; } // BoGo expects this
+
+      //Botan::TLS::Group_Params choose_key_exchange_group(const std::vector<Botan::TLS::Group_Params>& peer_groups) const override;
+
+      bool require_client_certificate_authentication() const override
+         {
+         return m_args.flag_set("require-any-client-certificate");
+         }
+
+      bool request_client_certificate_authentication() const override
+         {
+         return m_args.flag_set("verify-peer") ||
+            m_args.flag_set("fail-cert-callback") ||
+            require_client_certificate_authentication();
+         }
+
+      bool allow_insecure_renegotiation() const override
+         {
+         if(m_args.flag_set("expect-no-secure-renegotiation"))
+            return true;
+         else
+            return false;
+         }
+
+      //bool include_time_in_hello_random() const override;
+
+      bool allow_client_initiated_renegotiation() const override
+         {
+         if(m_args.flag_set("renegotiate-freely"))
+            return true;
+
+         if(m_args.flag_set("renegotiate-once") && m_sessions <= 1)
+            return true;
+
+         return false;
+         }
+
+      bool allow_server_initiated_renegotiation() const override
+         {
+         return allow_client_initiated_renegotiation(); // same logic
+         }
+
+      bool allow_version(Botan::TLS::Protocol_Version version) const
+         {
+         if(m_args.option_used("min-version"))
+            {
+            const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version"));
+            Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF);
+            if(min_version > version)
+               return false;
+            }
+
+         if(m_args.option_used("max-version"))
+            {
+            const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version"));
+            Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF);
+            if(version > max_version)
+               return false;
+            }
+
+         return version.known_version();
+         }
+
+      bool allow_tls10() const override
+         {
+         return !m_args.flag_set("dtls") &&
+            !m_args.flag_set("no-tls1") &&
+            allow_version(Botan::TLS::Protocol_Version::TLS_V10);
+         }
+
+      bool allow_tls11() const override
+         {
+         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls11") && allow_version(Botan::TLS::Protocol_Version::TLS_V11);
+         }
+
+      bool allow_tls12() const override
+         {
+         return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12);
+         }
+
+      bool allow_dtls10() const override
+         {
+         return m_args.flag_set("dtls") && !m_args.flag_set("no-tls1") && allow_version(Botan::TLS::Protocol_Version::DTLS_V10);
+         }
+
+      bool allow_dtls12() const override
+         {
+         return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::DTLS_V12);
+         }
+
+      //Botan::TLS::Group_Params default_dh_group() const override;
+
+      //size_t minimum_dh_group_size() const override;
+
+      size_t minimum_ecdsa_group_size() const override { return 224; }
+
+      size_t minimum_ecdh_group_size() const override { return 224; }
+
+      //size_t minimum_rsa_bits() const override;
+
+      //size_t minimum_dsa_group_size() const override;
+
+      //void check_peer_key_acceptable(const Botan::Public_Key& public_key) const override;
+
+      //bool hide_unknown_users() const override;
+
+      //uint32_t session_ticket_lifetime() const override;
+
+      std::vector<uint16_t> srtp_profiles() const override
+         {
+         if(m_args.option_used("srtp-profiles"))
+            {
+            std::string srtp = m_args.get_string_opt("srtp-profiles");
+
+            if(srtp == "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
+               return {1,2};
+            else if(srtp == "SRTP_AES128_CM_SHA1_80")
+               return {1};
+            else
+               shim_exit_with_error("unknown srtp-profiles");
+            }
+         else
+            return {};
+         }
+
+      bool only_resume_with_exact_version() const override
+         {
+         return false;
+         }
+
+      bool send_fallback_scsv(Botan::TLS::Protocol_Version) const override
+         {
+         return m_args.flag_set("fallback-scsv");
+         }
+
+      //bool server_uses_own_ciphersuite_preferences() const override;
+
+      //bool negotiate_encrypt_then_mac() const override;
+
+      bool support_cert_status_message() const override
+         {
+         if(m_args.flag_set("server"))
+            {
+            if(!m_args.option_used("ocsp-response"))
+               return false;
+            if(m_args.flag_set("decline-ocsp-callback"))
+               return false;
+            }
+         return true;
+         }
+
+      std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+                                             bool have_srp) const override;
+
+      size_t dtls_default_mtu() const override
+         {
+         return m_args.get_int_opt_or_else("mtu", 1500);
+         }
+
+      //size_t dtls_initial_timeout() const override;
+
+      //size_t dtls_maximum_timeout() const override;
+
+      bool allow_resumption_for_renegotiation() const override
+         {
+         return false; // BoGo expects this
+         }
+
+      bool abort_connection_on_undesired_renegotiation() const override
+         {
+         if(m_args.flag_set("renegotiate-ignore"))
+            return false;
+         else
+            return true;
+         }
+
+      size_t maximum_certificate_chain_size() const override
+         {
+         return m_args.get_int_opt_or_else("max-cert-list", 0);
+         }
+
+   private:
+      const Shim_Arguments& m_args;
+      size_t m_sessions;
+   };
+
+std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version version,
+                                                    bool have_srp) const
+   {
+   std::vector<uint16_t> ciphersuite_codes;
+
+   const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", "");
+   if(cipher_limit == "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA:[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]")
+      {
+      std::vector<std::string> suites = {
+         "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+         "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+         "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+         "ECDHE_RSA_WITH_AES_256_CBC_SHA",
+         "RSA_WITH_AES_256_GCM_SHA384",
+         "RSA_WITH_AES_256_CBC_SHA",
+      };
+
+      for(auto suite_name : suites)
+         {
+         const auto suite = Botan::TLS::Ciphersuite::from_name(suite_name);
+         if(suite.valid() == false)
+            shim_exit_with_error("Bad ciphersuite name " + suite_name);
+         ciphersuite_codes.push_back(suite.ciphersuite_code());
+         }
+      }
+   else
+      {
+      // Hack: go in reverse order to avoid preferring 3DES
+      auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites();
+      for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i)
+         {
+         const auto suite = *i;
+         // Can we use it?
+         if(suite.valid() == false)
+            continue;
+
+         // Are we doing SRP?
+         if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+            continue;
+
+         if(cipher_limit != "")
+            {
+            if(cipher_limit == "DEFAULT:!AES")
+               {
+               const std::string suite_algo = suite.cipher_algo();
+
+               if(suite_algo == "AES-128" || suite_algo == "AES-256" ||
+                  suite_algo == "AES-128/GCM" || suite_algo == "AES-256/GCM" ||
+                  suite_algo == "AES-128/CCM" || suite_algo == "AES-256/CCM" ||
+                  suite_algo == "AES-128/CCM(8)" || suite_algo == "AES-256/CCM(8)" ||
+                  suite_algo == "AES-128/OCB(12)" || suite_algo == "AES-256/OCB(12)")
+                  {
+                  continue;
+                  }
+               }
+            else
+               {
+               shim_exit_with_error("Unknown cipher " + cipher_limit);
+               }
+            }
+
+         if(!version.supports_aead_modes())
+            {
+            // Are we doing AEAD in a non-AEAD version?
+            if(suite.mac_algo() == "AEAD")
+               continue;
+
+            // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+            if(suite.mac_algo() != "SHA-1")
+               continue;
+            }
+
+         ciphersuite_codes.push_back(suite.ciphersuite_code());
+         }
+      }
+
+   return ciphersuite_codes;
+   }
+
+class Shim_Credentials final : public Botan::Credentials_Manager
+   {
+   public:
+      Shim_Credentials(const Shim_Arguments& args) : m_args(args)
+         {
+         m_psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
+
+         const std::string psk_str = m_args.get_string_opt_or_else("psk", "");
+         m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(psk_str.data()), psk_str.size());
+
+         if(m_args.option_used("key-file") && m_args.option_used("cert-file"))
+            {
+            Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file"));
+            m_key = Botan::PKCS8::load_key(key_stream);
+
+            Botan::DataSource_Stream cert_stream(m_args.get_string_opt("cert-file"));
+
+            while(!cert_stream.end_of_data())
+               {
+               try
+                  {
+                  m_cert_chain.push_back(Botan::X509_Certificate(cert_stream));
+                  }
+               catch(...) {}
+               }
+            }
+         }
+
+      std::string psk_identity(const std::string& /*type*/,
+                               const std::string& /*context*/,
+                               const std::string& /*identity_hint*/) override
+         {
+         return m_psk_identity;
+         }
+
+      std::string psk_identity_hint(const std::string& /*type*/,
+                                    const std::string& /*context*/) override
+         {
+         return m_psk_identity;
+         }
+
+      Botan::SymmetricKey psk(const std::string& type,
+                              const std::string& context,
+                              const std::string& identity) override
+         {
+         if(type == "tls-server" && context == "session-ticket")
+            {
+            if(!m_args.flag_set("no-ticket") && !m_args.flag_set("on-resume-no-ticket"))
+               return Botan::SymmetricKey("ABCDEF0123456789");
+            }
+
+         if(type == "tls-server" && context == "dtls-cookie-secret")
+            {
+            return Botan::SymmetricKey("F00FB00FD00F100F700F");
+            }
+
+         if(identity != m_psk_identity)
+            throw Shim_Exception("Unexpected PSK identity");
+         return m_psk;
+         }
+
+      std::vector<Botan::X509_Certificate> cert_chain(
+         const std::vector<std::string>& cert_key_types,
+         const std::string& /*type*/,
+         const std::string& /*context*/) override
+         {
+         if(m_args.flag_set("fail-cert-callback"))
+            throw std::runtime_error("Simulating cert verify callback failure");
+
+         if(m_key != nullptr && m_cert_chain.size() > 0)
+            {
+            for(std::string t : cert_key_types)
+               {
+               if(t == m_key->algo_name())
+                  return m_cert_chain;
+               }
+            }
+
+         return {};
+         }
+
+      Botan::Private_Key* private_key_for(const Botan::X509_Certificate& /*cert*/,
+                                          const std::string& /*type*/,
+                                          const std::string& /*context*/) override
+         {
+         // assumes cert == m_cert
+         return m_key.get();
+         }
+
+   private:
+      const Shim_Arguments& m_args;
+      Botan::SymmetricKey m_psk;
+      std::string m_psk_identity;
+      std::unique_ptr<Botan::Private_Key> m_key;
+      std::vector<Botan::X509_Certificate> m_cert_chain;
+   };
+
+class Shim_Callbacks final : public Botan::TLS::Callbacks
+   {
+   public:
+      Shim_Callbacks(const Shim_Arguments& args, Shim_Socket& socket, Shim_Policy& policy) :
+         m_channel(nullptr),
+         m_args(args),
+         m_policy(policy),
+         m_socket(socket),
+         m_is_datagram(args.flag_set("dtls")),
+         m_warning_alerts(0),
+         m_empty_records(0),
+         m_sessions_established(0),
+         m_got_close(false)
+         {}
+
+      size_t sessions_established() const { return m_sessions_established; }
+
+      void set_channel(Botan::TLS::Channel* channel)
+         {
+         m_channel = channel;
+         }
+
+      bool saw_close_notify() const { return m_got_close; }
+
+      void tls_emit_data(const uint8_t data[], size_t size) override
+         {
+         shim_log("sending record of len " + std::to_string(size));
+
+         if(m_is_datagram)
+            {
+            std::vector<uint8_t> packet(size + 5);
+
+            packet[0] = 'P';
+            for(size_t i = 0; i != 4; ++i)
+               packet[i+1] = static_cast<uint8_t>((size >> (24-8*i)) & 0xFF);
+            std::memcpy(packet.data() + 5, data, size);
+
+            m_socket.write(packet.data(), packet.size());
+            }
+         else
+            {
+            m_socket.write(data, size);
+            }
+         }
+
+      std::vector<uint8_t> tls_provide_cert_status(const std::vector<Botan::X509_Certificate>&,
+                                                   const Botan::TLS::Certificate_Status_Request&) override
+          {
+          if(m_args.flag_set("use-ocsp-callback") && m_args.flag_set("fail-ocsp-callback"))
+             throw std::runtime_error("Simulating failure from OCSP response callback");
+
+          if(m_args.flag_set("decline-ocsp-callback"))
+             return {};
+
+          if(m_args.option_used("ocsp-response"))
+             {
+             return m_args.get_b64_opt("ocsp-response");
+             }
+
+          return {};
+          }
+
+      void tls_record_received(uint64_t /*seq_no*/, const uint8_t data[], size_t size) override
+         {
+         if(size == 0)
+            {
+            m_empty_records += 1;
+            if(m_empty_records > 32)
+               shim_exit_with_error(":TOO_MANY_EMPTY_FRAGMENTS:");
+            }
+         else
+            {
+            m_empty_records = 0;
+            }
+
+         shim_log("Reflecting application_data len " + std::to_string(size));
+
+         std::vector<uint8_t> buf(data, data + size);
+         for(size_t i = 0; i != size; ++i)
+            buf[i] ^= 0xFF;
+
+         m_channel->send(buf);
+         }
+
+      bool tls_verify_message(const Botan::Public_Key& key,
+                              const std::string& emsa,
+                              Botan::Signature_Format format,
+                              const std::vector<uint8_t>& msg,
+                              const std::vector<uint8_t>& sig) override
+         {
+         if(m_args.option_used("expect-peer-signature-algorithm"))
+            {
+            const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(m_args.get_int_opt("expect-peer-signature-algorithm"));
+            if(scheme != Botan::TLS::Signature_Scheme::NONE)
+               {
+               const std::string exp_emsa = Botan::TLS::padding_string_for_scheme(scheme);
+               if(emsa != exp_emsa)
+                  shim_exit_with_error("Unexpected signature scheme got " + emsa + " expected " + exp_emsa);
+               }
+            }
+         return Botan::TLS::Callbacks::tls_verify_message(key, emsa, format, msg, sig);
+         }
+
+      void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& /*cert_chain*/,
+                                 const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& /*ocsp_responses*/,
+                                 const std::vector<Botan::Certificate_Store*>& /*trusted_roots*/,
+                                 Botan::Usage_Type /*usage*/,
+                                 const std::string& /*hostname*/,
+                                 const Botan::TLS::Policy& /*policy*/) override
+         {
+         if(m_args.flag_set("enable-ocsp-stapling") &&
+            m_args.flag_set("use-ocsp-callback") &&
+            m_args.flag_set("fail-ocsp-callback"))
+            {
+            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE,
+                                            "Simulated OCSP callback failure");
+            }
+
+         if(m_args.flag_set("verify-peer") && m_args.flag_set("verify-fail"))
+            {
+            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE,
+                                            "Test requires rejecting cert");
+            }
+         }
+
+      std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+         {
+         if(client_protos.empty())
+            return ""; // shouldn't happen?
+
+         if(m_args.flag_set("decline-alpn"))
+            return "";
+
+         if(m_args.option_used("expect-advertised-alpn"))
+            {
+            const std::vector<std::string> expected = m_args.get_alpn_string_vec_opt("expect-advertised-alpn");
+
+            if(client_protos != expected)
+               shim_exit_with_error("Bad ALPN from client");
+            }
+
+         if(m_args.option_used("select-alpn"))
+            return m_args.get_string_opt("select-alpn");
+
+         return client_protos[0]; // if not configured just pick something
+         }
+
+      void tls_alert(Botan::TLS::Alert alert) override
+         {
+         if(alert.is_fatal())
+            shim_log("Got a fatal alert " + alert.type_string());
+         else
+            shim_log("Got a warning alert " + alert.type_string());
+
+         if(alert.type() == Botan::TLS::Alert::RECORD_OVERFLOW)
+            {
+            shim_exit_with_error(":TLSV1_ALERT_RECORD_OVERFLOW:");
+            }
+
+         if(alert.type() == Botan::TLS::Alert::DECOMPRESSION_FAILURE)
+            {
+            shim_exit_with_error(":SSLV3_ALERT_DECOMPRESSION_FAILURE:");
+            }
+
+         if(!alert.is_fatal())
+            {
+            m_warning_alerts++;
+            if(m_warning_alerts > 5)
+               shim_exit_with_error(":TOO_MANY_WARNING_ALERTS:");
+            }
+
+         if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+            {
+            if(m_got_close == false && !m_args.flag_set("shim-shuts-down"))
+               {
+               shim_log("Sending return close notify");
+               m_channel->send_alert(alert);
+               }
+            m_got_close = true;
+            }
+         else if(alert.is_fatal())
+            {
+            shim_exit_with_error("Unexpected fatal alert " + alert.type_string());
+            }
+         }
+
+      bool tls_session_established(const Botan::TLS::Session& session) override
+         {
+         shim_log("Session established: " + Botan::hex_encode(session.session_id()) +
+                  " version " + session.version().to_string() +
+                  " cipher " + session.ciphersuite().to_string() +
+                  " EMS " + std::to_string(session.supports_extended_master_secret()));
+         // probably need tests here?
+
+         m_policy.incr_session_established();
+         m_sessions_established++;
+
+         if(m_args.flag_set("expect-no-session-id"))
+            {
+            // BoGo expects that ticket issuance implies no stateful session...
+            if(!m_args.flag_set("server") && session.session_id().size() > 0)
+               shim_exit_with_error("Unexpectedly got a session ID");
+            }
+         else if(m_args.flag_set("expect-session-id"))
+            {
+            if(session.session_id().empty())
+               shim_exit_with_error("Unexpectedly got no session ID");
+            }
+
+         if(m_args.option_used("expect-version"))
+            {
+            if(session.version().version_code() != m_args.get_int_opt("expect-version"))
+               shim_exit_with_error("Unexpected version");
+            }
+
+         if(m_args.flag_set("expect-secure-renegotiation"))
+            {
+            if(m_channel->secure_renegotiation_supported() == false)
+               shim_exit_with_error("Expected secure renegotiation");
+            }
+         else if(m_args.flag_set("expect-no-secure-renegotiation"))
+            {
+            if(m_channel->secure_renegotiation_supported() == true)
+               shim_exit_with_error("Expected no secure renegotation");
+            }
+
+         if(m_args.flag_set("expect-extended-master-secret"))
+            {
+            if(session.supports_extended_master_secret() == false)
+               shim_exit_with_error("Expected extended maseter secret");
+            }
+
+         return true;
+         }
+
+      void tls_session_activated() override
+         {
+         if(m_args.flag_set("send-alert"))
+            {
+            m_channel->send_fatal_alert(Botan::TLS::Alert::DECOMPRESSION_FAILURE);
+            return;
+            }
+
+         if(size_t length = m_args.get_int_opt_or_else("export-keying-material", 0))
+            {
+            const std::string label = m_args.get_string_opt("export-label");
+            const std::string context = m_args.get_string_opt("export-context");
+            const auto exported = m_channel->key_material_export(label, context, length);
+            shim_log("Sending " + std::to_string(length) + " bytes of key material");
+            m_channel->send(exported.bits_of());
+            }
+
+         const std::string alpn = m_channel->application_protocol();
+
+         if(m_args.option_used("expect-alpn"))
+            {
+            if(alpn != m_args.get_string_opt("expect-alpn"))
+               shim_exit_with_error("Got unexpected ALPN");
+            }
+
+         if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos"))
+            {
+            throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::ILLEGAL_PARAMETER,
+                                            "Unexpected ALPN protocol");
+            }
+
+         if(m_args.flag_set("shim-shuts-down"))
+            {
+            shim_log("Shim shutting down");
+            m_channel->close();
+            }
+
+         if(m_args.flag_set("write-different-record-sizes"))
+            {
+            static const size_t record_sizes[] = {
+               0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769
+            };
+
+            std::vector<uint8_t> buf(32769, 0x42);
+
+            for(size_t sz : record_sizes)
+               {
+               m_channel->send(buf.data(), sz);
+               }
+
+            m_channel->close();
+            }
+         }
+
+   private:
+      Botan::TLS::Channel* m_channel;
+      const Shim_Arguments& m_args;
+      Shim_Policy& m_policy;
+      Shim_Socket& m_socket;
+      const bool m_is_datagram;
+      size_t m_warning_alerts;
+      size_t m_empty_records;
+      size_t m_sessions_established;
+      bool m_got_close;
+   };
+
+}
+
+int main(int /*argc*/, char* argv[])
+   {
+   try
+      {
+      std::unique_ptr<Shim_Arguments> args = parse_options(argv);
+
+      if(args->flag_set("is-handshaker-supported"))
+         {
+         return shim_output("No\n");
+         }
+
+      const uint16_t port = static_cast<uint16_t>(args->get_int_opt("port"));
+      const size_t resume_count = args->get_int_opt_or_else("resume-count", 0);
+      const bool is_server = args->flag_set("server");
+      const bool is_datagram = args->flag_set("dtls");
+
+      const size_t buf_size = args->get_int_opt_or_else("read-size", 18*1024);
+
+      Botan::ChaCha_RNG rng(Botan::secure_vector<uint8_t>(64));
+      Botan::TLS::Session_Manager_In_Memory session_manager(rng, 1024);
+      Shim_Credentials creds(*args);
+
+      for(size_t i = 0; i != resume_count+1; ++i)
+         {
+         Shim_Socket socket("localhost", port);
+
+         shim_log("Connection " + std::to_string(i+1) + "/" + std::to_string(resume_count+1));
+
+         Shim_Policy policy(*args);
+         Shim_Callbacks callbacks(*args, socket, policy);
+
+         std::unique_ptr<Botan::TLS::Channel> chan;
+
+         if(is_server)
+            {
+            chan.reset(new Botan::TLS::Server(callbacks, session_manager, creds, policy, rng, is_datagram));
+            }
+         else
+            {
+            Botan::TLS::Protocol_Version offer_version = policy.latest_supported_version(is_datagram);
+            shim_log("Offering " + offer_version.to_string());
+
+            std::string host_name = args->get_string_opt_or_else("host-name", "localhost");
+            if(args->test_name().find("UnsolicitedServerNameAck-TLS1") == 0)
+               host_name = ""; // avoid sending SNI for this test
+
+            Botan::TLS::Server_Information server_info(host_name, port);
+            const std::vector<std::string> next_protocols = args->get_alpn_string_vec_opt("advertise-alpn");
+            chan.reset(new Botan::TLS::Client(callbacks, session_manager, creds, policy, rng,
+                                              server_info, offer_version, next_protocols));
+            }
+
+         callbacks.set_channel(chan.get());
+
+         std::vector<uint8_t> buf(buf_size);
+
+         for(;;)
+            {
+            if(is_datagram)
+               {
+               uint8_t opcode;
+               size_t got = socket.read(&opcode, 1);
+               if(got == 0)
+                  {
+                  shim_log("EOF on socket");
+                  break;
+                  }
+
+               if(opcode == 'P')
+                  {
+                  uint8_t len_bytes[4];
+                  socket.read_exactly(len_bytes, sizeof(len_bytes));
+
+                  size_t packet_len = Botan::load_be<uint32_t>(len_bytes, 0);
+
+                  if(buf.size() < packet_len)
+                     buf.resize(packet_len);
+                  socket.read_exactly(buf.data(), packet_len);
+
+                  chan->received_data(buf.data(), packet_len);
+                  }
+               else if(opcode == 'T')
+                  {
+                  uint8_t timeout_ack = 't';
+
+                  uint8_t timeout_bytes[8];
+                  socket.read_exactly(timeout_bytes, sizeof(timeout_bytes));
+
+                  const uint64_t nsec = Botan::load_be<uint64_t>(timeout_bytes, 0);
+
+                  shim_log("Timeout nsec " + std::to_string(nsec));
+
+                  // FIXME handle this!
+
+                  socket.write(&timeout_ack, 1); // ack it anyway
+                  }
+               else
+                  shim_exit_with_error("Unknown opcode " + std::to_string(opcode));
+               }
+            else
+               {
+               size_t got = socket.read(buf.data(), buf.size());
+               if(got == 0)
+                  {
+                  shim_log("EOF on socket");
+                  break;
+                  }
+
+               shim_log("Got packet of " + std::to_string(got));
+
+               if(args->flag_set("use-exporter-between-reads") && chan->is_active())
+                  {
+                  chan->key_material_export("some label", "some context", 42);
+                  }
+               const size_t needed = chan->received_data(buf.data(), got);
+
+               if(needed)
+                  shim_log("Short read still need " + std::to_string(needed));
+               }
+            }
+
+         if(args->flag_set("check-close-notify"))
+            {
+            if(!callbacks.saw_close_notify())
+               throw Shim_Exception("Unexpected SSL_shutdown result: -1 != 1");
+            }
+
+         if(args->option_used("expect-total-renegotiations"))
+            {
+            const size_t exp = args->get_int_opt("expect-total-renegotiations");
+
+            if(exp != callbacks.sessions_established() - 1)
+               throw Shim_Exception("Unexpected number of renegotiations: saw " +
+                                    std::to_string(callbacks.sessions_established() - 1) +
+                                    " exp " + std::to_string(exp));
+            }
+         shim_log("End of resume loop");
+         }
+
+      }
+   catch(Shim_Exception& e)
+      {
+      shim_exit_with_error(e.what(), e.rc());
+      }
+   catch(std::exception& e)
+      {
+      shim_exit_with_error(map_to_bogo_error(e.what()));
+      }
+   catch(...)
+      {
+      shim_exit_with_error("Unknown exception", 3);
+      }
+   return 0;
+   }
diff --git a/third_party/botan/src/build-data/detect_arch.cpp b/third_party/botan/src/build-data/detect_arch.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/build-data/detect_arch.cpp
@@ -0,0 +1,76 @@
+
+#if defined(__x86_64__) && defined(__ILP32__)
+  X32
+
+#elif defined(__x86_64__) || defined(_M_X64)
+  X86_64
+
+#elif defined(__i386__) || defined(__i386) || defined(_M_IX86)
+  X86_32
+
+#elif defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64)
+  ARM64
+
+#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_ARCH_7A__)
+  ARM32
+
+#elif defined(__powerpc64__) || defined(__ppc64__) || defined(_ARCH_PPC64)
+  PPC64
+
+#elif defined(__powerpc__) || defined(__ppc__) || defined(_ARCH_PPC)
+
+  #if defined(__SPE__)
+     POWERPCSPE
+  #else
+     PPC32
+  #endif
+
+#elif defined(__mips__) || defined(__mips)
+
+  #if defined(__LP64__) || defined(_LP64)
+  MIPS64
+  #else
+  MIPS32
+  #endif
+
+#elif defined(__sparc__)
+
+  #if defined(__LP64__) || defined(_LP64)
+  SPARC64
+  #else
+  SPARC32
+  #endif
+
+#elif defined(__alpha__)
+  ALPHA
+
+#elif defined(__hppa__) || defined(__hppa)
+  HPPA
+
+#elif defined(__ia64__)
+  IA64
+
+#elif defined(__m68k__)
+  M68K
+
+#elif defined(__sh__)
+  SH
+
+#elif defined(__s390x__)
+  S390X
+
+#elif defined(__s390__)
+  S390
+
+#elif defined(__riscv)
+
+  #if defined(__LP64__)
+     RISCV64
+  #else
+     RISCV32
+  #endif
+
+#else
+  UNKNOWN
+
+#endif
diff --git a/third_party/botan/src/build-data/detect_version.cpp b/third_party/botan/src/build-data/detect_version.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/build-data/detect_version.cpp
@@ -0,0 +1,60 @@
+/*
+* This file is preprocessed to produce output that is examined by
+* configure.py to determine the compilers version number.
+*/
+
+#if defined(_MSC_VER)
+
+   /*
+   _MSC_VER Defined as an integer literal that encodes the major and
+   minor number elements of the compiler's version number. The major
+   number is the first element of the period-delimited version number
+   and the minor number is the second element. For example, if the
+   version number of the Visual C++ compiler is 17.00.51106.1, the
+   _MSC_VER macro evaluates to 1700.
+   https://msdn.microsoft.com/en-us/library/b0084kay.aspx
+   */
+   MSVC _MSC_VER
+
+#elif defined(__ibmxl__)
+
+   XLC __ibmxl_version__ __ibmxl_release__
+
+#elif defined(__clang__) && defined(__apple_build_version__)
+
+   /*
+   Map Apple LLVM versions as used in XCode back to standard Clang.
+   This is not exact since the versions used in XCode are actually
+   forks of Clang and do not coorespond perfectly to standard Clang
+   releases. In addition we don't bother mapping very old versions
+   (anything before XCode 7 is treated like Clang 3.5, which is the
+   oldest version we support) and for "future" versions we simply
+   treat them as Clang 4.0, since we don't currenly rely on any
+   features not included in 4.0
+   */
+
+   #if __clang_major__ >= 9
+      CLANG 4 0
+   #elif __clang_major__ == 8
+      CLANG 3 9
+   #elif __clang_major__ == 7 && __clang_minor__ == 3
+      CLANG 3 8
+   #elif __clang_major__ == 7
+      CLANG 3 7
+   #else
+      CLANG 3 5
+   #endif
+
+#elif defined(__clang__)
+
+   CLANG __clang_major__ __clang_minor__
+
+#elif defined(__GNUG__)
+
+   GCC __GNUC__ __GNUC_MINOR__
+
+#else
+
+   UNKNOWN 0 0
+
+#endif
diff --git a/third_party/botan/src/cli/argon2.cpp b/third_party/botan/src/cli/argon2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/argon2.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ARGON2)
+   #include <botan/argon2.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_ARGON2)
+
+class Generate_Argon2 final : public Command
+   {
+   public:
+      Generate_Argon2() : Command("gen_argon2 --mem=65536 --p=1 --t=1 password") {}
+
+      std::string group() const override
+         {
+         return "passhash";
+         }
+
+      std::string description() const override
+         {
+         return "Calculate Argon2 password hash";
+         }
+
+      void go() override
+         {
+         const std::string password = get_passphrase_arg("Passphrase to hash", "password");
+         const size_t M = get_arg_sz("mem");
+         const size_t p = get_arg_sz("p");
+         const size_t t = get_arg_sz("t");
+
+         output() << Botan::argon2_generate_pwhash(password.data(), password.size(), rng(), p, M, t) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_argon2", Generate_Argon2);
+
+class Check_Argon2 final : public Command
+   {
+   public:
+      Check_Argon2() : Command("check_argon2 password hash") {}
+
+      std::string group() const override
+         {
+         return "passhash";
+         }
+
+      std::string description() const override
+         {
+         return "Verify Argon2 password hash";
+         }
+
+      void go() override
+         {
+         const std::string password = get_passphrase_arg("Password to check", "password");
+         const std::string hash = get_arg("hash");
+
+         const bool ok = Botan::argon2_check_pwhash(password.data(), password.size(), hash);
+
+         output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+
+         if(ok == false)
+            set_return_code(1);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("check_argon2", Check_Argon2);
+
+#endif // argon2
+
+}
diff --git a/third_party/botan/src/cli/argparse.h b/third_party/botan/src/cli/argparse.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/argparse.h
@@ -0,0 +1,280 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_ARGPARSE_H_
+#define BOTAN_CLI_ARGPARSE_H_
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <botan/parsing.h>
+#include "cli_exceptions.h"
+
+namespace Botan_CLI {
+
+class Argument_Parser final
+   {
+   public:
+      Argument_Parser(const std::string& spec,
+                      const std::vector<std::string>& extra_flags = {},
+                      const std::vector<std::string>& extra_opts = {});
+
+      void parse_args(const std::vector<std::string>& params);
+
+      bool flag_set(const std::string& flag) const;
+
+      bool has_arg(const std::string& opt_name) const;
+      std::string get_arg(const std::string& option) const;
+
+      std::string get_arg_or(const std::string& option, const std::string& otherwise) const;
+
+      size_t get_arg_sz(const std::string& option) const;
+
+      std::vector<std::string> get_arg_list(const std::string& what) const;
+
+   private:
+      // set in constructor
+      std::vector<std::string> m_spec_args;
+      std::set<std::string> m_spec_flags;
+      std::map<std::string, std::string> m_spec_opts;
+      std::string m_spec_rest;
+
+      // set in parse_args()
+      std::map<std::string, std::string> m_user_args;
+      std::set<std::string> m_user_flags;
+      std::vector<std::string> m_user_rest;
+   };
+
+bool Argument_Parser::flag_set(const std::string& flag_name) const
+   {
+   return m_user_flags.count(flag_name) > 0;
+   }
+
+bool Argument_Parser::has_arg(const std::string& opt_name) const
+   {
+   return m_user_args.count(opt_name) > 0;
+   }
+
+std::string Argument_Parser::get_arg(const std::string& opt_name) const
+   {
+   auto i = m_user_args.find(opt_name);
+   if(i == m_user_args.end())
+      {
+      // this shouldn't occur unless you passed the wrong thing to get_arg
+      throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
+      }
+   return i->second;
+   }
+
+std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const
+   {
+   auto i = m_user_args.find(opt_name);
+   if(i == m_user_args.end() || i->second.empty())
+      {
+      return otherwise;
+      }
+   return i->second;
+   }
+
+size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const
+   {
+   const std::string s = get_arg(opt_name);
+
+   try
+      {
+      return static_cast<size_t>(std::stoul(s));
+      }
+   catch(std::exception&)
+      {
+      throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
+      }
+   }
+
+std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const
+   {
+   if(what == m_spec_rest)
+      return m_user_rest;
+
+   return Botan::split_on(get_arg(what), ',');
+   }
+
+void Argument_Parser::parse_args(const std::vector<std::string>& params)
+   {
+   std::vector<std::string> args;
+   for(auto const& param : params)
+      {
+      if(param.find("--") == 0)
+         {
+         // option
+         const auto eq = param.find('=');
+
+         if(eq == std::string::npos)
+            {
+            const std::string opt_name = param.substr(2, std::string::npos);
+
+            if(m_spec_flags.count(opt_name) == 0)
+               {
+               if(m_spec_opts.count(opt_name))
+                  {
+                  throw CLI_Usage_Error("Invalid usage of option --" + opt_name +
+                                        " without value");
+                  }
+               else
+                  {
+                  throw CLI_Usage_Error("Unknown flag --" + opt_name);
+                  }
+               }
+            m_user_flags.insert(opt_name);
+            }
+         else
+            {
+            const std::string opt_name = param.substr(2, eq - 2);
+            const std::string opt_val = param.substr(eq + 1, std::string::npos);
+
+            if(m_spec_opts.count(opt_name) == 0)
+               {
+               throw CLI_Usage_Error("Unknown option --" + opt_name);
+               }
+
+            m_user_args.insert(std::make_pair(opt_name, opt_val));
+            }
+         }
+      else
+         {
+         // argument
+         args.push_back(param);
+         }
+      }
+
+   if(flag_set("help"))
+      return;
+
+   if(args.size() < m_spec_args.size())
+      {
+      // not enough arguments
+      throw CLI_Usage_Error("Invalid argument count, got " +
+                            std::to_string(args.size()) +
+                            " expected " +
+                            std::to_string(m_spec_args.size()));
+      }
+
+   bool seen_stdin_flag = false;
+   size_t arg_i = 0;
+   for(auto const& arg : m_spec_args)
+      {
+      m_user_args.insert(std::make_pair(arg, args[arg_i]));
+
+      if(args[arg_i] == "-")
+         {
+         if(seen_stdin_flag)
+            {
+            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
+            }
+         seen_stdin_flag = true;
+         }
+
+      ++arg_i;
+      }
+
+   if(m_spec_rest.empty())
+      {
+      if(arg_i != args.size())
+         {
+         throw CLI_Usage_Error("Too many arguments");
+         }
+      }
+   else
+      {
+      m_user_rest.assign(args.begin() + arg_i, args.end());
+      }
+
+   // Now insert any defaults for options not supplied by the user
+   for(auto const& opt : m_spec_opts)
+      {
+      if(m_user_args.count(opt.first) == 0)
+         {
+         m_user_args.insert(opt);
+         }
+      }
+   }
+
+Argument_Parser::Argument_Parser(const std::string& spec,
+                                 const std::vector<std::string>& extra_flags,
+                                 const std::vector<std::string>& extra_opts)
+   {
+   class CLI_Error_Invalid_Spec final : public CLI_Error
+      {
+      public:
+         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec)
+            : CLI_Error("Invalid command spec '" + bad_spec + "'") {}
+      };
+
+   const std::vector<std::string> parts = Botan::split_on(spec, ' ');
+
+   if(parts.size() == 0)
+      {
+      throw CLI_Error_Invalid_Spec(spec);
+      }
+
+   for(size_t i = 1; i != parts.size(); ++i)
+      {
+      const std::string s = parts[i];
+
+      if(s.empty()) // ?!? (shouldn't happen)
+         {
+         throw CLI_Error_Invalid_Spec(spec);
+         }
+
+      if(s.size() > 2 && s[0] == '-' && s[1] == '-')
+         {
+         // option or flag
+
+         auto eq = s.find('=');
+
+         if(eq == std::string::npos)
+            {
+            m_spec_flags.insert(s.substr(2, std::string::npos));
+            }
+         else
+            {
+            m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
+            }
+         }
+      else if(s[0] == '*')
+         {
+         // rest argument
+         if(m_spec_rest.empty() && s.size() > 2)
+            {
+            m_spec_rest = s.substr(1, std::string::npos);
+            }
+         else
+            {
+            throw CLI_Error_Invalid_Spec(spec);
+            }
+         }
+      else
+         {
+         // named argument
+         if(!m_spec_rest.empty()) // rest arg wasn't last
+            {
+            throw CLI_Error_Invalid_Spec(spec);
+            }
+
+         m_spec_args.push_back(s);
+         }
+      }
+
+   for(std::string flag : extra_flags)
+      m_spec_flags.insert(flag);
+   for(std::string opt : extra_opts)
+      m_spec_opts.insert(std::make_pair(opt, ""));
+   }
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/asn1.cpp b/third_party/botan/src/cli/asn1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/asn1.cpp
@@ -0,0 +1,89 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ASN1)
+
+#include <botan/asn1_print.h>
+#include <botan/data_src.h>
+
+#if defined(BOTAN_HAS_PEM_CODEC)
+  #include <botan/pem.h>
+#endif
+
+namespace Botan_CLI {
+
+class ASN1_Printer final : public Command
+   {
+   public:
+      ASN1_Printer() : Command("asn1print --skip-context-specific --print-limit=4096 --bin-limit=2048 --max-depth=64 --pem file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Decode and print file with ASN.1 Basic Encoding Rules (BER)";
+         }
+
+      bool first_n(const std::vector<uint8_t>& data, size_t n, uint8_t b)
+         {
+         if(data.size() < n)
+            return false;
+
+         for(size_t i = 0; i != n; ++i)
+            if(data[i] != b)
+               return false;
+
+         return true;
+         }
+
+      void go() override
+         {
+         const std::string input = get_arg("file");
+         const size_t print_limit = get_arg_sz("print-limit");
+         const size_t bin_limit = get_arg_sz("bin-limit");
+         const bool print_context_specific = flag_set("skip-context-specific") == false;
+         const size_t max_depth = get_arg_sz("max-depth");
+
+         const size_t value_column = 60;
+         const size_t initial_level = 0;
+
+         std::vector<uint8_t> file_contents = slurp_file(input);
+         std::vector<uint8_t> data;
+
+         if(flag_set("pem") ||
+            (input.size() > 4 && input.substr(input.size() - 4) == ".pem") ||
+            (file_contents.size() > 20 && first_n(file_contents, 5, '-')))
+            {
+#if defined(BOTAN_HAS_PEM_CODEC)
+            std::string pem_label;
+            Botan::DataSource_Memory src(file_contents);
+            data = unlock(Botan::PEM_Code::decode(src, pem_label));
+#else
+            throw CLI_Error_Unsupported("PEM decoding not available in this build");
+#endif
+            }
+         else
+            {
+            data.swap(file_contents);
+            }
+
+         Botan::ASN1_Pretty_Printer printer(print_limit, bin_limit, print_context_specific,
+                                            initial_level, value_column, max_depth);
+
+         printer.print_to_stream(output(), data.data(), data.size());
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("asn1print", ASN1_Printer);
+
+}
+
+#endif // BOTAN_HAS_ASN1 && BOTAN_HAS_PEM_CODEC
diff --git a/third_party/botan/src/cli/bcrypt.cpp b/third_party/botan/src/cli/bcrypt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/bcrypt.cpp
@@ -0,0 +1,89 @@
+/*
+* (C) 2009,2010,2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_BCRYPT)
+   #include <botan/bcrypt.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+class Generate_Bcrypt final : public Command
+   {
+   public:
+      Generate_Bcrypt() : Command("gen_bcrypt --work-factor=12 password") {}
+
+      std::string group() const override
+         {
+         return "passhash";
+         }
+
+      std::string description() const override
+         {
+         return "Calculate bcrypt password hash";
+         }
+
+      void go() override
+         {
+         const std::string password = get_passphrase_arg("Passphrase to hash", "password");
+         const size_t wf = get_arg_sz("work-factor");
+
+         if(wf < 4 || wf > 18)
+            {
+            error_output() << "Invalid bcrypt work factor\n";
+            }
+         else
+            {
+            const uint16_t wf16 = static_cast<uint16_t>(wf);
+            output() << Botan::generate_bcrypt(password, rng(), wf16) << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_bcrypt", Generate_Bcrypt);
+
+class Check_Bcrypt final : public Command
+   {
+   public:
+      Check_Bcrypt() : Command("check_bcrypt password hash") {}
+
+      std::string group() const override
+         {
+         return "passhash";
+         }
+
+      std::string description() const override
+         {
+         return "Verify bcrypt password hash";
+         }
+
+      void go() override
+         {
+         const std::string password = get_passphrase_arg("Password to check", "password");
+         const std::string hash = get_arg("hash");
+
+         if(hash.length() != 60)
+            {
+            error_output() << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid\n";
+            }
+
+         const bool ok = Botan::check_bcrypt(password, hash);
+
+         output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+
+         if(ok == false)
+            set_return_code(1);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("check_bcrypt", Check_Bcrypt);
+
+#endif // bcrypt
+
+}
diff --git a/third_party/botan/src/cli/cc_enc.cpp b/third_party/botan/src/cli/cc_enc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/cc_enc.cpp
@@ -0,0 +1,189 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_PBKDF)
+
+#include <botan/fpe_fe1.h>
+#include <botan/pbkdf.h>
+
+namespace Botan_CLI {
+
+namespace {
+
+uint8_t luhn_checksum(uint64_t cc_number)
+   {
+   uint8_t sum = 0;
+
+   bool alt = false;
+   while(cc_number)
+      {
+      uint8_t digit = cc_number % 10;
+      if(alt)
+         {
+         digit *= 2;
+         if(digit > 9)
+            {
+            digit -= 9;
+            }
+         }
+
+      sum += digit;
+
+      cc_number /= 10;
+      alt = !alt;
+      }
+
+   return (sum % 10);
+   }
+
+bool luhn_check(uint64_t cc_number)
+   {
+   return (luhn_checksum(cc_number) == 0);
+   }
+
+uint64_t cc_rank(uint64_t cc_number)
+   {
+   // Remove Luhn checksum
+   return cc_number / 10;
+   }
+
+uint64_t cc_derank(uint64_t cc_number)
+   {
+   for(size_t i = 0; i != 10; ++i)
+      {
+      if(luhn_check(cc_number * 10 + i))
+         {
+         return (cc_number * 10 + i);
+         }
+      }
+
+   return 0;
+   }
+
+uint64_t encrypt_cc_number(uint64_t cc_number,
+                           const Botan::secure_vector<uint8_t>& key,
+                           const std::vector<uint8_t>& tweak)
+   {
+   const Botan::BigInt n = 1000000000000000;
+
+   const uint64_t cc_ranked = cc_rank(cc_number);
+
+   const Botan::BigInt c = Botan::FPE::fe1_encrypt(n, cc_ranked, key, tweak);
+
+   if(c.bits() > 50)
+      {
+      throw Botan::Internal_Error("FPE produced a number too large");
+      }
+
+   uint64_t enc_cc = 0;
+   for(size_t i = 0; i != 7; ++i)
+      {
+      enc_cc = (enc_cc << 8) | c.byte_at(6 - i);
+      }
+   return cc_derank(enc_cc);
+   }
+
+uint64_t decrypt_cc_number(uint64_t enc_cc,
+                           const Botan::secure_vector<uint8_t>& key,
+                           const std::vector<uint8_t>& tweak)
+   {
+   const Botan::BigInt n = 1000000000000000;
+
+   const uint64_t cc_ranked = cc_rank(enc_cc);
+
+   const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, cc_ranked, key, tweak);
+
+   if(c.bits() > 50)
+      {
+      throw CLI_Error("FPE produced a number too large");
+      }
+
+   uint64_t dec_cc = 0;
+   for(size_t i = 0; i != 7; ++i)
+      {
+      dec_cc = (dec_cc << 8) | c.byte_at(6 - i);
+      }
+   return cc_derank(dec_cc);
+   }
+
+}
+
+class CC_Encrypt final : public Command
+   {
+   public:
+      CC_Encrypt() : Command("cc_encrypt CC passphrase --tweak=") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Encrypt the passed valid credit card number using FPE encryption";
+         }
+
+      void go() override
+         {
+         const uint64_t cc_number = std::stoull(get_arg("CC"));
+         const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+         const std::string pass = get_arg("passphrase");
+
+         std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+         if(!pbkdf)
+            {
+            throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+            }
+
+         Botan::secure_vector<uint8_t> key = pbkdf->pbkdf_iterations(32, pass, tweak.data(), tweak.size(), 100000);
+
+         output() << encrypt_cc_number(cc_number, key, tweak) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cc_encrypt", CC_Encrypt);
+
+class CC_Decrypt final : public Command
+   {
+   public:
+      CC_Decrypt() : Command("cc_decrypt CC passphrase --tweak=") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Decrypt the passed valid ciphertext credit card number using FPE decryption";
+         }
+
+      void go() override
+         {
+         const uint64_t cc_number = std::stoull(get_arg("CC"));
+         const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+         const std::string pass = get_arg("passphrase");
+
+         std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+         if(!pbkdf)
+            {
+            throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+            }
+
+         Botan::secure_vector<uint8_t> key = pbkdf->pbkdf_iterations(32, pass, tweak.data(), tweak.size(), 100000);
+
+         output() << decrypt_cc_number(cc_number, key, tweak) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cc_decrypt", CC_Decrypt);
+
+}
+
+#endif // FPE && PBKDF
diff --git a/third_party/botan/src/cli/cli.cpp b/third_party/botan/src/cli/cli.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/cli.cpp
@@ -0,0 +1,349 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "argparse.h"
+#include <botan/rng.h>
+#include <botan/parsing.h>
+#include <botan/internal/os_utils.h>
+#include <iostream>
+#include <fstream>
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+   #include <botan/hex.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+   #include <botan/base64.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+   #include <botan/base58.h>
+#endif
+
+namespace Botan_CLI {
+
+Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec)
+   {
+   // for checking all spec strings at load time
+   //m_args.reset(new Argument_Parser(m_spec));
+   }
+
+Command::~Command() { /* for unique_ptr */ }
+
+std::string Command::cmd_name() const
+   {
+   return m_spec.substr(0, m_spec.find(' '));
+   }
+
+std::string Command::help_text() const
+   {
+   return "Usage: " + m_spec;
+   }
+
+int Command::run(const std::vector<std::string>& params)
+   {
+   try
+      {
+      m_args.reset(new Argument_Parser(m_spec,
+                                       {"verbose", "help"},
+                                       {"output", "error-output", "rng-type", "drbg-seed"}));
+
+      m_args->parse_args(params);
+
+      if(m_args->has_arg("output"))
+         {
+         const std::string output_file = get_arg("output");
+
+         if(output_file != "")
+            {
+            m_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+            if(!m_output_stream->good())
+               throw CLI_IO_Error("opening", output_file);
+            }
+         }
+
+      if(m_args->has_arg("error-output"))
+         {
+         const std::string output_file = get_arg("error-output");
+
+         if(output_file != "")
+            {
+            m_error_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+            if(!m_error_output_stream->good())
+               throw CLI_IO_Error("opening", output_file);
+            }
+         }
+
+      if(flag_set("help"))
+         {
+         output() << help_text() << "\n";
+         return 2;
+         }
+
+      this->go();
+      return m_return_code;
+      }
+   catch(CLI_Usage_Error& e)
+      {
+      error_output() << "Usage error: " << e.what() << "\n";
+      error_output() << help_text() << "\n";
+      return 1;
+      }
+   catch(std::exception& e)
+      {
+      error_output() << "Error: " << e.what() << "\n";
+      return 2;
+      }
+   catch(...)
+      {
+      error_output() << "Error: unknown exception\n";
+      return 2;
+      }
+   }
+
+bool Command::flag_set(const std::string& flag_name) const
+   {
+   return m_args->flag_set(flag_name);
+   }
+
+std::string Command::get_arg(const std::string& opt_name) const
+   {
+   return m_args->get_arg(opt_name);
+   }
+
+/*
+* Like get_arg() but if the argument was not specified or is empty, returns otherwise
+*/
+std::string Command::get_arg_or(const std::string& opt_name, const std::string& otherwise) const
+   {
+   return m_args->get_arg_or(opt_name, otherwise);
+   }
+
+size_t Command::get_arg_sz(const std::string& opt_name) const
+   {
+   return m_args->get_arg_sz(opt_name);
+   }
+
+uint16_t Command::get_arg_u16(const std::string& opt_name) const
+   {
+   const size_t val = get_arg_sz(opt_name);
+   if(static_cast<uint16_t>(val) != val)
+      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
+   return static_cast<uint16_t>(val);
+   }
+
+uint32_t Command::get_arg_u32(const std::string& opt_name) const
+   {
+   const size_t val = get_arg_sz(opt_name);
+   if(static_cast<uint32_t>(val) != val)
+      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
+   return static_cast<uint32_t>(val);
+   }
+
+std::vector<std::string> Command::get_arg_list(const std::string& what) const
+   {
+   return m_args->get_arg_list(what);
+   }
+
+std::ostream& Command::output()
+   {
+   if(m_output_stream.get())
+      {
+      return *m_output_stream;
+      }
+   return std::cout;
+   }
+
+std::ostream& Command::error_output()
+   {
+   if(m_error_output_stream.get())
+      {
+      return *m_error_output_stream;
+      }
+   return std::cerr;
+   }
+
+std::vector<uint8_t> Command::slurp_file(const std::string& input_file,
+                                         size_t buf_size) const
+   {
+   std::vector<uint8_t> buf;
+   auto insert_fn = [&](const uint8_t b[], size_t l)
+      {
+      buf.insert(buf.end(), b, b + l);
+      };
+   this->read_file(input_file, insert_fn, buf_size);
+   return buf;
+   }
+
+std::string Command::slurp_file_as_str(const std::string& input_file,
+                                       size_t buf_size) const
+   {
+   std::string str;
+   auto insert_fn = [&](const uint8_t b[], size_t l)
+      {
+      str.append(reinterpret_cast<const char*>(b), l);
+      };
+   this->read_file(input_file, insert_fn, buf_size);
+   return str;
+   }
+
+void Command::read_file(const std::string& input_file,
+                        std::function<void (uint8_t[], size_t)> consumer_fn,
+                        size_t buf_size) const
+   {
+   if(input_file == "-")
+      {
+      do_read_file(std::cin, consumer_fn, buf_size);
+      }
+   else
+      {
+      std::ifstream in(input_file, std::ios::binary);
+      if(!in)
+         {
+         throw CLI_IO_Error("reading file", input_file);
+         }
+      do_read_file(in, consumer_fn, buf_size);
+      }
+   }
+
+void Command::do_read_file(std::istream& in,
+                           std::function<void (uint8_t[], size_t)> consumer_fn,
+                           size_t buf_size) const
+   {
+   // Avoid an infinite loop on --buf-size=0
+   std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
+
+   while(in.good())
+      {
+      in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+      const size_t got = static_cast<size_t>(in.gcount());
+      consumer_fn(buf.data(), got);
+      }
+   }
+
+Botan::RandomNumberGenerator& Command::rng()
+   {
+   if(m_rng == nullptr)
+      {
+      m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
+      }
+
+   return *m_rng.get();
+   }
+
+std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name)
+   {
+   const std::string s = get_arg(opt_name);
+   if(s != "-")
+      return s;
+   return get_passphrase(prompt);
+   }
+
+namespace {
+
+bool echo_suppression_supported()
+   {
+   auto echo = Botan::OS::suppress_echo_on_terminal();
+   return (echo != nullptr);
+   }
+
+}
+
+std::string Command::get_passphrase(const std::string& prompt)
+   {
+   if(echo_suppression_supported() == false)
+      error_output() << "Warning: terminal echo suppression not enabled for this platform\n";
+
+   error_output() << prompt << ": " << std::flush;
+   std::string pass;
+
+   auto echo_suppress = Botan::OS::suppress_echo_on_terminal();
+
+   std::getline(std::cin, pass);
+
+   return pass;
+   }
+
+//static
+std::string Command::format_blob(const std::string& format,
+                                 const uint8_t bits[], size_t len)
+   {
+#if defined(BOTAN_HAS_HEX_CODEC)
+   if(format == "hex")
+      {
+      return Botan::hex_encode(bits, len);
+      }
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+   if(format == "base64")
+      {
+      return Botan::base64_encode(bits, len);
+      }
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+   if(format == "base58")
+      {
+      return Botan::base58_encode(bits, len);
+      }
+   if(format == "base58check")
+      {
+      return Botan::base58_check_encode(bits, len);
+      }
+#endif
+
+   // If we supported format, we would have already returned
+   throw CLI_Usage_Error("Unknown or unsupported format type");
+   }
+
+// Registration code
+
+Command::Registration::Registration(const std::string& name, Command::cmd_maker_fn maker_fn)
+   {
+   std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+   if(reg.count(name) > 0)
+      {
+      throw CLI_Error("Duplicated registration of command " + name);
+      }
+
+   reg.insert(std::make_pair(name, maker_fn));
+   }
+
+//static
+std::map<std::string, Command::cmd_maker_fn>& Command::global_registry()
+   {
+   static std::map<std::string, Command::cmd_maker_fn> g_cmds;
+   return g_cmds;
+   }
+
+//static
+std::vector<std::string> Command::registered_cmds()
+   {
+   std::vector<std::string> cmds;
+   for(auto& cmd : Command::global_registry())
+      cmds.push_back(cmd.first);
+   return cmds;
+   }
+
+//static
+std::unique_ptr<Command> Command::get_cmd(const std::string& name)
+   {
+   const std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+   std::unique_ptr<Command> r;
+   auto i = reg.find(name);
+   if(i != reg.end())
+      {
+      r.reset(i->second());
+      }
+
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/cli/cli.h b/third_party/botan/src/cli/cli.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/cli.h
@@ -0,0 +1,219 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_H_
+#define BOTAN_CLI_H_
+
+#include <botan/build.h>
+#include <functional>
+#include <ostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include "cli_exceptions.h"
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+}
+
+namespace Botan_CLI {
+
+class Argument_Parser;
+
+/* Declared in cli_rng.cpp */
+std::unique_ptr<Botan::RandomNumberGenerator>
+cli_make_rng(const std::string& type = "", const std::string& hex_drbg_seed = "");
+
+class Command
+   {
+   public:
+
+      /**
+      * Get a registered command
+      */
+      static std::unique_ptr<Command> get_cmd(const std::string& name);
+
+      static std::vector<std::string> registered_cmds();
+
+      /**
+      * The spec string specifies the format of the command line, eg for
+      * a somewhat complicated command:
+      * cmd_name --flag --option1= --option2=opt2val input1 input2 *rest
+      *
+      * By default this is the value returned by help_text()
+      *
+      * The first value is always the command name. Options may appear
+      * in any order. Named arguments are taken from the command line
+      * in the order they appear in the spec.
+      *
+      * --flag can optionally be specified, and takes no value.
+      * Check for it in go() with flag_set()
+      *
+      * --option1 is an option whose default value (if the option
+      * does not appear on the command line) is the empty string.
+      *
+      * --option2 is an option whose default value is opt2val
+      * Read the values in go() using get_arg or get_arg_sz.
+      *
+      * The values input1 and input2 specify named arguments which must
+      * be provided. They are also access via get_arg/get_arg_sz
+      * Because options and arguments for a single command share the same
+      * namespace you can't have a spec like:
+      *   cmd --input input
+      * but you hopefully didn't want to do that anyway.
+      *
+      * The leading '*' on '*rest' specifies that all remaining arguments
+      * should be packaged in a list which is available as get_arg_list("rest").
+      * This can only appear on a single value and should be the final
+      * named argument.
+      *
+      * Every command has implicit flags --help, --verbose and implicit
+      * options --output= and --error-output= which override the default
+      * use of std::cout and std::cerr.
+      *
+      * Use of --help is captured in run() and returns help_text().
+      * Use of --verbose can be checked with verbose() or flag_set("verbose")
+      */
+      explicit Command(const std::string& cmd_spec);
+
+      virtual ~Command();
+
+      int run(const std::vector<std::string>& params);
+
+      virtual std::string group() const = 0;
+
+      virtual std::string description() const = 0;
+
+      virtual std::string help_text() const;
+
+      const std::string& cmd_spec() const
+         {
+         return m_spec;
+         }
+
+      std::string cmd_name() const;
+
+   protected:
+
+      /*
+      * The actual functionality of the cli command implemented in subclass.
+      * The return value from main will be zero.
+      */
+      virtual void go() = 0;
+
+      void set_return_code(int rc) { m_return_code = rc; }
+
+      std::ostream& output();
+
+      std::ostream& error_output();
+
+      bool verbose() const
+         {
+         return flag_set("verbose");
+         }
+
+      std::string get_passphrase(const std::string& prompt);
+
+      bool flag_set(const std::string& flag_name) const;
+
+      static std::string format_blob(const std::string& format, const uint8_t bits[], size_t len);
+
+      template<typename Alloc>
+      static std::string format_blob(const std::string& format,
+                                     const std::vector<uint8_t, Alloc>& vec)
+         {
+         return format_blob(format, vec.data(), vec.size());
+         }
+
+      std::string get_arg(const std::string& opt_name) const;
+
+      /**
+      * Like get_arg but if the value is '-' then reads a passphrase from
+      * the terminal with echo suppressed.
+      */
+      std::string get_passphrase_arg(const std::string& prompt,
+                                     const std::string& opt_name);
+
+      /*
+      * Like get_arg() but if the argument was not specified or is empty, returns otherwise
+      */
+      std::string get_arg_or(const std::string& opt_name, const std::string& otherwise) const;
+
+      size_t get_arg_sz(const std::string& opt_name) const;
+
+      uint16_t get_arg_u16(const std::string& opt_name) const;
+
+      uint32_t get_arg_u32(const std::string& opt_name) const;
+
+      std::vector<std::string> get_arg_list(const std::string& what) const;
+
+      /*
+      * Read an entire file into memory and return the contents
+      */
+      std::vector<uint8_t> slurp_file(const std::string& input_file,
+                                      size_t buf_size = 0) const;
+
+      std::string slurp_file_as_str(const std::string& input_file,
+                                    size_t buf_size = 0) const;
+
+      /*
+      * Read a file calling consumer_fn() with the inputs
+      */
+      void read_file(const std::string& input_file,
+                     std::function<void (uint8_t[], size_t)> consumer_fn,
+                     size_t buf_size = 0) const;
+
+
+      void do_read_file(std::istream& in,
+                        std::function<void (uint8_t[], size_t)> consumer_fn,
+                        size_t buf_size = 0) const;
+
+      template<typename Alloc>
+      void write_output(const std::vector<uint8_t, Alloc>& vec)
+         {
+         output().write(reinterpret_cast<const char*>(vec.data()), vec.size());
+         }
+
+      Botan::RandomNumberGenerator& rng();
+
+   private:
+      typedef std::function<Command* ()> cmd_maker_fn;
+      static std::map<std::string, cmd_maker_fn>& global_registry();
+
+      void parse_spec();
+
+      // set in constructor
+      std::string m_spec;
+
+      std::unique_ptr<Argument_Parser> m_args;
+      std::unique_ptr<std::ostream> m_output_stream;
+      std::unique_ptr<std::ostream> m_error_output_stream;
+
+      std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+
+      // possibly set by calling set_return_code()
+      int m_return_code = 0;
+
+   public:
+      // the registry interface:
+
+      class Registration final
+         {
+         public:
+            Registration(const std::string& name, cmd_maker_fn maker_fn);
+         };
+   };
+
+#define BOTAN_REGISTER_COMMAND(name, CLI_Class)                          \
+   Botan_CLI::Command::Registration reg_cmd_ ## CLI_Class(name,          \
+               []() -> Botan_CLI::Command* { return new CLI_Class; })
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/cli_exceptions.h b/third_party/botan/src/cli/cli_exceptions.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/cli_exceptions.h
@@ -0,0 +1,47 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_EXCEPTIONS_H_
+#define BOTAN_CLI_EXCEPTIONS_H_
+
+namespace Botan_CLI {
+
+class CLI_Error : public std::runtime_error
+   {
+   public:
+      explicit CLI_Error(const std::string& s) : std::runtime_error(s) {}
+   };
+
+class CLI_IO_Error final : public CLI_Error
+   {
+   public:
+      CLI_IO_Error(const std::string& op, const std::string& who) :
+         CLI_Error("Error " + op + " " + who) {}
+   };
+
+class CLI_Usage_Error final : public CLI_Error
+   {
+   public:
+      explicit CLI_Usage_Error(const std::string& what) : CLI_Error(what) {}
+   };
+
+/* Thrown eg when a requested feature was compiled out of the library
+   or is not available, eg hashing with MD2
+*/
+class CLI_Error_Unsupported final : public CLI_Error
+   {
+   public:
+
+      CLI_Error_Unsupported(const std::string& msg) : CLI_Error(msg) {}
+
+      CLI_Error_Unsupported(const std::string& what,
+                            const std::string& who)
+         : CLI_Error(what + " with '" + who + "' unsupported or not available") {}
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/cli_rng.cpp b/third_party/botan/src/cli/cli_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/cli_rng.cpp
@@ -0,0 +1,146 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/rng.h>
+#include <botan/entropy_src.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+   #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+   #include <botan/hmac_drbg.h>
+#endif
+
+namespace Botan_CLI {
+
+std::unique_ptr<Botan::RandomNumberGenerator>
+cli_make_rng(const std::string& rng_type, const std::string& hex_drbg_seed)
+   {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   if(rng_type == "system" || rng_type.empty())
+      {
+      return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::System_RNG);
+      }
+#endif
+
+   const std::vector<uint8_t> drbg_seed = Botan::hex_decode(hex_drbg_seed);
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+   if(rng_type == "auto" || rng_type == "entropy" || rng_type.empty())
+      {
+      std::unique_ptr<Botan::RandomNumberGenerator> rng;
+
+      if(rng_type == "entropy")
+         rng.reset(new Botan::AutoSeeded_RNG(Botan::Entropy_Sources::global_sources()));
+      else
+         rng.reset(new Botan::AutoSeeded_RNG);
+
+      if(drbg_seed.size() > 0)
+         rng->add_entropy(drbg_seed.data(), drbg_seed.size());
+      return rng;
+      }
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32)
+   if(rng_type == "drbg" || (rng_type.empty() && drbg_seed.empty() == false))
+      {
+      std::unique_ptr<Botan::MessageAuthenticationCode> mac =
+         Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+      std::unique_ptr<Botan::Stateful_RNG> rng(new Botan::HMAC_DRBG(std::move(mac)));
+      rng->add_entropy(drbg_seed.data(), drbg_seed.size());
+
+      if(rng->is_seeded() == false)
+         throw CLI_Error("For " + rng->name() + " a seed of at least " +
+                         std::to_string(rng->security_level()/8) +
+                         " bytes must be provided");
+
+      return std::unique_ptr<Botan::RandomNumberGenerator>(rng.release());
+      }
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   if(rng_type == "rdrand" || rng_type == "cpu" || rng_type.empty())
+      {
+      if(Botan::Processor_RNG::available())
+         return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::Processor_RNG);
+      else if(rng_type.empty() == false)
+         throw CLI_Error("RNG instruction not supported on this processor");
+      }
+#endif
+
+   if(rng_type.empty())
+      throw CLI_Error_Unsupported("No random number generator seems to be available in the current build");
+   else
+      throw CLI_Error_Unsupported("RNG", rng_type);
+   }
+
+class RNG final : public Command
+   {
+   public:
+      RNG() : Command("rng --format=hex --system --rdrand --auto --entropy --drbg --drbg-seed= *bytes") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Sample random bytes from the specified rng";
+         }
+
+      void go() override
+         {
+         const std::string format = get_arg("format");
+         std::string type = get_arg("rng-type");
+
+         if(type.empty())
+            {
+            for(std::string flag : { "system", "rdrand", "auto", "entropy", "drbg" })
+               {
+               if(flag_set(flag))
+                  {
+                  type = flag;
+                  break;
+                  }
+               }
+            }
+
+         const std::string drbg_seed = get_arg("drbg-seed");
+         std::unique_ptr<Botan::RandomNumberGenerator> rng = cli_make_rng(type, drbg_seed);
+
+         for(const std::string& req : get_arg_list("bytes"))
+            {
+            const size_t req_len = Botan::to_u32bit(req);
+            const auto blob = rng->random_vec(req_len);
+
+            if(format == "binary" || format == "raw")
+               {
+               output().write(reinterpret_cast<const char*>(blob.data()), blob.size());
+               }
+            else
+               {
+               output() << format_blob(format, blob) << "\n";
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("rng", RNG);
+
+}
diff --git a/third_party/botan/src/cli/codec.cpp b/third_party/botan/src/cli/codec.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/codec.cpp
@@ -0,0 +1,268 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+   #include <botan/hex.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+   #include <botan/base32.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+   #include <botan/base58.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+   #include <botan/base64.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+
+class Hex_Encode final : public Command
+   {
+   public:
+      Hex_Encode() : Command("hex_enc file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Hex encode a given file";
+         }
+
+      void go() override
+         {
+         auto hex_enc_f = [&](const uint8_t b[], size_t l) { output() << Botan::hex_encode(b, l); };
+         this->read_file(get_arg("file"), hex_enc_f, 2);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("hex_enc", Hex_Encode);
+
+class Hex_Decode final : public Command
+   {
+   public:
+      Hex_Decode() : Command("hex_dec file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Hex decode a given file";
+         }
+
+      void go() override
+         {
+         auto hex_dec_f = [&](const uint8_t b[], size_t l)
+            {
+            std::vector<uint8_t> bin = Botan::hex_decode(reinterpret_cast<const char*>(b), l);
+            output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+            };
+
+         this->read_file(get_arg("file"), hex_dec_f, 2);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("hex_dec", Hex_Decode);
+
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+
+class Base58_Encode final : public Command
+   {
+   public:
+      Base58_Encode() : Command("base58_enc --check file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Encode given file to Base58";
+         }
+
+      void go() override
+         {
+         auto data = slurp_file(get_arg("file"));
+
+         if(flag_set("check"))
+            output() << Botan::base58_check_encode(data);
+         else
+            output() << Botan::base58_encode(data);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base58_enc", Base58_Encode);
+
+class Base58_Decode final : public Command
+   {
+   public:
+      Base58_Decode() : Command("base58_dec --check file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Decode Base58 encoded file";
+         }
+
+      void go() override
+         {
+         auto data = slurp_file_as_str(get_arg("file"));
+
+         std::vector<uint8_t> bin;
+
+         if(flag_set("check"))
+            bin = Botan::base58_check_decode(data);
+         else
+            bin = Botan::base58_decode(data);
+
+         output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base58_dec", Base58_Decode);
+
+#endif // base58
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+
+class Base32_Encode final : public Command
+   {
+   public:
+      Base32_Encode() : Command("base32_enc file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Encode given file to Base32";
+         }
+
+      void go() override
+         {
+         auto onData = [&](const uint8_t b[], size_t l)
+            {
+            output() << Botan::base32_encode(b, l);
+            };
+         this->read_file(get_arg("file"), onData, 768);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base32_enc", Base32_Encode);
+
+class Base32_Decode final : public Command
+   {
+   public:
+      Base32_Decode() : Command("base32_dec file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Decode Base32 encoded file";
+         }
+
+      void go() override
+         {
+         auto write_bin = [&](const uint8_t b[], size_t l)
+            {
+            Botan::secure_vector<uint8_t> bin = Botan::base32_decode(reinterpret_cast<const char*>(b), l);
+            output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+            };
+
+         this->read_file(get_arg("file"), write_bin, 1024);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base32_dec", Base32_Decode);
+
+#endif // base32
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+
+class Base64_Encode final : public Command
+   {
+   public:
+      Base64_Encode() : Command("base64_enc file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Encode given file to Base64";
+         }
+
+      void go() override
+         {
+         auto onData = [&](const uint8_t b[], size_t l)
+            {
+            output() << Botan::base64_encode(b, l);
+            };
+         this->read_file(get_arg("file"), onData, 768);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base64_enc", Base64_Encode);
+
+class Base64_Decode final : public Command
+   {
+   public:
+      Base64_Decode() : Command("base64_dec file") {}
+
+      std::string group() const override
+         {
+         return "codec";
+         }
+
+      std::string description() const override
+         {
+         return "Decode Base64 encoded file";
+         }
+
+      void go() override
+         {
+         auto write_bin = [&](const uint8_t b[], size_t l)
+            {
+            Botan::secure_vector<uint8_t> bin = Botan::base64_decode(reinterpret_cast<const char*>(b), l);
+            output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+            };
+
+         this->read_file(get_arg("file"), write_bin, 1024);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("base64_dec", Base64_Decode);
+
+#endif // base64
+
+}
diff --git a/third_party/botan/src/cli/compress.cpp b/third_party/botan/src/cli/compress.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/compress.cpp
@@ -0,0 +1,190 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_COMPRESSION)
+   #include <botan/compression.h>
+   #include <fstream>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+class Compress final : public Command
+   {
+   public:
+      Compress() : Command("compress --type=gzip --level=6 --buf-size=8192 file") {}
+
+      std::string output_filename(const std::string& input_fsname, const std::string& comp_type)
+         {
+         const std::map<std::string, std::string> suffixes =
+            {
+               { "zlib", "zlib" },
+               { "gzip", "gz" },
+               { "bzip2", "bz2" },
+               { "lzma", "xz" },
+            };
+
+         auto suffix_info = suffixes.find(comp_type);
+         if(suffixes.count(comp_type) == 0)
+            {
+            throw CLI_Error_Unsupported("Compressing", comp_type);
+            }
+
+         return input_fsname + "." + suffix_info->second;
+         }
+
+      std::string group() const override
+         {
+         return "compression";
+         }
+
+      std::string description() const override
+         {
+         return "Compress a given file";
+         }
+
+      void go() override
+         {
+         const std::string comp_type = get_arg("type");
+         const size_t buf_size = get_arg_sz("buf-size");
+         const size_t comp_level = get_arg_sz("level");
+
+         std::unique_ptr<Botan::Compression_Algorithm> compress;
+
+         compress.reset(Botan::make_compressor(comp_type));
+
+         if(!compress)
+            {
+            throw CLI_Error_Unsupported("Compression", comp_type);
+            }
+
+         const std::string in_file = get_arg("file");
+         std::ifstream in(in_file, std::ios::binary);
+
+         if(!in.good())
+            {
+            throw CLI_IO_Error("reading", in_file);
+            }
+
+         const std::string out_file = output_filename(in_file, comp_type);
+         std::ofstream out(out_file, std::ios::binary);
+         if(!out.good())
+            {
+            throw CLI_IO_Error("writing", out_file);
+            }
+
+         Botan::secure_vector<uint8_t> buf;
+
+         compress->start(comp_level);
+
+         while(in.good())
+            {
+            buf.resize(buf_size);
+            in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+            buf.resize(in.gcount());
+
+            compress->update(buf);
+
+            out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+            }
+
+         buf.clear();
+         compress->finish(buf);
+         out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+         out.close();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("compress", Compress);
+
+class Decompress final : public Command
+   {
+   public:
+      Decompress() : Command("decompress --buf-size=8192 file") {}
+
+      void parse_extension(const std::string& in_file,
+                           std::string& out_file,
+                           std::string& suffix)
+         {
+         auto last_dot = in_file.find_last_of('.');
+         if(last_dot == std::string::npos || last_dot == 0)
+            {
+            throw CLI_Error("No extension detected in filename '" + in_file + "'");
+            }
+
+         out_file = in_file.substr(0, last_dot);
+         suffix = in_file.substr(last_dot + 1, std::string::npos);
+         }
+
+      std::string group() const override
+         {
+         return "compression";
+         }
+
+      std::string description() const override
+         {
+         return "Decompress a given compressed archive";
+         }
+
+      void go() override
+         {
+         const size_t buf_size = get_arg_sz("buf-size");
+         const std::string in_file = get_arg("file");
+         std::string out_file, suffix;
+         parse_extension(in_file, out_file, suffix);
+
+         std::ifstream in(in_file, std::ios::binary);
+
+         if(!in.good())
+            {
+            throw CLI_IO_Error("reading", in_file);
+            }
+
+         std::unique_ptr<Botan::Decompression_Algorithm> decompress;
+
+         decompress.reset(Botan::make_decompressor(suffix));
+
+         if(!decompress)
+            {
+            throw CLI_Error_Unsupported("Decompression", suffix);
+            }
+
+         std::ofstream out(out_file, std::ios::binary);
+         if(!out.good())
+            {
+            throw CLI_IO_Error("writing", out_file);
+            }
+
+         Botan::secure_vector<uint8_t> buf;
+
+         decompress->start();
+
+         while(in.good())
+            {
+            buf.resize(buf_size);
+            in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+            buf.resize(in.gcount());
+
+            decompress->update(buf);
+
+            out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+            }
+
+         buf.clear();
+         decompress->finish(buf);
+         out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+         out.close();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("decompress", Decompress);
+
+#endif
+
+}
diff --git a/third_party/botan/src/cli/encryption.cpp b/third_party/botan/src/cli/encryption.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/encryption.cpp
@@ -0,0 +1,127 @@
+/*
+* (C) 2015,2017 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if (defined(BOTAN_HAS_AES) || defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)) && defined(BOTAN_HAS_AEAD_MODES)
+
+#include <botan/aead.h>
+#include <botan/hex.h>
+#include <sstream>
+
+namespace Botan_CLI {
+
+namespace {
+
+auto VALID_MODES = std::map<std::string, std::string>{
+         // Don't add algorithms here without extending tests
+         // in `src/scripts/test_cli_crypt.py`
+         { "aes-128-cfb", "AES-128/CFB" },
+         { "aes-192-cfb", "AES-192/CFB" },
+         { "aes-256-cfb", "AES-256/CFB" },
+         { "aes-128-gcm", "AES-128/GCM" },
+         { "aes-192-gcm", "AES-192/GCM" },
+         { "aes-256-gcm", "AES-256/GCM" },
+         { "aes-128-ocb", "AES-128/OCB" },
+         { "aes-128-xts", "AES-128/XTS" },
+         { "aes-256-xts", "AES-256/XTS" },
+         { "chacha20poly1305", "ChaCha20Poly1305" },
+};
+
+Botan::secure_vector<uint8_t>
+do_crypt(const std::string &cipher,
+         const std::vector<uint8_t> &input,
+         const Botan::SymmetricKey &key,
+         const Botan::InitializationVector &iv,
+         const std::vector<uint8_t>& ad,
+         Botan::Cipher_Dir direction)
+   {
+   if(iv.size() == 0)
+      throw CLI_Usage_Error("IV must not be empty");
+
+   // TODO: implement streaming
+
+   std::unique_ptr<Botan::Cipher_Mode> processor(Botan::Cipher_Mode::create(cipher, direction));
+   if(!processor)
+      throw CLI_Error("Cipher algorithm not found");
+
+   // Set key
+   processor->set_key(key);
+
+   if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(processor.get()))
+      {
+      aead->set_ad(ad);
+      }
+   else if(ad.size() != 0)
+      {
+      throw CLI_Usage_Error("Cannot specify associated data with non-AEAD mode");
+      }
+
+   // Set IV
+   processor->start(iv.bits_of());
+
+   Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+   processor->finish(buf);
+
+   return buf;
+   }
+
+}
+
+class Encryption final : public Command
+   {
+   public:
+      Encryption() : Command("encryption --buf-size=4096 --decrypt --mode= --key= --iv= --ad=") {}
+
+      std::string group() const override
+         {
+         return "encryption";
+         }
+
+      std::string description() const override
+         {
+         return "Encrypt or decrypt a given file";
+         }
+
+      void go() override
+         {
+         std::string mode = get_arg_or("mode", "");
+         if (!VALID_MODES.count(mode))
+            {
+            std::ostringstream error;
+            error << "Invalid mode: '" << mode << "'\n"
+                  << "valid modes are:";
+            for (auto valid_mode : VALID_MODES) error << " " << valid_mode.first;
+
+            throw CLI_Usage_Error(error.str());
+            }
+
+         const std::string key_hex = get_arg("key");
+         const std::string iv_hex  = get_arg("iv");
+         const std::string ad_hex  = get_arg_or("ad", "");
+         const size_t buf_size = get_arg_sz("buf-size");
+
+         const std::vector<uint8_t> input = this->slurp_file("-", buf_size);
+
+         if (verbose())
+            {
+            error_output() << "Got " << input.size() << " bytes of input data.\n";
+            }
+
+         const Botan::SymmetricKey key(key_hex);
+         const Botan::InitializationVector iv(iv_hex);
+         const std::vector<uint8_t> ad = Botan::hex_decode(ad_hex);
+
+         auto direction = flag_set("decrypt") ? Botan::Cipher_Dir::DECRYPTION : Botan::Cipher_Dir::ENCRYPTION;
+         write_output(do_crypt(VALID_MODES[mode], input, key, iv, ad, direction));
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("encryption", Encryption);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/entropy.cpp b/third_party/botan/src/cli/entropy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/entropy.cpp
@@ -0,0 +1,104 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "../tests/test_rng.h" // FIXME
+
+#include <botan/entropy_src.h>
+
+#if defined(BOTAN_HAS_COMPRESSION)
+#include <botan/compression.h>
+#endif
+
+namespace Botan_CLI {
+
+class Entropy final : public Command
+   {
+   public:
+      Entropy() : Command("entropy --truncate-at=128 source") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Sample a raw entropy source";
+         }
+
+      void go() override
+         {
+         const std::string req_source = get_arg("source");
+         const size_t truncate_sample = get_arg_sz("truncate-at");
+
+         auto& entropy_sources = Botan::Entropy_Sources::global_sources();
+
+         std::vector<std::string> sources;
+         if(req_source == "all")
+            sources = entropy_sources.enabled_sources();
+         else
+            sources.push_back(req_source);
+
+         for(std::string source : sources)
+            {
+            Botan_Tests::SeedCapturing_RNG rng;
+            const size_t entropy_estimate = entropy_sources.poll_just(rng, source);
+
+            if(rng.samples() == 0)
+               {
+               output() << "Source " << source << " is unavailable\n";
+               continue;
+               }
+
+            const auto& sample = rng.seed_material();
+
+            output() << "Polling " << source << " gathered " << sample.size()
+                     << " bytes in " << rng.samples() << " outputs with estimated entropy "
+                     << entropy_estimate << "\n";
+
+#if defined(BOTAN_HAS_COMPRESSION)
+            if(!sample.empty())
+               {
+               std::unique_ptr<Botan::Compression_Algorithm> comp(Botan::make_compressor("zlib"));
+               if(comp)
+                  {
+                  try
+                     {
+                     Botan::secure_vector<uint8_t> compressed;
+                     compressed.assign(sample.begin(), sample.end());
+                     comp->start(9);
+                     comp->finish(compressed);
+
+                     if(compressed.size() < sample.size())
+                        {
+                        output() << "Sample from " << source << " was zlib compressed from " << sample.size()
+                                 << " bytes to " << compressed.size() << " bytes\n";
+                        }
+                     }
+                  catch(std::exception& e)
+                     {
+                     error_output() << "Error while attempting to compress: " << e.what() << "\n";
+                     }
+                  }
+               }
+#endif
+
+            if(sample.size() <= truncate_sample)
+               {
+               output() << Botan::hex_encode(sample) << "\n";
+               }
+            else if(truncate_sample > 0)
+               {
+               output() << Botan::hex_encode(&sample[0], truncate_sample) << "...\n";
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("entropy", Entropy);
+
+}
diff --git a/third_party/botan/src/cli/hash.cpp b/third_party/botan/src/cli/hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/hash.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2009,2010,2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_HASH)
+   #include <botan/hash.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HASH)
+
+class Hash final : public Command
+   {
+   public:
+      Hash() : Command("hash --algo=SHA-256 --buf-size=4096 --no-fsname --format=hex *files") {}
+
+      std::string group() const override
+         {
+         return "hash";
+         }
+
+      std::string description() const override
+         {
+         return "Compute the message digest of given file(s)";
+         }
+
+      void go() override
+         {
+         const std::string hash_algo = get_arg("algo");
+         const std::string format = get_arg("format");
+         const size_t buf_size = get_arg_sz("buf-size");
+         const bool no_fsname = flag_set("no-fsname");
+
+         std::unique_ptr<Botan::HashFunction> hash_fn(Botan::HashFunction::create(hash_algo));
+
+         if(!hash_fn)
+            {
+            throw CLI_Error_Unsupported("hashing", hash_algo);
+            }
+
+         std::vector<std::string> files = get_arg_list("files");
+         if(files.empty())
+            {
+            files.push_back("-");
+            } // read stdin if no arguments on command line
+
+         for(const std::string& fsname : files)
+            {
+            try
+               {
+               auto update_hash = [&](const uint8_t b[], size_t l) { hash_fn->update(b, l); };
+               read_file(fsname, update_hash, buf_size);
+
+               const std::string digest = format_blob(format, hash_fn->final());
+
+               if(no_fsname)
+                  output() << digest << "\n";
+               else
+                  output() << digest << " " << fsname << "\n";
+               }
+            catch(CLI_IO_Error& e)
+               {
+               error_output() << e.what() << "\n";
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("hash", Hash);
+
+#endif
+
+}
diff --git a/third_party/botan/src/cli/hmac.cpp b/third_party/botan/src/cli/hmac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/hmac.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_MAC)
+   #include <botan/mac.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HMAC)
+
+class HMAC final : public Command
+   {
+   public:
+      HMAC() : Command("hmac --hash=SHA-256 --buf-size=4096 --no-fsname key *files") {}
+
+      std::string group() const override
+         {
+         return "hmac";
+         }
+
+      std::string description() const override
+         {
+         return "Compute the HMAC tag of given file(s)";
+         }
+
+      void go() override
+         {
+         const bool no_fsname = flag_set("no-fsname");
+         const std::string hash_algo = get_arg("hash");
+         std::unique_ptr<Botan::MessageAuthenticationCode> hmac =
+            Botan::MessageAuthenticationCode::create("HMAC(" + hash_algo + ")");
+
+         if(!hmac)
+            { throw CLI_Error_Unsupported("HMAC", hash_algo); }
+
+         hmac->set_key(slurp_file(get_arg("key")));
+
+         const size_t buf_size = get_arg_sz("buf-size");
+
+         std::vector<std::string> files = get_arg_list("files");
+         if(files.empty())
+            { files.push_back("-"); } // read stdin if no arguments on command line
+
+         for(const std::string& fsname : files)
+            {
+            try
+               {
+               auto update_hmac = [&](const uint8_t b[], size_t l) { hmac->update(b, l); };
+               read_file(fsname, update_hmac, buf_size);
+               output() << Botan::hex_encode(hmac->final());
+
+               if(no_fsname == false)
+                  output() << " " << fsname;
+
+               output() << "\n";
+               }
+            catch(CLI_IO_Error& e)
+               {
+               error_output() << e.what() << "\n";
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("hmac", HMAC);
+
+#endif // hmac
+
+}
diff --git a/third_party/botan/src/cli/main.cpp b/third_party/botan/src/cli/main.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/main.cpp
@@ -0,0 +1,37 @@
+/*
+* (C) 2009,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/version.h>
+#include <iostream>
+#include <algorithm>
+
+int main(int argc, char* argv[])
+   {
+   std::cerr << Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
+
+   std::string cmd_name = "help";
+
+   if(argc >= 2)
+      {
+      cmd_name = argv[1];
+      if(cmd_name == "--help" || cmd_name == "-h")
+         cmd_name = "help";
+      if(cmd_name == "--version" || cmd_name == "-V")
+         cmd_name = "version";
+      }
+
+   std::unique_ptr<Botan_CLI::Command> cmd(Botan_CLI::Command::get_cmd(cmd_name));
+
+   if(!cmd)
+      {
+      std::cout << "Unknown command " << cmd_name << " (try --help)\n";
+      return 1;
+      }
+
+   std::vector<std::string> args(argv + std::min(argc, 2), argv + argc);
+   return cmd->run(args);
+   }
diff --git a/third_party/botan/src/cli/math.cpp b/third_party/botan/src/cli/math.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/math.cpp
@@ -0,0 +1,269 @@
+/*
+* (C) 2009,2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/numthry.h>
+#include <botan/monty.h>
+#include <iterator>
+
+namespace Botan_CLI {
+
+class Modular_Inverse final : public Command
+   {
+   public:
+      Modular_Inverse() : Command("mod_inverse n mod") {}
+
+      std::string group() const override
+         {
+         return "numtheory";
+         }
+
+      std::string description() const override
+         {
+         return "Calculates a modular inverse";
+         }
+
+      void go() override
+         {
+         const Botan::BigInt n(get_arg("n"));
+         const Botan::BigInt mod(get_arg("mod"));
+
+         output() << Botan::inverse_mod(n, mod) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("mod_inverse", Modular_Inverse);
+
+class Gen_Prime final : public Command
+   {
+   public:
+      Gen_Prime() : Command("gen_prime --count=1 bits") {}
+
+      std::string group() const override
+         {
+         return "numtheory";
+         }
+
+      std::string description() const override
+         {
+         return "Samples one or more primes";
+         }
+
+      void go() override
+         {
+         const size_t bits = get_arg_sz("bits");
+         const size_t cnt = get_arg_sz("count");
+
+         for(size_t i = 0; i != cnt; ++i)
+            {
+            const Botan::BigInt p = Botan::random_prime(rng(), bits);
+            output() << p << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_prime", Gen_Prime);
+
+class Is_Prime final : public Command
+   {
+   public:
+      Is_Prime() : Command("is_prime --prob=56 n") {}
+
+      std::string group() const override
+         {
+         return "numtheory";
+         }
+
+      std::string description() const override
+         {
+         return "Test if the integer n is composite or prime";
+         }
+
+      void go() override
+         {
+         Botan::BigInt n(get_arg("n"));
+         const size_t prob = get_arg_sz("prob");
+         const bool prime = Botan::is_prime(n, rng(), prob);
+
+         output() << n << " is " << (prime ? "probably prime" : "composite") << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("is_prime", Is_Prime);
+
+/*
+* Factor integers using a combination of trial division by small
+* primes, and Pollard's Rho algorithm
+*/
+class Factor final : public Command
+   {
+   public:
+      Factor() : Command("factor n") {}
+
+      std::string group() const override
+         {
+         return "numtheory";
+         }
+
+      std::string description() const override
+         {
+         return "Factor a given integer";
+         }
+
+      void go() override
+         {
+         Botan::BigInt n(get_arg("n"));
+
+         std::vector<Botan::BigInt> factors = factorize(n, rng());
+         std::sort(factors.begin(), factors.end());
+
+         output() << n << ": ";
+         std::copy(factors.begin(), factors.end(), std::ostream_iterator<Botan::BigInt>(output(), " "));
+         output() << std::endl;
+         }
+
+   private:
+
+      std::vector<Botan::BigInt> factorize(const Botan::BigInt& n_in,
+                                           Botan::RandomNumberGenerator& rng)
+         {
+         Botan::BigInt n = n_in;
+         std::vector<Botan::BigInt> factors = remove_small_factors(n);
+
+         while(n != 1)
+            {
+            if(Botan::is_prime(n, rng))
+               {
+               factors.push_back(n);
+               break;
+               }
+
+            Botan::BigInt a_factor = 0;
+            while(a_factor == 0)
+               {
+               a_factor = rho(n, rng);
+               }
+
+            std::vector<Botan::BigInt> rho_factored = factorize(a_factor, rng);
+            for(size_t j = 0; j != rho_factored.size(); j++)
+               {
+               factors.push_back(rho_factored[j]);
+               }
+
+            n /= a_factor;
+            }
+
+         return factors;
+         }
+
+      /*
+      * Pollard's Rho algorithm, as described in the MIT algorithms book.
+      * Uses Brent's cycle finding
+      */
+      Botan::BigInt rho(const Botan::BigInt& n, Botan::RandomNumberGenerator& rng)
+         {
+         auto monty_n = std::make_shared<Botan::Montgomery_Params>(n);
+
+         const Botan::Montgomery_Int one(monty_n, monty_n->R1(), false);
+
+         Botan::Montgomery_Int x(monty_n, Botan::BigInt::random_integer(rng, 2, n - 3), false);
+         Botan::Montgomery_Int y = x;
+         Botan::Montgomery_Int z = one;
+         Botan::Montgomery_Int t(monty_n);
+         Botan::BigInt d;
+
+         Botan::secure_vector<Botan::word> ws;
+
+         size_t i = 1, k = 2;
+
+         while(true)
+            {
+            i++;
+
+            if(i >= 0xFFFF0000) // bad seed? too slow? bail out
+               {
+               break;
+               }
+
+            x.square_this(ws); // x = x^2
+            x.add(one, ws);
+
+            t = y;
+            t.sub(x, ws);
+
+            z.mul_by(t, ws);
+
+            if(i == k || i % 128 == 0)
+               {
+               d = Botan::gcd(z.value(), n);
+               z = one;
+
+               if(d == n)
+                  {
+                  // TODO Should rewind here
+                  break;
+                  }
+
+               if(d != 1)
+                  return d;
+               }
+
+            if(i == k)
+               {
+               y = x;
+               k = 2 * k;
+               }
+            }
+
+         // failed
+         return 0;
+         }
+
+      // Remove (and return) any small (< 2^16) factors
+      std::vector<Botan::BigInt> remove_small_factors(Botan::BigInt& n)
+         {
+         std::vector<Botan::BigInt> factors;
+
+         while(n.is_even())
+            {
+            factors.push_back(2);
+            n /= 2;
+            }
+
+         for(size_t j = 0; j != Botan::PRIME_TABLE_SIZE; j++)
+            {
+            uint16_t prime = Botan::PRIMES[j];
+            if(n < prime)
+               {
+               break;
+               }
+
+            Botan::BigInt x = Botan::gcd(n, prime);
+
+            if(x != 1)
+               {
+               n /= x;
+
+               while(x != 1)
+                  {
+                  x /= prime;
+                  factors.push_back(prime);
+                  }
+               }
+            }
+
+         return factors;
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("factor", Factor);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/pbkdf.cpp b/third_party/botan/src/cli/pbkdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/pbkdf.cpp
@@ -0,0 +1,99 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PBKDF)
+   #include <botan/pwdhash.h>
+   #include <botan/internal/os_utils.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_PBKDF)
+
+class PBKDF_Tune final : public Command
+   {
+   public:
+      PBKDF_Tune() : Command("pbkdf_tune --algo=Scrypt --max-mem=256 --output-len=32 --check *times") {}
+
+      std::string group() const override
+         {
+         return "passhash";
+         }
+
+      std::string description() const override
+         {
+         return "Tune a PBKDF algo";
+         }
+
+      void go() override
+         {
+         const size_t output_len = get_arg_sz("output-len");
+         const std::string algo = get_arg("algo");
+         const size_t max_mem = get_arg_sz("max-mem");
+         const bool check_time = flag_set("check");
+
+         std::unique_ptr<Botan::PasswordHashFamily> pwdhash_fam =
+            Botan::PasswordHashFamily::create(algo);
+
+         if(!pwdhash_fam)
+            throw CLI_Error_Unsupported("Password hashing", algo);
+
+         for(const std::string& time : get_arg_list("times"))
+            {
+            std::unique_ptr<Botan::PasswordHash> pwhash;
+
+            if(time == "default")
+               {
+               pwhash = pwdhash_fam->default_params();
+               }
+            else
+               {
+               size_t msec = 0;
+               try
+                  {
+                  msec = std::stoul(time);
+                  }
+               catch(std::exception&)
+                  {
+                  throw CLI_Usage_Error("Unknown time value '" + time + "' for pbkdf_tune");
+                  }
+
+               pwhash = pwdhash_fam->tune(output_len, std::chrono::milliseconds(msec), max_mem);
+               }
+
+            output() << "For " << time << " ms selected " << pwhash->to_string();
+
+            if(pwhash->total_memory_usage() > 0)
+               {
+               output() << " using " << pwhash->total_memory_usage()/(1024*1024) << " MiB";
+               }
+
+            if(check_time)
+               {
+               std::vector<uint8_t> outbuf(output_len);
+               const uint8_t salt[8] = { 0 };
+
+               const uint64_t start_ns = Botan::OS::get_system_timestamp_ns();
+               pwhash->derive_key(outbuf.data(), outbuf.size(),
+                                  "test", 4, salt, sizeof(salt));
+               const uint64_t end_ns = Botan::OS::get_system_timestamp_ns();
+               const uint64_t dur_ns = end_ns - start_ns;
+
+               output() << " took " << (dur_ns / 1000000.0) << " msec to compute";
+               }
+
+            output() << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("pbkdf_tune", PBKDF_Tune);
+
+#endif
+
+}
diff --git a/third_party/botan/src/cli/pk_crypt.cpp b/third_party/botan/src/cli/pk_crypt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/pk_crypt.cpp
@@ -0,0 +1,229 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AEAD_MODES) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_PEM_CODEC) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/pubkey.h>
+#include <botan/x509_key.h>
+#include <botan/pkcs8.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/aead.h>
+#include <botan/pem.h>
+#include <botan/rng.h>
+
+namespace Botan_CLI {
+
+namespace {
+
+class PK_Encrypt final : public Command
+   {
+   public:
+      PK_Encrypt() : Command("pk_encrypt --aead=AES-256/GCM pubkey datafile") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Encrypt a file using a RSA public key";
+         }
+
+      void go() override
+         {
+         std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey")));
+         if(!key)
+            {
+            throw CLI_Error("Unable to load public key");
+            }
+
+         if(key->algo_name() != "RSA")
+            {
+            throw CLI_Usage_Error("This function requires an RSA key");
+            }
+
+         const std::string OAEP_HASH = "SHA-256";
+         const std::string aead_algo = get_arg("aead");
+
+         std::unique_ptr<Botan::AEAD_Mode> aead =
+            Botan::AEAD_Mode::create(aead_algo, Botan::ENCRYPTION);
+
+         if(!aead)
+            throw CLI_Usage_Error("The AEAD '" + aead_algo + "' is not available");
+
+         const Botan::OID aead_oid = Botan::OID::from_string(aead_algo);
+         if(aead_oid.empty())
+            throw CLI_Usage_Error("No OID defined for AEAD '" + aead_algo + "'");
+
+         Botan::secure_vector<uint8_t> data;
+         auto insert_fn = [&](const uint8_t b[], size_t l)
+            {
+            data.insert(data.end(), b, b + l);
+            };
+         this->read_file(get_arg("datafile"), insert_fn);
+
+         const Botan::AlgorithmIdentifier hash_id(OAEP_HASH, Botan::AlgorithmIdentifier::USE_EMPTY_PARAM);
+         const Botan::AlgorithmIdentifier pk_alg_id("RSA/OAEP", hash_id.BER_encode());
+
+         Botan::PK_Encryptor_EME enc(*key, rng(), "OAEP(" + OAEP_HASH + ")");
+
+         const Botan::secure_vector<uint8_t> file_key = rng().random_vec(aead->key_spec().maximum_keylength());
+
+         const std::vector<uint8_t> encrypted_key = enc.encrypt(file_key, rng());
+
+         const Botan::secure_vector<uint8_t> nonce = rng().random_vec(aead->default_nonce_length());
+         aead->set_key(file_key);
+         aead->set_associated_data_vec(encrypted_key);
+         aead->start(nonce);
+
+         aead->finish(data);
+
+         std::vector<uint8_t> buf;
+         Botan::DER_Encoder der(buf);
+
+         der.start_cons(Botan::SEQUENCE)
+            .encode(pk_alg_id)
+            .encode(encrypted_key, Botan::OCTET_STRING)
+            .encode(aead_oid)
+            .encode(nonce, Botan::OCTET_STRING)
+            .encode(data, Botan::OCTET_STRING)
+            .end_cons();
+
+         output() << Botan::PEM_Code::encode(buf, "PUBKEY ENCRYPTED MESSAGE", 72);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("pk_encrypt", PK_Encrypt);
+
+class PK_Decrypt final : public Command
+   {
+   public:
+      PK_Decrypt() : Command("pk_decrypt privkey datafile") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Decrypt a file using a RSA private key";
+         }
+
+      void go() override
+         {
+         Botan::DataSource_Stream input_stream(get_arg("privkey"));
+         auto get_pass = [this]() { return get_passphrase("Password"); };
+         std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input_stream, get_pass);
+
+         if(!key)
+            {
+            throw CLI_Error("Unable to load public key");
+            }
+
+         if(key->algo_name() != "RSA")
+            {
+            throw CLI_Usage_Error("This function requires an RSA key");
+            }
+
+         Botan::secure_vector<uint8_t> data;
+         std::vector<uint8_t> encrypted_key;
+         std::vector<uint8_t> nonce;
+         Botan::AlgorithmIdentifier pk_alg_id;
+         Botan::OID aead_oid;
+
+         try
+            {
+            Botan::DataSource_Stream input(get_arg("datafile"));
+
+            Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE"))
+               .start_cons(Botan::SEQUENCE)
+                  .decode(pk_alg_id)
+                  .decode(encrypted_key, Botan::OCTET_STRING)
+                  .decode(aead_oid)
+                  .decode(nonce, Botan::OCTET_STRING)
+                  .decode(data, Botan::OCTET_STRING)
+               .end_cons();
+            }
+         catch(Botan::Decoding_Error&)
+            {
+            error_output() << "Parsing input file failed: invalid format?\n";
+            return set_return_code(1);
+            }
+
+         const std::string aead_algo = Botan::OIDS::oid2str_or_empty(aead_oid);
+         if(aead_algo == "")
+            {
+            error_output() << "Ciphertext was encrypted with an unknown algorithm";
+            return set_return_code(1);
+            }
+
+         if(pk_alg_id.get_oid() != Botan::OID::from_string("RSA/OAEP"))
+            {
+            error_output() << "Ciphertext was encrypted with something other than RSA/OAEP";
+            return set_return_code(1);
+            }
+
+         Botan::AlgorithmIdentifier oaep_hash_id;
+         Botan::BER_Decoder(pk_alg_id.get_parameters()).decode(oaep_hash_id);
+
+         const std::string oaep_hash = Botan::OIDS::oid2str_or_empty(oaep_hash_id.get_oid());
+
+         if(oaep_hash.empty())
+            {
+            error_output() << "Unknown hash function used with OAEP, OID " << oaep_hash_id.get_oid().to_string() << "\n";
+            return set_return_code(1);
+            }
+
+         if(oaep_hash_id.get_parameters().empty() == false)
+            {
+            error_output() << "Unknown OAEP parameters used\n";
+            return set_return_code(1);
+            }
+
+         std::unique_ptr<Botan::AEAD_Mode> aead =
+            Botan::AEAD_Mode::create_or_throw(aead_algo, Botan::DECRYPTION);
+
+         const size_t expected_keylen = aead->key_spec().maximum_keylength();
+
+         Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + oaep_hash + ")");
+
+         const Botan::secure_vector<uint8_t> file_key =
+            dec.decrypt_or_random(encrypted_key.data(),
+                                  encrypted_key.size(),
+                                  expected_keylen,
+                                  rng());
+
+         aead->set_key(file_key);
+         aead->set_associated_data_vec(encrypted_key);
+         aead->start(nonce);
+
+         try
+            {
+            aead->finish(data);
+
+            output().write(reinterpret_cast<const char*>(data.data()), data.size());
+            }
+         catch(Botan::Integrity_Failure&)
+            {
+            error_output() << "Message authentication failure, possible ciphertext tampering\n";
+            return set_return_code(1);
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("pk_decrypt", PK_Decrypt);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/psk.cpp b/third_party/botan/src/cli/psk.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/psk.cpp
@@ -0,0 +1,106 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PSK_DB) && defined(BOTAN_HAS_SQLITE3)
+
+#include <botan/psk_db_sql.h>
+#include <botan/sqlite3.h>
+#include <botan/hex.h>
+
+namespace Botan_CLI {
+
+class PSK_Tool_Base : public Command
+   {
+   public:
+      PSK_Tool_Base(const std::string& spec) : Command(spec) {}
+
+      std::string group() const override
+         {
+         return "psk";
+         }
+
+      void go() override
+         {
+         const std::string db_filename = get_arg("db");
+         const Botan::secure_vector<uint8_t> db_key = Botan::hex_decode_locked(get_passphrase_arg("Database key", "db_key"));
+
+         std::shared_ptr<Botan::SQL_Database> db = std::make_shared<Botan::Sqlite3_Database>(db_filename);
+         Botan::Encrypted_PSK_Database_SQL psk(db_key, db, "psk");
+
+         psk_operation(psk);
+         }
+
+   private:
+      virtual void psk_operation(Botan::PSK_Database& db) = 0;
+   };
+
+class PSK_Tool_Set final : public PSK_Tool_Base
+   {
+   public:
+      PSK_Tool_Set() : PSK_Tool_Base("psk_set db db_key name psk") {}
+
+      std::string description() const override
+         {
+         return "Save a PSK encrypted in the database";
+         }
+
+   private:
+      void psk_operation(Botan::PSK_Database& db) override
+         {
+         const std::string name = get_arg("name");
+         const Botan::secure_vector<uint8_t> psk = Botan::hex_decode_locked(get_passphrase_arg("PSK", "psk"));
+         db.set_vec(name, psk);
+         }
+   };
+
+class PSK_Tool_Get final : public PSK_Tool_Base
+   {
+   public:
+      PSK_Tool_Get() : PSK_Tool_Base("psk_get db db_key name") {}
+
+      std::string description() const override
+         {
+         return "Read a value saved with psk_set";
+         }
+
+   private:
+      void psk_operation(Botan::PSK_Database& db) override
+         {
+         const std::string name = get_arg("name");
+         const Botan::secure_vector<uint8_t> val = db.get(name);
+         output() << Botan::hex_encode(val) << "\n";
+         }
+   };
+
+class PSK_Tool_List final : public PSK_Tool_Base
+   {
+   public:
+      PSK_Tool_List() : PSK_Tool_Base("psk_list db db_key") {}
+
+      std::string description() const override
+         {
+         return "List all values saved to the database";
+         }
+
+   private:
+      void psk_operation(Botan::PSK_Database& db) override
+         {
+         const std::set<std::string> names = db.list_names();
+
+         for(std::string name : names)
+            output() << name << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("psk_set", PSK_Tool_Set);
+BOTAN_REGISTER_COMMAND("psk_get", PSK_Tool_Get);
+BOTAN_REGISTER_COMMAND("psk_list", PSK_Tool_List);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/pubkey.cpp b/third_party/botan/src/cli/pubkey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/pubkey.cpp
@@ -0,0 +1,554 @@
+/*
+* (C) 2010,2014,2015,2019 Jack Lloyd
+* (C) 2019 Matthias Gierlings
+* (C) 2015 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include <botan/base64.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#include <botan/pk_keys.h>
+#include <botan/x509_key.h>
+#include <botan/pk_algs.h>
+#include <botan/pkcs8.h>
+#include <botan/pubkey.h>
+#include <botan/workfactor.h>
+#include <botan/data_src.h>
+
+#include <fstream>
+
+#if defined(BOTAN_HAS_DL_GROUP)
+   #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   #include <botan/ec_group.h>
+#endif
+
+namespace Botan_CLI {
+
+class PK_Keygen final : public Command
+   {
+   public:
+      PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300 --provider= --der-out") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Generate a PKCS #8 private key";
+         }
+
+      void go() override
+         {
+         const std::string algo = get_arg("algo");
+         const std::string params = get_arg("params");
+         const std::string provider = get_arg("provider");
+
+         std::unique_ptr<Botan::Private_Key> key =
+            Botan::create_private_key(algo, rng(), params, provider);
+
+         if(!key)
+            {
+            throw CLI_Error_Unsupported("keygen", algo);
+            }
+
+         const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
+         const bool der_out = flag_set("der-out");
+
+         const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis"));
+         const std::string pbe = get_arg("pbe");
+
+         if(der_out)
+            {
+            if(pass.empty())
+               {
+               write_output(Botan::PKCS8::BER_encode(*key));
+               }
+            else
+               {
+               write_output(Botan::PKCS8::BER_encode(*key, rng(), pass, pbe_millis, pbe));
+               }
+            }
+         else
+            {
+            if(pass.empty())
+               {
+               output() << Botan::PKCS8::PEM_encode(*key);
+               }
+            else
+               {
+               output() << Botan::PKCS8::PEM_encode(*key, rng(), pass, pbe_millis, pbe);
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+namespace {
+
+std::string choose_sig_padding(const std::string& key, const std::string& emsa, const std::string& hash)
+   {
+   std::string emsa_or_default = [&]() -> std::string
+      {
+      if(!emsa.empty())
+         {
+         return emsa;
+         }
+
+      if(key == "RSA")
+         {
+         return "EMSA4";
+         } // PSS
+      else if(key == "ECDSA" || key == "DSA")
+         {
+         return "EMSA1";
+         }
+      else if(key == "Ed25519")
+         {
+         return "";
+         }
+      else
+         {
+         return "EMSA1";
+         }
+      }();
+
+   if(emsa_or_default.empty())
+      {
+      return hash;
+      }
+
+   return emsa_or_default + "(" + hash + ")";
+   }
+
+}
+
+class PK_Fingerprint final : public Command
+   {
+   public:
+      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Calculate a public key fingerprint";
+         }
+
+      void go() override
+         {
+         const std::string hash_algo = get_arg("algo");
+         const bool no_fsname = flag_set("no-fsname");
+
+         for(std::string key_file : get_arg_list("keys"))
+            {
+            std::unique_ptr<Botan::Public_Key> key(
+               key_file == "-"
+               ? Botan::X509::load_key(this->slurp_file("-", 4096))
+               : Botan::X509::load_key(key_file));
+
+            const std::string fprint = key->fingerprint_public(hash_algo);
+
+            if(no_fsname || key_file == "-")
+               { output() << fprint << "\n"; }
+            else
+               { output() << key_file << ": " << fprint << "\n"; }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
+
+class PK_Sign final : public Command
+   {
+   public:
+      PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --emsa= --provider= key file") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Sign arbitrary data";
+         }
+
+      void go() override
+         {
+         const std::string key_file = get_arg("key");
+         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
+
+         Botan::DataSource_Stream input(key_file);
+         std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input, passphrase);
+
+         if(!key)
+            {
+            throw CLI_Error("Unable to load private key");
+            }
+
+         const std::string sig_padding =
+            choose_sig_padding(key->algo_name(), get_arg("emsa"), get_arg("hash"));
+
+         const Botan::Signature_Format format =
+            flag_set("der-format") ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+         const std::string provider = get_arg("provider");
+
+         Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider);
+
+         auto onData = [&signer](const uint8_t b[], size_t l)
+            {
+            signer.update(b, l);
+            };
+         this->read_file(get_arg("file"), onData);
+
+         std::vector<uint8_t> sig { signer.signature(rng()) };
+
+         if(key->stateful_operation())
+            {
+            std::ofstream updated_key(key_file);
+            if(passphrase.empty())
+               { updated_key << Botan::PKCS8::PEM_encode(*key); }
+            else
+               { updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase); }
+            }
+
+         output() << Botan::base64_encode(sig) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("sign", PK_Sign);
+
+class PK_Verify final : public Command
+   {
+   public:
+      PK_Verify() : Command("verify --der-format --hash=SHA-256 --emsa= pubkey file signature") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Verify the authenticity of the given file with the provided signature";
+         }
+
+      void go() override
+         {
+         std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey")));
+         if(!key)
+            {
+            throw CLI_Error("Unable to load public key");
+            }
+
+         const std::string sig_padding =
+            choose_sig_padding(key->algo_name(), get_arg("emsa"), get_arg("hash"));
+
+         const Botan::Signature_Format format =
+            flag_set("der-format") ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+         Botan::PK_Verifier verifier(*key, sig_padding, format);
+         auto onData = [&verifier](const uint8_t b[], size_t l)
+            {
+            verifier.update(b, l);
+            };
+         this->read_file(get_arg("file"), onData);
+
+         const Botan::secure_vector<uint8_t> signature =
+            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
+
+         const bool valid = verifier.check_signature(signature);
+
+         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("verify", PK_Verify);
+
+class PKCS8_Tool final : public Command
+   {
+   public:
+      PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --der-out --pass-out= --pbe= --pbe-millis=300 key") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Open a PKCS #8 formatted key";
+         }
+
+      void go() override
+         {
+         const std::string key_file = get_arg("key");
+         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
+
+         Botan::DataSource_Memory key_src(slurp_file(key_file));
+         std::unique_ptr<Botan::Private_Key> key;
+
+         if(pass_in.empty())
+            {
+            key.reset(Botan::PKCS8::load_key(key_src, rng()));
+            }
+         else
+            {
+            key.reset(Botan::PKCS8::load_key(key_src, rng(), pass_in));
+            }
+
+         const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis"));
+         const std::string pbe = get_arg("pbe");
+         const bool der_out = flag_set("der-out");
+
+         if(flag_set("pub-out"))
+            {
+            if(der_out)
+               {
+               write_output(Botan::X509::BER_encode(*key));
+               }
+            else
+               {
+               output() << Botan::X509::PEM_encode(*key);
+               }
+            }
+         else
+            {
+            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
+
+            if(der_out)
+               {
+               if(pass_out.empty())
+                  {
+                  write_output(Botan::PKCS8::BER_encode(*key));
+                  }
+               else
+                  {
+                  write_output(Botan::PKCS8::BER_encode(*key, rng(), pass_out, pbe_millis, pbe));
+                  }
+               }
+            else
+               {
+               if(pass_out.empty())
+                  {
+                  output() << Botan::PKCS8::PEM_encode(*key);
+                  }
+               else
+                  {
+                  output() << Botan::PKCS8::PEM_encode(*key, rng(), pass_out, pbe_millis, pbe);
+                  }
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
+
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+class EC_Group_Info final : public Command
+   {
+   public:
+      EC_Group_Info() : Command("ec_group_info --pem name") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Print raw elliptic curve domain parameters of the standardized curve name";
+         }
+
+      void go() override
+         {
+         Botan::EC_Group ec_group(get_arg("name"));
+
+         if(flag_set("pem"))
+            {
+            output() << ec_group.PEM_encode();
+            }
+         else
+            {
+            output() << "P = " << std::hex << ec_group.get_p() << "\n"
+                     << "A = " << std::hex << ec_group.get_a() << "\n"
+                     << "B = " << std::hex << ec_group.get_b() << "\n"
+                     << "N = " << std::hex << ec_group.get_order() << "\n"
+                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
+            }
+
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+class DL_Group_Info final : public Command
+   {
+   public:
+      DL_Group_Info() : Command("dl_group_info --pem name") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
+         }
+
+      void go() override
+         {
+         Botan::DL_Group dl_group(get_arg("name"));
+
+         if(flag_set("pem"))
+            {
+            output() << dl_group.PEM_encode(Botan::DL_Group::ANSI_X9_42_DH_PARAMETERS);
+            }
+         else
+            {
+            output() << "P = " << std::hex << dl_group.get_p() << "\n"
+                     << "G = " << dl_group.get_g() << "\n";
+            }
+
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
+
+class PK_Workfactor final : public Command
+   {
+   public:
+      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Provide estimate of strength of public key based on size";
+         }
+
+      void go() override
+         {
+         const size_t bits = get_arg_sz("bits");
+         const std::string type = get_arg("type");
+
+         if(type == "rsa")
+            { output() << Botan::if_work_factor(bits) << "\n"; }
+         else if(type == "dl")
+            { output() << Botan::dl_work_factor(bits) << "\n"; }
+         else if(type == "dl_exp")
+            { output() << Botan::dl_exponent_size(bits) << "\n"; }
+         else
+            { throw CLI_Usage_Error("Unknown type for pk_workfactor"); }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
+
+class Gen_DL_Group final : public Command
+   {
+   public:
+      Gen_DL_Group() : Command("gen_dl_group --pbits=1024 --qbits=0 --seed= --type=subgroup") {}
+
+      std::string group() const override
+         {
+         return "pubkey";
+         }
+
+      std::string description() const override
+         {
+         return "Generate ANSI X9.42 encoded Diffie-Hellman group parameters";
+         }
+
+      void go() override
+         {
+         const size_t pbits = get_arg_sz("pbits");
+         const size_t qbits = get_arg_sz("qbits");
+
+         const std::string type = get_arg("type");
+         const std::string seed_str = get_arg("seed");
+
+         if(type == "strong")
+            {
+            if(seed_str.size() > 0)
+               { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
+            Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
+            output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+            }
+         else if(type == "subgroup")
+            {
+            if(seed_str.size() > 0)
+               { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
+            Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
+            output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+            }
+         else if(type == "dsa")
+            {
+            size_t dsa_qbits = qbits;
+            if(dsa_qbits == 0)
+               {
+               if(pbits == 1024)
+                  { dsa_qbits = 160; }
+               else if(pbits == 2048 || pbits == 3072)
+                  { dsa_qbits = 256; }
+               else
+                  { throw CLI_Usage_Error("Invalid DSA p/q sizes"); }
+               }
+
+            if(seed_str.empty())
+               {
+               Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
+               output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+               }
+            else
+               {
+               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
+               Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
+               output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+               }
+
+            }
+         else
+            {
+            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
+
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/roughtime.cpp b/third_party/botan/src/cli/roughtime.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/roughtime.cpp
@@ -0,0 +1,215 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ROUGHTIME)
+
+#include <botan/roughtime.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <botan/base64.h>
+#include <botan/ed25519.h>
+#include <botan/hash.h>
+#include <botan/calendar.h>
+
+#include <iomanip>
+#include <fstream>
+
+namespace Botan_CLI {
+
+class RoughtimeCheck final : public Command
+   {
+   public:
+      RoughtimeCheck() : Command("roughtime_check --raw-time chain-file") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Parse and validate Roughtime chain file";
+         }
+
+      void go() override
+         {
+         const auto chain = Botan::Roughtime::Chain(slurp_file_as_str(get_arg("chain-file")));
+         unsigned i = 0;
+         for(const auto& response : chain.responses())
+            {
+            output() << std::setw(3) << ++i << ": UTC ";
+            if(flag_set("raw-time"))
+               { output() << Botan::Roughtime::Response::sys_microseconds64(response.utc_midpoint()).time_since_epoch().count(); }
+            else
+               { output() << Botan::calendar_value(response.utc_midpoint()).to_string(); }
+            output() << " (+-" << Botan::Roughtime::Response::microseconds32(response.utc_radius()).count() << "us)\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("roughtime_check", RoughtimeCheck);
+
+class Roughtime final : public Command
+   {
+   public:
+      Roughtime() :
+         Command("roughtime --raw-time --chain-file=roughtime-chain --max-chain-size=128 --check-local-clock=60 --host= --pubkey= --servers-file=") {}
+
+      std::string help_text() const override
+         {
+         return Command::help_text() + R"(
+
+--servers-file=<filename>
+   List of servers that will queried in sequence.
+
+   File contents syntax:
+      <name> <key type> <base 64 encoded public key> <protocol> <host:port>
+
+   Example servers:
+      Cloudflare-Roughtime ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= udp roughtime.cloudflare.com:2002
+      Google-Sandbox-Roughtime ed25519 etPaaIxcBMY1oUeGpwvPMCJMwlRVNxv51KK/tktoJTQ= udp roughtime.sandbox.google.com:2002
+
+--chain-file=<filename>
+   Succesfull queries are appended to this file.
+   If limit of --max-chain-size records is reached, the oldest records are truncated.
+   This queries records can be replayed using command roughtime_check <chain-file>.
+
+   File contents syntax:
+      <key type> <base 64 encoded public key> <base 64 encoded blind or nonce> <base 64 encoded server response>
+)";
+         }
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Retrieve time from Roughtime server";
+         }
+
+      void query(std::unique_ptr<Botan::Roughtime::Chain>& chain,
+                 const size_t max_chain_size,
+                 const std::string& address,
+                 const Botan::Ed25519_PublicKey& public_key)
+         {
+         Botan::Roughtime::Nonce nonce;
+         Botan::Roughtime::Nonce blind;
+         if(chain)
+            {
+            blind = Botan::Roughtime::Nonce(rng());
+            nonce = chain->next_nonce(blind);
+            }
+         else
+            {
+            nonce = Botan::Roughtime::Nonce(rng());
+            }
+         const auto response_raw = Botan::Roughtime::online_request(address, nonce, std::chrono::seconds(5));
+         const auto response = Botan::Roughtime::Response::from_bits(response_raw, nonce);
+         if(flag_set("raw-time"))
+            { output() << "UTC " << Botan::Roughtime::Response::sys_microseconds64(response.utc_midpoint()).time_since_epoch().count(); }
+         else
+            { output() << "UTC " << Botan::calendar_value(response.utc_midpoint()).to_string(); }
+         output() << " (+-" << Botan::Roughtime::Response::microseconds32(response.utc_radius()).count() << "us)";
+         if(!response.validate(public_key))
+            {
+            error_output() << "ERROR: Public key does not match!\n";
+            set_return_code(1);
+            return;
+            }
+         const auto tolerance = get_arg_sz("check-local-clock");
+         if(tolerance)
+            {
+            const auto now = std::chrono::system_clock::now();
+            const auto diff_abs = now >= response.utc_midpoint() ? now - response.utc_midpoint() : response.utc_midpoint() - now;
+            if(diff_abs > (response.utc_radius() + std::chrono::seconds(tolerance)))
+               {
+               error_output() << "ERROR: Local clock mismatch\n";
+               set_return_code(1);
+               return;
+               }
+            output() << " Local clock match";
+            }
+         if(chain)
+            chain->append({response_raw, public_key, blind}, max_chain_size);
+         output() << '\n';
+         }
+
+      void go() override
+         {
+
+         const auto max_chain_size = get_arg_sz("max-chain-size");
+         const auto chain_file = get_arg("chain-file");
+         const auto servers_file = get_arg_or("servers-file", "");
+         const auto host = get_arg_or("host", "");
+         const auto pk = get_arg_or("pubkey", "");
+
+         std::unique_ptr<Botan::Roughtime::Chain> chain;
+         if(!chain_file.empty() && max_chain_size >= 1)
+            {
+            try
+               {
+               chain.reset(new Botan::Roughtime::Chain(slurp_file_as_str(chain_file)));
+               }
+            catch(const CLI_IO_Error&)
+               {
+               chain.reset(new Botan::Roughtime::Chain()); //file is to still be created
+               }
+            }
+
+         const bool from_servers_file = !servers_file.empty();
+         const bool from_host_and_pk = !host.empty() && !pk.empty();
+         if(from_servers_file == from_host_and_pk)
+            {
+            error_output() << "Please specify either --servers-file or --host and --pubkey\n";
+            set_return_code(1);
+            return;
+            }
+
+         if(!servers_file.empty())
+            {
+            const auto servers = Botan::Roughtime::servers_from_str(slurp_file_as_str(servers_file));
+
+            for(const auto& s : servers)
+               {
+               output() << std::setw(25) << std::left << s.name() << ": ";
+               for(const auto& a : s.addresses())
+                  {
+                  try
+                     {
+                     query(chain, max_chain_size, a, s.public_key());
+                     break;
+                     }
+                  catch(const std::exception& ex) //network error, try next address
+                     {
+                     error_output() << ex.what() << '\n';
+                     }
+                  }
+               }
+
+            }
+         else
+            {
+            query(chain, max_chain_size, host, Botan::Ed25519_PublicKey(Botan::base64_decode(pk)));
+            }
+
+         if(chain)
+            {
+            std::ofstream out(chain_file);
+            out << chain->to_string();
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("roughtime", Roughtime);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/sandbox.cpp b/third_party/botan/src/cli/sandbox.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/sandbox.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2019 David Carlier <devnexen@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "sandbox.h"
+#include <botan/mem_ops.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_PLEDGE)
+  #include <unistd.h>
+#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER)
+  #include <sys/capsicum.h>
+  #include <unistd.h>
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+  #include <priv.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+struct SandboxPrivDelete {
+  void operator()(priv_set_t *ps)
+    {
+  ::priv_emptyset(ps);
+  ::priv_freeset(ps);
+    }
+};
+#endif
+
+Sandbox::Sandbox()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_PLEDGE)
+   m_name = "pledge";
+#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER)
+   m_name = "capsicum";
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+   m_name = "privilege";
+#else
+   m_name = "<none>";
+#endif
+   }
+
+bool Sandbox::init()
+   {
+   Botan::initialize_allocator();
+
+#if defined(BOTAN_TARGET_OS_HAS_PLEDGE)
+   const static char *opts = "stdio rpath inet error";
+   return (::pledge(opts, nullptr) == 0);
+#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER)
+   cap_rights_t wt, rd;
+
+   if (::cap_rights_init(&wt, CAP_READ, CAP_WRITE) == nullptr)
+      {
+      return false;
+      }
+
+   if (::cap_rights_init(&rd, CAP_FCNTL, CAP_EVENT, CAP_READ) == nullptr)
+      {
+      return false;
+      }
+
+   if (::cap_rights_limit(STDOUT_FILENO, &wt) == -1)
+      {
+      return false;
+      }
+
+   if (::cap_rights_limit(STDERR_FILENO, &wt) == -1)
+      {
+      return false;
+      }
+
+   if (::cap_rights_limit(STDIN_FILENO, &rd) == -1)
+      {
+      return false;
+      }
+
+   return (::cap_enter() == 0);
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+   priv_set_t *tmp;
+   std::unique_ptr<priv_set_t, SandboxPrivDelete> ps;
+   const char *const priv_perms[] = {
+	   PRIV_PROC_FORK,
+	   PRIV_PROC_EXEC,
+	   PRIV_PROC_INFO,
+	   PRIV_PROC_SESSION,
+   };
+
+   if ((tmp = ::priv_allocset()) == nullptr)
+      {
+      return false;
+      }
+
+   ps = std::unique_ptr<priv_set_t, SandboxPrivDelete>(tmp);
+   ::priv_basicset(ps.get());
+
+   for (auto perm: priv_perms)
+   {
+     if (::priv_delset(ps.get(), perm) == -1)
+     {
+        return false;
+     }
+   }
+
+   return true;
+#else
+   return true;
+#endif
+   }
+
+Sandbox::~Sandbox()
+   {
+   }
+}
diff --git a/third_party/botan/src/cli/sandbox.h b/third_party/botan/src/cli/sandbox.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/sandbox.h
@@ -0,0 +1,32 @@
+/*
+* (C) 2019 David Carlier <devnexen@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_SANDBOX_H_
+#define BOTAN_CLI_SANDBOX_H_
+
+#include <string>
+
+namespace Botan_CLI {
+
+class Sandbox
+   {
+   public:
+      explicit Sandbox();
+      virtual ~Sandbox();
+
+      bool init();
+
+      const std::string& name() const
+         {
+         return m_name;
+         }
+
+   private:
+      std::string m_name;
+   };
+}
+
+#endif
diff --git a/third_party/botan/src/cli/socket_utils.h b/third_party/botan/src/cli/socket_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/socket_utils.h
@@ -0,0 +1,105 @@
+/*
+* (C) 2014,2017 Jack Lloyd
+*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_SOCKET_UTILS_H_
+#define BOTAN_CLI_SOCKET_UTILS_H_
+
+#include <botan/build.h>
+#include "cli_exceptions.h"
+
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+#include <winsock2.h>
+#include <WS2tcpip.h>
+
+typedef SOCKET socket_type;
+
+inline socket_type invalid_socket() { return INVALID_SOCKET; }
+
+typedef size_t ssize_t;
+typedef int sendrecv_len_type;
+
+inline void close_socket(socket_type s) { ::closesocket(s); }
+
+#define STDIN_FILENO _fileno(stdin)
+
+inline void init_sockets()
+   {
+   WSAData wsa_data;
+   WORD wsa_version = MAKEWORD(2, 2);
+
+   if(::WSAStartup(wsa_version, &wsa_data) != 0)
+      {
+      throw Botan_CLI::CLI_Error("WSAStartup() failed: " + std::to_string(WSAGetLastError()));
+      }
+
+   if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+      {
+      ::WSACleanup();
+      throw Botan_CLI::CLI_Error("Could not find a usable version of Winsock.dll");
+      }
+   }
+
+inline void stop_sockets()
+   {
+   ::WSACleanup();
+   }
+
+inline std::string err_to_string(int e)
+   {
+   // TODO use strerror_s here
+   return "Error code " + std::to_string(e);
+   }
+
+inline int close(int fd)
+   {
+   return ::closesocket(fd);
+   }
+
+inline int read(int s, void* buf, size_t len)
+   {
+   return ::recv(s, reinterpret_cast<char*>(buf), static_cast<int>(len), 0);
+   }
+
+inline int send(int s, const uint8_t* buf, size_t len, int flags)
+   {
+   return ::send(s, reinterpret_cast<const char*>(buf), static_cast<int>(len), flags);
+   }
+
+#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+typedef int socket_type;
+typedef size_t sendrecv_len_type;
+
+inline socket_type invalid_socket() { return -1; }
+inline void close_socket(socket_type s) { ::close(s); }
+
+inline void init_sockets() {}
+inline void stop_sockets() {}
+
+inline std::string err_to_string(int e)
+   {
+   return std::strerror(e);
+   }
+
+#endif
+
+#if !defined(MSG_NOSIGNAL)
+   #define MSG_NOSIGNAL 0
+#endif
+
+#endif
diff --git a/third_party/botan/src/cli/speed.cpp b/third_party/botan/src/cli/speed.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/speed.cpp
@@ -0,0 +1,2344 @@
+/*
+* (C) 2009,2010,2014,2015,2017,2018 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "../tests/test_rng.h" // FIXME
+
+#include <sstream>
+#include <iomanip>
+#include <chrono>
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <set>
+
+// Always available:
+#include <botan/entropy_src.h>
+#include <botan/parsing.h>
+#include <botan/cpuid.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/timer.h>
+#include <botan/version.h>
+
+#if defined(BOTAN_HAS_BIGINT)
+   #include <botan/bigint.h>
+   #include <botan/divide.h>
+#endif
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+   #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+   #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+   #include <botan/cipher_mode.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+   #include <botan/mac.h>
+#endif
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+   #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+   #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+   #include <botan/chacha_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+   #include <botan/fpe_fe1.h>
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+   #include <botan/rfc3394.h>
+#endif
+
+#if defined(BOTAN_HAS_COMPRESSION)
+   #include <botan/compression.h>
+#endif
+
+#if defined(BOTAN_HAS_POLY_DBL)
+   #include <botan/internal/poly_dbl.h>
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+   #include <botan/pkcs8.h>
+   #include <botan/pubkey.h>
+   #include <botan/pk_algs.h>
+   #include <botan/x509_key.h>
+   #include <botan/workfactor.h>
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+   #include <botan/numthry.h>
+   #include <botan/reducer.h>
+   #include <botan/curve_nistp.h>
+   #include <botan/internal/primality.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   #include <botan/ec_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+   #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+   #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_NEWHOPE)
+   #include <botan/newhope.h>
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+   #include <botan/scrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+   #include <botan/argon2.h>
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+   #include <botan/bcrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+   #include <botan/passhash9.h>
+#endif
+
+namespace Botan_CLI {
+
+using Botan::Timer;
+
+namespace {
+
+class JSON_Output final
+   {
+   public:
+      void add(const Timer& timer) { m_results.push_back(timer); }
+
+      std::string print() const
+         {
+         std::ostringstream out;
+
+         out << "[\n";
+
+         for(size_t i = 0; i != m_results.size(); ++i)
+            {
+            if(i != 0)
+               out << ",";
+
+            const Timer& t = m_results[i];
+
+            out << '{';
+            out << "\"algo\": \"" << t.get_name() << "\", ";
+            out << "\"op\": \"" << t.doing() << "\", ";
+
+            out << "\"events\": " << t.events() << ", ";
+            if(t.cycles_consumed() > 0)
+               out << "\"cycles\": " << t.cycles_consumed() << ", ";
+            if(t.buf_size() > 0)
+               {
+               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
+               out << "\"buf_size\": " << t.buf_size() << ", ";
+               }
+
+            out << "\"nanos\": " << t.value();
+
+            out << "}\n";
+            }
+         out << "]\n";
+
+         return out.str();
+         }
+   private:
+      std::vector<Timer> m_results;
+   };
+
+class Summary final
+   {
+   public:
+      Summary() {}
+
+      void add(const Timer& t)
+         {
+         if(t.buf_size() == 0)
+            {
+            m_ops_entries.push_back(t);
+            }
+         else
+            {
+            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
+            }
+         }
+
+      std::string print()
+         {
+         const size_t name_padding = 35;
+         const size_t op_name_padding = 16;
+         const size_t op_padding = 16;
+
+         std::ostringstream result_ss;
+         result_ss << std::fixed;
+
+         if(m_bps_entries.size() > 0)
+            {
+            result_ss << "\n";
+
+            // add table header
+            result_ss << std::setw(name_padding) << std::left << "algo"
+                      << std::setw(op_name_padding) << std::left << "operation";
+
+            for(const Timer& t : m_bps_entries.begin()->second)
+               {
+               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
+               }
+            result_ss << "\n";
+
+            // add table entries
+            for(const auto& entry : m_bps_entries)
+               {
+               if(entry.second.empty())
+                  continue;
+
+               result_ss << std::setw(name_padding) << std::left << (entry.first.second)
+                         << std::setw(op_name_padding) << std::left << (entry.first.first);
+
+               for(const Timer& t : entry.second)
+                  {
+
+                  if(t.events() == 0)
+                     {
+                     result_ss << std::setw(op_padding) << std::right << "N/A";
+                     }
+                  else
+                     {
+                     result_ss << std::setw(op_padding) << std::right
+                               << std::setprecision(2) << (t.bytes_per_second() / 1000.0);
+                     }
+                  }
+
+               result_ss << "\n";
+               }
+
+            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
+            }
+
+         if(m_ops_entries.size() > 0)
+            {
+            result_ss << std::setprecision(6) << "\n";
+
+            // sort entries
+            std::sort(m_ops_entries.begin(), m_ops_entries.end());
+
+            // add table header
+            result_ss << std::setw(name_padding) << std::left << "algo"
+                      << std::setw(op_name_padding) << std::left << "operation"
+                      << std::setw(op_padding) << std::right << "sec/op"
+                      << std::setw(op_padding) << std::right << "op/sec"
+                      << "\n";
+
+            // add table entries
+            for(const Timer& entry : m_ops_entries)
+               {
+               result_ss << std::setw(name_padding) << std::left << entry.get_name()
+                         << std::setw(op_name_padding) << std::left << entry.doing()
+                         << std::setw(op_padding) << std::right << entry.seconds_per_event()
+                         << std::setw(op_padding) << std::right << entry.events_per_second()
+                         << "\n";
+               }
+            }
+
+         return result_ss.str();
+         }
+
+   private:
+      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
+      std::vector<Timer> m_ops_entries;
+   };
+
+std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg)
+   {
+   const size_t MAX_BUF_SIZE = 64*1024*1024;
+
+   std::set<size_t> buf;
+   for(std::string size_str : Botan::split_on(cmdline_arg, ','))
+      {
+      size_t x = 0;
+      try
+         {
+         size_t converted = 0;
+         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
+
+         if(converted != size_str.size())
+            throw CLI_Usage_Error("Invalid integer");
+         }
+      catch(std::exception&)
+         {
+         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
+         }
+
+      if(x == 0)
+         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
+
+      if(x > MAX_BUF_SIZE)
+         throw CLI_Usage_Error("Specified buffer size is too large");
+
+      buf.insert(x);
+      }
+
+   return std::vector<size_t>(buf.begin(), buf.end());
+   }
+
+}
+
+class Speed final : public Command
+   {
+   public:
+      Speed()
+         : Command("speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {}
+
+      std::vector<std::string> default_benchmark_list()
+         {
+         /*
+         This is not intended to be exhaustive: it just hits the high
+         points of the most interesting or widely used algorithms.
+         */
+
+         return {
+            /* Block ciphers */
+            "AES-128",
+            "AES-192",
+            "AES-256",
+            "ARIA-128",
+            "ARIA-192",
+            "ARIA-256",
+            "Blowfish",
+            "CAST-128",
+            "CAST-256",
+            "Camellia-128",
+            "Camellia-192",
+            "Camellia-256",
+            "DES",
+            "TripleDES",
+            "GOST-28147-89",
+            "IDEA",
+            "KASUMI",
+            "MISTY1",
+            "Noekeon",
+            "SHACAL2",
+            "SM4",
+            "Serpent",
+            "Threefish-512",
+            "Twofish",
+            "XTEA",
+
+            /* Cipher modes */
+            "AES-128/CBC",
+            "AES-128/CTR-BE",
+            "AES-128/EAX",
+            "AES-128/OCB",
+            "AES-128/GCM",
+            "AES-128/XTS",
+            "AES-128/SIV",
+
+            "Serpent/CBC",
+            "Serpent/CTR-BE",
+            "Serpent/EAX",
+            "Serpent/OCB",
+            "Serpent/GCM",
+            "Serpent/XTS",
+            "Serpent/SIV",
+
+            "ChaCha20Poly1305",
+
+            /* Stream ciphers */
+            "RC4",
+            "Salsa20",
+            "ChaCha20",
+
+            /* Hashes */
+            "SHA-160",
+            "SHA-256",
+            "SHA-512",
+            "SHA-3(256)",
+            "SHA-3(512)",
+            "RIPEMD-160",
+            "Skein-512",
+            "Blake2b",
+            "Tiger",
+            "Whirlpool",
+
+            /* MACs */
+            "CMAC(AES-128)",
+            "HMAC(SHA-256)",
+
+            /* pubkey */
+            "RSA",
+            "DH",
+            "ECDH",
+            "ECDSA",
+            "Ed25519",
+            "Curve25519",
+            "NEWHOPE",
+            "McEliece",
+            };
+         }
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Measures the speed of algorithms";
+         }
+
+      void go() override
+         {
+         std::chrono::milliseconds msec(get_arg_sz("msec"));
+         const std::string provider = get_arg("provider");
+         std::vector<std::string> ecc_groups = Botan::split_on(get_arg("ecc-groups"), ',');
+         const std::string format = get_arg("format");
+         const std::string clock_ratio = get_arg("cpu-clock-ratio");
+         m_clock_speed = get_arg_sz("cpu-clock-speed");
+
+         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
+
+         /*
+         * This argument is intended to be the ratio between the cycle counter
+         * and the actual machine cycles. It is extremely unlikely that there is
+         * any machine where the cycle counter increments faster than the actual
+         * clock.
+         */
+         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0)
+            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
+
+         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
+
+         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0)
+            {
+            error_output() << "The --cpu-clock-speed option is only intended to be used on "
+                              "platforms without access to a cycle counter.\n"
+                              "Expected incorrect results\n\n";
+            }
+
+         if(format == "table")
+            m_summary.reset(new Summary);
+         else if(format == "json")
+            m_json.reset(new JSON_Output);
+         else if(format != "default")
+            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+         if(ecc_groups.empty())
+            {
+            ecc_groups = { "secp256r1", "brainpool256r1",
+                           "secp384r1", "brainpool384r1",
+                           "secp521r1", "brainpool512r1" };
+            }
+         else if(ecc_groups.size() == 1 && ecc_groups[0] == "all")
+            {
+            auto all = Botan::EC_Group::known_named_groups();
+            ecc_groups.assign(all.begin(), all.end());
+            }
+#endif
+
+         std::vector<std::string> algos = get_arg_list("algos");
+
+         const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
+
+         Botan::CPUID::initialize();
+
+         for(std::string cpuid_to_clear : Botan::split_on(get_arg("clear-cpuid"), ','))
+            {
+            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
+            if(bits.empty())
+               {
+               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
+               }
+
+            for(auto bit : bits)
+               {
+               Botan::CPUID::clear_cpuid_bit(bit);
+               }
+            }
+
+         if(verbose() || m_summary)
+            {
+            output() << Botan::version_string() << "\n"
+                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
+            }
+
+         const bool using_defaults = (algos.empty());
+         if(using_defaults)
+            {
+            algos = default_benchmark_list();
+            }
+
+         for(auto algo : algos)
+            {
+            using namespace std::placeholders;
+
+            if(false)
+               {
+               // Since everything might be disabled, need a block to else if from
+               }
+#if defined(BOTAN_HAS_HASH)
+            else if(Botan::HashFunction::providers(algo).size() > 0)
+               {
+               bench_providers_of<Botan::HashFunction>(
+                  algo, provider, msec, buf_sizes,
+                  std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
+               }
+#endif
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+            else if(Botan::BlockCipher::providers(algo).size() > 0)
+               {
+               bench_providers_of<Botan::BlockCipher>(
+                  algo, provider, msec, buf_sizes,
+                  std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
+               }
+#endif
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+            else if(Botan::StreamCipher::providers(algo).size() > 0)
+               {
+               bench_providers_of<Botan::StreamCipher>(
+                  algo, provider, msec, buf_sizes,
+                  std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
+               }
+#endif
+#if defined(BOTAN_HAS_CIPHER_MODES)
+            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::ENCRYPTION, provider))
+               {
+               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::DECRYPTION, provider);
+               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
+               }
+#endif
+#if defined(BOTAN_HAS_MAC)
+            else if(Botan::MessageAuthenticationCode::providers(algo).size() > 0)
+               {
+               bench_providers_of<Botan::MessageAuthenticationCode>(
+                  algo, provider, msec, buf_sizes,
+                  std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
+               }
+#endif
+#if defined(BOTAN_HAS_RSA)
+            else if(algo == "RSA")
+               {
+               bench_rsa(provider, msec);
+               }
+            else if(algo == "RSA_keygen")
+               {
+               bench_rsa_keygen(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ECDSA)
+            else if(algo == "ECDSA")
+               {
+               bench_ecdsa(ecc_groups, provider, msec);
+               }
+            else if(algo == "ecdsa_recovery")
+               {
+               bench_ecdsa_recovery(ecc_groups, provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_SM2)
+            else if(algo == "SM2")
+               {
+               bench_sm2(ecc_groups, provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ECKCDSA)
+            else if(algo == "ECKCDSA")
+               {
+               bench_eckcdsa(ecc_groups, provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+            else if(algo == "GOST-34.10")
+               {
+               bench_gost_3410(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ECGDSA)
+            else if(algo == "ECGDSA")
+               {
+               bench_ecgdsa(ecc_groups, provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ED25519)
+            else if(algo == "Ed25519")
+               {
+               bench_ed25519(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+            else if(algo == "DH")
+               {
+               bench_dh(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_DSA)
+            else if(algo == "DSA")
+               {
+               bench_dsa(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ELGAMAL)
+            else if(algo == "ElGamal")
+               {
+               bench_elgamal(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ECDH)
+            else if(algo == "ECDH")
+               {
+               bench_ecdh(ecc_groups, provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_CURVE_25519)
+            else if(algo == "Curve25519")
+               {
+               bench_curve25519(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_MCELIECE)
+            else if(algo == "McEliece")
+               {
+               bench_mceliece(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+            else if(algo == "XMSS")
+               {
+               bench_xmss(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA_RNG)
+            else if(algo == "NEWHOPE")
+               {
+               bench_newhope(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_SCRYPT)
+            else if(algo == "scrypt")
+               {
+               bench_scrypt(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_ARGON2)
+            else if(algo == "argon2")
+               {
+               bench_argon2(provider, msec);
+               }
+#endif
+#if defined(BOTAN_HAS_BCRYPT)
+            else if(algo == "bcrypt")
+               {
+               bench_bcrypt();
+               }
+#endif
+#if defined(BOTAN_HAS_PASSHASH9)
+            else if(algo == "passhash9")
+               {
+               bench_passhash9();
+               }
+#endif
+#if defined(BOTAN_HAS_POLY_DBL)
+            else if(algo == "poly_dbl")
+               {
+               bench_poly_dbl(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+            else if(algo == "modexp")
+               {
+               bench_modexp(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_BIGINT)
+            else if(algo == "mp_mul")
+               {
+               bench_mp_mul(msec);
+               }
+            else if(algo == "mp_div")
+               {
+               bench_mp_div(msec);
+               }
+            else if(algo == "mp_div10")
+               {
+               bench_mp_div10(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+            else if(algo == "primality_test")
+               {
+               bench_primality_tests(msec);
+               }
+            else if(algo == "random_prime")
+               {
+               bench_random_prime(msec);
+               }
+            else if(algo == "inverse_mod")
+               {
+               bench_inverse_mod(msec);
+               }
+            else if(algo == "bn_redc")
+               {
+               bench_bn_redc(msec);
+               }
+            else if(algo == "nistp_redc")
+               {
+               bench_nistp_redc(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+            else if(algo == "fpe_fe1")
+               {
+               bench_fpe_fe1(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+            else if(algo == "rfc3394")
+               {
+               bench_rfc3394(msec);
+               }
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+            else if(algo == "ecc_mult")
+               {
+               bench_ecc_mult(ecc_groups, msec);
+               }
+            else if(algo == "ecc_ops")
+               {
+               bench_ecc_ops(ecc_groups, msec);
+               }
+            else if(algo == "ecc_init")
+               {
+               bench_ecc_init(ecc_groups, msec);
+               }
+            else if(algo == "os2ecp")
+               {
+               bench_os2ecp(ecc_groups, msec);
+               }
+#endif
+            else if(algo == "RNG")
+               {
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+               Botan::AutoSeeded_RNG auto_rng;
+               bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+               bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+               if(Botan::Processor_RNG::available())
+                  {
+                  Botan::Processor_RNG hwrng;
+                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
+                  }
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+               for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
+                  {
+                  Botan::HMAC_DRBG hmac_drbg(hash);
+                  bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
+                  }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+               // Provide a dummy seed
+               Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
+               bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
+#endif
+
+               }
+            else if(algo == "entropy")
+               {
+               bench_entropy_sources(msec);
+               }
+            else
+               {
+               if(verbose() || !using_defaults)
+                  {
+                  error_output() << "Unknown algorithm '" << algo << "'\n";
+                  }
+               }
+            }
+
+         if(m_json)
+            {
+            output() << m_json->print();
+            }
+         if(m_summary)
+            {
+            output() << m_summary->print() << "\n";
+            }
+
+         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0)
+            {
+            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
+            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
+            const double MHz = Hz / 1000000;
+            output() << "\nEstimated clock speed " << MHz << " MHz\n";
+            }
+         }
+
+   private:
+
+      size_t m_clock_speed = 0;
+      double m_clock_cycle_ratio = 0.0;
+      uint64_t m_cycles_consumed = 0;
+      uint64_t m_ns_taken = 0;
+      std::unique_ptr<Summary> m_summary;
+      std::unique_ptr<JSON_Output> m_json;
+
+      void record_result(const std::unique_ptr<Timer>& t)
+         {
+         m_ns_taken += t->value();
+         m_cycles_consumed += t->cycles_consumed();
+         if(m_json)
+            {
+            m_json->add(*t);
+            }
+         else
+            {
+            output() << t->to_string() << std::flush;
+            if(m_summary)
+               m_summary->add(*t);
+            }
+         }
+
+      template<typename T>
+      using bench_fn = std::function<void (T&,
+                                           std::string,
+                                           std::chrono::milliseconds,
+                                           const std::vector<size_t>&)>;
+
+      template<typename T>
+      void bench_providers_of(const std::string& algo,
+                              const std::string& provider, /* user request, if any */
+                              const std::chrono::milliseconds runtime,
+                              const std::vector<size_t>& buf_sizes,
+                              bench_fn<T> bench_one)
+         {
+         for(auto const& prov : T::providers(algo))
+            {
+            if(provider.empty() || provider == prov)
+               {
+               auto p = T::create(algo, prov);
+
+               if(p)
+                  {
+                  bench_one(*p, prov, runtime, buf_sizes);
+                  }
+               }
+            }
+         }
+
+      std::unique_ptr<Timer> make_timer(const std::string& name,
+                                        uint64_t event_mult = 1,
+                                        const std::string& what = "",
+                                        const std::string& provider = "",
+                                        size_t buf_size = 0)
+         {
+         return std::unique_ptr<Timer>(
+            new Timer(name, provider, what, event_mult, buf_size,
+                      m_clock_cycle_ratio, m_clock_speed));
+         }
+
+      std::unique_ptr<Timer> make_timer(const std::string& algo,
+                                        const std::string& provider,
+                                        const std::string& what)
+         {
+         return make_timer(algo, 1, what, provider, 0);
+         }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+      void bench_block_cipher(Botan::BlockCipher& cipher,
+                              const std::string& provider,
+                              std::chrono::milliseconds runtime,
+                              const std::vector<size_t>& buf_sizes)
+         {
+         std::unique_ptr<Timer> ks_timer = make_timer(cipher.name(), provider, "key schedule");
+
+         const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+         ks_timer->run([&]() { cipher.set_key(key); });
+
+         const size_t bs = cipher.block_size();
+         std::set<size_t> buf_sizes_in_blocks;
+         for(size_t buf_size : buf_sizes)
+            {
+            if(buf_size % bs == 0)
+               buf_sizes_in_blocks.insert(buf_size);
+            else
+               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
+            }
+
+         for(size_t buf_size : buf_sizes_in_blocks)
+            {
+            std::vector<uint8_t> buffer(buf_size);
+            const size_t blocks = buf_size / bs;
+
+            std::unique_ptr<Timer> encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
+            std::unique_ptr<Timer> decrypt_timer = make_timer(cipher.name(), buffer.size(), "decrypt", provider, buf_size);
+
+            encrypt_timer->run_until_elapsed(runtime, [&]() { cipher.encrypt_n(&buffer[0], &buffer[0], blocks); });
+            record_result(encrypt_timer);
+
+            decrypt_timer->run_until_elapsed(runtime, [&]() { cipher.decrypt_n(&buffer[0], &buffer[0], blocks); });
+            record_result(decrypt_timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+      void bench_stream_cipher(
+         Botan::StreamCipher& cipher,
+         const std::string& provider,
+         const std::chrono::milliseconds runtime,
+         const std::vector<size_t>& buf_sizes)
+         {
+         for(auto buf_size : buf_sizes)
+            {
+            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+            std::unique_ptr<Timer> encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
+
+            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+            cipher.set_key(key);
+
+            if(cipher.valid_iv_length(12))
+               {
+               const Botan::InitializationVector iv(rng(), 12);
+               cipher.set_iv(iv.begin(), iv.size());
+               }
+
+            while(encrypt_timer->under(runtime))
+               {
+               encrypt_timer->run([&]() { cipher.encipher(buffer); });
+               }
+
+            record_result(encrypt_timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+      void bench_hash(
+         Botan::HashFunction& hash,
+         const std::string& provider,
+         const std::chrono::milliseconds runtime,
+         const std::vector<size_t>& buf_sizes)
+         {
+         std::vector<uint8_t> output(hash.output_length());
+
+         for(auto buf_size : buf_sizes)
+            {
+            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+            std::unique_ptr<Timer> timer = make_timer(hash.name(), buffer.size(), "hash", provider, buf_size);
+            timer->run_until_elapsed(runtime, [&]() { hash.update(buffer); hash.final(output.data()); });
+            record_result(timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+      void bench_mac(
+         Botan::MessageAuthenticationCode& mac,
+         const std::string& provider,
+         const std::chrono::milliseconds runtime,
+         const std::vector<size_t>& buf_sizes)
+         {
+         std::vector<uint8_t> output(mac.output_length());
+
+         for(auto buf_size : buf_sizes)
+            {
+            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
+            mac.set_key(key);
+            mac.start(nullptr, 0);
+
+            std::unique_ptr<Timer> timer = make_timer(mac.name(), buffer.size(), "mac", provider, buf_size);
+            timer->run_until_elapsed(runtime, [&]() { mac.update(buffer); });
+            timer->run([&]() { mac.final(output.data()); });
+            record_result(timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+      void bench_cipher_mode(
+         Botan::Cipher_Mode& enc,
+         Botan::Cipher_Mode& dec,
+         const std::chrono::milliseconds runtime,
+         const std::vector<size_t>& buf_sizes)
+         {
+         std::unique_ptr<Timer> ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
+
+         const Botan::SymmetricKey key(rng(), enc.key_spec().maximum_keylength());
+
+         ks_timer->run([&]() { enc.set_key(key); });
+         ks_timer->run([&]() { dec.set_key(key); });
+
+         record_result(ks_timer);
+
+         for(auto buf_size : buf_sizes)
+            {
+            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+            std::unique_ptr<Timer> encrypt_timer = make_timer(enc.name(), buffer.size(), "encrypt", enc.provider(), buf_size);
+            std::unique_ptr<Timer> decrypt_timer = make_timer(dec.name(), buffer.size(), "decrypt", dec.provider(), buf_size);
+
+            Botan::secure_vector<uint8_t> iv = rng().random_vec(enc.default_nonce_length());
+
+            if(buf_size >= enc.minimum_final_size())
+               {
+               while(encrypt_timer->under(runtime) && decrypt_timer->under(runtime))
+                  {
+                  // Must run in this order, or AEADs will reject the ciphertext
+                  encrypt_timer->run([&]() { enc.start(iv); enc.finish(buffer); });
+
+                  decrypt_timer->run([&]() { dec.start(iv); dec.finish(buffer); });
+
+                  if(iv.size() > 0)
+                     {
+                     iv[iv.size()-1] += 1;
+                     }
+                  }
+               }
+
+            record_result(encrypt_timer);
+            record_result(decrypt_timer);
+            }
+         }
+#endif
+
+      void bench_rng(
+         Botan::RandomNumberGenerator& rng,
+         const std::string& rng_name,
+         const std::chrono::milliseconds runtime,
+         const std::vector<size_t>& buf_sizes)
+         {
+         for(auto buf_size : buf_sizes)
+            {
+            Botan::secure_vector<uint8_t> buffer(buf_size);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+            rng.reseed_from_rng(Botan::system_rng(), 256);
+#endif
+
+            std::unique_ptr<Timer> timer = make_timer(rng_name, buffer.size(), "generate", "", buf_size);
+            timer->run_until_elapsed(runtime, [&]() { rng.randomize(buffer.data(), buffer.size()); });
+            record_result(timer);
+            }
+         }
+
+      void bench_entropy_sources(const std::chrono::milliseconds)
+         {
+         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
+
+         for(auto src : srcs.enabled_sources())
+            {
+            size_t entropy_bits = 0;
+            Botan_Tests::SeedCapturing_RNG rng;
+
+            std::unique_ptr<Timer> timer = make_timer(src, "", "bytes");
+            timer->run([&]() { entropy_bits = srcs.poll_just(rng, src); });
+
+            size_t compressed_size = 0;
+
+#if defined(BOTAN_HAS_ZLIB)
+            std::unique_ptr<Botan::Compression_Algorithm> comp(Botan::make_compressor("zlib"));
+
+            if(comp)
+               {
+               Botan::secure_vector<uint8_t> compressed;
+               compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
+               comp->start(9);
+               comp->finish(compressed);
+
+               compressed_size = compressed.size();
+               }
+#endif
+
+            std::ostringstream msg;
+
+            msg << "Entropy source " << src << " output " << rng.seed_material().size() << " bytes"
+                << " estimated entropy " << entropy_bits << " in " << timer->milliseconds() << " ms";
+
+            if(compressed_size > 0)
+               {
+               msg << " output compressed to " << compressed_size << " bytes";
+               }
+
+            msg << " total samples " << rng.samples() << "\n";
+
+            timer->set_custom_msg(msg.str());
+
+            record_result(timer);
+            }
+         }
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+      void bench_ecc_ops(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+         {
+         for(std::string group_name : groups)
+            {
+            const Botan::EC_Group ec_group(group_name);
+
+            std::unique_ptr<Timer> add_timer = make_timer(group_name + " add");
+            std::unique_ptr<Timer> addf_timer = make_timer(group_name + " addf");
+            std::unique_ptr<Timer> dbl_timer = make_timer(group_name + " dbl");
+
+            const Botan::PointGFp& base_point = ec_group.get_base_point();
+            Botan::PointGFp non_affine_pt = ec_group.get_base_point() * 1776; // create a non-affine point
+            Botan::PointGFp pt = ec_group.get_base_point();
+
+            std::vector<Botan::BigInt> ws(Botan::PointGFp::WORKSPACE_SIZE);
+
+            while(add_timer->under(runtime) && addf_timer->under(runtime) && dbl_timer->under(runtime))
+               {
+               dbl_timer->run([&]() { pt.mult2(ws); });
+               add_timer->run([&]() { pt.add(non_affine_pt, ws); });
+               addf_timer->run([&]() { pt.add_affine(base_point, ws); });
+               }
+
+            record_result(dbl_timer);
+            record_result(add_timer);
+            record_result(addf_timer);
+            }
+         }
+
+      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+         {
+         for(std::string group_name : groups)
+            {
+            std::unique_ptr<Timer> timer = make_timer(group_name + " initialization");
+
+            while(timer->under(runtime))
+               {
+               Botan::EC_Group::clear_registered_curve_data();
+               timer->run([&]() { Botan::EC_Group group(group_name); });
+               }
+
+            record_result(timer);
+            }
+         }
+
+      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+         {
+         for(std::string group_name : groups)
+            {
+            const Botan::EC_Group ec_group(group_name);
+
+            std::unique_ptr<Timer> mult_timer = make_timer(group_name + " Montgomery ladder");
+            std::unique_ptr<Timer> blinded_mult_timer = make_timer(group_name + " blinded comb");
+            std::unique_ptr<Timer> blinded_var_mult_timer = make_timer(group_name + " blinded window");
+
+            const Botan::PointGFp& base_point = ec_group.get_base_point();
+
+            std::vector<Botan::BigInt> ws;
+
+            while(mult_timer->under(runtime) &&
+                  blinded_mult_timer->under(runtime) &&
+                  blinded_var_mult_timer->under(runtime))
+               {
+               const Botan::BigInt scalar(rng(), ec_group.get_p_bits());
+
+               const Botan::PointGFp r1 = mult_timer->run([&]() { return base_point * scalar; });
+
+               const Botan::PointGFp r2 = blinded_mult_timer->run(
+                  [&]() { return ec_group.blinded_base_point_multiply(scalar, rng(), ws); });
+
+               const Botan::PointGFp r3 = blinded_var_mult_timer->run(
+                  [&]() { return ec_group.blinded_var_point_multiply(base_point, scalar, rng(), ws); });
+
+               BOTAN_ASSERT_EQUAL(r1, r2, "Same point computed by Montgomery and comb");
+               BOTAN_ASSERT_EQUAL(r1, r3, "Same point computed by Montgomery and window");
+               }
+
+            record_result(mult_timer);
+            record_result(blinded_mult_timer);
+            record_result(blinded_var_mult_timer);
+            }
+         }
+
+      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+         {
+         std::unique_ptr<Timer> uncmp_timer = make_timer("OS2ECP uncompressed");
+         std::unique_ptr<Timer> cmp_timer = make_timer("OS2ECP compressed");
+
+         for(std::string group_name : groups)
+            {
+            const Botan::EC_Group ec_group(group_name);
+
+            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime))
+               {
+               const Botan::BigInt k(rng(), 256);
+               const Botan::PointGFp p = ec_group.get_base_point() * k;
+               const std::vector<uint8_t> os_cmp = p.encode(Botan::PointGFp::COMPRESSED);
+               const std::vector<uint8_t> os_uncmp = p.encode(Botan::PointGFp::UNCOMPRESSED);
+
+               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
+               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
+               }
+
+            record_result(uncmp_timer);
+            record_result(cmp_timer);
+            }
+         }
+
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+
+      void bench_fpe_fe1(const std::chrono::milliseconds runtime)
+         {
+         const Botan::BigInt n = 1000000000000000;
+
+         std::unique_ptr<Timer> enc_timer = make_timer("FPE_FE1 encrypt");
+         std::unique_ptr<Timer> dec_timer = make_timer("FPE_FE1 decrypt");
+
+         const Botan::SymmetricKey key(rng(), 32);
+         const std::vector<uint8_t> tweak(8); // 8 zeros
+
+         Botan::BigInt x = 1;
+
+         Botan::FPE_FE1 fpe_fe1(n);
+         fpe_fe1.set_key(key);
+
+         while(enc_timer->under(runtime))
+            {
+            enc_timer->start();
+            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
+            enc_timer->stop();
+            }
+
+         for(size_t i = 0; i != enc_timer->events(); ++i)
+            {
+            dec_timer->start();
+            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
+            dec_timer->stop();
+            }
+
+         BOTAN_ASSERT(x == 1, "FPE works");
+
+         record_result(enc_timer);
+         record_result(dec_timer);
+         }
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+
+      void bench_rfc3394(const std::chrono::milliseconds runtime)
+         {
+         std::unique_ptr<Timer> wrap_timer = make_timer("RFC3394 AES-256 key wrap");
+         std::unique_ptr<Timer> unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
+
+         const Botan::SymmetricKey kek(rng(), 32);
+         Botan::secure_vector<uint8_t> key(64, 0);
+
+         while(wrap_timer->under(runtime))
+            {
+            wrap_timer->start();
+            key = Botan::rfc3394_keywrap(key, kek);
+            wrap_timer->stop();
+
+            unwrap_timer->start();
+            key = Botan::rfc3394_keyunwrap(key, kek);
+            unwrap_timer->stop();
+
+            key[0] += 1;
+            }
+
+         record_result(wrap_timer);
+         record_result(unwrap_timer);
+         }
+#endif
+
+#if defined(BOTAN_HAS_BIGINT)
+
+      void bench_mp_mul(const std::chrono::milliseconds runtime)
+         {
+         std::chrono::milliseconds runtime_per_size = runtime;
+         for(size_t bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
+            {
+            std::unique_ptr<Timer> mul_timer = make_timer("BigInt mul " + std::to_string(bits));
+            std::unique_ptr<Timer> sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
+
+            const Botan::BigInt y(rng(), bits);
+            Botan::secure_vector<Botan::word> ws;
+
+            while(mul_timer->under(runtime_per_size))
+               {
+               Botan::BigInt x(rng(), bits);
+
+               sqr_timer->start();
+               x.square(ws);
+               sqr_timer->stop();
+
+               x.mask_bits(bits);
+
+               mul_timer->start();
+               x.mul(y, ws);
+               mul_timer->stop();
+               }
+
+            record_result(mul_timer);
+            record_result(sqr_timer);
+            }
+
+         }
+
+      void bench_mp_div(const std::chrono::milliseconds runtime)
+         {
+         std::chrono::milliseconds runtime_per_size = runtime;
+
+         for(size_t n_bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
+            {
+            const size_t q_bits = n_bits / 2;
+            const std::string bit_descr = std::to_string(n_bits) + "/" + std::to_string(q_bits);
+
+            std::unique_ptr<Timer> div_timer = make_timer("BigInt div " + bit_descr);
+            std::unique_ptr<Timer> ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
+
+            Botan::BigInt y;
+            Botan::BigInt x;
+            Botan::secure_vector<Botan::word> ws;
+
+            Botan::BigInt q1, r1, q2, r2;
+
+            while(ct_div_timer->under(runtime_per_size))
+               {
+               x.randomize(rng(), n_bits);
+               y.randomize(rng(), q_bits);
+
+               div_timer->start();
+               Botan::divide(x, y, q1, r1);
+               div_timer->stop();
+
+               ct_div_timer->start();
+               Botan::ct_divide(x, y, q2, r2);
+               ct_div_timer->stop();
+
+               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
+               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
+               }
+
+            record_result(div_timer);
+            record_result(ct_div_timer);
+            }
+         }
+
+      void bench_mp_div10(const std::chrono::milliseconds runtime)
+         {
+         std::chrono::milliseconds runtime_per_size = runtime;
+
+         for(size_t n_bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
+            {
+            const std::string bit_descr = std::to_string(n_bits) + "/10";
+
+            std::unique_ptr<Timer> div_timer = make_timer("BigInt div " + bit_descr);
+            std::unique_ptr<Timer> ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
+
+            Botan::BigInt x;
+            Botan::secure_vector<Botan::word> ws;
+
+            const Botan::BigInt ten(10);
+            Botan::BigInt q1, r1, q2;
+            uint8_t r2;
+
+            while(ct_div_timer->under(runtime_per_size))
+               {
+               x.randomize(rng(), n_bits);
+
+               div_timer->start();
+               Botan::divide(x, ten, q1, r1);
+               div_timer->stop();
+
+               ct_div_timer->start();
+               Botan::ct_divide_u8(x, 10, q2, r2);
+               ct_div_timer->stop();
+
+               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
+               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
+               }
+
+            record_result(div_timer);
+            record_result(ct_div_timer);
+            }
+         }
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+      void bench_modexp(const std::chrono::milliseconds runtime)
+         {
+         for(size_t group_bits : { 1024, 1536, 2048, 3072, 4096 })
+            {
+            const std::string group_bits_str = std::to_string(group_bits);
+            const Botan::DL_Group group("modp/srp/" + group_bits_str);
+
+            const size_t e_bits = Botan::dl_exponent_size(group_bits);
+            const size_t f_bits = group_bits - 1;
+
+            const Botan::BigInt random_e(rng(), e_bits);
+            const Botan::BigInt random_f(rng(), f_bits);
+
+            std::unique_ptr<Timer> e_timer = make_timer(group_bits_str + " short exponent");
+            std::unique_ptr<Timer> f_timer = make_timer(group_bits_str + "  full exponent");
+
+            while(f_timer->under(runtime))
+               {
+               e_timer->run([&]() { group.power_g_p(random_e); });
+               f_timer->run([&]() { group.power_g_p(random_f); });
+               }
+
+            record_result(e_timer);
+            record_result(f_timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+      void bench_nistp_redc(const std::chrono::milliseconds runtime)
+         {
+         Botan::secure_vector<Botan::word> ws;
+
+         std::unique_ptr<Timer> p192_timer = make_timer("P-192 redc");
+         Botan::BigInt r192(rng(), 192*2 - 1);
+         while(p192_timer->under(runtime))
+            {
+            Botan::BigInt r = r192;
+            p192_timer->run([&]() { Botan::redc_p192(r, ws); });
+            r192 += 1;
+            }
+         record_result(p192_timer);
+
+         std::unique_ptr<Timer> p224_timer = make_timer("P-224 redc");
+         Botan::BigInt r224(rng(), 224*2 - 1);
+         while(p224_timer->under(runtime))
+            {
+            Botan::BigInt r = r224;
+            p224_timer->run([&]() { Botan::redc_p224(r, ws); });
+            r224 += 1;
+            }
+         record_result(p224_timer);
+
+         std::unique_ptr<Timer> p256_timer = make_timer("P-256 redc");
+         Botan::BigInt r256(rng(), 256*2 - 1);
+         while(p256_timer->under(runtime))
+            {
+            Botan::BigInt r = r256;
+            p256_timer->run([&]() { Botan::redc_p256(r, ws); });
+            r256 += 1;
+            }
+         record_result(p256_timer);
+
+         std::unique_ptr<Timer> p384_timer = make_timer("P-384 redc");
+         Botan::BigInt r384(rng(), 384*2 - 1);
+         while(p384_timer->under(runtime))
+            {
+            Botan::BigInt r = r384;
+            p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
+            r384 += 1;
+            }
+         record_result(p384_timer);
+
+         std::unique_ptr<Timer> p521_timer = make_timer("P-521 redc");
+         Botan::BigInt r521(rng(), 521*2 - 1);
+         while(p521_timer->under(runtime))
+            {
+            Botan::BigInt r = r521;
+            p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
+            r521 += 1;
+            }
+         record_result(p521_timer);
+         }
+
+      void bench_bn_redc(const std::chrono::milliseconds runtime)
+         {
+         for(size_t bitsize : { 512, 1024, 2048, 4096 })
+            {
+            Botan::BigInt p(rng(), bitsize);
+
+            std::string bit_str = std::to_string(bitsize);
+            std::unique_ptr<Timer> barrett_timer = make_timer("Barrett-" + bit_str);
+            std::unique_ptr<Timer> schoolbook_timer = make_timer("Schoolbook-" + bit_str);
+
+            Botan::Modular_Reducer mod_p(p);
+
+            while(schoolbook_timer->under(runtime))
+               {
+               const Botan::BigInt x(rng(), p.bits() * 2 - 2);
+
+               const Botan::BigInt r1 = barrett_timer->run(
+                  [&] { return mod_p.reduce(x); });
+               const Botan::BigInt r2 = schoolbook_timer->run(
+                  [&] { return x % p; });
+
+               BOTAN_ASSERT(r1 == r2, "Computed different results");
+               }
+
+            record_result(barrett_timer);
+            record_result(schoolbook_timer);
+            }
+         }
+
+      void bench_inverse_mod(const std::chrono::milliseconds runtime)
+         {
+         for(size_t bits : { 256, 384, 512, 1024, 2048 })
+            {
+            const std::string bit_str = std::to_string(bits);
+
+            std::unique_ptr<Timer> timer = make_timer("inverse_mod-" + bit_str);
+
+            while(timer->under(runtime))
+               {
+               const Botan::BigInt x(rng(), bits - 1);
+               Botan::BigInt mod(rng(), bits);
+
+               const Botan::BigInt x_inv = timer->run(
+                  [&] { return Botan::inverse_mod(x, mod); });
+
+               if(x_inv == 0)
+                  {
+                  const Botan::BigInt g = gcd(x, mod);
+                  BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
+                  }
+               else
+                  {
+                  const Botan::BigInt check = (x_inv*x) % mod;
+                  BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
+                  }
+               }
+
+            record_result(timer);
+            }
+         }
+
+      void bench_primality_tests(const std::chrono::milliseconds runtime)
+         {
+         for(size_t bits : { 256, 512, 1024 })
+            {
+            std::unique_ptr<Timer> mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
+            std::unique_ptr<Timer> bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
+            std::unique_ptr<Timer> lucas_timer = make_timer("Lucas-" + std::to_string(bits));
+
+            Botan::BigInt n = Botan::random_prime(rng(), bits);
+
+            while(lucas_timer->under(runtime))
+               {
+               Botan::Modular_Reducer mod_n(n);
+
+               mr_timer->run([&]() {
+                  return Botan::is_miller_rabin_probable_prime(n, mod_n, rng(), 2); });
+
+               bpsw_timer->run([&]() {
+                  return Botan::is_bailie_psw_probable_prime(n, mod_n); });
+
+               lucas_timer->run([&]() {
+                  return Botan::is_lucas_probable_prime(n, mod_n); });
+
+               n += 2;
+               }
+
+            record_result(mr_timer);
+            record_result(bpsw_timer);
+            record_result(lucas_timer);
+            }
+         }
+
+      void bench_random_prime(const std::chrono::milliseconds runtime)
+         {
+         const size_t coprime = 65537; // simulates RSA key gen
+
+         for(size_t bits : { 256, 384, 512, 768, 1024, 1536 })
+            {
+            std::unique_ptr<Timer> genprime_timer = make_timer("random_prime " + std::to_string(bits));
+            std::unique_ptr<Timer> gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
+            std::unique_ptr<Timer> is_prime_timer = make_timer("is_prime " + std::to_string(bits));
+
+            while(gensafe_timer->under(runtime))
+               {
+               const Botan::BigInt p = genprime_timer->run([&]
+                  {
+                  return Botan::random_prime(rng(), bits, coprime);
+                  });
+
+               if(!is_prime_timer->run([&] { return Botan::is_prime(p, rng(), 64, true); }))
+                  {
+                  error_output() << "Generated prime " << p << " which failed a primality test";
+                  }
+
+               const Botan::BigInt sg = gensafe_timer->run([&]
+                  {
+                  return Botan::random_safe_prime(rng(), bits);
+                  });
+
+               if(!is_prime_timer->run([&] { return Botan::is_prime(sg, rng(), 64, true); }))
+                  {
+                  error_output() << "Generated safe prime " << sg << " which failed a primality test";
+                  }
+
+               if(!is_prime_timer->run([&] { return Botan::is_prime(sg / 2, rng(), 64, true); }))
+                  {
+                  error_output() << "Generated prime " << sg/2 << " which failed a primality test";
+                  }
+
+               // Now test p+2, p+4, ... which may or may not be prime
+               for(size_t i = 2; i <= 64; i += 2)
+                  {
+                  is_prime_timer->run([&]() { Botan::is_prime(p + i, rng(), 64, true); });
+                  }
+               }
+
+            record_result(genprime_timer);
+            record_result(gensafe_timer);
+            record_result(is_prime_timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+      void bench_pk_enc(
+         const Botan::Private_Key& key,
+         const std::string& nm,
+         const std::string& provider,
+         const std::string& padding,
+         std::chrono::milliseconds msec)
+         {
+         std::vector<uint8_t> plaintext, ciphertext;
+
+         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
+         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
+
+         std::unique_ptr<Timer> enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
+         std::unique_ptr<Timer> dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
+
+         while(enc_timer->under(msec) || dec_timer->under(msec))
+            {
+            // Generate a new random ciphertext to decrypt
+            if(ciphertext.empty() || enc_timer->under(msec))
+               {
+               rng().random_vec(plaintext, enc.maximum_input_size());
+               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
+               }
+
+            if(dec_timer->under(msec))
+               {
+               const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); });
+
+               if(!(dec_pt == plaintext)) // sanity check
+                  {
+                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
+                  }
+               }
+            }
+
+         record_result(enc_timer);
+         record_result(dec_timer);
+         }
+
+      void bench_pk_ka(const std::string& algo,
+                       const std::string& nm,
+                       const std::string& params,
+                       const std::string& provider,
+                       std::chrono::milliseconds msec)
+         {
+         const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice
+
+         std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+         std::unique_ptr<Botan::Private_Key> key1(keygen_timer->run([&]
+            {
+            return Botan::create_private_key(algo, rng(), params);
+            }));
+         std::unique_ptr<Botan::Private_Key> key2(keygen_timer->run([&]
+            {
+            return Botan::create_private_key(algo, rng(), params);
+            }));
+
+         record_result(keygen_timer);
+
+         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
+         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
+
+         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
+         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
+
+         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
+         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
+
+         std::unique_ptr<Timer> ka_timer = make_timer(nm, provider, "key agreements");
+
+         while(ka_timer->under(msec))
+            {
+            Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); });
+            Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); });
+
+            if(symkey1 != symkey2)
+               {
+               error_output() << "Key agreement mismatch in PK bench\n";
+               }
+            }
+
+         record_result(ka_timer);
+         }
+
+      void bench_pk_kem(const Botan::Private_Key& key,
+                        const std::string& nm,
+                        const std::string& provider,
+                        const std::string& kdf,
+                        std::chrono::milliseconds msec)
+         {
+         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
+         Botan::PK_KEM_Encryptor enc(key, rng(), kdf, provider);
+
+         std::unique_ptr<Timer> kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
+         std::unique_ptr<Timer> kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
+
+         while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec))
+            {
+            Botan::secure_vector<uint8_t> encap_key, enc_shared_key;
+            Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
+
+            kem_enc_timer->start();
+            enc.encrypt(encap_key, enc_shared_key, 64, rng(), salt);
+            kem_enc_timer->stop();
+
+            kem_dec_timer->start();
+            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(encap_key, 64, salt);
+            kem_dec_timer->stop();
+
+            if(enc_shared_key != dec_shared_key)
+               {
+               error_output() << "KEM mismatch in PK bench\n";
+               }
+            }
+
+         record_result(kem_enc_timer);
+         record_result(kem_dec_timer);
+         }
+
+      void bench_pk_sig_ecc(const std::string& algo,
+                            const std::string& emsa,
+                            const std::string& provider,
+                            const std::vector<std::string>& params,
+                            std::chrono::milliseconds msec)
+         {
+         for(std::string grp : params)
+            {
+            const std::string nm = grp.empty() ? algo : (algo + "-" + grp);
+
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return Botan::create_private_key(algo, rng(), grp);
+               }));
+
+            record_result(keygen_timer);
+            bench_pk_sig(*key, nm, provider, emsa, msec);
+            }
+         }
+
+      size_t bench_pk_sig(const Botan::Private_Key& key,
+                          const std::string& nm,
+                          const std::string& provider,
+                          const std::string& padding,
+                          std::chrono::milliseconds msec)
+         {
+         std::vector<uint8_t> message, signature, bad_signature;
+
+         Botan::PK_Signer   sig(key, rng(), padding, Botan::IEEE_1363, provider);
+         Botan::PK_Verifier ver(key, padding, Botan::IEEE_1363, provider);
+
+         std::unique_ptr<Timer> sig_timer = make_timer(nm + " " + padding, provider, "sign");
+         std::unique_ptr<Timer> ver_timer = make_timer(nm + " " + padding, provider, "verify");
+
+         size_t invalid_sigs = 0;
+
+         while(ver_timer->under(msec) || sig_timer->under(msec))
+            {
+            if(signature.empty() || sig_timer->under(msec))
+               {
+               /*
+               Length here is kind of arbitrary, but 48 bytes fits into a single
+               hash block so minimizes hashing overhead versus the PK op itself.
+               */
+               rng().random_vec(message, 48);
+
+               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
+
+               bad_signature = signature;
+               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
+               }
+
+            if(ver_timer->under(msec))
+               {
+               const bool verified = ver_timer->run([&]
+                  {
+                  return ver.verify_message(message, signature);
+                  });
+
+               if(!verified)
+                  {
+                  invalid_sigs += 1;
+                  }
+
+               const bool verified_bad = ver_timer->run([&]
+                  {
+                  return ver.verify_message(message, bad_signature);
+                  });
+
+               if(verified_bad)
+                  {
+                  error_output() << "Bad signature accepted in PK signature bench\n";
+                  }
+               }
+            }
+
+         if(invalid_sigs > 0)
+            error_output() << invalid_sigs << " generated signatures rejected in PK signature bench\n";
+
+         const size_t events = static_cast<size_t>(std::min(sig_timer->events(), ver_timer->events()));
+
+         record_result(sig_timer);
+         record_result(ver_timer);
+
+         return events;
+         }
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+      void bench_rsa_keygen(const std::string& provider,
+                            std::chrono::milliseconds msec)
+         {
+         for(size_t keylen : { 1024, 2048, 3072, 4096 })
+            {
+            const std::string nm = "RSA-" + std::to_string(keylen);
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            while(keygen_timer->under(msec))
+               {
+               std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&] {
+                  return Botan::create_private_key("RSA", rng(), std::to_string(keylen));
+                  }));
+
+               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
+               }
+
+            record_result(keygen_timer);
+            }
+         }
+
+      void bench_rsa(const std::string& provider,
+                     std::chrono::milliseconds msec)
+         {
+         for(size_t keylen : { 1024, 2048, 3072, 4096 })
+            {
+            const std::string nm = "RSA-" + std::to_string(keylen);
+
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return Botan::create_private_key("RSA", rng(), std::to_string(keylen));
+               }));
+
+            record_result(keygen_timer);
+
+            // Using PKCS #1 padding so OpenSSL provider can play along
+            bench_pk_sig(*key, nm, provider, "EMSA-PKCS1-v1_5(SHA-256)", msec);
+
+            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
+            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
+            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+      void bench_ecdsa(const std::vector<std::string>& groups,
+                       const std::string& provider,
+                       std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("ECDSA", "EMSA1(SHA-256)", provider, groups, msec);
+         }
+
+      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
+                                const std::string&,
+                                std::chrono::milliseconds msec)
+         {
+         for(std::string group_name : groups)
+            {
+            Botan::EC_Group group(group_name);
+            std::unique_ptr<Timer> recovery_timer = make_timer("ECDSA recovery " + group_name);
+
+            while(recovery_timer->under(msec))
+               {
+               Botan::ECDSA_PrivateKey key(rng(), group);
+
+               std::vector<uint8_t> message(group.get_order_bits() / 8);
+               rng().randomize(message.data(), message.size());
+
+               Botan::PK_Signer signer(key, rng(), "Raw");
+               signer.update(message);
+               std::vector<uint8_t> signature = signer.signature(rng());
+
+               Botan::PK_Verifier verifier(key, "Raw", Botan::IEEE_1363, "base");
+               verifier.update(message);
+               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
+
+               Botan::BigInt r(signature.data(), signature.size()/2);
+               Botan::BigInt s(signature.data() + signature.size()/2, signature.size()/2);
+
+               const uint8_t v = key.recovery_param(message, r, s);
+
+               recovery_timer->run([&]() {
+                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
+                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
+                  });
+               }
+
+            record_result(recovery_timer);
+            }
+
+         }
+
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+      void bench_eckcdsa(const std::vector<std::string>& groups,
+                         const std::string& provider,
+                         std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("ECKCDSA", "EMSA1(SHA-256)", provider, groups, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+      void bench_gost_3410(const std::string& provider,
+                           std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("GOST-34.10", "EMSA1(GOST-34.11)", provider, {"gost_256A"}, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+      void bench_sm2(const std::vector<std::string>& groups,
+                     const std::string& provider,
+                     std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+      void bench_ecgdsa(const std::vector<std::string>& groups,
+                        const std::string& provider,
+                        std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("ECGDSA", "EMSA1(SHA-256)", provider, groups, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+      void bench_ed25519(const std::string& provider,
+                         std::chrono::milliseconds msec)
+         {
+         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+      void bench_dh(const std::string& provider,
+                    std::chrono::milliseconds msec)
+         {
+         for(size_t bits : { 1024, 1536, 2048, 3072, 4096, 6144, 8192 })
+            {
+            bench_pk_ka("DH",
+                        "DH-" + std::to_string(bits),
+                        "modp/ietf/" + std::to_string(bits),
+                        provider, msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec)
+         {
+         for(size_t bits : { 1024, 2048, 3072 })
+            {
+            const std::string nm = "DSA-" + std::to_string(bits);
+
+            const std::string params =
+               (bits == 1024) ? "dsa/jce/1024" : ("dsa/botan/" + std::to_string(bits));
+
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return Botan::create_private_key("DSA", rng(), params);
+               }));
+
+            record_result(keygen_timer);
+
+            bench_pk_sig(*key, nm, provider, "EMSA1(SHA-256)", msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec)
+         {
+         for(size_t keylen : { 1024, 2048, 3072, 4096 })
+            {
+            const std::string nm = "ElGamal-" + std::to_string(keylen);
+
+            const std::string params = "modp/ietf/" + std::to_string(keylen);
+
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return Botan::create_private_key("ElGamal", rng(), params);
+               }));
+
+            record_result(keygen_timer);
+
+            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+      void bench_ecdh(const std::vector<std::string>& groups,
+                      const std::string& provider,
+                      std::chrono::milliseconds msec)
+         {
+         for(std::string grp : groups)
+            {
+            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+      void bench_curve25519(const std::string& provider,
+                            std::chrono::milliseconds msec)
+         {
+         bench_pk_ka("Curve25519", "Curve25519", "", provider, msec);
+         }
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+      void bench_mceliece(const std::string& provider,
+                          std::chrono::milliseconds msec)
+         {
+         /*
+         SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
+         SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
+         SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
+         SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
+         SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
+         SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
+         */
+
+         const std::vector<std::pair<size_t, size_t>> mce_params =
+            {
+               { 2480, 45 },
+               { 2960, 57 },
+               { 3408, 67 },
+               { 4624, 95 },
+               { 6624, 115 }
+            };
+
+         for(auto params : mce_params)
+            {
+            size_t n = params.first;
+            size_t t = params.second;
+
+            const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
+                                   " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
+
+            std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return new Botan::McEliece_PrivateKey(rng(), n, t);
+               }));
+
+            record_result(keygen_timer);
+            bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+      void bench_xmss(const std::string& provider,
+                      std::chrono::milliseconds msec)
+         {
+         /*
+         We only test H10 signatures here since already they are quite slow (a
+         few seconds per signature). On a fast machine, H16 signatures take 1-2
+         minutes to generate and H20 signatures take 5-10 minutes to generate
+         */
+         std::vector<std::string> xmss_params
+            {
+            "XMSS-SHA2_10_256",
+            "XMSS-SHAKE_10_256",
+            "XMSS-SHA2_10_512",
+            "XMSS-SHAKE_10_512",
+            };
+
+         for(std::string params : xmss_params)
+            {
+            std::unique_ptr<Timer> keygen_timer = make_timer(params, provider, "keygen");
+
+            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+               {
+               return Botan::create_private_key("XMSS", rng(), params);
+               }));
+
+            record_result(keygen_timer);
+            if(bench_pk_sig(*key, params, provider, "", msec) == 1)
+               break;
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_POLY_DBL)
+      void bench_poly_dbl(std::chrono::milliseconds msec)
+         {
+         for(size_t sz : { 8, 16, 24, 32, 64, 128 })
+            {
+            std::unique_ptr<Timer> be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
+            std::unique_ptr<Timer> le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
+
+            std::vector<uint8_t> buf(sz);
+            rng().randomize(buf.data(), sz);
+
+            be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
+            le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
+
+            record_result(be_timer);
+            record_result(le_timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+      void bench_bcrypt()
+         {
+         const std::string password = "not a very good password";
+
+         for(uint8_t work_factor = 4; work_factor <= 14; ++work_factor)
+            {
+            std::unique_ptr<Timer> timer = make_timer("bcrypt wf=" + std::to_string(work_factor));
+
+            timer->run([&] {
+               Botan::generate_bcrypt(password, rng(), work_factor);
+                  });
+
+            record_result(timer);
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+
+      void bench_passhash9()
+         {
+         const std::string password = "not a very good password";
+
+         for(uint8_t alg = 0; alg <= 4; ++alg)
+            {
+            if(Botan::is_passhash9_alg_supported(alg) == false)
+               continue;
+
+            for(auto work_factor : { 10, 15 })
+               {
+               std::unique_ptr<Timer> timer = make_timer("passhash9 alg=" + std::to_string(alg) +
+                                                         " wf=" + std::to_string(work_factor));
+
+               timer->run([&] {
+                  Botan::generate_passhash9(password, rng(), static_cast<uint8_t>(work_factor), alg);
+                  });
+
+               record_result(timer);
+               }
+            }
+         }
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+
+      void bench_scrypt(const std::string& /*provider*/,
+                        std::chrono::milliseconds msec)
+         {
+
+         for(size_t N : { 8192, 16384, 32768, 65536 })
+            {
+            for(size_t r : { 1, 8, 16 })
+               {
+               for(size_t p : { 1, 4 })
+                  {
+                  std::unique_ptr<Timer> scrypt_timer = make_timer(
+                     "scrypt-" + std::to_string(N) + "-" +
+                     std::to_string(r) + "-" + std::to_string(p) +
+                     " (" + std::to_string(Botan::scrypt_memory_usage(N, r, p) / (1024*1024)) + " MiB)");
+
+                  uint8_t out[64];
+                  uint8_t salt[8];
+                  rng().randomize(salt, sizeof(salt));
+
+                  while(scrypt_timer->under(msec))
+                     {
+                     scrypt_timer->run([&] {
+                        Botan::scrypt(out, sizeof(out), "password",
+                                      salt, sizeof(salt), N, r, p);
+                        });
+                     }
+
+                  record_result(scrypt_timer);
+
+                  if(scrypt_timer->events() == 1)
+                     break;
+                  }
+               }
+            }
+
+         }
+
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+
+      void bench_argon2(const std::string& /*provider*/,
+                        std::chrono::milliseconds msec)
+         {
+         const uint8_t mode = 2; // Argon2id
+
+         for(size_t M : { 8*1024, 64*1024, 256*1024 })
+            {
+            for(size_t t : { 1, 2, 4 })
+               {
+               for(size_t p : { 1 })
+                  {
+                  std::unique_ptr<Timer> timer = make_timer(
+                     "Argon2id M=" + std::to_string(M) + " t=" + std::to_string(t) + " p=" + std::to_string(p));
+
+                  uint8_t out[64];
+                  uint8_t salt[16];
+                  rng().randomize(salt, sizeof(salt));
+
+                  while(timer->under(msec))
+                     {
+                     timer->run([&] {
+                                Botan::argon2(out, sizeof(out), "password", 8,
+                                              salt, sizeof(salt), nullptr, 0, nullptr, 0,
+                                              mode, p, M, t);
+                                });
+                     }
+
+                  record_result(timer);
+                  }
+               }
+            }
+         }
+
+#endif
+
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA_RNG)
+      void bench_newhope(const std::string& /*provider*/,
+                         std::chrono::milliseconds msec)
+         {
+         const std::string nm = "NEWHOPE";
+
+         std::unique_ptr<Timer> keygen_timer = make_timer(nm, "", "keygen");
+         std::unique_ptr<Timer> shareda_timer = make_timer(nm, "", "shareda");
+         std::unique_ptr<Timer> sharedb_timer = make_timer(nm, "", "sharedb");
+
+         Botan::ChaCha_RNG nh_rng(Botan::secure_vector<uint8_t>(32));
+
+         while(sharedb_timer->under(msec))
+            {
+            std::vector<uint8_t> send_a(Botan::NEWHOPE_SENDABYTES), send_b(Botan::NEWHOPE_SENDBBYTES);
+            std::vector<uint8_t> shared_a(32), shared_b(32);
+
+            Botan::newhope_poly sk_a;
+
+            keygen_timer->start();
+            Botan::newhope_keygen(send_a.data(), &sk_a, nh_rng);
+            keygen_timer->stop();
+
+            sharedb_timer->start();
+            Botan::newhope_sharedb(shared_b.data(), send_b.data(), send_a.data(), nh_rng);
+            sharedb_timer->stop();
+
+            shareda_timer->start();
+            Botan::newhope_shareda(shared_a.data(), &sk_a, send_b.data());
+            shareda_timer->stop();
+
+            BOTAN_ASSERT(shared_a == shared_b, "Same derived key");
+            }
+
+         record_result(keygen_timer);
+         record_result(shareda_timer);
+         record_result(sharedb_timer);
+         }
+#endif
+
+   };
+
+BOTAN_REGISTER_COMMAND("speed", Speed);
+
+}
diff --git a/third_party/botan/src/cli/timing_tests.cpp b/third_party/botan/src/cli/timing_tests.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/timing_tests.cpp
@@ -0,0 +1,617 @@
+/*
+* Timing Analysis Tests
+*
+* These tests are not for performance, but verifying that two inputs are not handled
+* in a way that is vulnerable to simple timing attacks.
+*
+* Produces output which can be analyzed with the Mona reporting library
+*
+* $ git clone https://github.com/seecurity/mona-timing-report.git
+* $ cd mona-timing-report && ant
+* $ java -jar ReportingTool.jar --lowerBound=0.4 --upperBound=0.5 --inputFile=$file --name=$file
+*
+* (C) 2016 Juraj Somorovsky - juraj.somorovsky@hackmanit.de
+* (C) 2017 Neverhub
+* (C) 2017,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+#include <sstream>
+#include <fstream>
+
+#include <botan/rng.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_HAS_BIGINT)
+   #include <botan/bigint.h>
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+   #include <botan/numthry.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   #include <botan/ec_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+   #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_RAW)
+   #include <botan/pubkey.h>
+   #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+   #include <botan/internal/tls_cbc.h>
+   #include <botan/tls_exceptn.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   #include <botan/pubkey.h>
+   #include <botan/ecdsa.h>
+#endif
+
+namespace Botan_CLI {
+
+typedef uint64_t ticks;
+
+class Timing_Test
+   {
+   public:
+      Timing_Test()
+         {
+         /*
+         A constant seed is ok here since the timing test rng just needs to be
+         "random" but not cryptographically secure - even std::rand() would be ok.
+         */
+         const std::string drbg_seed(64, 'A');
+         m_rng = cli_make_rng("", drbg_seed); // throws if it can't find anything to use
+         }
+
+      virtual ~Timing_Test() = default;
+
+      std::vector<std::vector<ticks>> execute_evaluation(
+                                      const std::vector<std::string>& inputs,
+                                      size_t warmup_runs,
+                                      size_t measurement_runs);
+
+      virtual std::vector<uint8_t> prepare_input(const std::string& input)
+         {
+         return Botan::hex_decode(input);
+         }
+
+      virtual ticks measure_critical_function(const std::vector<uint8_t>& input) = 0;
+
+   protected:
+      static ticks get_ticks()
+         {
+         // Returns CPU counter or best approximation (monotonic clock of some kind)
+         //return Botan::OS::get_high_resolution_clock();
+         return Botan::OS::get_system_timestamp_ns();
+         }
+
+      Botan::RandomNumberGenerator& timing_test_rng()
+         {
+         return (*m_rng);
+         }
+
+   private:
+      std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+   };
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_HAS_EME_RAW)
+
+class Bleichenbacker_Timing_Test final : public Timing_Test
+   {
+   public:
+      Bleichenbacker_Timing_Test(size_t keysize)
+         : m_privkey(timing_test_rng(), keysize)
+         , m_pubkey(m_privkey)
+         , m_enc(m_pubkey, timing_test_rng(), "Raw")
+         , m_dec(m_privkey, timing_test_rng(), "PKCS1v15") {}
+
+      std::vector<uint8_t> prepare_input(const std::string& input) override
+         {
+         const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+         const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, timing_test_rng());
+         return encrypted;
+         }
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override
+         {
+         const ticks start = get_ticks();
+         m_dec.decrypt_or_random(input.data(), m_ctext_length, m_expected_content_size, timing_test_rng());
+         const ticks end = get_ticks();
+         return (end - start);
+         }
+
+   private:
+      const size_t m_expected_content_size = 48;
+      const size_t m_ctext_length = 256;
+      Botan::RSA_PrivateKey m_privkey;
+      Botan::RSA_PublicKey m_pubkey;
+      Botan::PK_Encryptor_EME m_enc;
+      Botan::PK_Decryptor_EME m_dec;
+   };
+
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+
+/*
+* Test Manger OAEP side channel
+*
+* "A Chosen Ciphertext Attack on RSA Optimal Asymmetric Encryption
+* Padding (OAEP) as Standardized in PKCS #1 v2.0" James Manger
+* http://archiv.infsec.ethz.ch/education/fs08/secsem/Manger01.pdf
+*/
+class Manger_Timing_Test final : public Timing_Test
+   {
+   public:
+      Manger_Timing_Test(size_t keysize)
+         : m_privkey(timing_test_rng(), keysize)
+         , m_pubkey(m_privkey)
+         , m_enc(m_pubkey, timing_test_rng(), m_encrypt_padding)
+         , m_dec(m_privkey, timing_test_rng(), m_decrypt_padding) {}
+
+      std::vector<uint8_t> prepare_input(const std::string& input) override
+         {
+         const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+         const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, timing_test_rng());
+         return encrypted;
+         }
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override
+         {
+         ticks start = get_ticks();
+         try
+            {
+            m_dec.decrypt(input.data(), m_ctext_length);
+            }
+         catch(Botan::Decoding_Error&)
+            {
+            }
+         ticks end = get_ticks();
+
+         return (end - start);
+         }
+
+   private:
+      const std::string m_encrypt_padding = "Raw";
+      const std::string m_decrypt_padding = "EME1(SHA-256)";
+      const size_t m_ctext_length = 256;
+      Botan::RSA_PrivateKey m_privkey;
+      Botan::RSA_PublicKey m_pubkey;
+      Botan::PK_Encryptor_EME m_enc;
+      Botan::PK_Decryptor_EME m_dec;
+   };
+
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+
+/*
+* Test handling of countermeasure to the Lucky13 attack
+*/
+class Lucky13_Timing_Test final : public Timing_Test
+   {
+   public:
+      Lucky13_Timing_Test(const std::string& mac_name, size_t mac_keylen)
+         : m_mac_algo(mac_name)
+         , m_mac_keylen(mac_keylen)
+         , m_dec(Botan::BlockCipher::create_or_throw("AES-128"),
+                 Botan::MessageAuthenticationCode::create_or_throw("HMAC(" + m_mac_algo + ")"),
+                 16, m_mac_keylen, Botan::TLS::Protocol_Version::TLS_V11, false) {}
+
+      std::vector<uint8_t> prepare_input(const std::string& input) override;
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+   private:
+      const std::string m_mac_algo;
+      const size_t m_mac_keylen;
+      Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption m_dec;
+   };
+
+std::vector<uint8_t> Lucky13_Timing_Test::prepare_input(const std::string& input)
+   {
+   const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+   const std::vector<uint8_t> key(16);
+   const std::vector<uint8_t> iv(16);
+
+   std::unique_ptr<Botan::Cipher_Mode> enc(Botan::Cipher_Mode::create("AES-128/CBC/NoPadding", Botan::ENCRYPTION));
+   enc->set_key(key);
+   enc->start(iv);
+   Botan::secure_vector<uint8_t> buf(input_vector.begin(), input_vector.end());
+   enc->finish(buf);
+
+   return unlock(buf);
+   }
+
+ticks Lucky13_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+   {
+   Botan::secure_vector<uint8_t> data(input.begin(), input.end());
+   Botan::secure_vector<uint8_t> aad(13);
+   const Botan::secure_vector<uint8_t> iv(16);
+   Botan::secure_vector<uint8_t> key(16 + m_mac_keylen);
+
+   m_dec.set_key(unlock(key));
+   m_dec.set_ad(unlock(aad));
+   m_dec.start(unlock(iv));
+
+   ticks start = get_ticks();
+   try
+      {
+      m_dec.finish(data);
+      }
+   catch(Botan::TLS::TLS_Exception&)
+      {
+      }
+   ticks end = get_ticks();
+   return (end - start);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_Timing_Test final : public Timing_Test
+   {
+   public:
+      ECDSA_Timing_Test(std::string ecgroup);
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+   private:
+      const Botan::EC_Group m_group;
+      const Botan::ECDSA_PrivateKey m_privkey;
+      const Botan::BigInt& m_x;
+      std::vector<Botan::BigInt> m_ws;
+      Botan::BigInt m_b, m_b_inv;
+   };
+
+ECDSA_Timing_Test::ECDSA_Timing_Test(std::string ecgroup)
+   : m_group(ecgroup)
+   , m_privkey(timing_test_rng(), m_group)
+   , m_x(m_privkey.private_value())
+   {
+      m_b = m_group.random_scalar(timing_test_rng());
+      m_b_inv = m_group.inverse_mod_order(m_b);
+   }
+
+ticks ECDSA_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+   {
+   const Botan::BigInt k(input.data(), input.size());
+   Botan::BigInt m(5); // fixed message to minimize noise
+
+   ticks start = get_ticks();
+
+   // the following ECDSA operations involve and should not leak any information about k
+   const Botan::BigInt r = m_group.mod_order(
+      m_group.blinded_base_point_multiply_x(k, timing_test_rng(), m_ws));
+   const Botan::BigInt k_inv = m_group.inverse_mod_order(k);
+
+   m_b = m_group.square_mod_order(m_b);
+   m_b_inv = m_group.square_mod_order(m_b_inv);
+
+   m = m_group.multiply_mod_order(m_b, m_group.mod_order(m));
+   const Botan::BigInt xr_m = m_group.mod_order(m_group.multiply_mod_order(m_x, m_b, r) + m);
+
+   const Botan::BigInt s = m_group.multiply_mod_order(k_inv, xr_m, m_b_inv);
+
+   BOTAN_UNUSED(r, s);
+
+   ticks end = get_ticks();
+
+   return (end - start);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+class ECC_Mul_Timing_Test final : public Timing_Test
+   {
+   public:
+      ECC_Mul_Timing_Test(std::string ecgroup) :
+         m_group(ecgroup)
+         {}
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+   private:
+      const Botan::EC_Group m_group;
+      std::vector<Botan::BigInt> m_ws;
+   };
+
+ticks ECC_Mul_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+   {
+   const Botan::BigInt k(input.data(), input.size());
+
+   ticks start = get_ticks();
+
+   const Botan::PointGFp k_times_P = m_group.blinded_base_point_multiply(k, timing_test_rng(), m_ws);
+
+   ticks end = get_ticks();
+
+   return (end - start);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+class Powmod_Timing_Test final : public Timing_Test
+   {
+   public:
+      Powmod_Timing_Test(const std::string& dl_group) : m_group(dl_group)
+         {
+         }
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+   private:
+      Botan::DL_Group m_group;
+   };
+
+ticks Powmod_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+   {
+   const Botan::BigInt x(input.data(), input.size());
+   const size_t max_x_bits = m_group.p_bits();
+
+   ticks start = get_ticks();
+
+   const Botan::BigInt g_x_p = m_group.power_g_p(x, max_x_bits);
+
+   ticks end = get_ticks();
+
+   return (end - start);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+class Invmod_Timing_Test final : public Timing_Test
+   {
+   public:
+      Invmod_Timing_Test(size_t p_bits)
+         {
+         m_p = Botan::random_prime(timing_test_rng(), p_bits);
+         }
+
+      ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+   private:
+      Botan::BigInt m_p;
+   };
+
+ticks Invmod_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+   {
+   const Botan::BigInt k(input.data(), input.size());
+
+   ticks start = get_ticks();
+
+   const Botan::BigInt inv = inverse_mod(k, m_p);
+
+   ticks end = get_ticks();
+
+   return (end - start);
+   }
+
+#endif
+
+std::vector<std::vector<ticks>> Timing_Test::execute_evaluation(
+                                const std::vector<std::string>& raw_inputs,
+                                size_t warmup_runs, size_t measurement_runs)
+   {
+   std::vector<std::vector<ticks>> all_results(raw_inputs.size());
+   std::vector<std::vector<uint8_t>> inputs(raw_inputs.size());
+
+   for(auto& result : all_results)
+      {
+      result.reserve(measurement_runs);
+      }
+
+   for(size_t i = 0; i != inputs.size(); ++i)
+      {
+      inputs[i] = prepare_input(raw_inputs[i]);
+      }
+
+   // arbitrary upper bounds of 1 and 10 million resp
+   if(warmup_runs > 1000000 || measurement_runs > 100000000)
+      {
+      throw CLI_Error("Requested execution counts too large, rejecting");
+      }
+
+   size_t total_runs = 0;
+   std::vector<ticks> results(inputs.size());
+
+   while(total_runs < (warmup_runs + measurement_runs))
+      {
+      for(size_t i = 0; i != inputs.size(); ++i)
+         {
+         results[i] = measure_critical_function(inputs[i]);
+         }
+
+      total_runs++;
+
+      if(total_runs > warmup_runs)
+         {
+         for(size_t i = 0; i != results.size(); ++i)
+            {
+            all_results[i].push_back(results[i]);
+            }
+         }
+      }
+
+   return all_results;
+   }
+
+class Timing_Test_Command final : public Command
+   {
+   public:
+      Timing_Test_Command()
+         : Command("timing_test test_type --test-data-file= --test-data-dir=src/tests/data/timing "
+                   "--warmup-runs=5000 --measurement-runs=50000") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Run various timing side channel tests";
+         }
+
+      void go() override
+         {
+         const std::string test_type = get_arg("test_type");
+         const size_t warmup_runs = get_arg_sz("warmup-runs");
+         const size_t measurement_runs = get_arg_sz("measurement-runs");
+
+         std::unique_ptr<Timing_Test> test = lookup_timing_test(test_type);
+
+         if(!test)
+            {
+            throw CLI_Error("Unknown or unavailable test type '" + test_type + "'");
+            }
+
+         std::string filename = get_arg_or("test-data-file", "");
+
+         if(filename.empty())
+            {
+            const std::string test_data_dir = get_arg("test-data-dir");
+            filename = test_data_dir + "/" + test_type + ".vec";
+            }
+
+         std::vector<std::string> lines = read_testdata(filename);
+
+         std::vector<std::vector<ticks>> results = test->execute_evaluation(lines, warmup_runs, measurement_runs);
+
+         size_t unique_id = 0;
+         std::ostringstream oss;
+         for(size_t secret_id = 0; secret_id != results.size(); ++secret_id)
+            {
+            for(size_t i = 0; i != results[secret_id].size(); ++i)
+               {
+               oss << unique_id++ << ";" << secret_id << ";" << results[secret_id][i] << "\n";
+               }
+            }
+
+         output() << oss.str();
+         }
+   private:
+
+      std::vector<std::string> read_testdata(const std::string& filename)
+         {
+         std::vector<std::string> lines;
+         std::ifstream infile(filename);
+         if(infile.good() == false)
+            {
+            throw CLI_Error("Error reading test data from '" + filename + "'");
+            }
+         std::string line;
+         while(std::getline(infile, line))
+            {
+            if(line.size() > 0 && line.at(0) != '#')
+               {
+               lines.push_back(line);
+               }
+            }
+         return lines;
+         }
+
+      std::unique_ptr<Timing_Test> lookup_timing_test(const std::string& test_type);
+
+      std::string help_text() const override
+         {
+         // TODO check feature macros
+         return (Command::help_text() +
+                 "\ntest_type can take on values "
+                 "bleichenbacher "
+                 "manger "
+                 "ecdsa "
+                 "ecc_mul "
+                 "inverse_mod "
+                 "pow_mod "
+                 "lucky13sec3 "
+                 "lucky13sec4sha1 "
+                 "lucky13sec4sha256 "
+                 "lucky13sec4sha384 "
+                );
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("timing_test", Timing_Test_Command);
+
+std::unique_ptr<Timing_Test> Timing_Test_Command::lookup_timing_test(const std::string& test_type)
+   {
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_HAS_EME_RAW)
+   if(test_type == "bleichenbacher")
+      {
+      return std::unique_ptr<Timing_Test>(new Bleichenbacker_Timing_Test(2048));
+      }
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+   if(test_type == "manger")
+      {
+      return std::unique_ptr<Timing_Test>(new Manger_Timing_Test(2048));
+      }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   if(test_type == "ecdsa")
+      {
+      return std::unique_ptr<Timing_Test>(new ECDSA_Timing_Test("secp384r1"));
+      }
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   if(test_type == "ecc_mul")
+      {
+      return std::unique_ptr<Timing_Test>(new ECC_Mul_Timing_Test("brainpool512r1"));
+      }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+   if(test_type == "inverse_mod")
+      {
+      return std::unique_ptr<Timing_Test>(new Invmod_Timing_Test(512));
+      }
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+   if(test_type == "pow_mod")
+      {
+      return std::unique_ptr<Timing_Test>(new Powmod_Timing_Test("modp/ietf/1024"));
+      }
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+   if(test_type == "lucky13sec3" || test_type == "lucky13sec4sha1")
+      {
+      return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-1", 20));
+      }
+   if(test_type == "lucky13sec4sha256")
+      {
+      return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-256", 32));
+      }
+   if(test_type == "lucky13sec4sha384")
+      {
+      return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-384", 48));
+      }
+#endif
+
+   BOTAN_UNUSED(test_type);
+
+   return nullptr;
+   }
+
+
+}
diff --git a/third_party/botan/src/cli/tls_client.cpp b/third_party/botan/src/cli/tls_client.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_client.cpp
@@ -0,0 +1,436 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*     2016 Matthias Gierlings
+*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#include <botan/tls_client.h>
+#include <botan/tls_policy.h>
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+#include <fstream>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+   #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include <string>
+#include <memory>
+
+#include "socket_utils.h"
+#include "tls_helpers.h"
+
+namespace Botan_CLI {
+
+class CLI_Policy final : public Botan::TLS::Policy
+   {
+   public:
+
+      CLI_Policy(Botan::TLS::Protocol_Version req_version) : m_version(req_version) {}
+
+      std::vector<std::string> allowed_ciphers() const override
+         {
+         // Allow CBC mode only in versions which don't support AEADs
+         if(m_version.supports_aead_modes() == false)
+            {
+            return { "AES-256", "AES-128" };
+            }
+
+         return Botan::TLS::Policy::allowed_ciphers();
+         }
+
+      bool allow_tls10() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V10; }
+      bool allow_tls11() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V11; }
+      bool allow_tls12() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V12; }
+
+   private:
+      Botan::TLS::Protocol_Version m_version;
+   };
+
+class TLS_Client final : public Command, public Botan::TLS::Callbacks
+   {
+   public:
+      TLS_Client()
+         : Command("tls_client host --port=443 --print-certs --policy=default "
+                   "--tls1.0 --tls1.1 --tls1.2 "
+                   "--skip-system-cert-store --trusted-cas= "
+                   "--session-db= --session-db-pass= --next-protocols= --type=tcp")
+         {
+         init_sockets();
+         }
+
+      ~TLS_Client()
+         {
+         stop_sockets();
+         }
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Connect to a host using TLS/DTLS";
+         }
+
+      void go() override
+         {
+         // TODO client cert auth
+
+         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+         const std::string sessions_db = get_arg("session-db");
+         const std::string host = get_arg("host");
+         const uint16_t port = get_arg_u16("port");
+         const std::string transport = get_arg("type");
+         const std::string next_protos = get_arg("next-protocols");
+         const bool use_system_cert_store = flag_set("skip-system-cert-store") == false;
+         const std::string trusted_CAs = get_arg("trusted-cas");
+
+         if(!sessions_db.empty())
+            {
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+            const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+#else
+            error_output() << "Ignoring session DB file, sqlite not enabled\n";
+#endif
+            }
+
+         if(!session_mgr)
+            {
+            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+            }
+
+         auto policy = load_tls_policy(get_arg("policy"));
+
+         if(transport != "tcp" && transport != "udp")
+            {
+            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
+            }
+
+         const bool use_tcp = (transport == "tcp");
+
+         const std::vector<std::string> protocols_to_offer = Botan::split_on(next_protos, ',');
+
+         Botan::TLS::Protocol_Version version =
+            use_tcp ? Botan::TLS::Protocol_Version::TLS_V12 : Botan::TLS::Protocol_Version::DTLS_V12;
+
+         if(flag_set("tls1.0"))
+            {
+            version = Botan::TLS::Protocol_Version::TLS_V10;
+            if(!policy)
+               policy.reset(new CLI_Policy(version));
+            }
+         else if(flag_set("tls1.1"))
+            {
+            version = Botan::TLS::Protocol_Version::TLS_V11;
+            if(!policy)
+               policy.reset(new CLI_Policy(version));
+            }
+         else if(flag_set("tls1.2"))
+            {
+            version = Botan::TLS::Protocol_Version::TLS_V12;
+            if(!policy)
+               policy.reset(new CLI_Policy(version));
+            }
+         else if(!policy)
+            {
+            policy.reset(new Botan::TLS::Policy);
+            }
+
+         if(policy->acceptable_protocol_version(version) == false)
+            {
+            throw CLI_Usage_Error("The policy specified does not allow the requested TLS version");
+            }
+
+         struct sockaddr_storage addrbuf;
+         std::string hostname;
+         if(!host.empty() &&
+               inet_pton(AF_INET, host.c_str(), &addrbuf) != 1 &&
+               inet_pton(AF_INET6, host.c_str(), &addrbuf) != 1)
+            {
+            hostname = host;
+            }
+
+         m_sockfd = connect_to_host(host, port, use_tcp);
+
+         Basic_Credentials_Manager creds(use_system_cert_store, trusted_CAs);
+
+         Botan::TLS::Client client(*this, *session_mgr, creds, *policy, rng(),
+                                   Botan::TLS::Server_Information(hostname, port),
+                                   version, protocols_to_offer);
+
+         bool first_active = true;
+
+         while(!client.is_closed())
+            {
+            fd_set readfds;
+            FD_ZERO(&readfds);
+            FD_SET(m_sockfd, &readfds);
+
+            if(client.is_active())
+               {
+               FD_SET(STDIN_FILENO, &readfds);
+               if(first_active && !protocols_to_offer.empty())
+                  {
+                  std::string app = client.application_protocol();
+                  if(app != "")
+                     {
+                     output() << "Server choose protocol: " << client.application_protocol() << "\n";
+                     }
+                  first_active = false;
+                  }
+               }
+
+            struct timeval timeout = { 1, 0 };
+
+            ::select(static_cast<int>(m_sockfd + 1), &readfds, nullptr, nullptr, &timeout);
+
+            if(FD_ISSET(m_sockfd, &readfds))
+               {
+               uint8_t buf[4 * 1024] = { 0 };
+
+               ssize_t got = ::read(m_sockfd, buf, sizeof(buf));
+
+               if(got == 0)
+                  {
+                  output() << "EOF on socket\n";
+                  break;
+                  }
+               else if(got == -1)
+                  {
+                  output() << "Socket error: " << errno << " " << err_to_string(errno) << "\n";
+                  continue;
+                  }
+
+               client.received_data(buf, got);
+               }
+
+            if(FD_ISSET(STDIN_FILENO, &readfds))
+               {
+               uint8_t buf[1024] = { 0 };
+               ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));
+
+               if(got == 0)
+                  {
+                  output() << "EOF on stdin\n";
+                  client.close();
+                  break;
+                  }
+               else if(got == -1)
+                  {
+                  output() << "Stdin error: " << errno << " " << err_to_string(errno) << "\n";
+                  continue;
+                  }
+
+               if(got == 2 && buf[1] == '\n')
+                  {
+                  char cmd = buf[0];
+
+                  if(cmd == 'R' || cmd == 'r')
+                     {
+                     output() << "Client initiated renegotiation\n";
+                     client.renegotiate(cmd == 'R');
+                     }
+                  else if(cmd == 'Q')
+                     {
+                     output() << "Client initiated close\n";
+                     client.close();
+                     }
+                  }
+               else
+                  {
+                  client.send(buf, got);
+                  }
+               }
+
+            if(client.timeout_check())
+               {
+               output() << "Timeout detected\n";
+               }
+            }
+
+         ::close(m_sockfd);
+         }
+
+   private:
+      socket_type connect_to_host(const std::string& host, uint16_t port, bool tcp)
+         {
+         addrinfo hints;
+         Botan::clear_mem(&hints, 1);
+         hints.ai_family = AF_UNSPEC;
+         hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM;
+         addrinfo* res, *rp = nullptr;
+
+         if(::getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res) != 0)
+            {
+            throw CLI_Error("getaddrinfo failed for " + host);
+            }
+
+         socket_type fd = 0;
+
+         for(rp = res; rp != nullptr; rp = rp->ai_next)
+            {
+            fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+            if(fd == invalid_socket())
+               {
+               continue;
+               }
+
+            if(::connect(fd, rp->ai_addr, static_cast<socklen_t>(rp->ai_addrlen)) != 0)
+               {
+               ::close(fd);
+               continue;
+               }
+
+            break;
+            }
+
+         ::freeaddrinfo(res);
+
+         if(rp == nullptr) // no address succeeded
+            {
+            throw CLI_Error("connect failed");
+            }
+
+         return fd;
+         }
+
+      void tls_verify_cert_chain(
+         const std::vector<Botan::X509_Certificate>& cert_chain,
+         const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp,
+         const std::vector<Botan::Certificate_Store*>& trusted_roots,
+         Botan::Usage_Type usage,
+         const std::string& hostname,
+         const Botan::TLS::Policy& policy) override
+         {
+         if(cert_chain.empty())
+            {
+            throw Botan::Invalid_Argument("Certificate chain was empty");
+            }
+
+         Botan::Path_Validation_Restrictions restrictions(
+            policy.require_cert_revocation_info(),
+            policy.minimum_signature_strength());
+
+         auto ocsp_timeout = std::chrono::milliseconds(1000);
+
+         Botan::Path_Validation_Result result = Botan::x509_path_validate(
+               cert_chain,
+               restrictions,
+               trusted_roots,
+               hostname,
+               usage,
+               std::chrono::system_clock::now(),
+               ocsp_timeout,
+               ocsp);
+
+         output() << "Certificate validation status: " << result.result_string() << "\n";
+         if(result.successful_validation())
+            {
+            auto status = result.all_statuses();
+
+            if(status.size() > 0 && status[0].count(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD))
+               {
+               output() << "Valid OCSP response for this server\n";
+               }
+            }
+         }
+
+      bool tls_session_established(const Botan::TLS::Session& session) override
+         {
+         output() << "Handshake complete, " << session.version().to_string()
+                  << " using " << session.ciphersuite().to_string() << "\n";
+
+         if(!session.session_id().empty())
+            {
+            output() << "Session ID " << Botan::hex_encode(session.session_id()) << "\n";
+            }
+
+         if(!session.session_ticket().empty())
+            {
+            output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << "\n";
+            }
+
+         if(flag_set("print-certs"))
+            {
+            const std::vector<Botan::X509_Certificate>& certs = session.peer_certs();
+
+            for(size_t i = 0; i != certs.size(); ++i)
+               {
+               output() << "Certificate " << i + 1 << "/" << certs.size() << "\n";
+               output() << certs[i].to_string();
+               output() << certs[i].PEM_encode();
+               }
+            }
+
+         return true;
+         }
+
+      static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length)
+         {
+         int r = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+
+         if(r == -1)
+            {
+            throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
+            }
+         }
+
+      void tls_emit_data(const uint8_t buf[], size_t length) override
+         {
+         size_t offset = 0;
+
+         while(length)
+            {
+            ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL);
+
+            if(sent == -1)
+               {
+               if(errno == EINTR)
+                  {
+                  sent = 0;
+                  }
+               else
+                  {
+                  throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
+                  }
+               }
+
+            offset += sent;
+            length -= sent;
+            }
+         }
+
+      void tls_alert(Botan::TLS::Alert alert) override
+         {
+         output() << "Alert: " << alert.type_string() << "\n";
+         }
+
+      void tls_record_received(uint64_t /*seq_no*/, const uint8_t buf[], size_t buf_size) override
+         {
+         for(size_t i = 0; i != buf_size; ++i)
+            {
+            output() << buf[i];
+            }
+         }
+
+      socket_type m_sockfd = invalid_socket();
+   };
+
+BOTAN_REGISTER_COMMAND("tls_client", TLS_Client);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/tls_helpers.h b/third_party/botan/src/cli/tls_helpers.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_helpers.h
@@ -0,0 +1,244 @@
+/*
+* (C) 2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_TLS_HELPERS_H_
+#define BOTAN_CLI_TLS_HELPERS_H_
+
+#include <botan/pkcs8.h>
+#include <botan/credentials_manager.h>
+#include <botan/tls_policy.h>
+#include <botan/x509self.h>
+#include <botan/data_src.h>
+#include <memory>
+#include <fstream>
+
+#include "cli_exceptions.h"
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+   #include <botan/certstor_system.h>
+#endif
+
+inline bool value_exists(const std::vector<std::string>& vec,
+                         const std::string& val)
+   {
+   for(size_t i = 0; i != vec.size(); ++i)
+      {
+      if(vec[i] == val)
+         {
+         return true;
+         }
+      }
+   return false;
+   }
+
+class Basic_Credentials_Manager : public Botan::Credentials_Manager
+   {
+   public:
+      Basic_Credentials_Manager(bool use_system_store,
+                                const std::string& ca_path)
+         {
+         if(ca_path.empty() == false)
+            {
+            m_certstores.push_back(std::make_shared<Botan::Certificate_Store_In_Memory>(ca_path));
+            }
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+         if(use_system_store)
+            {
+            m_certstores.push_back(std::make_shared<Botan::System_Certificate_Store>());
+            }
+#else
+         BOTAN_UNUSED(use_system_store);
+#endif
+         }
+
+      Basic_Credentials_Manager(Botan::RandomNumberGenerator& rng,
+                                const std::string& server_crt,
+                                const std::string& server_key)
+         {
+         Certificate_Info cert;
+
+         cert.key.reset(Botan::PKCS8::load_key(server_key, rng));
+
+         Botan::DataSource_Stream in(server_crt);
+         while(!in.end_of_data())
+            {
+            try
+               {
+               cert.certs.push_back(Botan::X509_Certificate(in));
+               }
+            catch(std::exception&)
+               {
+               }
+            }
+
+         // TODO: attempt to validate chain ourselves
+
+         m_creds.push_back(cert);
+         }
+
+      std::vector<Botan::Certificate_Store*>
+      trusted_certificate_authorities(const std::string& type,
+                                      const std::string& /*hostname*/) override
+         {
+         std::vector<Botan::Certificate_Store*> v;
+
+         // don't ask for client certs
+         if(type == "tls-server")
+            {
+            return v;
+            }
+
+         for(auto const& cs : m_certstores)
+            {
+            v.push_back(cs.get());
+            }
+
+         return v;
+         }
+
+      std::vector<Botan::X509_Certificate> cert_chain(
+         const std::vector<std::string>& algos,
+         const std::string& type,
+         const std::string& hostname) override
+         {
+         BOTAN_UNUSED(type);
+
+         for(auto const& i : m_creds)
+            {
+            if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end())
+               {
+               continue;
+               }
+
+            if(hostname != "" && !i.certs[0].matches_dns_name(hostname))
+               {
+               continue;
+               }
+
+            return i.certs;
+            }
+
+         return std::vector<Botan::X509_Certificate>();
+         }
+
+      Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+                                          const std::string& /*type*/,
+                                          const std::string& /*context*/) override
+         {
+         for(auto const& i : m_creds)
+            {
+            if(cert == i.certs[0])
+               {
+               return i.key.get();
+               }
+            }
+
+         return nullptr;
+         }
+
+   private:
+      struct Certificate_Info
+         {
+         std::vector<Botan::X509_Certificate> certs;
+         std::shared_ptr<Botan::Private_Key> key;
+         };
+
+      std::vector<Certificate_Info> m_creds;
+      std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
+   };
+
+class TLS_All_Policy final : public Botan::TLS::Policy
+   {
+   public:
+      std::vector<std::string> allowed_ciphers() const override
+         {
+         return std::vector<std::string>
+            {
+            "ChaCha20Poly1305",
+            "AES-256/OCB(12)",
+            "AES-128/OCB(12)",
+            "AES-256/GCM",
+            "AES-128/GCM",
+            "AES-256/CCM",
+            "AES-128/CCM",
+            "AES-256/CCM(8)",
+            "AES-128/CCM(8)",
+            "Camellia-256/GCM",
+            "Camellia-128/GCM",
+            "ARIA-256/GCM",
+            "ARIA-128/GCM",
+            "AES-256",
+            "AES-128",
+            "Camellia-256",
+            "Camellia-128",
+            "SEED"
+            "3DES"
+            };
+         }
+
+      std::vector<std::string> allowed_key_exchange_methods() const override
+         {
+         return { "SRP_SHA", "ECDHE_PSK", "DHE_PSK", "PSK", "CECPQ1", "ECDH", "DH", "RSA" };
+         }
+
+      std::vector<std::string> allowed_signature_methods() const override
+         {
+         return { "ECDSA", "RSA", "DSA", "IMPLICIT" };
+         }
+
+      bool allow_tls10() const override { return true; }
+      bool allow_tls11() const override { return true; }
+      bool allow_tls12() const override { return true; }
+   };
+
+inline std::unique_ptr<Botan::TLS::Policy> load_tls_policy(const std::string policy_type)
+   {
+   std::unique_ptr<Botan::TLS::Policy> policy;
+
+   if(policy_type == "default" || policy_type == "")
+      {
+      policy.reset(new Botan::TLS::Policy);
+      }
+   else if(policy_type == "suiteb_128")
+      {
+      policy.reset(new Botan::TLS::NSA_Suite_B_128);
+      }
+   else if(policy_type == "suiteb_192" || policy_type == "suiteb")
+      {
+      policy.reset(new Botan::TLS::NSA_Suite_B_192);
+      }
+   else if(policy_type == "strict")
+      {
+      policy.reset(new Botan::TLS::Strict_Policy);
+      }
+   else if(policy_type == "bsi")
+      {
+      policy.reset(new Botan::TLS::BSI_TR_02102_2);
+      }
+   else if(policy_type == "datagram")
+      {
+      policy.reset(new Botan::TLS::Strict_Policy);
+      }
+   else if(policy_type == "all" || policy_type == "everything")
+      {
+      policy.reset(new TLS_All_Policy);
+      }
+   else
+      {
+      // assume it's a file
+      std::ifstream policy_stream(policy_type);
+      if(!policy_stream.good())
+         {
+         throw Botan_CLI::CLI_Usage_Error("Unknown TLS policy: not a file or known short name");
+         }
+      policy.reset(new Botan::TLS::Text_Policy(policy_stream));
+      }
+
+   return policy;
+   }
+
+#endif
diff --git a/third_party/botan/src/cli/tls_http_server.cpp b/third_party/botan/src/cli/tls_http_server.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_http_server.cpp
@@ -0,0 +1,579 @@
+/*
+* (C) 2014,2015,2017,2019 Jack Lloyd
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <string>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#define _GLIBCXX_HAVE_GTHR_DEFAULT
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <botan/internal/os_utils.h>
+
+#include <botan/tls_server.h>
+#include <botan/tls_messages.h>
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/version.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+   #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include "tls_helpers.h"
+
+#if BOOST_VERSION >= 107000
+#define GET_IO_SERVICE(s) (static_cast<boost::asio::io_context&>((s).get_executor().context()))
+#else
+#define GET_IO_SERVICE(s) ((s).get_io_service())
+#endif
+
+namespace Botan_CLI {
+
+namespace {
+
+using boost::asio::ip::tcp;
+
+inline void log_exception(const char* where, const std::exception& e)
+   {
+   std::cout << where << ' ' << e.what() << std::endl;
+   }
+
+class ServerStatus
+   {
+   public:
+      ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {}
+
+      bool should_exit() const
+         {
+         if(m_max_clients == 0)
+            return false;
+
+         return clients_serviced() >= m_max_clients;
+         }
+
+      void client_serviced() { m_clients_serviced++; }
+
+      size_t clients_serviced() const { return m_clients_serviced.load(); }
+   private:
+      size_t m_max_clients;
+      std::atomic<size_t> m_clients_serviced;
+   };
+
+/*
+* This is an incomplete and highly buggy HTTP request parser. It is just
+* barely sufficient to handle a GET request sent by a browser.
+*/
+class HTTP_Parser final
+   {
+   public:
+      class Request
+         {
+         public:
+            const std::string& verb() const { return m_verb; }
+            const std::string& location() const { return m_location; }
+            const std::map<std::string, std::string>& headers() const { return m_headers; }
+
+            Request(const std::string& verb,
+                    const std::string& location,
+                    const std::map<std::string, std::string>& headers) :
+               m_verb(verb),
+               m_location(location),
+               m_headers(headers)
+               {}
+
+         private:
+            std::string m_verb;
+            std::string m_location;
+            std::map<std::string, std::string> m_headers;
+         };
+
+      class Callbacks
+         {
+         public:
+            virtual void handle_http_request(const Request& request) = 0;
+            virtual ~Callbacks() = default;
+         };
+
+      HTTP_Parser(Callbacks& cb) : m_cb(cb) {}
+
+      void consume_input(const uint8_t buf[], size_t buf_len)
+         {
+         m_req_buf.append(reinterpret_cast<const char*>(buf), buf_len);
+
+         std::istringstream strm(m_req_buf);
+
+         std::string http_version;
+         std::string verb;
+         std::string location;
+         std::map<std::string, std::string> headers;
+
+         strm >> verb >> location >> http_version;
+
+         if(verb.empty() || location.empty())
+            return;
+
+         while(true)
+            {
+            std::string header_line;
+            std::getline(strm, header_line);
+
+            if(header_line == "\r")
+               {
+               continue;
+               }
+
+            auto delim = header_line.find(": ");
+            if(delim == std::string::npos)
+               {
+               break;
+               }
+
+            const std::string hdr_name = header_line.substr(0, delim);
+            const std::string hdr_val = header_line.substr(delim + 2, std::string::npos);
+
+            headers[hdr_name] = hdr_val;
+
+            if(headers.size() > 1024)
+               throw Botan::Invalid_Argument("Too many HTTP headers sent in request");
+            }
+
+         if(verb != "" && location != "")
+            {
+            Request req(verb, location, headers);
+            m_cb.handle_http_request(req);
+            m_req_buf.clear();
+            }
+         else
+            printf("ignoring\n");
+         }
+   private:
+      Callbacks& m_cb;
+      std::string m_req_buf;
+   };
+
+static const size_t READBUF_SIZE = 4096;
+
+class TLS_Asio_HTTP_Session final : public std::enable_shared_from_this<TLS_Asio_HTTP_Session>,
+                                    public Botan::TLS::Callbacks,
+                                    public HTTP_Parser::Callbacks
+   {
+   public:
+      typedef std::shared_ptr<TLS_Asio_HTTP_Session> pointer;
+
+      static pointer create(
+         boost::asio::io_service& io,
+         Botan::TLS::Session_Manager& session_manager,
+         Botan::Credentials_Manager& credentials,
+         Botan::TLS::Policy& policy)
+         {
+         return pointer(new TLS_Asio_HTTP_Session(io, session_manager, credentials, policy));
+         }
+
+      tcp::socket& client_socket()
+         {
+         return m_client_socket;
+         }
+
+      void start()
+         {
+         m_c2s.resize(READBUF_SIZE);
+         client_read(boost::system::error_code(), 0); // start read loop
+         }
+
+      void stop()
+         {
+         m_tls.close();
+         }
+
+   private:
+      TLS_Asio_HTTP_Session(boost::asio::io_service& io,
+                            Botan::TLS::Session_Manager& session_manager,
+                            Botan::Credentials_Manager& credentials,
+                            Botan::TLS::Policy& policy)
+         : m_strand(io)
+         , m_client_socket(io)
+         , m_rng(cli_make_rng())
+         , m_tls(*this, session_manager, credentials, policy, *m_rng) {}
+
+      void client_read(const boost::system::error_code& error,
+                       size_t bytes_transferred)
+         {
+         if(error)
+            {
+            return stop();
+            }
+
+         try
+            {
+            m_tls.received_data(&m_c2s[0], bytes_transferred);
+            }
+         catch(Botan::Exception& e)
+            {
+            log_exception("TLS connection failed", e);
+            return stop();
+            }
+
+         m_client_socket.async_read_some(
+            boost::asio::buffer(&m_c2s[0], m_c2s.size()),
+            m_strand.wrap(
+               boost::bind(
+                  &TLS_Asio_HTTP_Session::client_read, shared_from_this(),
+                  boost::asio::placeholders::error,
+                  boost::asio::placeholders::bytes_transferred)));
+         }
+
+      void handle_client_write_completion(const boost::system::error_code& error)
+         {
+         if(error)
+            {
+            return stop();
+            }
+
+         m_s2c.clear();
+
+         if(m_s2c_pending.empty() && m_tls.is_closed())
+            {
+            m_client_socket.close();
+            }
+         tls_emit_data(nullptr, 0); // initiate another write if needed
+         }
+
+      std::string tls_server_choose_app_protocol(const std::vector<std::string>& /*client_protos*/) override
+         {
+         return "http/1.1";
+         }
+
+      void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
+         {
+         if(!m_http_parser)
+            m_http_parser.reset(new HTTP_Parser(*this));
+
+         m_http_parser->consume_input(buf, buf_len);
+         }
+
+      std::string summarize_request(const HTTP_Parser::Request& request)
+         {
+         std::ostringstream strm;
+
+         strm << "Client " << client_socket().remote_endpoint().address().to_string()
+              << " requested " << request.verb() << " " << request.location() << "\n";
+
+         if(request.headers().empty() == false)
+            {
+            strm << "Client HTTP headers:\n";
+            for(auto kv : request.headers())
+               strm << " " << kv.first << ": " << kv.second << "\n";
+            }
+
+         return strm.str();
+         }
+
+      void handle_http_request(const HTTP_Parser::Request& request) override
+         {
+         std::ostringstream response;
+         if(request.verb() == "GET")
+            {
+            if(request.location() == "/" || request.location() == "/status")
+               {
+               const std::string http_summary = summarize_request(request);
+
+               const std::string report = m_session_summary + m_chello_summary + http_summary;
+
+               response << "HTTP/1.0 200 OK\r\n";
+               response << "Server: " << Botan::version_string() << "\r\n";
+               response << "Content-Type: text/plain\r\n";
+               response << "Content-Length: " << report.size() << "\r\n";
+               response << "\r\n";
+
+               response << report;
+               }
+            else
+               {
+               response << "HTTP/1.0 404 Not Found\r\n\r\n";
+               }
+            }
+         else
+            {
+            response << "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+            }
+
+         const std::string response_str = response.str();
+         m_tls.send(response_str);
+         m_tls.close();
+         }
+
+      void tls_emit_data(const uint8_t buf[], size_t buf_len) override
+         {
+         if(buf_len > 0)
+            {
+            m_s2c_pending.insert(m_s2c_pending.end(), buf, buf + buf_len);
+            }
+
+         // no write now active and we still have output pending
+         if(m_s2c.empty() && !m_s2c_pending.empty())
+            {
+            std::swap(m_s2c_pending, m_s2c);
+
+            boost::asio::async_write(
+               m_client_socket,
+               boost::asio::buffer(&m_s2c[0], m_s2c.size()),
+               m_strand.wrap(
+                  boost::bind(
+                     &TLS_Asio_HTTP_Session::handle_client_write_completion,
+                     shared_from_this(),
+                     boost::asio::placeholders::error)));
+            }
+         }
+
+      bool tls_session_established(const Botan::TLS::Session& session) override
+         {
+         std::ostringstream strm;
+
+         strm << "TLS negotiation with " << Botan::version_string() << " test server\n\n";
+
+         strm << "Version: " << session.version().to_string() << "\n";
+         strm << "Ciphersuite: " << session.ciphersuite().to_string() << "\n";
+         if(session.session_id().empty() == false)
+            {
+            strm << "SessionID: " << Botan::hex_encode(session.session_id()) << "\n";
+            }
+         if(session.server_info().hostname() != "")
+            {
+            strm << "SNI: " << session.server_info().hostname() << "\n";
+            }
+
+         m_session_summary = strm.str();
+         return true;
+         }
+
+      void tls_inspect_handshake_msg(const Botan::TLS::Handshake_Message& message) override
+         {
+         if(message.type() == Botan::TLS::CLIENT_HELLO)
+            {
+            const Botan::TLS::Client_Hello& client_hello = dynamic_cast<const Botan::TLS::Client_Hello&>(message);
+
+            std::ostringstream strm;
+
+            strm << "Client random: " << Botan::hex_encode(client_hello.random()) << "\n";
+
+            strm << "Client offered following ciphersuites:\n";
+            for(uint16_t suite_id : client_hello.ciphersuites())
+               {
+               Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(suite_id);
+
+               strm << " - 0x"
+                    << std::hex << std::setfill('0') << std::setw(4) << suite_id
+                    << std::dec << std::setfill(' ') << std::setw(0) << " ";
+
+               if(ciphersuite.valid())
+                  strm << ciphersuite.to_string() << "\n";
+               else if(suite_id == 0x00FF)
+                  strm << "Renegotiation SCSV\n";
+               else
+                  strm << "Unknown ciphersuite\n";
+               }
+
+            m_chello_summary = strm.str();
+            }
+
+         }
+
+      void tls_alert(Botan::TLS::Alert alert) override
+         {
+         if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+            {
+            m_tls.close();
+            return;
+            }
+         else
+            {
+            std::cout << "Alert " << alert.type_string() << std::endl;
+            }
+         }
+
+      boost::asio::io_service::strand m_strand;
+
+      tcp::socket m_client_socket;
+
+      std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+      Botan::TLS::Server m_tls;
+      std::string m_chello_summary;
+      std::string m_session_summary;
+      std::unique_ptr<HTTP_Parser> m_http_parser;
+
+      std::vector<uint8_t> m_c2s;
+      std::vector<uint8_t> m_s2c;
+      std::vector<uint8_t> m_s2c_pending;
+   };
+
+class TLS_Asio_HTTP_Server final
+   {
+   public:
+      typedef TLS_Asio_HTTP_Session session;
+
+      TLS_Asio_HTTP_Server(
+         boost::asio::io_service& io, unsigned short port,
+         Botan::Credentials_Manager& creds,
+         Botan::TLS::Policy& policy,
+         Botan::TLS::Session_Manager& session_mgr,
+         size_t max_clients)
+         : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
+         , m_creds(creds)
+         , m_policy(policy)
+         , m_session_manager(session_mgr)
+         , m_status(max_clients)
+         {
+         session::pointer new_session = make_session();
+
+         m_acceptor.async_accept(
+            new_session->client_socket(),
+            boost::bind(
+               &TLS_Asio_HTTP_Server::handle_accept,
+               this,
+               new_session,
+               boost::asio::placeholders::error));
+         }
+
+   private:
+      session::pointer make_session()
+         {
+         return session::create(
+                   GET_IO_SERVICE(m_acceptor),
+                   m_session_manager,
+                   m_creds,
+                   m_policy);
+         }
+
+      void handle_accept(session::pointer new_session,
+                         const boost::system::error_code& error)
+         {
+         if(!error)
+            {
+            new_session->start();
+            new_session = make_session();
+
+            m_status.client_serviced();
+
+            if(m_status.should_exit() == false)
+               {
+               m_acceptor.async_accept(
+                  new_session->client_socket(),
+                  boost::bind(
+                     &TLS_Asio_HTTP_Server::handle_accept,
+                     this,
+                     new_session,
+                     boost::asio::placeholders::error));
+               }
+            }
+         }
+
+      tcp::acceptor m_acceptor;
+
+      Botan::Credentials_Manager& m_creds;
+      Botan::TLS::Policy& m_policy;
+      Botan::TLS::Session_Manager& m_session_manager;
+      ServerStatus m_status;
+   };
+
+}
+
+class TLS_HTTP_Server final : public Command
+   {
+   public:
+      TLS_HTTP_Server() : Command("tls_http_server server_cert server_key "
+                                  "--port=443 --policy=default --threads=0 --max-clients=0 "
+                                  "--session-db= --session-db-pass=") {}
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Provides a simple HTTP server";
+         }
+
+      size_t thread_count() const
+         {
+         if(size_t t = get_arg_sz("threads"))
+            return t;
+         if(size_t t = Botan::OS::get_cpu_available())
+            return t;
+         return 2;
+         }
+
+      void go() override
+         {
+         const uint16_t listen_port = get_arg_u16("port");
+
+         const std::string server_crt = get_arg("server_cert");
+         const std::string server_key = get_arg("server_key");
+
+         const size_t num_threads = thread_count();
+         const size_t max_clients = get_arg_sz("max-clients");
+
+         Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+         auto policy = load_tls_policy(get_arg("policy"));
+
+         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+         const std::string sessions_db = get_arg("session-db");
+
+         if(!sessions_db.empty())
+            {
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+            const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+#else
+            throw CLI_Error_Unsupported("Sqlite3 support not available");
+#endif
+            }
+
+         if(!session_mgr)
+            {
+            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+            }
+
+         boost::asio::io_service io;
+
+         TLS_Asio_HTTP_Server server(io, listen_port, creds, *policy, *session_mgr, max_clients);
+
+         std::vector<std::shared_ptr<std::thread>> threads;
+
+         // run forever... first thread is main calling io.run below
+         for(size_t i = 2; i <= num_threads; ++i)
+            {
+            threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+            }
+
+         io.run();
+
+         for(size_t i = 0; i < threads.size(); ++i)
+            {
+            threads[i]->join();
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tls_http_server", TLS_HTTP_Server);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/tls_proxy.cpp b/third_party/botan/src/cli/tls_proxy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_proxy.cpp
@@ -0,0 +1,526 @@
+/*
+* TLS Server Proxy
+* (C) 2014,2015,2019 Jack Lloyd
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#define _GLIBCXX_HAVE_GTHR_DEFAULT
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <botan/internal/os_utils.h>
+
+#include <botan/tls_server.h>
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+   #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include "tls_helpers.h"
+
+#if BOOST_VERSION >= 107000
+#define GET_IO_SERVICE(s) (static_cast<boost::asio::io_context&>((s).get_executor().context()))
+#else
+#define GET_IO_SERVICE(s) ((s).get_io_service())
+#endif
+
+namespace Botan_CLI {
+
+namespace {
+
+using boost::asio::ip::tcp;
+
+void log_exception(const char* where, const std::exception& e)
+   {
+   std::cout << where << ' ' << e.what() << std::endl;
+   }
+
+void log_error(const char* where, const boost::system::error_code& error)
+   {
+   std::cout << where << ' ' << error.message() << std::endl;
+   }
+
+void log_binary_message(const char* where, const uint8_t buf[], size_t buf_len)
+   {
+   BOTAN_UNUSED(where, buf, buf_len);
+   //std::cout << where << ' ' << Botan::hex_encode(buf, buf_len) << std::endl;
+   }
+
+void log_text_message(const char* where,  const uint8_t buf[], size_t buf_len)
+   {
+   BOTAN_UNUSED(where, buf, buf_len);
+   //const char* c = reinterpret_cast<const char*>(buf);
+   //std::cout << where << ' ' << std::string(c, c + buf_len)  << std::endl;
+   }
+
+class ServerStatus
+   {
+   public:
+      ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {}
+
+      bool should_exit() const
+         {
+         if(m_max_clients == 0)
+            return false;
+
+         return clients_serviced() >= m_max_clients;
+         }
+
+      void client_serviced() { m_clients_serviced++; }
+
+      size_t clients_serviced() const { return m_clients_serviced.load(); }
+   private:
+      size_t m_max_clients;
+      std::atomic<size_t> m_clients_serviced;
+   };
+
+class tls_proxy_session final : public std::enable_shared_from_this<tls_proxy_session>,
+                                public Botan::TLS::Callbacks
+   {
+   public:
+      enum { readbuf_size = 17 * 1024 };
+
+      typedef std::shared_ptr<tls_proxy_session> pointer;
+
+      static pointer create(
+         boost::asio::io_service& io,
+         Botan::TLS::Session_Manager& session_manager,
+         Botan::Credentials_Manager& credentials,
+         Botan::TLS::Policy& policy,
+         tcp::resolver::iterator endpoints)
+         {
+         return pointer(
+                   new tls_proxy_session(
+                      io,
+                      session_manager,
+                      credentials,
+                      policy,
+                      endpoints)
+                );
+         }
+
+      tcp::socket& client_socket()
+         {
+         return m_client_socket;
+         }
+
+      void start()
+         {
+         m_c2p.resize(readbuf_size);
+         client_read(boost::system::error_code(), 0); // start read loop
+         }
+
+      void stop()
+         {
+         if(m_is_closed == false)
+            {
+            /*
+            Don't need to talk to the server anymore
+            Client socket is closed during write callback
+            */
+            m_server_socket.close();
+            m_tls.close();
+            m_is_closed = true;
+            }
+         }
+
+   private:
+      tls_proxy_session(
+         boost::asio::io_service& io,
+         Botan::TLS::Session_Manager& session_manager,
+         Botan::Credentials_Manager& credentials,
+         Botan::TLS::Policy& policy,
+         tcp::resolver::iterator endpoints)
+         : m_strand(io)
+         , m_server_endpoints(endpoints)
+         , m_client_socket(io)
+         , m_server_socket(io)
+         , m_rng(cli_make_rng())
+         , m_tls(*this,
+                 session_manager,
+                 credentials,
+                 policy,
+                 *m_rng) {}
+
+      void client_read(const boost::system::error_code& error,
+                       size_t bytes_transferred)
+         {
+         if(error)
+            {
+            log_error("Read failed", error);
+            stop();
+            return;
+            }
+
+         try
+            {
+            if(!m_tls.is_active())
+               {
+               log_binary_message("From client", &m_c2p[0], bytes_transferred);
+               }
+            m_tls.received_data(&m_c2p[0], bytes_transferred);
+            }
+         catch(Botan::Exception& e)
+            {
+            log_exception("TLS connection failed", e);
+            stop();
+            return;
+            }
+
+         m_client_socket.async_read_some(
+            boost::asio::buffer(&m_c2p[0], m_c2p.size()),
+            m_strand.wrap(
+               boost::bind(
+                  &tls_proxy_session::client_read, shared_from_this(),
+                  boost::asio::placeholders::error,
+                  boost::asio::placeholders::bytes_transferred)));
+         }
+
+      void handle_client_write_completion(const boost::system::error_code& error)
+         {
+         if(error)
+            {
+            log_error("Client write", error);
+            stop();
+            return;
+            }
+
+         m_p2c.clear();
+
+         if(m_p2c_pending.empty() && m_tls.is_closed())
+            {
+            m_client_socket.close();
+            }
+         tls_emit_data(nullptr, 0); // initiate another write if needed
+         }
+
+      void handle_server_write_completion(const boost::system::error_code& error)
+         {
+         if(error)
+            {
+            log_error("Server write", error);
+            stop();
+            return;
+            }
+
+         m_p2s.clear();
+         proxy_write_to_server(nullptr, 0); // initiate another write if needed
+         }
+
+      void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
+         {
+         // Immediately bounce message to server
+         proxy_write_to_server(buf, buf_len);
+         }
+
+      void tls_emit_data(const uint8_t buf[], size_t buf_len) override
+         {
+         if(buf_len > 0)
+            {
+            m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
+            }
+
+         // no write now active and we still have output pending
+         if(m_p2c.empty() && !m_p2c_pending.empty())
+            {
+            std::swap(m_p2c_pending, m_p2c);
+
+            log_binary_message("To Client", &m_p2c[0], m_p2c.size());
+
+            boost::asio::async_write(
+               m_client_socket,
+               boost::asio::buffer(&m_p2c[0], m_p2c.size()),
+               m_strand.wrap(
+                  boost::bind(
+                     &tls_proxy_session::handle_client_write_completion,
+                     shared_from_this(),
+                     boost::asio::placeholders::error)));
+            }
+         }
+
+      void proxy_write_to_server(const uint8_t buf[], size_t buf_len)
+         {
+         if(buf_len > 0)
+            {
+            m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
+            }
+
+         // no write now active and we still have output pending
+         if(m_p2s.empty() && !m_p2s_pending.empty())
+            {
+            std::swap(m_p2s_pending, m_p2s);
+
+            log_text_message("To Server", &m_p2s[0], m_p2s.size());
+
+            boost::asio::async_write(
+               m_server_socket,
+               boost::asio::buffer(&m_p2s[0], m_p2s.size()),
+               m_strand.wrap(
+                  boost::bind(
+                     &tls_proxy_session::handle_server_write_completion,
+                     shared_from_this(),
+                     boost::asio::placeholders::error)));
+            }
+         }
+
+      void server_read(const boost::system::error_code& error,
+                       size_t bytes_transferred)
+         {
+         if(error)
+            {
+            log_error("Server read failed", error);
+            stop();
+            return;
+            }
+
+         try
+            {
+            if(bytes_transferred)
+               {
+               log_text_message("Server to client", &m_s2p[0], m_s2p.size());
+               log_binary_message("Server to client", &m_s2p[0], m_s2p.size());
+               m_tls.send(&m_s2p[0], bytes_transferred);
+               }
+            }
+         catch(Botan::Exception& e)
+            {
+            log_exception("TLS connection failed", e);
+            stop();
+            return;
+            }
+
+         m_s2p.resize(readbuf_size);
+
+         m_server_socket.async_read_some(
+            boost::asio::buffer(&m_s2p[0], m_s2p.size()),
+            m_strand.wrap(
+               boost::bind(&tls_proxy_session::server_read, shared_from_this(),
+                           boost::asio::placeholders::error,
+                           boost::asio::placeholders::bytes_transferred)));
+         }
+
+      bool tls_session_established(const Botan::TLS::Session& session) override
+         {
+         m_hostname = session.server_info().hostname();
+
+         auto onConnect = [this](boost::system::error_code ec, tcp::resolver::iterator /*endpoint*/)
+            {
+            if(ec)
+               {
+               log_error("Server connection", ec);
+               return;
+               }
+            server_read(boost::system::error_code(), 0); // start read loop
+            proxy_write_to_server(nullptr, 0);
+            };
+         async_connect(m_server_socket, m_server_endpoints, onConnect);
+         return true;
+         }
+
+      void tls_alert(Botan::TLS::Alert alert) override
+         {
+         if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+            {
+            m_tls.close();
+            return;
+            }
+         }
+
+      boost::asio::io_service::strand m_strand;
+
+      tcp::resolver::iterator m_server_endpoints;
+
+      tcp::socket m_client_socket;
+      tcp::socket m_server_socket;
+
+      std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+      Botan::TLS::Server m_tls;
+      std::string m_hostname;
+
+      std::vector<uint8_t> m_c2p;
+      std::vector<uint8_t> m_p2c;
+      std::vector<uint8_t> m_p2c_pending;
+
+      std::vector<uint8_t> m_s2p;
+      std::vector<uint8_t> m_p2s;
+      std::vector<uint8_t> m_p2s_pending;
+
+      bool m_is_closed = false;
+   };
+
+class tls_proxy_server final
+   {
+   public:
+      typedef tls_proxy_session session;
+
+      tls_proxy_server(
+         boost::asio::io_service& io, unsigned short port,
+         tcp::resolver::iterator endpoints,
+         Botan::Credentials_Manager& creds,
+         Botan::TLS::Policy& policy,
+         Botan::TLS::Session_Manager& session_mgr,
+         size_t max_clients)
+         : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
+         , m_server_endpoints(endpoints)
+         , m_creds(creds)
+         , m_policy(policy)
+         , m_session_manager(session_mgr)
+         , m_status(max_clients)
+         {
+         session::pointer new_session = make_session();
+
+         m_acceptor.async_accept(
+            new_session->client_socket(),
+            boost::bind(
+               &tls_proxy_server::handle_accept,
+               this,
+               new_session,
+               boost::asio::placeholders::error));
+         }
+
+   private:
+      session::pointer make_session()
+         {
+         return session::create(
+                   GET_IO_SERVICE(m_acceptor),
+                   m_session_manager,
+                   m_creds,
+                   m_policy,
+                   m_server_endpoints);
+         }
+
+      void handle_accept(session::pointer new_session,
+                         const boost::system::error_code& error)
+         {
+         if(!error)
+            {
+            new_session->start();
+            new_session = make_session();
+
+            m_status.client_serviced();
+
+            if(m_status.should_exit() == false)
+               {
+               m_acceptor.async_accept(
+                  new_session->client_socket(),
+                  boost::bind(
+                     &tls_proxy_server::handle_accept,
+                     this,
+                     new_session,
+                     boost::asio::placeholders::error));
+               }
+            }
+         }
+
+      tcp::acceptor m_acceptor;
+      tcp::resolver::iterator m_server_endpoints;
+
+      Botan::Credentials_Manager& m_creds;
+      Botan::TLS::Policy& m_policy;
+      Botan::TLS::Session_Manager& m_session_manager;
+      ServerStatus m_status;
+   };
+
+}
+
+class TLS_Proxy final : public Command
+   {
+   public:
+      TLS_Proxy() : Command("tls_proxy listen_port target_host target_port server_cert server_key "
+                               "--policy=default --threads=0 --max-clients=0 --session-db= --session-db-pass=") {}
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Proxies requests between a TLS client and a TLS server";
+         }
+
+      size_t thread_count() const
+         {
+         if(size_t t = get_arg_sz("threads"))
+            return t;
+         if(size_t t = Botan::OS::get_cpu_available())
+            return t;
+         return 2;
+         }
+
+      void go() override
+         {
+         const uint16_t listen_port = get_arg_u16("listen_port");
+         const std::string target = get_arg("target_host");
+         const std::string target_port = get_arg("target_port");
+
+         const std::string server_crt = get_arg("server_cert");
+         const std::string server_key = get_arg("server_key");
+
+         const size_t num_threads = thread_count();
+         const size_t max_clients = get_arg_sz("max-clients");
+
+         Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+         auto policy = load_tls_policy(get_arg("policy"));
+
+         boost::asio::io_service io;
+
+         tcp::resolver resolver(io);
+         auto server_endpoint_iterator = resolver.resolve({ target, target_port });
+
+         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+         const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+         const std::string sessions_db = get_arg("session-db");
+
+         if(!sessions_db.empty())
+            {
+            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+            }
+#endif
+         if(!session_mgr)
+            {
+            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+            }
+
+         tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, *policy, *session_mgr, max_clients);
+
+         std::vector<std::shared_ptr<std::thread>> threads;
+
+         // run forever... first thread is main calling io.run below
+         for(size_t i = 2; i <= num_threads; ++i)
+            {
+            threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+            }
+
+         io.run();
+
+         for(size_t i = 0; i < threads.size(); ++i)
+            {
+            threads[i]->join();
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/tls_server.cpp b/third_party/botan/src/cli/tls_server.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_server.cpp
@@ -0,0 +1,364 @@
+/*
+* TLS echo server using BSD sockets
+* (C) 2014 Jack Lloyd
+*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "sandbox.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && \
+   defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#if defined(SO_USER_COOKIE)
+#define SOCKET_ID 1
+#else
+#define SOCKET_ID 0
+#endif
+
+#include <botan/tls_server.h>
+#include <botan/tls_policy.h>
+#include <botan/hex.h>
+#include <botan/internal/os_utils.h>
+#include <botan/mem_ops.h>
+
+#include <list>
+#include <fstream>
+
+#include "tls_helpers.h"
+#include "socket_utils.h"
+
+namespace Botan_CLI {
+
+class TLS_Server final : public Command, public Botan::TLS::Callbacks
+   {
+   public:
+#if SOCKET_ID
+      TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
+#else
+      TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=default --dump-traces= --max-clients=0")
+#endif
+         {
+         init_sockets();
+         }
+
+      ~TLS_Server()
+         {
+         stop_sockets();
+         }
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Accept TLS/DTLS connections from TLS/DTLS clients";
+         }
+
+      void go() override
+         {
+         const std::string server_crt = get_arg("cert");
+         const std::string server_key = get_arg("key");
+         const uint16_t port = get_arg_u16("port");
+         const size_t max_clients = get_arg_sz("max-clients");
+         const std::string transport = get_arg("type");
+         const std::string dump_traces_to = get_arg("dump-traces");
+#if SOCKET_ID
+         m_socket_id = get_arg_sz("socket-id");
+#endif
+
+         if(transport != "tcp" && transport != "udp")
+            {
+            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
+            }
+
+         m_is_tcp = (transport == "tcp");
+
+         auto policy = load_tls_policy(get_arg("policy"));
+
+         Botan::TLS::Session_Manager_In_Memory session_manager(rng()); // TODO sqlite3
+
+         Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+         output() << "Listening for new connections on " << transport << " port " << port << std::endl;
+
+         if(!m_sandbox.init())
+            {
+            error_output() << "Failed sandboxing\n";
+            return;
+            }
+
+         socket_type server_fd = make_server_socket(port);
+         size_t clients_served = 0;
+
+         while(true)
+            {
+            if(max_clients > 0 && clients_served >= max_clients)
+               break;
+
+            if(m_is_tcp)
+               {
+               m_socket = ::accept(server_fd, nullptr, nullptr);
+               }
+            else
+               {
+               struct sockaddr_in from;
+               socklen_t from_len = sizeof(sockaddr_in);
+
+               void* peek_buf = nullptr;
+               size_t peek_len = 0;
+
+#if defined(BOTAN_TARGET_OS_IS_MACOS)
+               // macOS handles zero size buffers differently - it will return 0 even if there's no incoming data,
+               // and after that connect() will fail as sockaddr_in from is not initialized
+               int dummy;
+               peek_buf = &dummy;
+               peek_len = sizeof(dummy);
+#endif
+
+               if(::recvfrom(server_fd, static_cast<char*>(peek_buf), static_cast<sendrecv_len_type>(peek_len),
+                             MSG_PEEK, reinterpret_cast<struct sockaddr*>(&from), &from_len) != 0)
+                  {
+                  throw CLI_Error("Could not peek next packet");
+                  }
+
+               if(::connect(server_fd, reinterpret_cast<struct sockaddr*>(&from), from_len) != 0)
+                  {
+                  throw CLI_Error("Could not connect UDP socket");
+                  }
+               m_socket = server_fd;
+               }
+
+            clients_served++;
+
+            Botan::TLS::Server server(
+               *this,
+               session_manager,
+               creds,
+               *policy,
+               rng(),
+               m_is_tcp == false);
+
+            std::unique_ptr<std::ostream> dump_stream;
+
+            if(!dump_traces_to.empty())
+               {
+               uint64_t timestamp = Botan::OS::get_high_resolution_clock();
+               const std::string dump_file =
+                  dump_traces_to + "/tls_" + std::to_string(timestamp) + ".bin";
+               dump_stream.reset(new std::ofstream(dump_file.c_str()));
+               }
+
+            try
+               {
+               while(!server.is_closed())
+                  {
+                  try
+                     {
+                     uint8_t buf[4 * 1024] = { 0 };
+                     ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0);
+
+                     if(got == -1)
+                        {
+                        error_output() << "Error in socket read - " << err_to_string(errno) << std::endl;
+                        break;
+                        }
+
+                     if(got == 0)
+                        {
+                        error_output() << "EOF on socket" << std::endl;
+                        break;
+                        }
+
+                     if(dump_stream)
+                        {
+                        dump_stream->write(reinterpret_cast<const char*>(buf), got);
+                        }
+
+                     server.received_data(buf, got);
+
+                     while(server.is_active() && !m_pending_output.empty())
+                        {
+                        std::string output = m_pending_output.front();
+                        m_pending_output.pop_front();
+                        server.send(output);
+
+                        if(output == "quit\n")
+                           {
+                           server.close();
+                           }
+                        }
+                     }
+                  catch(std::exception& e)
+                     {
+                     error_output() << "Connection problem: " << e.what() << std::endl;
+                     if(m_is_tcp)
+                        {
+                        close_socket(m_socket);
+                        m_socket = invalid_socket();
+                        }
+                     }
+                  }
+               }
+            catch(Botan::Exception& e)
+               {
+               error_output() << "Connection failed: " << e.what() << "\n";
+               }
+
+            if(m_is_tcp)
+               {
+               close_socket(m_socket);
+               m_socket = invalid_socket();
+               }
+            }
+
+         close_socket(server_fd);
+         }
+   private:
+      socket_type make_server_socket(uint16_t port)
+         {
+         const int type = m_is_tcp ? SOCK_STREAM : SOCK_DGRAM;
+
+         socket_type fd = ::socket(PF_INET, type, 0);
+         if(fd == invalid_socket())
+            {
+            throw CLI_Error("Unable to acquire socket");
+            }
+
+         sockaddr_in socket_info;
+         Botan::clear_mem(&socket_info, 1);
+         socket_info.sin_family = AF_INET;
+         socket_info.sin_port = htons(port);
+
+         // FIXME: support limiting listeners
+         socket_info.sin_addr.s_addr = INADDR_ANY;
+
+         if(::bind(fd, reinterpret_cast<struct sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
+            {
+            close_socket(fd);
+            throw CLI_Error("server bind failed");
+            }
+
+         if(m_is_tcp)
+            {
+            if(::listen(fd, 100) != 0)
+               {
+               close_socket(fd);
+               throw CLI_Error("listen failed");
+               }
+            }
+         if(m_socket_id > 0)
+            {
+#if SOCKET_ID
+            // Other oses could have other means to trace sockets
+#if defined(SO_USER_COOKIE)
+            if(::setsockopt(fd, SOL_SOCKET, SO_USER_COOKIE, reinterpret_cast<const void *>(&m_socket_id), sizeof(m_socket_id)) != 0)
+               {
+               // Failed but not world-ending issue
+               output() << "set socket cookie id failed" << std::endl;
+               }
+#endif
+#endif
+            }
+         return fd;
+         }
+
+      bool tls_session_established(const Botan::TLS::Session& session) override
+         {
+         output() << "Handshake complete, " << session.version().to_string()
+                  << " using " << session.ciphersuite().to_string() << std::endl;
+
+         if(!session.session_id().empty())
+            {
+            output() << "Session ID " << Botan::hex_encode(session.session_id()) << std::endl;
+            }
+
+         if(!session.session_ticket().empty())
+            {
+            output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl;
+            }
+
+         return true;
+         }
+
+      void tls_record_received(uint64_t, const uint8_t input[], size_t input_len) override
+         {
+         for(size_t i = 0; i != input_len; ++i)
+            {
+            const char c = static_cast<char>(input[i]);
+            m_line_buf += c;
+            if(c == '\n')
+               {
+               m_pending_output.push_back(m_line_buf);
+               m_line_buf.clear();
+               }
+            }
+         }
+
+      void tls_emit_data(const uint8_t buf[], size_t length) override
+         {
+         if(m_is_tcp)
+            {
+            ssize_t sent = ::send(m_socket, buf, static_cast<sendrecv_len_type>(length), MSG_NOSIGNAL);
+
+            if(sent == -1)
+               {
+               error_output() << "Error writing to socket - " << err_to_string(errno) << std::endl;
+               }
+            else if(sent != static_cast<ssize_t>(length))
+               {
+               error_output() << "Packet of length " << length << " truncated to " << sent << std::endl;
+               }
+            }
+         else
+            {
+            while(length)
+               {
+               ssize_t sent = ::send(m_socket, buf, static_cast<sendrecv_len_type>(length), MSG_NOSIGNAL);
+
+               if(sent == -1)
+                  {
+                  if(errno == EINTR)
+                     {
+                     sent = 0;
+                     }
+                  else
+                     {
+                     throw CLI_Error("Socket write failed");
+                     }
+                  }
+
+               buf += sent;
+               length -= sent;
+               }
+            }
+         }
+
+      void tls_alert(Botan::TLS::Alert alert) override
+         {
+         output() << "Alert: " << alert.type_string() << std::endl;
+         }
+
+      std::string tls_server_choose_app_protocol(const std::vector<std::string>&) override
+         {
+         // we ignore whatever the client sends here
+         return "echo/0.1";
+         }
+
+      socket_type m_socket = invalid_socket();
+      bool m_is_tcp = false;
+      uint32_t m_socket_id = 0;
+      std::string m_line_buf;
+      std::list<std::string> m_pending_output;
+      Sandbox m_sandbox;
+   };
+
+BOTAN_REGISTER_COMMAND("tls_server", TLS_Server);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/tls_utils.cpp b/third_party/botan/src/cli/tls_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tls_utils.cpp
@@ -0,0 +1,226 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/tls_policy.h>
+#include <botan/tls_version.h>
+#include <botan/tls_messages.h>
+#include <botan/loadstor.h>
+#include <botan/hex.h>
+#include <sstream>
+
+#include "tls_helpers.h"
+
+namespace Botan_CLI {
+
+class TLS_Ciphersuites final : public Command
+   {
+   public:
+      TLS_Ciphersuites()
+         : Command("tls_ciphers --policy=default --version=tls1.2") {}
+
+      static Botan::TLS::Protocol_Version::Version_Code tls_version_from_str(const std::string& str)
+         {
+         if(str == "tls1.2" || str == "TLS1.2" || str == "TLS-1.2")
+            {
+            return Botan::TLS::Protocol_Version::TLS_V12;
+            }
+         else if(str == "tls1.1" || str == "TLS1.1" || str == "TLS-1.1")
+            {
+            return Botan::TLS::Protocol_Version::TLS_V11;
+            }
+         else if(str == "tls1.0" || str == "TLS1.1" || str == "TLS-1.1")
+            {
+            return Botan::TLS::Protocol_Version::TLS_V10;
+            }
+         if(str == "dtls1.2" || str == "DTLS1.2" || str == "DTLS-1.2")
+            {
+            return Botan::TLS::Protocol_Version::DTLS_V12;
+            }
+         else if(str == "dtls1.0" || str == "DTLS1.0" || str == "DTLS-1.0")
+            {
+            return Botan::TLS::Protocol_Version::DTLS_V10;
+            }
+         else
+            {
+            throw CLI_Error("Unknown TLS version '" + str + "'");
+            }
+         }
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Lists all ciphersuites for a policy and TLS version";
+         }
+
+      void go() override
+         {
+         const std::string policy_type = get_arg("policy");
+         const Botan::TLS::Protocol_Version version(tls_version_from_str(get_arg("version")));
+         const bool with_srp = false; // fixme
+
+         auto policy = load_tls_policy(policy_type);
+
+         if(policy->acceptable_protocol_version(version) == false)
+            {
+            error_output() << "Error: the policy specified does not allow the given TLS version\n";
+            return;
+            }
+
+         for(uint16_t suite_id : policy->ciphersuite_list(version, with_srp))
+            {
+            const Botan::TLS::Ciphersuite suite(Botan::TLS::Ciphersuite::by_id(suite_id));
+            output() << suite.to_string() << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tls_ciphers", TLS_Ciphersuites);
+
+class TLS_Client_Hello_Reader final : public Command
+   {
+   public:
+      TLS_Client_Hello_Reader()
+         : Command("tls_client_hello --hex input") {}
+
+      std::string group() const override
+         {
+         return "tls";
+         }
+
+      std::string description() const override
+         {
+         return "Parse a TLS client hello message";
+         }
+
+      void go() override
+         {
+         const std::string input_file = get_arg("input");
+         std::vector<uint8_t> input;
+
+         if(flag_set("hex"))
+            {
+            input = Botan::hex_decode(slurp_file_as_str(input_file));
+            }
+         else
+            {
+            input = slurp_file(input_file);
+            }
+
+         if(input.size() < 45)
+            {
+            error_output() << "Input too short to be valid\n";
+            return;
+            }
+
+         // Input also contains the record layer header, strip it
+         if(input[0] == 22)
+            {
+            const size_t len = Botan::make_uint16(input[3], input[4]);
+
+            if(input.size() != len + 5)
+               {
+               error_output() << "Record layer length invalid\n";
+               return;
+               }
+
+            input = std::vector<uint8_t>(input.begin() + 5, input.end());
+            }
+
+         // Assume the handshake header is there, strip it
+         if(input[0] == 1)
+            {
+            const size_t hs_len = Botan::make_uint32(0, input[1], input[2], input[3]);
+
+            if(input.size() != hs_len + 4)
+               {
+               error_output() << "Handshake layer length invalid\n";
+               return;
+               }
+
+            input = std::vector<uint8_t>(input.begin() + 4, input.end());
+            }
+
+         try
+            {
+            Botan::TLS::Client_Hello hello(input);
+
+            output() << format_hello(hello);
+            }
+         catch(std::exception& e)
+            {
+            error_output() << "Parsing client hello failed: " << e.what() << "\n";
+            }
+         }
+
+   private:
+      std::string format_hello(const Botan::TLS::Client_Hello& hello)
+         {
+         std::ostringstream oss;
+         oss << "Version: " << hello.version().to_string() << "\n"
+             << "Random: " << Botan::hex_encode(hello.random()) << "\n";
+
+         if(!hello.session_id().empty())
+            oss << "SessionID: " << Botan::hex_encode(hello.session_id()) << "\n";
+         for(uint16_t csuite_id : hello.ciphersuites())
+            {
+            auto csuite = Botan::TLS::Ciphersuite::by_id(csuite_id);
+            if(csuite.valid())
+               oss << "Cipher: " << csuite.to_string()  << "\n";
+            else if(csuite_id == 0x00FF)
+               oss << "Cipher: EMPTY_RENEGOTIATION_INFO_SCSV\n";
+            else
+               oss << "Cipher: Unknown (" << std::hex << csuite_id << ")\n";
+            }
+
+         oss << "Supported signature schemes: ";
+
+         if(hello.signature_schemes().empty())
+            {
+            oss << "Did not send signature_algorithms extension\n";
+            }
+         else
+            {
+            for(Botan::TLS::Signature_Scheme scheme : hello.signature_schemes())
+               {
+               try
+                  {
+                  auto s = sig_scheme_to_string(scheme);
+                  oss << s << " ";
+                  }
+               catch(...)
+                  {
+                  oss << "(" << std::hex << static_cast<uint16_t>(scheme) << ") ";
+                  }
+               }
+            oss << "\n";
+            }
+
+         std::map<std::string, bool> hello_flags;
+         hello_flags["ALPN"] = hello.supports_alpn();
+         hello_flags["Encrypt Then Mac"] = hello.supports_encrypt_then_mac();
+         hello_flags["Extended Master Secret"] = hello.supports_extended_master_secret();
+         hello_flags["Session Ticket"] = hello.supports_session_ticket();
+
+         for(auto&& i : hello_flags)
+            oss << "Supports " << i.first << "? " << (i.second ? "yes" : "no") << "\n";
+
+         return oss.str();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tls_client_hello", TLS_Client_Hello_Reader);
+
+}
+
+#endif
diff --git a/third_party/botan/src/cli/tss.cpp b/third_party/botan/src/cli/tss.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/tss.cpp
@@ -0,0 +1,138 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+   #include <botan/tss.h>
+   #include <botan/hex.h>
+   #include <botan/rng.h>
+   #include <fstream>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+
+class TSS_Split final : public Command
+   {
+   public:
+      TSS_Split() : Command("tss_split M N input --id= --share-prefix=share --share-suffix=tss --hash=SHA-256") {}
+
+      std::string group() const override
+         {
+         return "tss";
+         }
+
+      std::string description() const override
+         {
+         return "Split a secret into parts";
+         }
+
+      void go() override
+         {
+         const std::string hash_algo = get_arg("hash");
+         const std::string input = get_arg("input");
+         const std::string id_str = get_arg("id");
+         const std::string share_prefix = get_arg("share-prefix");
+         const std::string share_suffix = get_arg("share-suffix");
+         const size_t N = get_arg_sz("N");
+         const size_t M = get_arg_sz("M");
+
+         if(M <= 1 || N <= 1 || M > N || N >= 255)
+            throw CLI_Usage_Error("Invalid N/M parameters for secret splitting");
+
+         Botan::secure_vector<uint8_t> secret = slurp_file_lvec(input);
+
+         if(secret.size() > 0xFFFF)
+            throw CLI_Usage_Error("Secret is too large for this TSS format");
+
+         std::vector<uint8_t> id = Botan::hex_decode(id_str);
+
+         if(id.empty())
+            {
+            id.resize(16);
+            rng().randomize(id.data(), id.size());
+            }
+
+         std::vector<Botan::RTSS_Share> shares =
+            Botan::RTSS_Share::split(static_cast<uint8_t>(M), static_cast<uint8_t>(N),
+                                     secret.data(), static_cast<uint16_t>(secret.size()),
+                                     id, hash_algo, rng());
+
+         for(size_t i = 0; i != shares.size(); ++i)
+            {
+            const std::string share_name = share_prefix + std::to_string(i + 1) + "." + share_suffix;
+            std::ofstream out(share_name.c_str());
+            if(!out)
+               throw CLI_Error("Failed to open output file " + share_name);
+
+            out.write(reinterpret_cast<const char*>(shares[i].data().data()), shares[i].data().size());
+            }
+
+         }
+
+   private:
+      Botan::secure_vector<uint8_t> slurp_file_lvec(const std::string& input_file)
+         {
+         Botan::secure_vector<uint8_t> buf;
+         auto insert_fn = [&](const uint8_t b[], size_t l)
+            {
+            buf.insert(buf.end(), b, b + l);
+            };
+         this->read_file(input_file, insert_fn, 4096);
+         return buf;
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tss_split", TSS_Split);
+
+class TSS_Recover final : public Command
+   {
+   public:
+      TSS_Recover() : Command("tss_recover *shares") {}
+
+      std::string group() const override
+         {
+         return "tss";
+         }
+
+      std::string description() const override
+         {
+         return "Recover a split secret";
+         }
+
+      void go() override
+         {
+         const std::vector<std::string> share_names = get_arg_list("shares");
+
+         if(share_names.empty())
+            {
+            output() << help_text() << "\n";
+            this->set_return_code(1);
+            return;
+            }
+
+         std::vector<Botan::RTSS_Share> shares;
+
+         for(std::string share_fsname : get_arg_list("shares"))
+            {
+            auto v = slurp_file(share_fsname);
+            shares.push_back(Botan::RTSS_Share(v.data(), v.size()));
+            }
+
+         Botan::secure_vector<uint8_t> rec = Botan::RTSS_Share::reconstruct(shares);
+
+         output().write(Botan::cast_uint8_ptr_to_char(rec.data()), rec.size());
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("tss_recover", TSS_Recover);
+
+#endif
+
+}
+
diff --git a/third_party/botan/src/cli/utils.cpp b/third_party/botan/src/cli/utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/utils.cpp
@@ -0,0 +1,391 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#include <botan/version.h>
+#include <botan/cpuid.h>
+#include <botan/internal/stl_util.h>
+#include <botan/internal/os_utils.h>
+#include <sstream>
+#include <iomanip>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+   #include <botan/http_util.h>
+#endif
+
+#if defined(BOTAN_HAS_UUID)
+   #include <botan/uuid.h>
+#endif
+
+namespace Botan_CLI {
+
+class Print_Help final : public Command
+   {
+   public:
+      Print_Help() : Command("help") {}
+
+      std::string help_text() const override
+         {
+         std::map<std::string, std::vector<std::unique_ptr<Command>>> grouped_commands;
+
+         auto reg_commands = Command::registered_cmds();
+         for(const auto& cmd_name : reg_commands)
+            {
+            auto cmd = Command::get_cmd(cmd_name);
+            if(cmd)
+               {
+               grouped_commands[cmd->group()].push_back(std::move(cmd));
+               }
+            }
+
+         const std::map<std::string, std::string> groups_description {
+            { "encryption", "Encryption" },
+            { "compression", "Compression" },
+            { "codec", "Encoders/Decoders" },
+            { "hash", "Hash Functions" },
+            { "hmac", "HMAC" },
+            { "info", "Informational" },
+            { "numtheory", "Number Theory" },
+            { "passhash", "Password Hashing" },
+            { "psk", "PSK Database" },
+            { "pubkey", "Public Key Cryptography" },
+            { "tls", "TLS" },
+            { "tss", "Secret Sharing" },
+            { "x509", "X.509" },
+            { "misc", "Miscellaneous" }
+         };
+
+      std::ostringstream oss;
+
+      oss << "Usage: botan <cmd> <cmd-options>\n";
+      oss << "All commands support --verbose --help --output= --error-output= --rng-type= --drbg-seed=\n\n";
+      oss << "Available commands:\n\n";
+
+      for(const auto& commands : grouped_commands)
+         {
+         std::string desc = commands.first;
+         if(desc.empty())
+            {
+            continue;
+            }
+
+         oss << Botan::search_map(groups_description, desc, desc) << ":\n";
+         for(auto& cmd : commands.second)
+            {
+            oss << "   " << std::setw(16) << std::left << cmd->cmd_name() << "   " << cmd->description() << "\n";
+            }
+         oss << "\n";
+         }
+
+      return oss.str();
+      }
+
+      std::string group() const override
+         {
+         return "";
+         }
+
+      std::string description() const override
+         {
+         return "Prints a help string";
+         }
+
+      void go() override
+         {
+         this->set_return_code(1);
+         output() << help_text();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("help", Print_Help);
+
+class Has_Command final : public Command
+   {
+   public:
+      Has_Command() : Command("has_command cmd") {}
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "Test if a command is available";
+         }
+
+      void go() override
+         {
+         const std::string cmd = get_arg("cmd");
+
+         bool exists = false;
+         for(auto registered_cmd : Command::registered_cmds())
+            {
+            if(cmd == registered_cmd)
+               {
+               exists = true;
+               break;
+               }
+            }
+
+         if(verbose())
+            {
+            output() << "Command '" << cmd << "' is "
+                     << (exists ? "": "not ") << "available\n";
+            }
+
+         if(exists == false)
+            this->set_return_code(1);
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("has_command", Has_Command);
+
+class Config_Info final : public Command
+   {
+   public:
+      Config_Info() : Command("config info_type") {}
+
+      std::string help_text() const override
+         {
+         return "Usage: config info_type\n"
+                "   prefix: Print install prefix\n"
+                "   cflags: Print include params\n"
+                "   ldflags: Print linker params\n"
+                "   libs: Print libraries\n";
+         }
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "Print the used prefix, cflags, ldflags or libs";
+         }
+
+      void go() override
+         {
+         const std::string arg = get_arg("info_type");
+
+         if(arg == "prefix")
+            {
+            output() << BOTAN_INSTALL_PREFIX << "\n";
+            }
+         else if(arg == "cflags")
+            {
+            output() << "-I" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_HEADER_DIR << "\n";
+            }
+         else if(arg == "ldflags")
+            {
+            if(*BOTAN_LINK_FLAGS)
+               output() << BOTAN_LINK_FLAGS << ' ';
+            output() << "-L" << BOTAN_INSTALL_LIB_DIR << "\n";
+            }
+         else if(arg == "libs")
+            {
+            output() << "-lbotan-" << Botan::version_major() << " " << BOTAN_LIB_LINK << "\n";
+            }
+         else
+            {
+            throw CLI_Usage_Error("Unknown option to botan config " + arg);
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("config", Config_Info);
+
+class Version_Info final : public Command
+   {
+   public:
+      Version_Info() : Command("version --full") {}
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "Print version info";
+         }
+
+      void go() override
+         {
+         if(flag_set("full"))
+            {
+            output() << Botan::version_string() << "\n";
+            }
+         else
+            {
+            output() << Botan::short_version_string() << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("version", Version_Info);
+
+class Print_Cpuid final : public Command
+   {
+   public:
+      Print_Cpuid() : Command("cpuid") {}
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "List available processor flags (aes_ni, SIMD extensions, ...)";
+         }
+
+      void go() override
+         {
+         output() << "CPUID flags: " << Botan::CPUID::to_string() << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cpuid", Print_Cpuid);
+
+class Cycle_Counter final : public Command
+   {
+   public:
+      Cycle_Counter() : Command("cpu_clock --test-duration=500") {}
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "Estimate the speed of the CPU cycle counter";
+         }
+
+      void go() override
+         {
+         if(Botan::OS::get_cpu_cycle_counter() == 0)
+            {
+            output() << "No CPU cycle counter on this machine\n";
+            return;
+            }
+
+         const uint64_t test_duration_ns = get_arg_sz("test-duration") * 1000000;
+
+         if(test_duration_ns == 0)
+            {
+            output() << "Invalid test duration\n";
+            return;
+            }
+
+         const uint64_t cc_start = Botan::OS::get_cpu_cycle_counter();
+         const uint64_t ns_start = Botan::OS::get_system_timestamp_ns();
+
+         uint64_t cc_end = 0;
+         uint64_t ns_end = ns_start;
+
+         while((ns_end - ns_start) < test_duration_ns)
+            {
+            ns_end = Botan::OS::get_system_timestamp_ns();
+            cc_end = Botan::OS::get_cpu_cycle_counter();
+            }
+
+         if(cc_end <= cc_start)
+            {
+            output() << "Cycle counter seems to have wrapped, try again\n";
+            return;
+            }
+
+         if(ns_end <= ns_start)
+            {
+            output() << "System clock seems to have wrapped (?!?)\n";
+            return;
+            }
+
+         const uint64_t ns_duration = ns_end - ns_start;
+         const uint64_t cc_duration = cc_end - cc_start;
+
+         const double ratio = static_cast<double>(cc_duration) / ns_duration;
+
+         if(ratio >= 1.0)
+            {
+            // GHz
+            output() << "Estimated CPU clock " << std::setprecision(2) << ratio << " GHz\n";
+            }
+         else
+            {
+            // MHz
+            output() << "Estimated CPU clock " << static_cast<size_t>(ratio * 1000) << " MHz\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cpu_clock", Cycle_Counter);
+
+#if defined(BOTAN_HAS_UUID)
+
+class Print_UUID final : public Command
+   {
+   public:
+      Print_UUID() : Command("uuid") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Print a random UUID";
+         }
+
+      void go() override
+         {
+         Botan::UUID uuid(rng());
+         output() << uuid.to_string() << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("uuid", Print_UUID);
+
+#endif
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+class HTTP_Get final : public Command
+   {
+   public:
+      HTTP_Get() : Command("http_get --redirects=1 --timeout=3000 url") {}
+
+      std::string group() const override
+         {
+         return "misc";
+         }
+
+      std::string description() const override
+         {
+         return "Retrieve resource from the passed http/https url";
+         }
+
+      void go() override
+         {
+         const std::string url = get_arg("url");
+         const std::chrono::milliseconds timeout(get_arg_sz("timeout"));
+         const size_t redirects = get_arg_sz("redirects");
+
+         output() << Botan::HTTP::GET_sync(url, redirects, timeout) << "\n";
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("http_get", HTTP_Get);
+
+#endif // http_util
+
+}
diff --git a/third_party/botan/src/cli/x509.cpp b/third_party/botan/src/cli/x509.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/cli/x509.cpp
@@ -0,0 +1,417 @@
+/*
+* (C) 2010,2014,2015,2018 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/certstor.h>
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/x509_ca.h>
+#include <botan/x509cert.h>
+#include <botan/x509path.h>
+#include <botan/x509self.h>
+#include <botan/data_src.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_OCSP)
+   #include <botan/ocsp.h>
+#endif
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+   #include <botan/certstor_system.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+
+class Trust_Root_Info final : public Command
+   {
+   public:
+      Trust_Root_Info() : Command("trust_roots --dn --dn-only --display") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "List certs in the system trust store";
+         }
+
+      void go() override
+         {
+         Botan::System_Certificate_Store trust_roots;
+
+         const auto dn_list = trust_roots.all_subjects();
+
+         if(flag_set("dn-only"))
+            {
+            for(auto dn : dn_list)
+               output() << dn << "\n";
+            }
+         else
+            {
+            for(auto dn : dn_list)
+               {
+               // Some certstores have more than one cert with a particular DN
+               for(auto cert : trust_roots.find_all_certs(dn, std::vector<uint8_t>()))
+                  {
+                  if(flag_set("dn"))
+                     output() << "# " << dn << "\n";
+
+                  if(flag_set("display"))
+                     output() << cert->to_string() << "\n";
+
+                  output() << cert->PEM_encode() << "\n";
+                  }
+               }
+
+            }
+         }
+
+   };
+
+BOTAN_REGISTER_COMMAND("trust_roots", Trust_Root_Info);
+
+#endif
+
+class Sign_Cert final : public Command
+   {
+   public:
+      Sign_Cert()
+         : Command("sign_cert --ca-key-pass= --hash=SHA-256 "
+                   "--duration=365 --emsa= ca_cert ca_key pkcs10_req") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Create a CA-signed X.509 certificate from a PKCS #10 CSR";
+         }
+
+      void go() override
+         {
+         Botan::X509_Certificate ca_cert(get_arg("ca_cert"));
+
+         const std::string key_file = get_arg("ca_key");
+         const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass");
+         const std::string emsa = get_arg("emsa");
+         const std::string hash = get_arg("hash");
+
+         std::unique_ptr<Botan::Private_Key> key;
+         if(!pass.empty())
+            {
+            key.reset(Botan::PKCS8::load_key(key_file, rng(), pass));
+            }
+         else
+            {
+            key.reset(Botan::PKCS8::load_key(key_file, rng()));
+            }
+
+         if(!key)
+            {
+            throw CLI_Error("Failed to load key from " + key_file);
+            }
+
+         std::map<std::string, std::string> options;
+         if(emsa.empty() == false)
+            options["padding"] = emsa;
+
+         Botan::X509_CA ca(ca_cert, *key, options, hash, rng());
+
+         Botan::PKCS10_Request req(get_arg("pkcs10_req"));
+
+         auto now = std::chrono::system_clock::now();
+
+         Botan::X509_Time start_time(now);
+
+         typedef std::chrono::duration<int, std::ratio<86400>> days;
+
+         Botan::X509_Time end_time(now + days(get_arg_sz("duration")));
+
+         Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time);
+
+         output() << new_cert.PEM_encode();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("sign_cert", Sign_Cert);
+
+class Cert_Info final : public Command
+   {
+   public:
+      Cert_Info() : Command("cert_info --fingerprint file") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Parse X.509 certificate and display data fields";
+         }
+
+      void go() override
+         {
+         const std::string arg_file = get_arg("file");
+
+         std::vector<uint8_t> data = slurp_file(get_arg("file"));
+
+         Botan::DataSource_Memory in(data);
+
+         while(!in.end_of_data())
+            {
+            try
+               {
+               Botan::X509_Certificate cert(in);
+
+               try
+                  {
+                  output() << cert.to_string() << std::endl;
+                  }
+               catch(Botan::Exception& e)
+                  {
+                  // to_string failed - report the exception and continue
+                  output() << "X509_Certificate::to_string failed: " << e.what() << "\n";
+                  }
+
+               if(flag_set("fingerprint"))
+                  output() << "Fingerprint: " << cert.fingerprint("SHA-256") << std::endl;
+               }
+            catch(Botan::Exception& e)
+               {
+               if(!in.end_of_data())
+                  {
+                  output() << "X509_Certificate parsing failed " << e.what() << "\n";
+                  }
+               }
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cert_info", Cert_Info);
+
+#if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL)
+
+class OCSP_Check final : public Command
+   {
+   public:
+      OCSP_Check() : Command("ocsp_check --timeout=3000 subject issuer") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Verify an X.509 certificate against the issuers OCSP responder";
+         }
+
+      void go() override
+         {
+         Botan::X509_Certificate subject(get_arg("subject"));
+         Botan::X509_Certificate issuer(get_arg("issuer"));
+         std::chrono::milliseconds timeout(get_arg_sz("timeout"));
+
+         Botan::Certificate_Store_In_Memory cas;
+         cas.add_certificate(issuer);
+         Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, &cas, timeout);
+
+         auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now());
+
+         if(status == Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD)
+            {
+            output() << "OCSP check OK\n";
+            }
+         else
+            {
+            output() << "OCSP check failed " << Botan::Path_Validation_Result::status_string(status) << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check);
+
+#endif // OCSP && HTTP
+
+class Cert_Verify final : public Command
+   {
+   public:
+      Cert_Verify() : Command("cert_verify subject *ca_certs") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Verify if the passed X.509 certificate passes path validation";
+         }
+
+      void go() override
+         {
+         Botan::X509_Certificate subject_cert(get_arg("subject"));
+         Botan::Certificate_Store_In_Memory trusted;
+
+         for(auto const& certfile : get_arg_list("ca_certs"))
+            {
+            trusted.add_certificate(Botan::X509_Certificate(certfile));
+            }
+
+         Botan::Path_Validation_Restrictions restrictions;
+
+         Botan::Path_Validation_Result result =
+            Botan::x509_path_validate(subject_cert,
+                                      restrictions,
+                                      trusted);
+
+         if(result.successful_validation())
+            {
+            output() << "Certificate passes validation checks\n";
+            }
+         else
+            {
+            output() << "Certificate did not validate - " << result.result_string() << "\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cert_verify", Cert_Verify);
+
+class Gen_Self_Signed final : public Command
+   {
+   public:
+      Gen_Self_Signed()
+         : Command("gen_self_signed key CN --country= --dns= "
+                   "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash=SHA-256 --emsa= --der") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Generate a self signed X.509 certificate";
+         }
+
+      void go() override
+         {
+         const std::string key_file = get_arg("key");
+         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
+         std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(key_file, rng(), passphrase));
+
+         if(!key)
+            {
+            throw CLI_Error("Failed to load key from " + get_arg("key"));
+            }
+
+         const uint32_t lifetime = static_cast<uint32_t>(get_arg_sz("days") * 24 * 60 * 60);
+
+         Botan::X509_Cert_Options opts("", lifetime);
+
+         opts.common_name  = get_arg("CN");
+         opts.country      = get_arg("country");
+         opts.organization = get_arg("organization");
+         opts.email        = get_arg("email");
+         opts.more_dns = Botan::split_on(get_arg("dns"), ',');
+         const bool der_format = flag_set("der");
+
+         std::string emsa = get_arg("emsa");
+
+         if(emsa.empty() == false)
+            opts.set_padding_scheme(emsa);
+
+         if(flag_set("ca"))
+            {
+            opts.CA_key(get_arg_sz("path-limit"));
+            }
+
+         Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng());
+
+         if(der_format)
+            {
+            auto der = cert.BER_encode();
+            output().write(reinterpret_cast<const char*>(der.data()), der.size());
+            }
+         else
+            output() << cert.PEM_encode();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_self_signed", Gen_Self_Signed);
+
+class Generate_PKCS10 final : public Command
+   {
+   public:
+      Generate_PKCS10()
+         : Command("gen_pkcs10 key CN --country= --organization= "
+                   "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash=SHA-256 --emsa=") {}
+
+      std::string group() const override
+         {
+         return "x509";
+         }
+
+      std::string description() const override
+         {
+         return "Generate a PKCS #10 certificate signing request (CSR)";
+         }
+
+      void go() override
+         {
+         std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"), rng(), get_arg("key-pass")));
+
+         if(!key)
+            {
+            throw CLI_Error("Failed to load key from " + get_arg("key"));
+            }
+
+         Botan::X509_Cert_Options opts;
+
+         opts.common_name  = get_arg("CN");
+         opts.country      = get_arg("country");
+         opts.organization = get_arg("organization");
+         opts.email        = get_arg("email");
+         opts.more_dns     = Botan::split_on(get_arg("dns"), ',');
+
+         if(flag_set("ca"))
+            {
+            opts.CA_key(get_arg_sz("path-limit"));
+            }
+
+         for(std::string ext_ku : Botan::split_on(get_arg("ext-ku"), ','))
+            {
+            opts.add_ex_constraint(ext_ku);
+            }
+
+         std::string emsa = get_arg("emsa");
+
+         if(emsa.empty() == false)
+            opts.set_padding_scheme(emsa);
+
+         Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
+
+         output() << req.PEM_encode();
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10);
+
+}
+
+#endif
diff --git a/third_party/botan/src/fuzzer/asn1.cpp b/third_party/botan/src/fuzzer/asn1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/asn1.cpp
@@ -0,0 +1,43 @@
+/*
+* (C) 2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/asn1_print.h>
+#include <fstream>
+
+class ASN1_Parser final : public Botan::ASN1_Formatter
+   {
+   public:
+      ASN1_Parser() : Botan::ASN1_Formatter(true, 64) {}
+
+   protected:
+      std::string format(Botan::ASN1_Tag, Botan::ASN1_Tag, size_t, size_t,
+                         const std::string&) const override
+         {
+         return "";
+         }
+
+      std::string format_bin(Botan::ASN1_Tag, Botan::ASN1_Tag,
+                             const std::vector<uint8_t>&) const override
+         {
+         return "";
+         }
+   };
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   try
+      {
+      /*
+      * Here we use an uninitialized ofstream so the fuzzer doesn't spend time
+      * on actual output formatting, no memory is allocated, etc.
+      */
+      std::ofstream out;
+      ASN1_Parser printer;
+      printer.print_to_stream(out, in, len);
+      }
+   catch(Botan::Exception& e) { }
+   }
diff --git a/third_party/botan/src/fuzzer/barrett.cpp b/third_party/botan/src/fuzzer/barrett.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/barrett.cpp
@@ -0,0 +1,49 @@
+/*
+* (C) 2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/divide.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static const size_t max_bits = 4096;
+
+   if(len <= 4)
+      return;
+
+   if(len > 2*(max_bits/8))
+      return;
+
+   const size_t x_len = 2 * ((len + 2) / 3);
+
+   Botan::BigInt x = Botan::BigInt::decode(in, x_len);
+   const Botan::BigInt p = Botan::BigInt::decode(in + x_len, len - x_len);
+
+   if(p.is_zero())
+      return;
+
+   const size_t x_bits = x.bits();
+   if(x_bits % 8 == 0 && x_bits / 8 == x_len)
+      x.flip_sign();
+
+   const Botan::BigInt ref = x % p;
+
+   const Botan::Modular_Reducer mod_p(p);
+   const Botan::BigInt z = mod_p.reduce(x);
+
+   const Botan::BigInt ct = ct_modulo(x, p);
+
+   if(ref != z || ref != ct)
+      {
+      FUZZER_WRITE_AND_CRASH("X = " << x << "\n"
+                             << "P = " << p << "\n"
+                             << "Barrett = " << z << "\n"
+                             << "Ct = " << ct << "\n"
+                             << "Ref = " << ref << "\n");
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/bn_sqr.cpp b/third_party/botan/src/fuzzer/bn_sqr.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/bn_sqr.cpp
@@ -0,0 +1,24 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+
+#include <botan/bigint.h>
+#include <botan/numthry.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 8192/8)
+      return;
+
+   Botan::BigInt x = Botan::BigInt::decode(in, len);
+
+   Botan::BigInt x_sqr = square(x);
+   Botan::BigInt x_mul = x * x;
+
+   FUZZER_ASSERT_EQUAL(x_sqr, x_mul);
+   }
+
diff --git a/third_party/botan/src/fuzzer/cert.cpp b/third_party/botan/src/fuzzer/cert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/cert.cpp
@@ -0,0 +1,22 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/x509cert.h>
+#include <botan/data_src.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > max_fuzzer_input_size)
+      return;
+
+   try
+      {
+      Botan::DataSource_Memory input(in, len);
+      Botan::X509_Certificate cert(input);
+      }
+   catch(Botan::Exception& e) { }
+   }
diff --git a/third_party/botan/src/fuzzer/crl.cpp b/third_party/botan/src/fuzzer/crl.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/crl.cpp
@@ -0,0 +1,19 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/x509_crl.h>
+#include <botan/data_src.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   try
+      {
+      Botan::DataSource_Memory input(in, len);
+      Botan::X509_CRL crl(input);
+      }
+   catch(Botan::Exception& e) {}
+   }
diff --git a/third_party/botan/src/fuzzer/divide.cpp b/third_party/botan/src/fuzzer/divide.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/divide.cpp
@@ -0,0 +1,52 @@
+/*
+* (C) 2015,2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/divide.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len % 2 == 1 || len > 2*4096/8)
+      return;
+
+   // Save on allocations by making these static
+   static Botan::BigInt x, y, q, r, ct_q, ct_r, z;
+
+   x = Botan::BigInt::decode(in, len / 2);
+   y = Botan::BigInt::decode(in + len / 2, len / 2);
+
+   if(y == 0)
+      return;
+
+   Botan::divide(x, y, q, r);
+
+   FUZZER_ASSERT_TRUE(r < y);
+
+   z = q*y + r;
+
+   FUZZER_ASSERT_EQUAL(z, x);
+
+   Botan::ct_divide(x, y, ct_q, ct_r);
+
+   FUZZER_ASSERT_EQUAL(q, ct_q);
+   FUZZER_ASSERT_EQUAL(r, ct_r);
+
+   // Now divide by just low byte of y
+
+   y = y.byte_at(0);
+   if(y == 0)
+      y = 251;
+   Botan::divide(x, y, q, r);
+
+   z = q*y + r;
+   FUZZER_ASSERT_EQUAL(z, x);
+
+   uint8_t r8;
+   Botan::ct_divide_u8(x, y.byte_at(0), ct_q, r8);
+   FUZZER_ASSERT_EQUAL(ct_q, q);
+   FUZZER_ASSERT_EQUAL(r8, r.byte_at(0));
+
+   }
+
diff --git a/third_party/botan/src/fuzzer/ecc_bp256.cpp b/third_party/botan/src/fuzzer/ecc_bp256.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ecc_bp256.cpp
@@ -0,0 +1,16 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include "ecc_helper.h"
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*256/8)
+      return;
+
+   static Botan::EC_Group bp256("brainpool256r1");
+   return check_ecc_math(bp256, in, len);
+   }
diff --git a/third_party/botan/src/fuzzer/ecc_helper.h b/third_party/botan/src/fuzzer/ecc_helper.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ecc_helper.h
@@ -0,0 +1,107 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef ECC_HELPERS_H_
+#define ECC_HELPERS_H_
+
+#include "fuzzers.h"
+#include <botan/ec_group.h>
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+
+namespace {
+
+inline std::ostream& operator<<(std::ostream& o, const Botan::PointGFp& point)
+   {
+   o << point.get_affine_x() << "," << point.get_affine_y();
+   return o;
+   }
+
+Botan::BigInt decompress_point(bool yMod2,
+                               const Botan::BigInt& x,
+                               const Botan::BigInt& curve_p,
+                               const Botan::BigInt& curve_a,
+                               const Botan::BigInt& curve_b)
+   {
+   Botan::BigInt xpow3 = x * x * x;
+
+   Botan::BigInt g = curve_a * x;
+   g += xpow3;
+   g += curve_b;
+   g = g % curve_p;
+
+   Botan::BigInt z = ressol(g, curve_p);
+
+   if(z < 0)
+      throw Botan::Exception("Could not perform square root");
+
+   if(z.get_bit(0) != yMod2)
+      z = curve_p - z;
+
+   return z;
+   }
+
+void check_ecc_math(const Botan::EC_Group& group,
+                    const uint8_t in[], size_t len)
+   {
+   // These depend only on the group, which is also static
+   static const Botan::PointGFp base_point = group.get_base_point();
+
+   // This is shared across runs to reduce overhead
+   static std::vector<Botan::BigInt> ws(Botan::PointGFp::WORKSPACE_SIZE);
+
+   const size_t hlen = len / 2;
+   const Botan::BigInt a = Botan::BigInt::decode(in, hlen);
+   const Botan::BigInt b = Botan::BigInt::decode(in + hlen, len - hlen);
+   const Botan::BigInt c = a + b;
+
+   const Botan::PointGFp P1 = base_point * a;
+   const Botan::PointGFp Q1 = base_point * b;
+   const Botan::PointGFp R1 = base_point * c;
+
+   const Botan::PointGFp S1 = P1 + Q1;
+   const Botan::PointGFp T1 = Q1 + P1;
+
+   FUZZER_ASSERT_EQUAL(S1, R1);
+   FUZZER_ASSERT_EQUAL(T1, R1);
+
+   const Botan::PointGFp P2 = group.blinded_base_point_multiply(a, fuzzer_rng(), ws);
+   const Botan::PointGFp Q2 = group.blinded_base_point_multiply(b, fuzzer_rng(), ws);
+   const Botan::PointGFp R2 = group.blinded_base_point_multiply(c, fuzzer_rng(), ws);
+   const Botan::PointGFp S2 = P2 + Q2;
+   const Botan::PointGFp T2 = Q2 + P2;
+
+   FUZZER_ASSERT_EQUAL(S2, R2);
+   FUZZER_ASSERT_EQUAL(T2, R2);
+
+   const Botan::PointGFp P3 = group.blinded_var_point_multiply(base_point, a, fuzzer_rng(), ws);
+   const Botan::PointGFp Q3 = group.blinded_var_point_multiply(base_point, b, fuzzer_rng(), ws);
+   const Botan::PointGFp R3 = group.blinded_var_point_multiply(base_point, c, fuzzer_rng(), ws);
+   const Botan::PointGFp S3 = P3 + Q3;
+   const Botan::PointGFp T3 = Q3 + P3;
+
+   FUZZER_ASSERT_EQUAL(S3, R3);
+   FUZZER_ASSERT_EQUAL(T3, R3);
+
+   FUZZER_ASSERT_EQUAL(S1, S2);
+   FUZZER_ASSERT_EQUAL(S1, S3);
+
+   try
+      {
+      const auto yp = decompress_point(true, a, group.get_p(), group.get_a(), group.get_b());
+      const auto pt_p = group.blinded_var_point_multiply(group.point(a, yp), b, fuzzer_rng(), ws);
+
+      const auto yn = -yp;
+      const auto pt_n = group.blinded_var_point_multiply(group.point(a, yn), b, fuzzer_rng(), ws);
+
+      FUZZER_ASSERT_EQUAL(pt_p, -pt_n);
+      }
+   catch(...) {}
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/fuzzer/ecc_p256.cpp b/third_party/botan/src/fuzzer/ecc_p256.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ecc_p256.cpp
@@ -0,0 +1,15 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include "ecc_helper.h"
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*256/8)
+      return;
+   static Botan::EC_Group p256("secp256r1");
+   return check_ecc_math(p256, in, len);
+   }
diff --git a/third_party/botan/src/fuzzer/ecc_p384.cpp b/third_party/botan/src/fuzzer/ecc_p384.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ecc_p384.cpp
@@ -0,0 +1,15 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include "ecc_helper.h"
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*384/8)
+      return;
+   static Botan::EC_Group p384("secp384r1");
+   return check_ecc_math(p384, in, len);
+   }
diff --git a/third_party/botan/src/fuzzer/ecc_p521.cpp b/third_party/botan/src/fuzzer/ecc_p521.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ecc_p521.cpp
@@ -0,0 +1,15 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include "ecc_helper.h"
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*(521+7)/8)
+      return;
+   static Botan::EC_Group p521("secp521r1");
+   return check_ecc_math(p521, in, len);
+   }
diff --git a/third_party/botan/src/fuzzer/fuzzers.h b/third_party/botan/src/fuzzer/fuzzers.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/fuzzers.h
@@ -0,0 +1,150 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FUZZER_DRIVER_H_
+#define BOTAN_FUZZER_DRIVER_H_
+
+#include <stdint.h>
+#include <stdlib.h> // for setenv
+#include <iostream>
+#include <vector>
+#include <botan/exceptn.h>
+#include <botan/chacha_rng.h>
+
+static const size_t max_fuzzer_input_size = 8192;
+
+extern void fuzz(const uint8_t in[], size_t len);
+
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv);
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len);
+
+extern "C" int LLVMFuzzerInitialize(int *, char ***)
+   {
+   /*
+   * This disables the mlock pool, as overwrites within the pool are
+   * opaque to ASan or other instrumentation.
+   */
+   ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", 1);
+   return 0;
+   }
+
+// Called by main() in libFuzzer or in main for AFL below
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len)
+   {
+   if(len <= max_fuzzer_input_size)
+      {
+      fuzz(in, len);
+      }
+   return 0;
+   }
+
+// Some helpers for the fuzzer jigs
+
+inline Botan::RandomNumberGenerator& fuzzer_rng()
+   {
+   static Botan::ChaCha_RNG rng(Botan::secure_vector<uint8_t>(32));
+   return rng;
+   }
+
+#define FUZZER_WRITE_AND_CRASH(expr) \
+   do { std::cerr << expr; abort(); } while(0)
+
+#define FUZZER_ASSERT_EQUAL(x, y) do {                                  \
+   if(x != y) {                                                         \
+      FUZZER_WRITE_AND_CRASH(#x << " = " << x << " !=\n"                \
+                             << #y << " = " << y << "\n");              \
+   } } while(0)
+
+#define FUZZER_ASSERT_TRUE(e)                                           \
+   do {                                                                 \
+   if(!(e)) {                                                           \
+      FUZZER_WRITE_AND_CRASH("Expression " << #e << " was false");      \
+   } } while(0)
+
+#if defined(BOTAN_FUZZER_IS_AFL) || defined(BOTAN_FUZZER_IS_TEST)
+
+/* Stub for AFL */
+
+#if defined(BOTAN_FUZZER_IS_AFL) && !defined(__AFL_COMPILER)
+   #error "Build configured for AFL but not being compiled by AFL compiler"
+#endif
+
+#if defined(BOTAN_FUZZER_IS_TEST)
+
+#include <fstream>
+
+namespace {
+
+int fuzz_files(char* files[])
+   {
+   for(size_t i = 0; files[i]; ++i)
+      {
+      std::ifstream in(files[i]);
+
+      if(in.good())
+         {
+         std::vector<uint8_t> buf(max_fuzzer_input_size);
+         in.read((char*)buf.data(), buf.size());
+         const size_t got = in.gcount();
+         buf.resize(got);
+         buf.shrink_to_fit();
+
+         LLVMFuzzerTestOneInput(buf.data(), got);
+         }
+      }
+
+   return 0;
+   }
+
+}
+
+#endif
+
+int main(int argc, char* argv[])
+   {
+   LLVMFuzzerInitialize(&argc, &argv);
+
+#if defined(BOTAN_FUZZER_IS_TEST)
+   if(argc > 1)
+      {
+      return fuzz_files(&argv[1]);
+      }
+#endif
+
+#if defined(__AFL_LOOP)
+   while(__AFL_LOOP(1000))
+#endif
+      {
+      std::vector<uint8_t> buf(max_fuzzer_input_size);
+      std::cin.read((char*)buf.data(), buf.size());
+      const size_t got = std::cin.gcount();
+
+      buf.resize(got);
+      buf.shrink_to_fit();
+
+      LLVMFuzzerTestOneInput(buf.data(), got);
+      }
+   }
+
+#elif defined(BOTAN_FUZZER_IS_KLEE)
+
+#include <klee/klee.h>
+
+int main(int argc, char* argv[])
+   {
+   LLVMFuzzerInitialize(&argc, &argv);
+
+   uint8_t input[max_fuzzer_input_size] = { 0 };
+   klee_make_symbolic(&input, sizeof(input), "input");
+
+   size_t input_len = klee_range(0, sizeof(input), "input_len");
+
+   LLVMFuzzerTestOneInput(input, input_len);
+   }
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/fuzzer/invert.cpp b/third_party/botan/src/fuzzer/invert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/invert.cpp
@@ -0,0 +1,82 @@
+/*
+* (C) 2015,2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/numthry.h>
+
+namespace {
+
+Botan::BigInt ref_inverse_mod(const Botan::BigInt& n, const Botan::BigInt& mod)
+   {
+   if(n == 0 || mod < 2)
+      return 0;
+   if(n.is_even() && mod.is_even())
+      return 0;
+   Botan::BigInt u = mod, v = n;
+   Botan::BigInt A = 1, B = 0, C = 0, D = 1;
+
+   while(u.is_nonzero())
+      {
+      const size_t u_zero_bits = Botan::low_zero_bits(u);
+      u >>= u_zero_bits;
+      for(size_t i = 0; i != u_zero_bits; ++i)
+         {
+         if(A.is_odd() || B.is_odd())
+            { A += n; B -= mod; }
+         A >>= 1; B >>= 1;
+         }
+
+      const size_t v_zero_bits = Botan::low_zero_bits(v);
+      v >>= v_zero_bits;
+      for(size_t i = 0; i != v_zero_bits; ++i)
+         {
+         if(C.is_odd() || D.is_odd())
+            { C += n; D -= mod; }
+         C >>= 1; D >>= 1;
+         }
+
+      if(u >= v) { u -= v; A -= C; B -= D; }
+      else       { v -= u; C -= A; D -= B; }
+      }
+
+   if(v != 1)
+      return 0; // no modular inverse
+
+   while(D.is_negative()) D += mod;
+   while(D >= mod) D -= mod;
+
+   return D;
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static const size_t max_bits = 4096;
+
+   if(len > 2*max_bits/8)
+      return;
+
+   const Botan::BigInt x = Botan::BigInt::decode(in, len / 2);
+   Botan::BigInt mod = Botan::BigInt::decode(in + len / 2, len - len / 2);
+
+   if(mod < 2)
+      return;
+
+   const Botan::BigInt lib = Botan::inverse_mod(x, mod);
+   const Botan::BigInt ref = ref_inverse_mod(x, mod);
+
+   if(ref != lib)
+      {
+      FUZZER_WRITE_AND_CRASH("X = " << x << "\n"
+                             << "Mod = " << mod << "\n"
+                             << "GCD(X,Mod) = " << gcd(x, mod) << "\n"
+                             << "RefInv(X,Mod) = " << ref << "\n"
+                             << "LibInv(X,Mod)  = " << lib << "\n"
+                             << "RefCheck = " << (x*ref)%mod << "\n"
+                             << "LibCheck  = " << (x*lib)%mod << "\n");
+      }
+   }
+
diff --git a/third_party/botan/src/fuzzer/mem_pool.cpp b/third_party/botan/src/fuzzer/mem_pool.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/mem_pool.cpp
@@ -0,0 +1,193 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/internal/mem_pool.h>
+#include <botan/internal/bit_ops.h>
+#include <vector>
+#include <map>
+#include <utility>
+
+#include <stdlib.h>
+
+namespace {
+
+size_t compute_expected_alignment(size_t plen)
+   {
+   if(Botan::is_power_of_2(plen))
+      {
+      return plen;
+      }
+   else
+      {
+      return 8;
+      }
+   }
+
+struct RawPage
+   {
+   public:
+      RawPage(void* p) : m_p(p) {}
+      ~RawPage() { std::free(m_p); }
+
+      RawPage(const RawPage& other) = default;
+      RawPage& operator=(const RawPage& other) = default;
+
+      RawPage(RawPage&& other) : m_p(nullptr)
+         {
+         std::swap(m_p, other.m_p);
+         }
+
+      RawPage& operator=(RawPage&& other)
+         {
+         if(this != &other)
+            {
+            std::swap(m_p, other.m_p);
+            }
+         return (*this);
+         }
+
+      void* ptr() const { return m_p; }
+   private:
+      void* m_p;
+   };
+
+std::vector<RawPage> allocate_raw_pages(size_t count, size_t page_size)
+   {
+   std::vector<RawPage> pages;
+   pages.reserve(count);
+
+   for(size_t i = 0; i != count; ++i)
+      {
+      void* ptr = nullptr;
+
+      int rc = ::posix_memalign(&ptr, page_size, page_size);
+      FUZZER_ASSERT_EQUAL(rc, 0);
+
+      if(ptr)
+         {
+         pages.push_back(RawPage(ptr));
+         }
+      }
+
+   return pages;
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t in_len)
+   {
+   const size_t page_count = 4;
+   const size_t page_size = 4096;
+
+   // static to avoid repeated allocations
+   static std::vector<RawPage> raw_mem = allocate_raw_pages(page_count, page_size);
+
+   std::vector<void*> mem_pages;
+   mem_pages.reserve(raw_mem.size());
+   for(size_t i = 0; i != raw_mem.size(); ++i)
+      mem_pages.push_back(raw_mem[i].ptr());
+
+   Botan::Memory_Pool pool(mem_pages, page_size);
+   std::map<uint8_t*, size_t> ptrs;
+
+   while(in_len > 0)
+      {
+      const uint8_t op = in[0] % 2;
+      size_t idx = (in[0] >> 1);
+      in += 1;
+      in_len -= 1;
+
+      if(in_len > 0 && idx < 4)
+         {
+         idx = idx * 256 + in[0];
+         in += 1;
+         in_len -= 1;
+         }
+
+      //printf("%d %d\n", op, idx);
+
+      if(op == 0)
+         {
+         const size_t plen = idx + 1; // ensure non-zero
+         uint8_t* p = static_cast<uint8_t*>(pool.allocate(plen));
+
+         if(p)
+            {
+            const size_t expected_alignment = compute_expected_alignment(plen);
+            const size_t alignment = reinterpret_cast<uintptr_t>(p) % expected_alignment;
+            if(alignment != 0)
+               {
+               FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer " << static_cast<void*>(p) << " for len " << plen
+                                      << " expected " << expected_alignment << " got " << alignment);
+               }
+
+            //printf("alloc %d -> %p\n", plen, p);
+
+            for(size_t i = 0; i != plen; ++i)
+               {
+               if(p[i] != 0)
+                  {
+                  FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory");
+                  }
+               }
+
+            // verify it becomes zeroed later
+            std::memset(p, idx, plen);
+
+            auto insert = ptrs.insert(std::make_pair(p, plen));
+            if(insert.second == false)
+               {
+               FUZZER_WRITE_AND_CRASH("Pointer " << static_cast<void*>(p) << " already existed\n");
+               }
+
+            auto itr = insert.first;
+
+            // Verify this pointer doesn't overlap with the one before it
+            if(itr != ptrs.begin())
+               {
+               auto before = std::prev(itr);
+               auto ptr_before = *before;
+
+               if(ptr_before.first + ptr_before.second > p)
+                  {
+                  FUZZER_WRITE_AND_CRASH("Previous " << static_cast<void*>(ptr_before.first) << "/" << ptr_before.second <<
+                                         " overlaps with new " << static_cast<void*>(p));
+                  }
+               }
+
+            auto after = std::next(itr);
+
+            if(after != ptrs.end())
+               {
+               if(p + plen > after->first)
+                  {
+                  FUZZER_WRITE_AND_CRASH("New " << static_cast<void*>(p) << "/" << plen
+                                         << " overlaps following " << static_cast<void*>(after->first));
+                  }
+               }
+            }
+         }
+      else if(op == 1)
+         {
+         if(ptrs.empty())
+            return;
+
+         size_t which_ptr = idx % ptrs.size();
+
+         auto itr = ptrs.begin();
+
+         while(which_ptr-- > 0)
+            {
+            ++itr;
+            }
+
+         //printf("free %p %d\n", itr->first, itr->second);
+         FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second));
+         ptrs.erase(itr);
+         }
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/mode_padding.cpp b/third_party/botan/src/fuzzer/mode_padding.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/mode_padding.cpp
@@ -0,0 +1,169 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/mode_pad.h>
+#include <botan/internal/tls_cbc.h>
+
+namespace {
+
+size_t ref_pkcs7_unpad(const uint8_t in[], size_t len)
+   {
+   if(len <= 2)
+      return len;
+
+   const size_t padding_length = in[len-1];
+
+   if(padding_length == 0 || padding_length > len)
+      return len;
+
+   const size_t padding_start = len - padding_length;
+
+   for(size_t i = padding_start; i != len; ++i)
+      {
+      if(in[i] != padding_length)
+         return len;
+      }
+
+   return len - padding_length;
+   }
+
+size_t ref_x923_unpad(const uint8_t in[], size_t len)
+   {
+   if(len <= 2)
+      return len;
+
+   const size_t padding_length = in[len-1];
+
+   if(padding_length == 0 || padding_length > len)
+      return len;
+   const size_t padding_start = len - padding_length;
+
+   for(size_t i = padding_start; i != len - 1; ++i)
+      {
+      if(in[i] != 0)
+         {
+         return len;
+         }
+      }
+
+   return len - padding_length;
+   }
+
+size_t ref_oneandzero_unpad(const uint8_t in[], size_t len)
+   {
+   if(len <= 2)
+      return len;
+
+   size_t idx = len - 1;
+
+   for(;;)
+      {
+      if(in[idx] == 0)
+         {
+         if(idx == 0)
+            return len;
+         idx -= 1;
+         continue;
+         }
+      else if(in[idx] == 0x80)
+         {
+         return idx;
+         }
+      else
+         return len;
+      }
+
+   return len;
+   }
+
+size_t ref_esp_unpad(const uint8_t in[], size_t len)
+   {
+   if(len <= 2)
+      return len;
+
+   const size_t padding_bytes = in[len - 1];
+
+   if(padding_bytes == 0 || padding_bytes > len)
+      {
+      return len;
+      }
+
+   const size_t padding_start = len - padding_bytes;
+   for(size_t i = padding_start; i != len; ++i)
+      {
+      if(in[i] != (i - padding_start + 1))
+         {
+         return len;
+         }
+      }
+
+   return len - padding_bytes;
+   }
+
+uint16_t ref_tls_cbc_unpad(const uint8_t in[], size_t len)
+   {
+   if(len == 0)
+      return 0;
+
+   const size_t padding_length = in[(len-1)];
+
+   if(padding_length >= len)
+      return 0;
+
+   /*
+   * TLS v1.0 and up require all the padding bytes be the same value
+   * and allows up to 255 bytes.
+   */
+   for(size_t i = 0; i != 1 + padding_length; ++i)
+      {
+      if(in[(len-i-1)] != padding_length)
+         return 0;
+      }
+   return padding_length + 1;
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static Botan::PKCS7_Padding pkcs7;
+   static Botan::ANSI_X923_Padding x923;
+   static Botan::OneAndZeros_Padding oneandzero;
+   static Botan::ESP_Padding esp;
+
+   if(pkcs7.valid_blocksize(len))
+      {
+      const size_t ct_pkcs7 = pkcs7.unpad(in, len);
+      const size_t ref_pkcs7 = ref_pkcs7_unpad(in, len);
+      FUZZER_ASSERT_EQUAL(ct_pkcs7, ref_pkcs7);
+      }
+
+   if(x923.valid_blocksize(len))
+      {
+      const size_t ct_x923 = x923.unpad(in, len);
+      const size_t ref_x923 = ref_x923_unpad(in, len);
+      FUZZER_ASSERT_EQUAL(ct_x923, ref_x923);
+      }
+
+   if(oneandzero.valid_blocksize(len))
+      {
+      const size_t ct_oneandzero = oneandzero.unpad(in, len);
+      const size_t ref_oneandzero = ref_oneandzero_unpad(in, len);
+      FUZZER_ASSERT_EQUAL(ct_oneandzero, ref_oneandzero);
+      }
+
+   if(esp.valid_blocksize(len))
+      {
+      const size_t ct_esp = esp.unpad(in, len);
+      const size_t ref_esp = ref_esp_unpad(in, len);
+      FUZZER_ASSERT_EQUAL(ct_esp, ref_esp);
+      }
+
+   const uint16_t ct_cbc = Botan::TLS::check_tls_cbc_padding(in, len);
+   const uint16_t ref_cbc = ref_tls_cbc_unpad(in, len);
+   FUZZER_ASSERT_EQUAL(ct_cbc, ref_cbc);
+   }
diff --git a/third_party/botan/src/fuzzer/oaep.cpp b/third_party/botan/src/fuzzer/oaep.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/oaep.cpp
@@ -0,0 +1,102 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+
+#include <botan/oaep.h>
+#include <botan/hex.h>
+
+namespace {
+
+Botan::secure_vector<uint8_t>
+ref_oaep_unpad(uint8_t& valid_mask,
+               const uint8_t in[], size_t len,
+               const Botan::secure_vector<uint8_t>& Phash)
+   {
+   const size_t hlen = Phash.size();
+
+   if(len < 2*hlen + 1)
+      {
+      return Botan::secure_vector<uint8_t>();
+      }
+
+   for(size_t i = hlen; i != 2*hlen; ++i)
+      {
+      if(in[i] != Phash[i-hlen])
+         {
+         return Botan::secure_vector<uint8_t>();
+         }
+      }
+
+   for(size_t i = 2*hlen; i != len; ++i)
+      {
+      if(in[i] != 0x00 && in[i] != 0x01)
+         {
+         return Botan::secure_vector<uint8_t>();
+         }
+
+      if(in[i] == 0x01)
+         {
+         valid_mask = 0xFF;
+         return Botan::secure_vector<uint8_t>(in + i + 1, in + len);
+         }
+      }
+
+   return Botan::secure_vector<uint8_t>();
+   }
+
+inline bool all_zeros(const Botan::secure_vector<uint8_t>& v)
+   {
+   for(size_t i = 0; i != v.size(); ++i)
+      {
+      if(v[i] != 0)
+         return false;
+      }
+   return true;
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static const Botan::secure_vector<uint8_t> Phash = { 1, 2, 3, 4 };
+
+   uint8_t lib_valid_mask = 0;
+   const Botan::secure_vector<uint8_t> lib_output = Botan::oaep_find_delim(lib_valid_mask, in, len, Phash);
+   FUZZER_ASSERT_TRUE(lib_valid_mask == 0 || lib_valid_mask == 0xFF);
+
+   uint8_t ref_valid_mask = 0;
+   const Botan::secure_vector<uint8_t> ref_output = ref_oaep_unpad(ref_valid_mask, in, len, Phash);
+   FUZZER_ASSERT_TRUE(ref_valid_mask == 0 || ref_valid_mask == 0xFF);
+
+   if(ref_valid_mask == 0xFF && lib_valid_mask == 0x00)
+      {
+      FUZZER_WRITE_AND_CRASH("Ref accepted but library rejected, output " << Botan::hex_encode(ref_output) << "\n");
+      }
+   else if(ref_valid_mask == 0x00 && lib_valid_mask == 0xFF)
+      {
+      FUZZER_WRITE_AND_CRASH("Lib accepted but ref rejected, output = " << Botan::hex_encode(lib_output) << "\n");
+      }
+
+   if(ref_valid_mask == 0x00)
+      {
+      FUZZER_ASSERT_TRUE(all_zeros(ref_output));
+      }
+
+   if(lib_valid_mask == 0x00)
+      {
+      FUZZER_ASSERT_TRUE(all_zeros(lib_output));
+      }
+
+   if(ref_valid_mask && lib_valid_mask)
+      {
+      if(ref_output != lib_output)
+         {
+         FUZZER_WRITE_AND_CRASH("Ref and lib both accepted but produced different output:"
+                                << " ref = " << Botan::hex_encode(ref_output)
+                                << " lib = " << Botan::hex_encode(lib_output));
+         }
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/ocsp.cpp b/third_party/botan/src/fuzzer/ocsp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ocsp.cpp
@@ -0,0 +1,17 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/ocsp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   try
+      {
+      Botan::OCSP::Response response(in, len);
+      }
+   catch(Botan::Exception& e) { }
+   }
diff --git a/third_party/botan/src/fuzzer/os2ecp.cpp b/third_party/botan/src/fuzzer/os2ecp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/os2ecp.cpp
@@ -0,0 +1,44 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/ec_group.h>
+#include <botan/point_gfp.h>
+
+namespace {
+
+void check_os2ecp(const Botan::EC_Group& group, const uint8_t in[], size_t len)
+   {
+   try
+      {
+      Botan::PointGFp point = group.OS2ECP(in, len);
+      }
+   catch(Botan::Exception& e) {}
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len >= 256)
+      return;
+
+   static Botan::EC_Group p192("secp192r1");
+   static Botan::EC_Group p224("secp224r1");
+   static Botan::EC_Group p256("secp256r1");
+   static Botan::EC_Group p384("secp384r1");
+   static Botan::EC_Group p521("secp521r1");
+   static Botan::EC_Group bp256("brainpool256r1");
+   static Botan::EC_Group bp512("brainpool512r1");
+
+   check_os2ecp(p192, in, len);
+   check_os2ecp(p224, in, len);
+   check_os2ecp(p256, in, len);
+   check_os2ecp(p384, in, len);
+   check_os2ecp(p521, in, len);
+   check_os2ecp(p521, in, len);
+   check_os2ecp(bp256, in, len);
+   check_os2ecp(bp512, in, len);
+   }
diff --git a/third_party/botan/src/fuzzer/pkcs1.cpp b/third_party/botan/src/fuzzer/pkcs1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/pkcs1.cpp
@@ -0,0 +1,75 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+
+#include <botan/eme_pkcs.h>
+#include <botan/hex.h>
+
+namespace {
+
+std::vector<uint8_t> simple_pkcs1_unpad(const uint8_t in[], size_t len)
+   {
+   if(len < 10)
+      throw Botan::Decoding_Error("bad len");
+
+   if(in[0] != 0 || in[1] != 2)
+      throw Botan::Decoding_Error("bad header field");
+
+   for(size_t i = 2; i < len; ++i)
+      {
+      if(in[i] == 0)
+         {
+         if(i < 10) // at least 8 padding bytes required
+            throw Botan::Decoding_Error("insufficient padding bytes");
+         return std::vector<uint8_t>(in + i + 1, in + len);
+         }
+      }
+
+   throw Botan::Decoding_Error("delim not found");
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static Botan::EME_PKCS1v15 pkcs1;
+
+   Botan::secure_vector<uint8_t> lib_result;
+   std::vector<uint8_t> ref_result;
+   bool lib_rejected = false, ref_rejected = false;
+
+   try
+      {
+      uint8_t valid_mask = 0;
+      Botan::secure_vector<uint8_t> decoded = (static_cast<Botan::EME*>(&pkcs1))->unpad(valid_mask, in, len);
+
+      if(valid_mask == 0)
+         lib_rejected = true;
+      else if(valid_mask == 0xFF)
+         lib_rejected = false;
+      else
+         FUZZER_WRITE_AND_CRASH("Invalid valid_mask from unpad");
+      }
+   catch(Botan::Decoding_Error&) { lib_rejected = true; }
+
+   try
+      {
+      ref_result = simple_pkcs1_unpad(in, len);
+      }
+   catch(Botan::Decoding_Error& e) { ref_rejected = true; }
+
+   if(lib_rejected == true && ref_rejected == false)
+      {
+      FUZZER_WRITE_AND_CRASH("Library rejected input accepted by ref "
+                             << Botan::hex_encode(ref_result));
+      }
+   else if(ref_rejected == true && lib_rejected == false)
+      {
+      FUZZER_WRITE_AND_CRASH("Library accepted input rejected by ref "
+                             << Botan::hex_encode(lib_result));
+      }
+   // otherwise the two implementations agree
+   }
diff --git a/third_party/botan/src/fuzzer/pkcs8.cpp b/third_party/botan/src/fuzzer/pkcs8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/pkcs8.cpp
@@ -0,0 +1,27 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/ec_group.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   try
+      {
+      Botan::DataSource_Memory input(in, len);
+      std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input);
+      }
+   catch(Botan::Exception& e) { }
+
+   /*
+   * This avoids OOMs in OSS-Fuzz caused by storing precomputations
+   * for thousands of curves randomly generated by the fuzzer.
+   */
+   Botan::EC_Group::clear_registered_curve_data();
+   }
diff --git a/third_party/botan/src/fuzzer/pow_mod.cpp b/third_party/botan/src/fuzzer/pow_mod.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/pow_mod.cpp
@@ -0,0 +1,72 @@
+/*
+* (C) 2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+namespace {
+
+Botan::BigInt simple_power_mod(Botan::BigInt x,
+                               Botan::BigInt n,
+                               const Botan::BigInt& p)
+   {
+   if(n == 0)
+      {
+      if(p == 1)
+         return 0;
+      return 1;
+      }
+
+   Botan::Modular_Reducer mod_p(p);
+   Botan::BigInt y = 1;
+
+   while(n > 1)
+      {
+      if(n.is_odd())
+         {
+         y = mod_p.multiply(x, y);
+         }
+      x = mod_p.square(x);
+      n >>= 1;
+      }
+   return mod_p.multiply(x, y);
+   }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   static const size_t max_bits = 2048;
+
+   if(len % 3 != 0)
+      return;
+
+   const size_t part_size = len / 3;
+
+   if(part_size * 8 > max_bits)
+      return;
+
+   const Botan::BigInt g = Botan::BigInt::decode(in, part_size);
+   const Botan::BigInt x = Botan::BigInt::decode(in + part_size, part_size);
+   const Botan::BigInt p = Botan::BigInt::decode(in + 2*part_size, part_size);
+
+   try
+      {
+      const Botan::BigInt ref = simple_power_mod(g, x, p);
+      const Botan::BigInt z = Botan::power_mod(g, x, p);
+
+      if(ref != z)
+         {
+         FUZZER_WRITE_AND_CRASH("G = " << g << "\n"
+                                << "X = " << x << "\n"
+                                << "P = " << p << "\n"
+                                << "Z = " << z << "\n"
+                                << "R = " << ref << "\n");
+         }
+      }
+   catch(Botan::Exception& e) {}
+   }
diff --git a/third_party/botan/src/fuzzer/redc_p192.cpp b/third_party/botan/src/fuzzer/redc_p192.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/redc_p192.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*192/8)
+      return;
+
+   static const Botan::BigInt& prime = Botan::prime_p192();
+   static const Botan::BigInt prime_2 = prime * prime;
+   static Botan::Modular_Reducer prime_redc(prime);
+
+   Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+   if(input < prime_2)
+      {
+      const Botan::BigInt ref = prime_redc.reduce(input);
+
+      Botan::secure_vector<Botan::word> ws;
+      Botan::redc_p192(input, ws);
+
+      FUZZER_ASSERT_EQUAL(ref, input);
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/redc_p224.cpp b/third_party/botan/src/fuzzer/redc_p224.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/redc_p224.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*224/8)
+      return;
+
+   static const Botan::BigInt& prime = Botan::prime_p224();
+   static const Botan::BigInt prime_2 = prime * prime;
+   static Botan::Modular_Reducer prime_redc(prime);
+
+   Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+   if(input < prime_2)
+      {
+      const Botan::BigInt ref = prime_redc.reduce(input);
+
+      Botan::secure_vector<Botan::word> ws;
+      Botan::redc_p224(input, ws);
+
+      FUZZER_ASSERT_EQUAL(ref, input);
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/redc_p256.cpp b/third_party/botan/src/fuzzer/redc_p256.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/redc_p256.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*256/8)
+      return;
+
+   static const Botan::BigInt& prime = Botan::prime_p256();
+   static const Botan::BigInt prime_2 = prime * prime;
+   static Botan::Modular_Reducer prime_redc(prime);
+
+   Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+   if(input < prime_2)
+      {
+      const Botan::BigInt ref = prime_redc.reduce(input);
+
+      Botan::secure_vector<Botan::word> ws;
+      Botan::redc_p256(input, ws);
+
+      FUZZER_ASSERT_EQUAL(ref, input);
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/redc_p384.cpp b/third_party/botan/src/fuzzer/redc_p384.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/redc_p384.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*384/8)
+      return;
+
+   static const Botan::BigInt& prime = Botan::prime_p384();
+   static const Botan::BigInt prime_2 = prime * prime;
+   static Botan::Modular_Reducer prime_redc(prime);
+
+   Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+   if(input < prime_2)
+      {
+      const Botan::BigInt ref = prime_redc.reduce(input);
+
+      Botan::secure_vector<Botan::word> ws;
+      Botan::redc_p384(input, ws);
+
+      FUZZER_ASSERT_EQUAL(ref, input);
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/redc_p521.cpp b/third_party/botan/src/fuzzer/redc_p521.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/redc_p521.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > 2*(521+7)/8)
+      return;
+
+   static const Botan::BigInt& prime = Botan::prime_p521();
+   static const Botan::BigInt prime_2 = prime * prime;
+   static Botan::Modular_Reducer prime_redc(prime);
+
+   Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+   if(input < prime_2)
+      {
+      const Botan::BigInt ref = prime_redc.reduce(input);
+
+      Botan::secure_vector<Botan::word> ws;
+      Botan::redc_p521(input, ws);
+
+      FUZZER_ASSERT_EQUAL(ref, input);
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/ressol.cpp b/third_party/botan/src/fuzzer/ressol.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/ressol.cpp
@@ -0,0 +1,44 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   // Ressol is mostly used for ECC point decompression so best to test smaller sizes
+   static const size_t p_bits = 256;
+   static const Botan::BigInt p = random_prime(fuzzer_rng(), p_bits);
+   static const Botan::Modular_Reducer mod_p(p);
+
+   if(len > p_bits / 8)
+      return;
+
+   try
+      {
+      const Botan::BigInt a = Botan::BigInt::decode(in, len);
+      Botan::BigInt a_sqrt = Botan::ressol(a, p);
+
+      if(a_sqrt > 0)
+         {
+         const Botan::BigInt a_redc = mod_p.reduce(a);
+         const Botan::BigInt z = mod_p.square(a_sqrt);
+
+         if(z != a_redc)
+            {
+            FUZZER_WRITE_AND_CRASH("A = " << a << "\n"
+                                   << "P = " << p << "\n"
+                                   << "R = " << a_sqrt << "\n"
+                                   << "Z = " << z << "\n");
+            }
+         }
+      }
+   catch(Botan::Exception& e) {}
+
+   return;
+   }
+
diff --git a/third_party/botan/src/fuzzer/tls_client.cpp b/third_party/botan/src/fuzzer/tls_client.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/tls_client.cpp
@@ -0,0 +1,130 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_client.h>
+
+class Fuzzer_TLS_Client_Creds : public Botan::Credentials_Manager
+   {
+   public:
+      std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; }
+      std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }
+      Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override
+         {
+         return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
+         }
+   };
+
+class Fuzzer_TLS_Policy : public Botan::TLS::Policy
+   {
+   public:
+      std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+                                             bool have_srp) const
+         {
+         std::vector<uint16_t> ciphersuites;
+
+         for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites())
+            {
+            if(suite.valid() == false)
+               continue;
+
+            // Are we doing SRP?
+            if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+               continue;
+
+            if(!version.supports_aead_modes())
+               {
+               // Are we doing AEAD in a non-AEAD version?
+               if(suite.mac_algo() == "AEAD")
+                  continue;
+
+               // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+               if(suite.mac_algo() != "SHA-1")
+                  continue;
+               }
+
+            ciphersuites.push_back(suite.ciphersuite_code());
+            }
+
+         return ciphersuites;
+         }
+   };
+
+class Fuzzer_TLS_Client_Callbacks : public Botan::TLS::Callbacks
+   {
+   public:
+       void tls_emit_data(const uint8_t[], size_t) override
+         {
+         // discard
+         }
+
+      void tls_record_received(uint64_t, const uint8_t[], size_t) override
+         {
+         // ignore peer data
+         }
+
+      void tls_alert(Botan::TLS::Alert) override
+         {
+         // ignore alert
+         }
+
+      bool tls_session_established(const Botan::TLS::Session&) override
+         {
+         return true; // cache it
+         }
+
+      void tls_verify_cert_chain(
+         const std::vector<Botan::X509_Certificate>& cert_chain,
+         const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+         const std::vector<Botan::Certificate_Store*>& trusted_roots,
+         Botan::Usage_Type usage,
+         const std::string& hostname,
+         const Botan::TLS::Policy& policy) override
+         {
+         try
+            {
+            // try to validate to exercise those code paths
+            Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses,
+                                                         trusted_roots, usage, hostname, policy);
+            }
+         catch(...)
+            {
+            // ignore validation result
+            }
+         }
+
+   };
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len == 0)
+      return;
+
+   Botan::TLS::Session_Manager_Noop session_manager;
+   Fuzzer_TLS_Policy policy;
+   Botan::TLS::Protocol_Version client_offer = Botan::TLS::Protocol_Version::TLS_V12;
+   Botan::TLS::Server_Information info("server.name", 443);
+   Fuzzer_TLS_Client_Callbacks callbacks;
+   Fuzzer_TLS_Client_Creds creds;
+
+   Botan::TLS::Client client(callbacks,
+                             session_manager,
+                             creds,
+                             policy,
+                             fuzzer_rng(),
+                             info,
+                             client_offer);
+
+   try
+      {
+      client.received_data(in, len);
+      }
+   catch(std::exception& e)
+      {
+      }
+
+   }
+
diff --git a/third_party/botan/src/fuzzer/tls_client_hello.cpp b/third_party/botan/src/fuzzer/tls_client_hello.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/tls_client_hello.cpp
@@ -0,0 +1,18 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_messages.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   try
+      {
+      std::vector<uint8_t> v(in, in + len);
+      Botan::TLS::Client_Hello ch(v);
+      }
+   catch(Botan::Exception& e) {}
+   }
diff --git a/third_party/botan/src/fuzzer/tls_server.cpp b/third_party/botan/src/fuzzer/tls_server.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/tls_server.cpp
@@ -0,0 +1,227 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_server.h>
+#include <botan/data_src.h>
+
+const char* fixed_rsa_key =
+   "-----BEGIN PRIVATE KEY-----\n"
+   "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCe6qqpMQVJ7zCJ\n"
+   "oSnpxia0yO6M7Ie3FGqPcd0DzueC+kWPvuHQ+PpP5vfO6qqRaDVII37PFX5NUZQm\n"
+   "GK/rAm7spjIHTCMgqSZ8pN13LU8m1gDwIdu9al16LXN9zZjB67uLlFn2trtLi234\n"
+   "i0cnyeF8IC0cz7tgCOzMSVEBcqJjkdgGrZ3WUgOXecVm2lXVrYlEiaSxFp4VOE9k\n"
+   "RFeVrELCjmNtc4hRd1yJsF+vObCtvyqGYQE1Qcb0MVSQDBHMkiUVmO6zuW7td5ef\n"
+   "O/1OyntQJGyVa+SnWbkSLCybta2J7MreHENrF5GA0K1KL140SNRHeWifRMuNQua7\n"
+   "qmKXMBTFAgMBAAECggEAIk3fxyQI0zvpy1vZ01ft1QqmzA7nAPNMSWi33/GS8iga\n"
+   "SfxXfKeySPs/tQ/dAARxs//NiOBH4mLgyxR7LQzaawU5OXALCSraXv+ruuUx990s\n"
+   "WKnGaG4EfbJAAwEVn47Gbkv425P4fEc91vAhzQn8PbIoatbAyOtESpjs/pYDTeC/\n"
+   "mnJId8gqO90cqyRECEMjk9sQ8iEjWPlik4ayGlUVbeeMu6/pJ9F8IZEgkLZiNDAB\n"
+   "4anmOFaT7EmqUjI4IlcaqfbbXyDXlvWUYukidEss+CNvPuqbQHBDnpFVvBxdDR2N\n"
+   "Uj2D5Xd5blcIe2/+1IVRnznjoQ5zvutzb7ThBmMehQKBgQDOITKG0ht2kXLxjVoR\n"
+   "r/pVpx+f3hs3H7wE0+vrLHoQgkVjpMWXQ47YuZTT9rCOOYNI2cMoH2D27t1j78/B\n"
+   "9kGYABUVpvQQ+6amqJDI1eYI6e68TPueEDjeALfSCdmPNiI3lZZrCIK9XLpkoy8K\n"
+   "tGYBRRJ+JJxjj1zPXj9SGshPgwKBgQDFXUtoxY3mCStH3+0b1qxGG9r1L5goHEmd\n"
+   "Am8WBYDheNpL0VqPNzouhuM/ZWMGyyAs/py6aLATe+qhR1uX5vn7LVZwjCSONZ4j\n"
+   "7ieEEUh1BHetPI1oI5PxgokRYfVuckotqVseanI/536Er3Yf2FXNQ1/ceVp9WykX\n"
+   "3mYTKMhQFwKBgQDKakcXpZNaZ5IcKdZcsBZ/rdGcR5sqEnursf9lvRNQytwg8Vkn\n"
+   "JSxNHlBLpV/TCh8lltHRwJ6TXhUBYij+KzhWbx5FWOErHDOWTMmArqtp7W6GcoJT\n"
+   "wVJWjxXzp8CApYQMWVSQXpckJL7UvHohZO0WKiHyxTjde5aD++TqV2qEyQKBgBbD\n"
+   "jvoTpy08K4DLxCZs2Uvw1I1pIuylbpwsdrGciuP2s38BM6fHH+/T4Qwj3osfDKQD\n"
+   "7gHWJ1Dn/wUBHQBlRLoC3bB3iZPZfVb5lhc2gxv0GvWhQVIcoGi/vJ2DpfJKPmIL\n"
+   "4ZWdg3X5dm9JaZ98rVDSj5D3ckd5J0E4hp95GbmbAoGBAJJHM4O9lx60tIjw9Sf/\n"
+   "QmKWyUk0NLnt8DcgRMW7fVxtzPNDy9DBKGIkDdWZ2s+ForICA3C9WSxBC1EOEHGG\n"
+   "xkg2xKt66CeutGroP6M191mHQrRClt1VbEYzQFX21BCk5kig9i/BURyoTHtFiV+t\n"
+   "kbf4VLg8Vk9u/R3RU1HsYWhe\n"
+   "-----END PRIVATE KEY-----\n";
+
+const char* fixed_rsa_cert =
+   "-----BEGIN CERTIFICATE-----\n"
+   "MIIDUDCCAjgCCQD7pIb1ZsoafjANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJW\n"
+   "VDEQMA4GA1UECAwHVmVybW9udDEWMBQGA1UEBwwNVGhlIEludGVybmV0czEUMBIG\n"
+   "A1UECgwLTWFuZ29zIFIgVXMxGzAZBgNVBAMMEnNlcnZlci5leGFtcGxlLmNvbTAe\n"
+   "Fw0xNjAxMDYxNzQ3MjNaFw0yNjAxMDMxNzQ3MjNaMGoxCzAJBgNVBAYTAlZUMRAw\n"
+   "DgYDVQQIDAdWZXJtb250MRYwFAYDVQQHDA1UaGUgSW50ZXJuZXRzMRQwEgYDVQQK\n"
+   "DAtNYW5nb3MgUiBVczEbMBkGA1UEAwwSc2VydmVyLmV4YW1wbGUuY29tMIIBIjAN\n"
+   "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnuqqqTEFSe8wiaEp6cYmtMjujOyH\n"
+   "txRqj3HdA87ngvpFj77h0Pj6T+b3zuqqkWg1SCN+zxV+TVGUJhiv6wJu7KYyB0wj\n"
+   "IKkmfKTddy1PJtYA8CHbvWpdei1zfc2Yweu7i5RZ9ra7S4tt+ItHJ8nhfCAtHM+7\n"
+   "YAjszElRAXKiY5HYBq2d1lIDl3nFZtpV1a2JRImksRaeFThPZERXlaxCwo5jbXOI\n"
+   "UXdcibBfrzmwrb8qhmEBNUHG9DFUkAwRzJIlFZjus7lu7XeXnzv9Tsp7UCRslWvk\n"
+   "p1m5Eiwsm7WtiezK3hxDaxeRgNCtSi9eNEjUR3lon0TLjULmu6pilzAUxQIDAQAB\n"
+   "MA0GCSqGSIb3DQEBCwUAA4IBAQA1eZGc/4V7z/E/6eG0hVkzoAZeuTcSP7WqBSx+\n"
+   "OP2yh0163UYjoa6nehmkKYQQ9PbYPZGzIcl+dBFyYzy6jcp0NdtzpWnTFrjl4rMq\n"
+   "akcQ1D0LTYjJXVP9G/vF/SvatOFeVTnQmLlLt/a8ZtRUINqejeZZPzH8ifzFW6tu\n"
+   "mlhTVIEKyPHpxClh5Y3ubw/mZYygekFTqMkTx3FwJxKU8J6rYGZxanWAODUIvCUo\n"
+   "Fxer1qC5Love3uWl3vXPLEZWZdORnExSRByzz2immBP2vX4zYZoeZRhTQ9ae1TIV\n"
+   "Dk02a/1AOJZdZReDbgXhlqaUx5pk/rzo4mDzvu5HSCeXmClz\n"
+   "-----END CERTIFICATE-----\n";
+
+class Fuzzer_TLS_Server_Creds : public Botan::Credentials_Manager
+   {
+   public:
+      Fuzzer_TLS_Server_Creds()
+         {
+         Botan::DataSource_Memory cert_in(fixed_rsa_cert);
+         Botan::DataSource_Memory key_in(fixed_rsa_key);
+
+         m_rsa_cert.reset(new Botan::X509_Certificate(cert_in));
+         //m_rsa_key.reset(Botan::PKCS8::load_key(key_in, fuzzer_rng());
+         }
+
+      std::vector<Botan::X509_Certificate> cert_chain(
+         const std::vector<std::string>& algos,
+         const std::string& /*type*/,
+         const std::string& /*hostname*/) override
+         {
+         std::vector<Botan::X509_Certificate> v;
+
+         for(auto algo : algos)
+            {
+            if(algo == "RSA")
+               {
+               v.push_back(*m_rsa_cert);
+               break;
+               }
+            }
+
+         return v;
+         }
+
+      Botan::Private_Key* private_key_for(const Botan::X509_Certificate& /*cert*/,
+                                          const std::string& /*type*/,
+                                          const std::string& /*context*/) override
+         {
+         return m_rsa_key.get();
+         }
+
+      std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; }
+      std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }
+      Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override
+         {
+         return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
+         }
+   private:
+      std::unique_ptr<Botan::X509_Certificate> m_rsa_cert;
+      std::unique_ptr<Botan::Private_Key> m_rsa_key;
+   };
+
+class Fuzzer_TLS_Policy : public Botan::TLS::Policy
+   {
+   public:
+      std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+                                             bool have_srp) const
+         {
+         std::vector<uint16_t> ciphersuites;
+
+         for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites())
+            {
+            if(suite.valid() == false)
+               continue;
+
+            // Are we doing SRP?
+            if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+               continue;
+
+            if(!version.supports_aead_modes())
+               {
+               // Are we doing AEAD in a non-AEAD version?
+               if(suite.mac_algo() == "AEAD")
+                  continue;
+
+               // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+               if(suite.mac_algo() != "SHA-1")
+                  continue;
+               }
+
+            ciphersuites.push_back(suite.ciphersuite_code());
+            }
+
+         return ciphersuites;
+         }
+   };
+
+class Fuzzer_TLS_Server_Callbacks : public Botan::TLS::Callbacks
+   {
+   public:
+       void tls_emit_data(const uint8_t[], size_t) override
+         {
+         // discard
+         }
+
+      void tls_record_received(uint64_t, const uint8_t[], size_t) override
+         {
+         // ignore peer data
+         }
+
+      void tls_alert(Botan::TLS::Alert) override
+         {
+         // ignore alert
+         }
+
+      bool tls_session_established(const Botan::TLS::Session&) override
+         {
+         return true; // cache it
+         }
+
+      std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+         {
+         if(client_protos.size() > 1)
+            return client_protos[0];
+         else
+            return "fuzzy";
+         }
+
+      void tls_verify_cert_chain(
+         const std::vector<Botan::X509_Certificate>& cert_chain,
+         const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+         const std::vector<Botan::Certificate_Store*>& trusted_roots,
+         Botan::Usage_Type usage,
+         const std::string& hostname,
+         const Botan::TLS::Policy& policy) override
+         {
+         try
+            {
+            // try to validate to exercise those code paths
+            Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses,
+                                                         trusted_roots, usage, hostname, policy);
+            }
+         catch(...)
+            {
+            // ignore validation result
+            }
+         }
+
+   };
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len <= 1)
+      return;
+
+   Botan::TLS::Session_Manager_Noop session_manager;
+   Fuzzer_TLS_Policy policy;
+   Botan::TLS::Server_Information info("server.name", 443);
+   Fuzzer_TLS_Server_Creds creds;
+   Fuzzer_TLS_Server_Callbacks callbacks;
+
+   const bool is_datagram = in[0] & 1;
+
+   Botan::TLS::Server server(callbacks,
+                             session_manager,
+                             creds,
+                             policy,
+                             fuzzer_rng(),
+                             is_datagram);
+
+   try
+      {
+      server.received_data(in + 1, len - 1);
+      }
+   catch(std::exception& e)
+      {
+      }
+   }
diff --git a/third_party/botan/src/fuzzer/uri.cpp b/third_party/botan/src/fuzzer/uri.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/uri.cpp
@@ -0,0 +1,20 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/internal/uri.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   if(len > max_fuzzer_input_size)
+      return;
+
+   try
+      {
+      Botan::URI::fromAny(std::string(reinterpret_cast<const char*>(in), len));
+      }
+   catch(Botan::Exception& e) { }
+   }
diff --git a/third_party/botan/src/fuzzer/x509_dn.cpp b/third_party/botan/src/fuzzer/x509_dn.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/fuzzer/x509_dn.cpp
@@ -0,0 +1,41 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/x509_dn.h>
+#include <botan/ber_dec.h>
+#include <botan/hex.h>
+
+void fuzz(const uint8_t in[], size_t len)
+   {
+   Botan::X509_DN dn1;
+   Botan::X509_DN dn2;
+
+   try
+      {
+      Botan::BER_Decoder ber(in, len);
+      dn1.decode_from(ber);
+      dn2.decode_from(ber);
+      }
+   catch(...) { return; }
+
+   const bool eq = dn1 == dn2;
+   const bool lt1 = dn1 < dn2;
+   const bool lt2 = dn2 < dn1;
+
+   if(lt1 == false && lt2 == false)
+      {
+      FUZZER_ASSERT_TRUE(eq);
+      }
+   else
+      {
+      // one is less than the other
+      FUZZER_ASSERT_TRUE(lt1 || lt2);
+
+      // it is not the case that both are less than the other
+      FUZZER_ASSERT_TRUE(!lt1 || !lt2);
+      }
+   }
diff --git a/third_party/botan/src/lib/asn1/alg_id.cpp b/third_party/botan/src/lib/asn1/alg_id.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/alg_id.cpp
@@ -0,0 +1,109 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/alg_id.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+                                         const std::vector<uint8_t>& param) :
+   oid(alg_id),
+   parameters(param)
+   {}
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+                                         const std::vector<uint8_t>& param) :
+   AlgorithmIdentifier(OID::from_string(alg_id), param)
+   {}
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+                                         Encoding_Option option) :
+   oid(alg_id),
+   parameters()
+   {
+   const uint8_t DER_NULL[] = { 0x05, 0x00 };
+
+   if(option == USE_NULL_PARAM)
+      parameters.assign(DER_NULL, DER_NULL + 2);
+   }
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+                                         Encoding_Option option) :
+   oid(OID::from_string(alg_id)),
+   parameters()
+   {
+   const uint8_t DER_NULL[] = { 0x05, 0x00 };
+
+   if(option == USE_NULL_PARAM)
+      parameters.assign(DER_NULL, DER_NULL + 2);
+   }
+
+bool AlgorithmIdentifier::parameters_are_null() const
+   {
+   return (parameters.size() == 2 && (parameters[0] == 0x05) && (parameters[1] == 0x00));
+   }
+
+bool operator==(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+   {
+   if(a1.get_oid() != a2.get_oid())
+      return false;
+
+   /*
+   * Treat NULL and empty as equivalent
+   */
+   if(a1.parameters_are_null_or_empty() &&
+      a2.parameters_are_null_or_empty())
+      {
+      return true;
+      }
+
+   return (a1.get_parameters() == a2.get_parameters());
+   }
+
+bool operator!=(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+   {
+   return !(a1 == a2);
+   }
+
+/*
+* DER encode an AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const
+   {
+   codec.start_cons(SEQUENCE)
+      .encode(get_oid())
+      .raw_bytes(get_parameters())
+   .end_cons();
+   }
+
+/*
+* Decode a BER encoded AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::decode_from(BER_Decoder& codec)
+   {
+   codec.start_cons(SEQUENCE)
+      .decode(oid)
+      .raw_bytes(parameters)
+   .end_cons();
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/alg_id.h b/third_party/botan/src/lib/asn1/alg_id.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/alg_id.h
@@ -0,0 +1,67 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ALGORITHM_IDENTIFIER_H_
+#define BOTAN_ALGORITHM_IDENTIFIER_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Algorithm Identifier
+*/
+class BOTAN_PUBLIC_API(2,0) AlgorithmIdentifier final : public ASN1_Object
+   {
+   public:
+      enum Encoding_Option { USE_NULL_PARAM, USE_EMPTY_PARAM };
+
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      AlgorithmIdentifier() = default;
+
+      AlgorithmIdentifier(const OID& oid, Encoding_Option enc);
+      AlgorithmIdentifier(const std::string& oid_name, Encoding_Option enc);
+
+      AlgorithmIdentifier(const OID& oid, const std::vector<uint8_t>& params);
+      AlgorithmIdentifier(const std::string& oid_name, const std::vector<uint8_t>& params);
+
+      const OID& get_oid() const { return oid; }
+      const std::vector<uint8_t>& get_parameters() const { return parameters; }
+
+      bool parameters_are_null() const;
+      bool parameters_are_empty() const { return parameters.empty(); }
+
+      bool parameters_are_null_or_empty() const
+         {
+         return parameters_are_empty() || parameters_are_null();
+         }
+
+   BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+      /*
+      * These values are public for historical reasons, but in a future release
+      * they will be made private. Do not access them.
+      */
+      OID oid;
+      std::vector<uint8_t> parameters;
+   };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_PUBLIC_API(2,0) operator==(const AlgorithmIdentifier&,
+                                      const AlgorithmIdentifier&);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const AlgorithmIdentifier&,
+                                      const AlgorithmIdentifier&);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_attribute.cpp b/third_party/botan/src/lib/asn1/asn1_attribute.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_attribute.cpp
@@ -0,0 +1,58 @@
+/*
+* Attribute
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_attribute.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const OID& attr_oid, const std::vector<uint8_t>& attr_value) :
+   oid(attr_oid),
+   parameters(attr_value)
+   {}
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const std::string& attr_oid,
+                     const std::vector<uint8_t>& attr_value) :
+   oid(OID::from_string(attr_oid)),
+   parameters(attr_value)
+   {}
+
+/*
+* DER encode a Attribute
+*/
+void Attribute::encode_into(DER_Encoder& codec) const
+   {
+   codec.start_cons(SEQUENCE)
+      .encode(oid)
+      .start_cons(SET)
+         .raw_bytes(parameters)
+      .end_cons()
+   .end_cons();
+   }
+
+/*
+* Decode a BER encoded Attribute
+*/
+void Attribute::decode_from(BER_Decoder& codec)
+   {
+   codec.start_cons(SEQUENCE)
+      .decode(oid)
+      .start_cons(SET)
+         .raw_bytes(parameters)
+      .end_cons()
+   .end_cons();
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_attribute.h b/third_party/botan/src/lib/asn1/asn1_attribute.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_attribute.h
@@ -0,0 +1,45 @@
+/*
+* ASN.1 Attribute
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_ATTRIBUTE_H_
+#define BOTAN_ASN1_ATTRIBUTE_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Attribute
+*/
+class BOTAN_PUBLIC_API(2,0) Attribute final : public ASN1_Object
+   {
+   public:
+      void encode_into(class DER_Encoder& to) const override;
+      void decode_from(class BER_Decoder& from) override;
+
+      Attribute() = default;
+      Attribute(const OID&, const std::vector<uint8_t>&);
+      Attribute(const std::string&, const std::vector<uint8_t>&);
+
+      const OID& get_oid() const { return oid; }
+
+      const std::vector<uint8_t>& get_parameters() const { return parameters; }
+
+   BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+      /*
+      * These values are public for historical reasons, but in a future release
+      * they will be made private. Do not access them.
+      */
+      OID oid;
+      std::vector<uint8_t> parameters;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_obj.cpp b/third_party/botan/src/lib/asn1/asn1_obj.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_obj.cpp
@@ -0,0 +1,238 @@
+/*
+* ASN.1 Internals
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/data_src.h>
+#include <botan/internal/stl_util.h>
+#include <sstream>
+
+namespace Botan {
+
+std::vector<uint8_t> ASN1_Object::BER_encode() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder der(output);
+   this->encode_into(der);
+   return output;
+   }
+
+/*
+* Check a type invariant on BER data
+*/
+void BER_Object::assert_is_a(ASN1_Tag type_tag_, ASN1_Tag class_tag_,
+                             const std::string& descr) const
+   {
+   if(this->is_a(type_tag_, class_tag_) == false)
+      {
+      std::stringstream msg;
+
+      msg << "Tag mismatch when decoding " << descr << " got ";
+
+      if(class_tag == NO_OBJECT && type_tag == NO_OBJECT)
+         {
+         msg << "EOF";
+         }
+      else
+         {
+         if(class_tag == UNIVERSAL || class_tag == CONSTRUCTED)
+            {
+            msg << asn1_tag_to_string(type_tag);
+            }
+         else
+            {
+            msg << std::to_string(type_tag);
+            }
+
+         msg << "/" << asn1_class_to_string(class_tag);
+         }
+
+      msg << " expected ";
+
+      if(class_tag_ == UNIVERSAL || class_tag_ == CONSTRUCTED)
+         {
+         msg << asn1_tag_to_string(type_tag_);
+         }
+      else
+         {
+         msg << std::to_string(type_tag_);
+         }
+
+      msg << "/" << asn1_class_to_string(class_tag_);
+
+      throw BER_Decoding_Error(msg.str());
+      }
+   }
+
+bool BER_Object::is_a(ASN1_Tag type_tag_, ASN1_Tag class_tag_) const
+   {
+   return (type_tag == type_tag_ && class_tag == class_tag_);
+   }
+
+bool BER_Object::is_a(int type_tag_, ASN1_Tag class_tag_) const
+   {
+   return is_a(ASN1_Tag(type_tag_), class_tag_);
+   }
+
+void BER_Object::set_tagging(ASN1_Tag t, ASN1_Tag c)
+   {
+   type_tag = t;
+   class_tag = c;
+   }
+
+std::string asn1_class_to_string(ASN1_Tag type)
+   {
+   switch(type)
+      {
+      case UNIVERSAL:
+         return "UNIVERSAL";
+      case CONSTRUCTED:
+         return "CONSTRUCTED";
+      case CONTEXT_SPECIFIC:
+         return "CONTEXT_SPECIFIC";
+      case APPLICATION:
+         return "APPLICATION";
+      case CONSTRUCTED | CONTEXT_SPECIFIC:
+         return "PRIVATE";
+      case Botan::NO_OBJECT:
+         return "NO_OBJECT";
+      default:
+         return "CLASS(" + std::to_string(static_cast<size_t>(type)) + ")";
+      }
+   }
+
+std::string asn1_tag_to_string(ASN1_Tag type)
+   {
+   switch(type)
+      {
+      case Botan::SEQUENCE:
+         return "SEQUENCE";
+
+      case Botan::SET:
+         return "SET";
+
+      case Botan::PRINTABLE_STRING:
+         return "PRINTABLE STRING";
+
+      case Botan::NUMERIC_STRING:
+         return "NUMERIC STRING";
+
+      case Botan::IA5_STRING:
+         return "IA5 STRING";
+
+      case Botan::T61_STRING:
+         return "T61 STRING";
+
+      case Botan::UTF8_STRING:
+         return "UTF8 STRING";
+
+      case Botan::VISIBLE_STRING:
+         return "VISIBLE STRING";
+
+      case Botan::BMP_STRING:
+         return "BMP STRING";
+
+      case Botan::UNIVERSAL_STRING:
+         return "UNIVERSAL STRING";
+
+      case Botan::UTC_TIME:
+         return "UTC TIME";
+
+      case Botan::GENERALIZED_TIME:
+         return "GENERALIZED TIME";
+
+      case Botan::OCTET_STRING:
+         return "OCTET STRING";
+
+      case Botan::BIT_STRING:
+         return "BIT STRING";
+
+      case Botan::ENUMERATED:
+         return "ENUMERATED";
+
+      case Botan::INTEGER:
+         return "INTEGER";
+
+      case Botan::NULL_TAG:
+         return "NULL";
+
+      case Botan::OBJECT_ID:
+         return "OBJECT";
+
+      case Botan::BOOLEAN:
+         return "BOOLEAN";
+
+      case Botan::NO_OBJECT:
+         return "NO_OBJECT";
+
+      default:
+         return "TAG(" + std::to_string(static_cast<size_t>(type)) + ")";
+      }
+   }
+
+/*
+* BER Decoding Exceptions
+*/
+BER_Decoding_Error::BER_Decoding_Error(const std::string& str) :
+   Decoding_Error("BER: " + str) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str, ASN1_Tag tag) :
+      BER_Decoding_Error(str + ": " + std::to_string(tag)) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str,
+                         ASN1_Tag tag1, ASN1_Tag tag2) :
+   BER_Decoding_Error(str + ": " + std::to_string(tag1) + "/" + std::to_string(tag2)) {}
+
+namespace ASN1 {
+
+/*
+* Put some arbitrary bytes into a SEQUENCE
+*/
+std::vector<uint8_t> put_in_sequence(const std::vector<uint8_t>& contents)
+   {
+   return ASN1::put_in_sequence(contents.data(), contents.size());
+   }
+
+std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len)
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .raw_bytes(bits, len)
+      .end_cons();
+   return output;
+   }
+
+/*
+* Convert a BER object into a string object
+*/
+std::string to_string(const BER_Object& obj)
+   {
+   return std::string(cast_uint8_ptr_to_char(obj.bits()),
+                      obj.length());
+   }
+
+/*
+* Do heuristic tests for BER data
+*/
+bool maybe_BER(DataSource& source)
+   {
+   uint8_t first_u8;
+   if(!source.peek_byte(first_u8))
+      {
+      BOTAN_ASSERT_EQUAL(source.read_byte(first_u8), 0, "Expected EOF");
+      throw Stream_IO_Error("ASN1::maybe_BER: Source was empty");
+      }
+
+   if(first_u8 == (SEQUENCE | CONSTRUCTED))
+      return true;
+   return false;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_obj.h b/third_party/botan/src/lib/asn1/asn1_obj.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_obj.h
@@ -0,0 +1,190 @@
+/*
+* ASN.1 Internals
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_H_
+#define BOTAN_ASN1_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+class BER_Decoder;
+class DER_Encoder;
+
+/**
+* ASN.1 Type and Class Tags
+* This will become an enum class in a future major release
+*/
+enum ASN1_Tag : uint32_t {
+   UNIVERSAL        = 0x00,
+   APPLICATION      = 0x40,
+   CONTEXT_SPECIFIC = 0x80,
+
+   CONSTRUCTED      = 0x20,
+
+   PRIVATE          = CONSTRUCTED | CONTEXT_SPECIFIC,
+
+   EOC              = 0x00,
+   BOOLEAN          = 0x01,
+   INTEGER          = 0x02,
+   BIT_STRING       = 0x03,
+   OCTET_STRING     = 0x04,
+   NULL_TAG         = 0x05,
+   OBJECT_ID        = 0x06,
+   ENUMERATED       = 0x0A,
+   SEQUENCE         = 0x10,
+   SET              = 0x11,
+
+   UTF8_STRING      = 0x0C,
+   NUMERIC_STRING   = 0x12,
+   PRINTABLE_STRING = 0x13,
+   T61_STRING       = 0x14,
+   IA5_STRING       = 0x16,
+   VISIBLE_STRING   = 0x1A,
+   UNIVERSAL_STRING = 0x1C,
+   BMP_STRING       = 0x1E,
+
+   UTC_TIME                = 0x17,
+   GENERALIZED_TIME        = 0x18,
+   UTC_OR_GENERALIZED_TIME = 0x19,
+
+   NO_OBJECT        = 0xFF00,
+   DIRECTORY_STRING = 0xFF01
+};
+
+std::string BOTAN_UNSTABLE_API asn1_tag_to_string(ASN1_Tag type);
+std::string BOTAN_UNSTABLE_API asn1_class_to_string(ASN1_Tag type);
+
+/**
+* Basic ASN.1 Object Interface
+*/
+class BOTAN_PUBLIC_API(2,0) ASN1_Object
+   {
+   public:
+      /**
+      * Encode whatever this object is into to
+      * @param to the DER_Encoder that will be written to
+      */
+      virtual void encode_into(DER_Encoder& to) const = 0;
+
+      /**
+      * Decode whatever this object is from from
+      * @param from the BER_Decoder that will be read from
+      */
+      virtual void decode_from(BER_Decoder& from) = 0;
+
+      /**
+      * Return the encoding of this object. This is a convenience
+      * method when just one object needs to be serialized. Use
+      * DER_Encoder for complicated encodings.
+      */
+      std::vector<uint8_t> BER_encode() const;
+
+      ASN1_Object() = default;
+      ASN1_Object(const ASN1_Object&) = default;
+      ASN1_Object & operator=(const ASN1_Object&) = default;
+      virtual ~ASN1_Object() = default;
+   };
+
+/**
+* BER Encoded Object
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Object final
+   {
+   public:
+      BER_Object() : type_tag(NO_OBJECT), class_tag(UNIVERSAL) {}
+
+      BER_Object(const BER_Object& other) = default;
+
+      BER_Object& operator=(const BER_Object& other) = default;
+
+      BER_Object(BER_Object&& other) = default;
+
+      BER_Object& operator=(BER_Object&& other) = default;
+
+      bool is_set() const { return type_tag != NO_OBJECT; }
+
+      ASN1_Tag tagging() const { return ASN1_Tag(type() | get_class()); }
+
+      ASN1_Tag type() const { return type_tag; }
+      ASN1_Tag get_class() const { return class_tag; }
+
+      const uint8_t* bits() const { return value.data(); }
+
+      size_t length() const { return value.size(); }
+
+      void assert_is_a(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                       const std::string& descr = "object") const;
+
+      bool is_a(ASN1_Tag type_tag, ASN1_Tag class_tag) const;
+
+      bool is_a(int type_tag, ASN1_Tag class_tag) const;
+
+   BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+      /*
+      * The following member variables are public for historical reasons, but
+      * will be made private in a future major release. Use the accessor
+      * functions above.
+      */
+      ASN1_Tag type_tag, class_tag;
+      secure_vector<uint8_t> value;
+
+   private:
+
+      friend class BER_Decoder;
+
+      void set_tagging(ASN1_Tag type_tag, ASN1_Tag class_tag);
+
+      uint8_t* mutable_bits(size_t length)
+         {
+         value.resize(length);
+         return value.data();
+         }
+   };
+
+/*
+* ASN.1 Utility Functions
+*/
+class DataSource;
+
+namespace ASN1 {
+
+std::vector<uint8_t> put_in_sequence(const std::vector<uint8_t>& val);
+std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len);
+std::string to_string(const BER_Object& obj);
+
+/**
+* Heuristics tests; is this object possibly BER?
+* @param src a data source that will be peeked at but not modified
+*/
+bool maybe_BER(DataSource& src);
+
+}
+
+/**
+* General BER Decoding Error Exception
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Decoding_Error : public Decoding_Error
+   {
+   public:
+      explicit BER_Decoding_Error(const std::string&);
+   };
+
+/**
+* Exception For Incorrect BER Taggings
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Bad_Tag final : public BER_Decoding_Error
+   {
+   public:
+      BER_Bad_Tag(const std::string& msg, ASN1_Tag tag);
+      BER_Bad_Tag(const std::string& msg, ASN1_Tag tag1, ASN1_Tag tag2);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_oid.cpp b/third_party/botan/src/lib/asn1/asn1_oid.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_oid.cpp
@@ -0,0 +1,215 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_oid.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+// returns empty on invalid
+std::vector<uint32_t> parse_oid_str(const std::string& oid)
+   {
+   try
+      {
+      std::string elem;
+      std::vector<uint32_t> oid_elems;
+
+      for(char c : oid)
+         {
+         if(c == '.')
+            {
+            if(elem.empty())
+               return std::vector<uint32_t>();
+            oid_elems.push_back(to_u32bit(elem));
+            elem.clear();
+            }
+         else
+            {
+            elem += c;
+            }
+         }
+
+      if(elem.empty())
+         return std::vector<uint32_t>();
+      oid_elems.push_back(to_u32bit(elem));
+
+      if(oid_elems.size() < 2)
+         return std::vector<uint32_t>();
+
+      return oid_elems;
+      }
+   catch(Invalid_Argument&) // thrown by to_u32bit
+      {
+      return std::vector<uint32_t>();
+      }
+   }
+
+}
+
+//static
+OID OID::from_string(const std::string& str)
+   {
+   if(str.empty())
+      throw Invalid_Argument("OID::from_string argument must be non-empty");
+
+   const OID o = OIDS::str2oid_or_empty(str);
+   if(o.has_value())
+      return o;
+
+   std::vector<uint32_t> raw = parse_oid_str(str);
+
+   if(raw.size() > 0)
+      return OID(std::move(raw));
+
+   throw Lookup_Error("No OID associated with name " + str);
+   }
+
+/*
+* ASN.1 OID Constructor
+*/
+OID::OID(const std::string& oid_str)
+   {
+   if(!oid_str.empty())
+      {
+      m_id = parse_oid_str(oid_str);
+
+      if(m_id.size() < 2 || m_id[0] > 2)
+         throw Invalid_OID(oid_str);
+      if((m_id[0] == 0 || m_id[0] == 1) && m_id[1] > 39)
+         throw Invalid_OID(oid_str);
+      }
+   }
+
+/*
+* Return this OID as a string
+*/
+std::string OID::to_string() const
+   {
+   std::ostringstream oss;
+   for(size_t i = 0; i != m_id.size(); ++i)
+      {
+      oss << m_id[i];
+      if(i != m_id.size() - 1)
+         oss << ".";
+      }
+   return oss.str();
+   }
+
+std::string OID::to_formatted_string() const
+   {
+   const std::string s = OIDS::oid2str_or_empty(*this);
+   if(!s.empty())
+      return s;
+   return this->to_string();
+   }
+
+/*
+* Append another component to the OID
+*/
+OID operator+(const OID& oid, uint32_t new_component)
+   {
+   std::vector<uint32_t> val = oid.get_components();
+   val.push_back(new_component);
+   return OID(std::move(val));
+   }
+
+/*
+* Compare two OIDs
+*/
+bool operator<(const OID& a, const OID& b)
+   {
+   const std::vector<uint32_t>& oid1 = a.get_components();
+   const std::vector<uint32_t>& oid2 = b.get_components();
+
+   return std::lexicographical_compare(oid1.begin(), oid1.end(),
+                                       oid2.begin(), oid2.end());
+   }
+
+/*
+* DER encode an OBJECT IDENTIFIER
+*/
+void OID::encode_into(DER_Encoder& der) const
+   {
+   if(m_id.size() < 2)
+      throw Invalid_Argument("OID::encode_into: OID is invalid");
+
+   std::vector<uint8_t> encoding;
+
+   if(m_id[0] > 2 || m_id[1] >= 40)
+      throw Encoding_Error("Invalid OID prefix, cannot encode");
+
+   encoding.push_back(static_cast<uint8_t>(40 * m_id[0] + m_id[1]));
+
+   for(size_t i = 2; i != m_id.size(); ++i)
+      {
+      if(m_id[i] == 0)
+         encoding.push_back(0);
+      else
+         {
+         size_t blocks = high_bit(m_id[i]) + 6;
+         blocks = (blocks - (blocks % 7)) / 7;
+
+         BOTAN_ASSERT(blocks > 0, "Math works");
+
+         for(size_t j = 0; j != blocks - 1; ++j)
+            encoding.push_back(0x80 | ((m_id[i] >> 7*(blocks-j-1)) & 0x7F));
+         encoding.push_back(m_id[i] & 0x7F);
+         }
+      }
+   der.add_object(OBJECT_ID, UNIVERSAL, encoding);
+   }
+
+/*
+* Decode a BER encoded OBJECT IDENTIFIER
+*/
+void OID::decode_from(BER_Decoder& decoder)
+   {
+   BER_Object obj = decoder.get_next_object();
+   if(obj.tagging() != OBJECT_ID)
+      throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
+
+   const size_t length = obj.length();
+   const uint8_t* bits = obj.bits();
+
+   if(length < 2 && !(length == 1 && bits[0] == 0))
+      {
+      throw BER_Decoding_Error("OID encoding is too short");
+      }
+
+   m_id.clear();
+   m_id.push_back(bits[0] / 40);
+   m_id.push_back(bits[0] % 40);
+
+   size_t i = 0;
+   while(i != length - 1)
+      {
+      uint32_t component = 0;
+      while(i != length - 1)
+         {
+         ++i;
+
+         if(component >> (32-7))
+            throw Decoding_Error("OID component overflow");
+
+         component = (component << 7) + (bits[i] & 0x7F);
+
+         if(!(bits[i] & 0x80))
+            break;
+         }
+      m_id.push_back(component);
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_oid.h b/third_party/botan/src/lib/asn1/asn1_oid.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_oid.h
@@ -0,0 +1,153 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_OID_H_
+#define BOTAN_ASN1_OID_H_
+
+#include <botan/asn1_obj.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* This class represents ASN.1 object identifiers.
+*/
+class BOTAN_PUBLIC_API(2,0) OID final : public ASN1_Object
+   {
+   public:
+
+      /**
+      * Create an uninitialied OID object
+      */
+      explicit OID() {}
+
+      /**
+      * Construct an OID from a string.
+      * @param str a string in the form "a.b.c" etc., where a,b,c are numbers
+      */
+      explicit OID(const std::string& str);
+
+      /**
+      * Initialize an OID from a sequence of integer values
+      */
+      explicit OID(std::initializer_list<uint32_t> init) : m_id(init) {}
+
+      /**
+      * Initialize an OID from a vector of integer values
+      */
+      explicit OID(std::vector<uint32_t>&& init) : m_id(init) {}
+
+      /**
+      * Construct an OID from a string.
+      * @param str a string in the form "a.b.c" etc., where a,b,c are numbers
+      *        or any known OID name (for example "RSA" or "X509v3.SubjectKeyIdentifier")
+      */
+      static OID from_string(const std::string& str);
+
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      /**
+      * Find out whether this OID is empty
+      * @return true is no OID value is set
+      */
+      bool empty() const { return m_id.empty(); }
+
+      /**
+      * Find out whether this OID has a value
+      * @return true is this OID has a value
+      */
+      bool has_value() const { return (m_id.empty() == false); }
+
+      /**
+      * Get this OID as list (vector) of its components.
+      * @return vector representing this OID
+      */
+      const std::vector<uint32_t>& get_components() const { return m_id; }
+
+      const std::vector<uint32_t>& get_id() const { return get_components(); }
+
+      /**
+      * Get this OID as a string
+      * @return string representing this OID
+      */
+      std::string BOTAN_DEPRECATED("Use OID::to_string") as_string() const
+         {
+         return this->to_string();
+         }
+
+      /**
+      * Get this OID as a dotted-decimal string
+      * @return string representing this OID
+      */
+      std::string to_string() const;
+
+      /**
+      * If there is a known name associated with this OID, return that.
+      * Otherwise return the result of to_string
+      */
+      std::string to_formatted_string() const;
+
+      /**
+      * Compare two OIDs.
+      * @return true if they are equal, false otherwise
+      */
+      bool operator==(const OID& other) const
+         {
+         return m_id == other.m_id;
+         }
+
+      /**
+      * Reset this instance to an empty OID.
+      */
+      void BOTAN_DEPRECATED("Avoid mutation of OIDs") clear() { m_id.clear(); }
+
+      /**
+      * Add a component to this OID.
+      * @param new_comp the new component to add to the end of this OID
+      * @return reference to *this
+      */
+      BOTAN_DEPRECATED("Avoid mutation of OIDs") OID& operator+=(uint32_t new_comp)
+         {
+         m_id.push_back(new_comp);
+         return (*this);
+         }
+
+   private:
+      std::vector<uint32_t> m_id;
+   };
+
+/**
+* Append another component onto the OID.
+* @param oid the OID to add the new component to
+* @param new_comp the new component to add
+*/
+OID BOTAN_PUBLIC_API(2,0) operator+(const OID& oid, uint32_t new_comp);
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is not equal to b
+*/
+inline bool operator!=(const OID& a, const OID& b)
+   {
+   return !(a == b);
+   }
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is lexicographically smaller than b
+*/
+bool BOTAN_PUBLIC_API(2,0) operator<(const OID& a, const OID& b);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_print.cpp b/third_party/botan/src/lib/asn1/asn1_print.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_print.cpp
@@ -0,0 +1,329 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_print.h>
+#include <botan/bigint.h>
+#include <botan/hex.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_time.h>
+#include <botan/asn1_str.h>
+#include <botan/oids.h>
+#include <iomanip>
+#include <sstream>
+#include <cctype>
+
+namespace Botan {
+
+namespace {
+
+bool all_printable_chars(const uint8_t bits[], size_t bits_len)
+   {
+   for(size_t i = 0; i != bits_len; ++i)
+      {
+      int c = bits[i];
+      if(c > 127)
+         return false;
+
+      if((std::isalnum(c) || c == '.' || c == ':' || c == '/' || c == '-') == false)
+         return false;
+      }
+   return true;
+   }
+
+/*
+* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
+*/
+bool possibly_a_general_name(const uint8_t bits[], size_t bits_len)
+   {
+   if(bits_len <= 2)
+      return false;
+
+   if(bits[0] != 0x82 && bits[0] != 0x86)
+      return false;
+
+   if(bits[1] != bits_len - 2)
+      return false;
+
+   if(all_printable_chars(bits + 2, bits_len - 2) == false)
+      return false;
+
+   return true;
+   }
+
+}
+
+std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const
+   {
+   std::ostringstream output;
+   print_to_stream(output, in, len);
+   return output.str();
+   }
+
+void ASN1_Formatter::print_to_stream(std::ostream& output,
+                                     const uint8_t in[],
+                                     size_t len) const
+   {
+   BER_Decoder dec(in, len);
+   decode(output, dec, 0);
+   }
+
+void ASN1_Formatter::decode(std::ostream& output,
+                            BER_Decoder& decoder,
+                            size_t level) const
+   {
+   BER_Object obj = decoder.get_next_object();
+
+   const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
+
+   while(obj.is_set())
+      {
+      const ASN1_Tag type_tag = obj.type();
+      const ASN1_Tag class_tag = obj.get_class();
+      const size_t length = obj.length();
+
+      /* hack to insert the tag+length back in front of the stuff now
+         that we've gotten the type info */
+      std::vector<uint8_t> bits;
+      DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
+
+      BER_Decoder data(bits);
+
+      if(class_tag & CONSTRUCTED)
+         {
+         BER_Decoder cons_info(obj.bits(), obj.length());
+
+         if(recurse_deeper)
+            {
+            output << format(type_tag, class_tag, level, length, "");
+            decode(output, cons_info, level + 1); // recurse
+            }
+         else
+            {
+            output << format(type_tag, class_tag, level, length,
+                             format_bin(type_tag, class_tag, bits));
+            }
+         }
+      else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+         {
+         bool success_parsing_cs = false;
+
+         if(m_print_context_specific)
+            {
+            try
+               {
+               if(possibly_a_general_name(bits.data(), bits.size()))
+                  {
+                  output << format(type_tag, class_tag, level, level,
+                                   std::string(cast_uint8_ptr_to_char(&bits[2]), bits.size() - 2));
+                  success_parsing_cs = true;
+                  }
+               else if(recurse_deeper)
+                  {
+                  std::vector<uint8_t> inner_bits;
+                  data.decode(inner_bits, type_tag);
+
+                  BER_Decoder inner(inner_bits);
+                  std::ostringstream inner_data;
+                  decode(inner_data, inner, level + 1); // recurse
+                  output << inner_data.str();
+                  success_parsing_cs = true;
+                  }
+               }
+            catch(...)
+               {
+               }
+            }
+
+         if(success_parsing_cs == false)
+            {
+            output << format(type_tag, class_tag, level, length,
+                             format_bin(type_tag, class_tag, bits));
+            }
+         }
+      else if(type_tag == OBJECT_ID)
+         {
+         OID oid;
+         data.decode(oid);
+
+         std::string out = OIDS::oid2str_or_empty(oid);
+         if(out.empty())
+            {
+            out = oid.to_string();
+            }
+         else
+            {
+            out += " [" + oid.to_string() + "]";
+            }
+
+         output << format(type_tag, class_tag, level, length, out);
+         }
+      else if(type_tag == INTEGER || type_tag == ENUMERATED)
+         {
+         BigInt number;
+
+         if(type_tag == INTEGER)
+            {
+            data.decode(number);
+            }
+         else if(type_tag == ENUMERATED)
+            {
+            data.decode(number, ENUMERATED, class_tag);
+            }
+
+         std::vector<uint8_t> rep = BigInt::encode(number);
+         if(rep.empty()) // if zero
+            rep.resize(1);
+
+         output << format(type_tag, class_tag, level, length, hex_encode(rep));
+         }
+      else if(type_tag == BOOLEAN)
+         {
+         bool boolean;
+         data.decode(boolean);
+         output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
+         }
+      else if(type_tag == NULL_TAG)
+         {
+         output << format(type_tag, class_tag, level, length, "");
+         }
+      else if(type_tag == OCTET_STRING || type_tag == BIT_STRING)
+         {
+         std::vector<uint8_t> decoded_bits;
+         data.decode(decoded_bits, type_tag);
+         bool printing_octet_string_worked = false;
+
+         if(recurse_deeper)
+            {
+            try
+               {
+               BER_Decoder inner(decoded_bits);
+
+               std::ostringstream inner_data;
+               decode(inner_data, inner, level + 1); // recurse
+
+               output << format(type_tag, class_tag, level, length, "");
+               output << inner_data.str();
+               printing_octet_string_worked = true;
+               }
+            catch(...)
+               {
+               }
+            }
+
+         if(!printing_octet_string_worked)
+            {
+            output << format(type_tag, class_tag, level, length,
+                             format_bin(type_tag, class_tag, decoded_bits));
+            }
+         }
+      else if(ASN1_String::is_string_type(type_tag))
+         {
+         ASN1_String str;
+         data.decode(str);
+         output << format(type_tag, class_tag, level, length, str.value());
+         }
+      else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
+         {
+         X509_Time time;
+         data.decode(time);
+         output << format(type_tag, class_tag, level, length, time.readable_string());
+         }
+      else
+         {
+         output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag)
+                << " type=" << static_cast<int>(type_tag) << "\n";
+         }
+
+      obj = decoder.get_next_object();
+      }
+   }
+
+namespace {
+
+std::string format_type(ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if(class_tag == UNIVERSAL)
+      return asn1_tag_to_string(type_tag);
+
+   if(class_tag == CONSTRUCTED && (type_tag == SEQUENCE || type_tag == SET))
+      return asn1_tag_to_string(type_tag);
+
+   std::string name;
+
+   if(class_tag & CONSTRUCTED)
+      name += "cons ";
+
+   name += "[" + std::to_string(type_tag) + "]";
+
+   if(class_tag & APPLICATION)
+      {
+      name += " appl";
+      }
+   if(class_tag & CONTEXT_SPECIFIC)
+      {
+      name += " context";
+      }
+
+   return name;
+   }
+
+}
+
+std::string ASN1_Pretty_Printer::format(ASN1_Tag type_tag,
+                                        ASN1_Tag class_tag,
+                                        size_t level,
+                                        size_t length,
+                                        const std::string& value) const
+   {
+   bool should_skip = false;
+
+   if(value.length() > m_print_limit)
+      {
+      should_skip = true;
+      }
+
+   if((type_tag == OCTET_STRING || type_tag == BIT_STRING) &&
+      value.length() > m_print_binary_limit)
+      {
+      should_skip = true;
+      }
+
+   level += m_initial_level;
+
+   std::ostringstream oss;
+
+   oss << "  d=" << std::setw(2) << level
+       << ", l=" << std::setw(4) << length << ":"
+       << std::string(level + 1, ' ') << format_type(type_tag, class_tag);
+
+   if(value != "" && !should_skip)
+      {
+      const size_t current_pos = static_cast<size_t>(oss.tellp());
+      const size_t spaces_to_align =
+         (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
+
+      oss << std::string(spaces_to_align, ' ') << value;
+      }
+
+   oss << "\n";
+
+   return oss.str();
+   }
+
+std::string ASN1_Pretty_Printer::format_bin(ASN1_Tag /*type_tag*/,
+                                            ASN1_Tag /*class_tag*/,
+                                            const std::vector<uint8_t>& vec) const
+   {
+   if(all_printable_chars(vec.data(), vec.size()))
+      {
+      return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size());
+      }
+   else
+      return hex_encode(vec);
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_print.h b/third_party/botan/src/lib/asn1/asn1_print.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_print.h
@@ -0,0 +1,125 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_PRINT_H_
+#define BOTAN_ASN1_PRINT_H_
+
+#include <botan/asn1_obj.h>
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+namespace Botan {
+
+class BER_Decoder;
+
+/**
+* Format ASN.1 data and call a virtual to format
+*/
+class BOTAN_PUBLIC_API(2,4) ASN1_Formatter
+   {
+   public:
+      virtual ~ASN1_Formatter() = default;
+
+      /**
+      * @param print_context_specific if true, try to parse nested context specific data.
+      * @param max_depth do not recurse more than this many times. If zero, recursion
+      *        is unbounded.
+      */
+      ASN1_Formatter(bool print_context_specific, size_t max_depth) :
+         m_print_context_specific(print_context_specific),
+         m_max_depth(max_depth)
+         {}
+
+      void print_to_stream(std::ostream& out,
+                           const uint8_t in[],
+                           size_t len) const;
+
+      std::string print(const uint8_t in[], size_t len) const;
+
+      template<typename Alloc>
+      std::string print(const std::vector<uint8_t, Alloc>& vec) const
+         {
+         return print(vec.data(), vec.size());
+         }
+
+   protected:
+      /**
+      * This is called for each element
+      */
+      virtual std::string format(ASN1_Tag type_tag,
+                                 ASN1_Tag class_tag,
+                                 size_t level,
+                                 size_t length,
+                                 const std::string& value) const = 0;
+
+      /**
+      * This is called to format binary elements that we don't know how to
+      * convert to a string The result will be passed as value to format; the
+      * tags are included as a hint to aid decoding.
+      */
+      virtual std::string format_bin(ASN1_Tag type_tag,
+                                     ASN1_Tag class_tag,
+                                     const std::vector<uint8_t>& vec) const = 0;
+
+   private:
+      void decode(std::ostream& output,
+                  BER_Decoder& decoder,
+                  size_t level) const;
+
+      const bool m_print_context_specific;
+      const size_t m_max_depth;
+   };
+
+/**
+* Format ASN.1 data into human readable output. The exact form of the output for
+* any particular input is not guaranteed and may change from release to release.
+*/
+class BOTAN_PUBLIC_API(2,4) ASN1_Pretty_Printer final : public ASN1_Formatter
+   {
+   public:
+      /**
+      * @param print_limit strings larger than this are not printed
+      * @param print_binary_limit binary strings larger than this are not printed
+      * @param print_context_specific if true, try to parse nested context specific data.
+      * @param initial_level the initial depth (0 or 1 are the only reasonable values)
+      * @param value_column ASN.1 values are lined up at this column in output
+      * @param max_depth do not recurse more than this many times. If zero, recursion
+      *        is unbounded.
+      */
+      ASN1_Pretty_Printer(size_t print_limit = 4096,
+                          size_t print_binary_limit = 2048,
+                          bool print_context_specific = true,
+                          size_t initial_level = 0,
+                          size_t value_column = 60,
+                          size_t max_depth = 64) :
+         ASN1_Formatter(print_context_specific, max_depth),
+         m_print_limit(print_limit),
+         m_print_binary_limit(print_binary_limit),
+         m_initial_level(initial_level),
+         m_value_column(value_column)
+         {}
+
+   private:
+      std::string format(ASN1_Tag type_tag,
+                         ASN1_Tag class_tag,
+                         size_t level,
+                         size_t length,
+                         const std::string& value) const override;
+
+      std::string format_bin(ASN1_Tag type_tag,
+                             ASN1_Tag class_tag,
+                             const std::vector<uint8_t>& vec) const override;
+
+      const size_t m_print_limit;
+      const size_t m_print_binary_limit;
+      const size_t m_initial_level;
+      const size_t m_value_column;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_str.cpp b/third_party/botan/src/lib/asn1/asn1_str.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_str.cpp
@@ -0,0 +1,153 @@
+/*
+* Simple ASN.1 String Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_str.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/charset.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Choose an encoding for the string
+*/
+ASN1_Tag choose_encoding(const std::string& str)
+   {
+   static const uint8_t IS_PRINTABLE[256] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+      0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00 };
+
+   for(size_t i = 0; i != str.size(); ++i)
+      {
+      if(!IS_PRINTABLE[static_cast<uint8_t>(str[i])])
+         {
+         return UTF8_STRING;
+         }
+      }
+   return PRINTABLE_STRING;
+   }
+
+void assert_is_string_type(ASN1_Tag tag)
+   {
+   if(!ASN1_String::is_string_type(tag))
+      {
+      throw Invalid_Argument("ASN1_String: Unknown string type " +
+                             std::to_string(tag));
+      }
+   }
+
+}
+
+//static
+bool ASN1_String::is_string_type(ASN1_Tag tag)
+   {
+   return (tag == NUMERIC_STRING ||
+           tag == PRINTABLE_STRING ||
+           tag == VISIBLE_STRING ||
+           tag == T61_STRING ||
+           tag == IA5_STRING ||
+           tag == UTF8_STRING ||
+           tag == BMP_STRING ||
+           tag == UNIVERSAL_STRING);
+   }
+
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str, ASN1_Tag t) : m_utf8_str(str), m_tag(t)
+   {
+   if(m_tag == DIRECTORY_STRING)
+      {
+      m_tag = choose_encoding(m_utf8_str);
+      }
+
+   assert_is_string_type(m_tag);
+   }
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str) :
+   m_utf8_str(str),
+   m_tag(choose_encoding(m_utf8_str))
+   {}
+
+/*
+* Return this string in ISO 8859-1 encoding
+*/
+std::string ASN1_String::iso_8859() const
+   {
+   return utf8_to_latin1(m_utf8_str);
+   }
+
+/*
+* DER encode an ASN1_String
+*/
+void ASN1_String::encode_into(DER_Encoder& encoder) const
+   {
+   if(m_data.empty())
+      {
+      encoder.add_object(tagging(), UNIVERSAL, m_utf8_str);
+      }
+   else
+      {
+      // If this string was decoded, reserialize using original encoding
+      encoder.add_object(tagging(), UNIVERSAL, m_data.data(), m_data.size());
+      }
+   }
+
+/*
+* Decode a BER encoded ASN1_String
+*/
+void ASN1_String::decode_from(BER_Decoder& source)
+   {
+   BER_Object obj = source.get_next_object();
+
+   assert_is_string_type(obj.type());
+
+   m_tag = obj.type();
+   m_data.assign(obj.bits(), obj.bits() + obj.length());
+
+   if(m_tag == BMP_STRING)
+      {
+      m_utf8_str = ucs2_to_utf8(m_data.data(), m_data.size());
+      }
+   else if(m_tag == UNIVERSAL_STRING)
+      {
+      m_utf8_str = ucs4_to_utf8(m_data.data(), m_data.size());
+      }
+   else
+      {
+      // All other supported string types are UTF-8 or some subset thereof
+      m_utf8_str = ASN1::to_string(obj);
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_str.h b/third_party/botan/src/lib/asn1/asn1_str.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_str.h
@@ -0,0 +1,55 @@
+/*
+* ASN.1 string type
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_STRING_H_
+#define BOTAN_ASN1_STRING_H_
+
+#include <botan/asn1_obj.h>
+
+namespace Botan {
+
+/**
+* ASN.1 string type
+* This class normalizes all inputs to a UTF-8 std::string
+*/
+class BOTAN_PUBLIC_API(2,0) ASN1_String final : public ASN1_Object
+   {
+   public:
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      ASN1_Tag tagging() const { return m_tag; }
+
+      const std::string& value() const { return m_utf8_str; }
+
+      size_t size() const { return value().size(); }
+
+      bool empty() const { return m_utf8_str.empty(); }
+
+      std::string BOTAN_DEPRECATED("Use value() to get UTF-8 string instead")
+         iso_8859() const;
+
+      /**
+      * Return true iff this is a tag for a known string type we can handle.
+      * This ignores string types that are not supported, eg teletexString
+      */
+      static bool is_string_type(ASN1_Tag tag);
+
+      bool operator==(const ASN1_String& other) const
+         { return value() == other.value(); }
+
+      explicit ASN1_String(const std::string& utf8 = "");
+      ASN1_String(const std::string& utf8, ASN1_Tag tag);
+   private:
+      std::vector<uint8_t> m_data;
+      std::string m_utf8_str;
+      ASN1_Tag m_tag;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/asn1_time.cpp b/third_party/botan/src/lib/asn1/asn1_time.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_time.cpp
@@ -0,0 +1,290 @@
+/*
+* X.509 Time Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_time.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/calendar.h>
+#include <sstream>
+#include <iomanip>
+
+namespace Botan {
+
+X509_Time::X509_Time(const std::chrono::system_clock::time_point& time)
+   {
+   calendar_point cal = calendar_value(time);
+
+   m_year   = cal.get_year();
+   m_month  = cal.get_month();
+   m_day    = cal.get_day();
+   m_hour   = cal.get_hour();
+   m_minute = cal.get_minutes();
+   m_second = cal.get_seconds();
+
+   m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
+   }
+
+X509_Time::X509_Time(const std::string& t_spec, ASN1_Tag tag)
+   {
+   set_to(t_spec, tag);
+   }
+
+void X509_Time::encode_into(DER_Encoder& der) const
+   {
+   BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME,
+                   "X509_Time: Bad encoding tag");
+
+   der.add_object(m_tag, UNIVERSAL, to_string());
+   }
+
+void X509_Time::decode_from(BER_Decoder& source)
+   {
+   BER_Object ber_time = source.get_next_object();
+
+   set_to(ASN1::to_string(ber_time), ber_time.type());
+   }
+
+std::string X509_Time::to_string() const
+   {
+   if(time_is_set() == false)
+      throw Invalid_State("X509_Time::to_string: No time set");
+
+   uint32_t full_year = m_year;
+
+   if(m_tag == UTC_TIME)
+      {
+      if(m_year < 1950 || m_year >= 2050)
+         throw Encoding_Error("X509_Time: The time " + readable_string() +
+                              " cannot be encoded as a UTCTime");
+
+      full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
+      }
+
+   const uint64_t YEAR_FACTOR = 10000000000ULL;
+   const uint64_t MON_FACTOR  = 100000000;
+   const uint64_t DAY_FACTOR  = 1000000;
+   const uint64_t HOUR_FACTOR = 10000;
+   const uint64_t MIN_FACTOR  = 100;
+
+   const uint64_t int_repr =
+      YEAR_FACTOR * full_year +
+      MON_FACTOR * m_month +
+      DAY_FACTOR * m_day +
+      HOUR_FACTOR * m_hour +
+      MIN_FACTOR * m_minute +
+      m_second;
+
+   std::string repr = std::to_string(int_repr) + "Z";
+
+   uint32_t desired_size = (m_tag == UTC_TIME) ? 13 : 15;
+
+   while(repr.size() < desired_size)
+      repr = "0" + repr;
+
+   return repr;
+   }
+
+std::string X509_Time::readable_string() const
+   {
+   if(time_is_set() == false)
+      throw Invalid_State("X509_Time::readable_string: No time set");
+
+   // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC"
+   std::stringstream output;
+   output << std::setfill('0')
+          << std::setw(4) << m_year << "/"
+          << std::setw(2) << m_month << "/"
+          << std::setw(2) << m_day
+          << " "
+          << std::setw(2) << m_hour << ":"
+          << std::setw(2) << m_minute << ":"
+          << std::setw(2) << m_second
+          << " UTC";
+
+   return output.str();
+   }
+
+bool X509_Time::time_is_set() const
+   {
+   return (m_year != 0);
+   }
+
+int32_t X509_Time::cmp(const X509_Time& other) const
+   {
+   if(time_is_set() == false)
+      throw Invalid_State("X509_Time::cmp: No time set");
+
+   const int32_t EARLIER = -1, LATER = 1, SAME_TIME = 0;
+
+   if(m_year < other.m_year)     return EARLIER;
+   if(m_year > other.m_year)     return LATER;
+   if(m_month < other.m_month)   return EARLIER;
+   if(m_month > other.m_month)   return LATER;
+   if(m_day < other.m_day)       return EARLIER;
+   if(m_day > other.m_day)       return LATER;
+   if(m_hour < other.m_hour)     return EARLIER;
+   if(m_hour > other.m_hour)     return LATER;
+   if(m_minute < other.m_minute) return EARLIER;
+   if(m_minute > other.m_minute) return LATER;
+   if(m_second < other.m_second) return EARLIER;
+   if(m_second > other.m_second) return LATER;
+
+   return SAME_TIME;
+   }
+
+void X509_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
+   {
+   if(spec_tag == UTC_OR_GENERALIZED_TIME)
+      {
+      try
+         {
+         set_to(t_spec, GENERALIZED_TIME);
+         return;
+         }
+      catch(Invalid_Argument&) {} // Not a generalized time. Continue
+
+      try
+         {
+         set_to(t_spec, UTC_TIME);
+         return;
+         }
+      catch(Invalid_Argument&) {} // Not a UTC time. Continue
+
+      throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
+      }
+
+   BOTAN_ASSERT(spec_tag == UTC_TIME || spec_tag == GENERALIZED_TIME, "Invalid tag.");
+
+   BOTAN_ARG_CHECK(t_spec.size() > 0, "Time string must not be empty.");
+
+   BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support times with timezones other than Z");
+
+   if(spec_tag == GENERALIZED_TIME)
+      {
+      BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime string");
+      }
+   else if(spec_tag == UTC_TIME)
+      {
+      BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime string");
+      }
+
+   const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4;
+
+   std::vector<std::string> params;
+   std::string current;
+
+   for(size_t j = 0; j != YEAR_SIZE; ++j)
+      current += t_spec[j];
+   params.push_back(current);
+   current.clear();
+
+   for(size_t j = YEAR_SIZE; j != t_spec.size() - 1; ++j)
+      {
+      current += t_spec[j];
+      if(current.size() == 2)
+         {
+         params.push_back(current);
+         current.clear();
+         }
+      }
+
+   m_year   = to_u32bit(params[0]);
+   m_month  = to_u32bit(params[1]);
+   m_day    = to_u32bit(params[2]);
+   m_hour   = to_u32bit(params[3]);
+   m_minute = to_u32bit(params[4]);
+   m_second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
+   m_tag    = spec_tag;
+
+   if(spec_tag == UTC_TIME)
+      {
+      if(m_year >= 50) m_year += 1900;
+      else             m_year += 2000;
+      }
+
+   if(!passes_sanity_check())
+      throw Invalid_Argument("Time " + t_spec + " does not seem to be valid");
+   }
+
+/*
+* Do a general sanity check on the time
+*/
+bool X509_Time::passes_sanity_check() const
+   {
+   // AppVeyor's trust store includes a cert with expiration date in 3016 ...
+   if(m_year < 1950 || m_year > 3100)
+      return false;
+   if(m_month == 0 || m_month > 12)
+      return false;
+
+   const uint32_t days_in_month[12] = { 31, 28+1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+   if(m_day == 0 || m_day > days_in_month[m_month-1])
+      return false;
+
+   if(m_month == 2 && m_day == 29)
+      {
+      if(m_year % 4 != 0)
+         return false; // not a leap year
+
+      if(m_year % 100 == 0 && m_year % 400 != 0)
+         return false;
+      }
+
+   if(m_hour >= 24 || m_minute >= 60 || m_second > 60)
+      return false;
+
+   if (m_tag == UTC_TIME)
+      {
+      /*
+      UTCTime limits the value of components such that leap seconds
+      are not covered. See "UNIVERSAL 23" in "Information technology
+      Abstract Syntax Notation One (ASN.1): Specification of basic notation"
+
+      http://www.itu.int/ITU-T/studygroups/com17/languages/
+      */
+      if(m_second > 59)
+         {
+         return false;
+         }
+      }
+
+   return true;
+   }
+
+std::chrono::system_clock::time_point X509_Time::to_std_timepoint() const
+   {
+   return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
+   }
+
+uint64_t X509_Time::time_since_epoch() const
+   {
+   auto tp = this->to_std_timepoint();
+   return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
+   }
+
+/*
+* Compare two X509_Times for in various ways
+*/
+bool operator==(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) == 0); }
+bool operator!=(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) != 0); }
+
+bool operator<=(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) <= 0); }
+bool operator>=(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) >= 0); }
+
+bool operator<(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) < 0); }
+bool operator>(const X509_Time& t1, const X509_Time& t2)
+   { return (t1.cmp(t2) > 0); }
+
+}
diff --git a/third_party/botan/src/lib/asn1/asn1_time.h b/third_party/botan/src/lib/asn1/asn1_time.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/asn1_time.h
@@ -0,0 +1,82 @@
+/*
+* ASN.1 Time Representation
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_TIME_H_
+#define BOTAN_ASN1_TIME_H_
+
+#include <botan/asn1_obj.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* X.509 Time
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Time final : public ASN1_Object
+   {
+   public:
+      /// DER encode a X509_Time
+      void encode_into(DER_Encoder&) const override;
+
+      // Decode a BER encoded X509_Time
+      void decode_from(BER_Decoder&) override;
+
+      /// Return an internal string representation of the time
+      std::string to_string() const;
+
+      /// Returns a human friendly string replesentation of no particular formatting
+      std::string readable_string() const;
+
+      /// Return if the time has been set somehow
+      bool time_is_set() const;
+
+      ///  Compare this time against another
+      int32_t cmp(const X509_Time& other) const;
+
+      /// Create an invalid X509_Time
+      X509_Time() = default;
+
+      /// Create a X509_Time from a time point
+      explicit X509_Time(const std::chrono::system_clock::time_point& time);
+
+      /// Create an X509_Time from string
+      X509_Time(const std::string& t_spec, ASN1_Tag tag);
+
+      /// Returns a STL timepoint object
+      std::chrono::system_clock::time_point to_std_timepoint() const;
+
+      /// Return time since epoch
+      uint64_t time_since_epoch() const;
+
+   private:
+      void set_to(const std::string& t_spec, ASN1_Tag);
+      bool passes_sanity_check() const;
+
+      uint32_t m_year = 0;
+      uint32_t m_month = 0;
+      uint32_t m_day = 0;
+      uint32_t m_hour = 0;
+      uint32_t m_minute = 0;
+      uint32_t m_second = 0;
+      ASN1_Tag m_tag = NO_OBJECT;
+   };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_PUBLIC_API(2,0) operator==(const X509_Time&, const X509_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_Time&, const X509_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator<=(const X509_Time&, const X509_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator>=(const X509_Time&, const X509_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator<(const X509_Time&, const X509_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator>(const X509_Time&, const X509_Time&);
+
+typedef X509_Time ASN1_Time;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/ber_dec.cpp b/third_party/botan/src/lib/asn1/ber_dec.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/ber_dec.cpp
@@ -0,0 +1,549 @@
+/*
+* BER Decoder
+* (C) 1999-2008,2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/loadstor.h>
+#include <botan/internal/safeint.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* This value is somewhat arbitrary. OpenSSL allows up to 128 nested
+* indefinite length sequences. If you increase this, also increase the
+* limit in the test in test_asn1.cpp
+*/
+const size_t ALLOWED_EOC_NESTINGS = 16;
+
+/*
+* BER decode an ASN.1 type tag
+*/
+size_t decode_tag(DataSource* ber, ASN1_Tag& type_tag, ASN1_Tag& class_tag)
+   {
+   uint8_t b;
+   if(!ber->read_byte(b))
+      {
+      class_tag = type_tag = NO_OBJECT;
+      return 0;
+      }
+
+   if((b & 0x1F) != 0x1F)
+      {
+      type_tag = ASN1_Tag(b & 0x1F);
+      class_tag = ASN1_Tag(b & 0xE0);
+      return 1;
+      }
+
+   size_t tag_bytes = 1;
+   class_tag = ASN1_Tag(b & 0xE0);
+
+   size_t tag_buf = 0;
+   while(true)
+      {
+      if(!ber->read_byte(b))
+         throw BER_Decoding_Error("Long-form tag truncated");
+      if(tag_buf & 0xFF000000)
+         throw BER_Decoding_Error("Long-form tag overflowed 32 bits");
+      ++tag_bytes;
+      tag_buf = (tag_buf << 7) | (b & 0x7F);
+      if((b & 0x80) == 0) break;
+      }
+   type_tag = ASN1_Tag(tag_buf);
+   return tag_bytes;
+   }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource* src, size_t allow_indef);
+
+/*
+* BER decode an ASN.1 length field
+*/
+size_t decode_length(DataSource* ber, size_t& field_size, size_t allow_indef)
+   {
+   uint8_t b;
+   if(!ber->read_byte(b))
+      throw BER_Decoding_Error("Length field not found");
+   field_size = 1;
+   if((b & 0x80) == 0)
+      return b;
+
+   field_size += (b & 0x7F);
+   if(field_size > 5)
+      throw BER_Decoding_Error("Length field is too large");
+
+   if(field_size == 1)
+      {
+      if(allow_indef == 0)
+         {
+         throw BER_Decoding_Error("Nested EOC markers too deep, rejecting to avoid stack exhaustion");
+         }
+      else
+         {
+         return find_eoc(ber, allow_indef - 1);
+         }
+      }
+
+   size_t length = 0;
+
+   for(size_t i = 0; i != field_size - 1; ++i)
+      {
+      if(get_byte(0, length) != 0)
+         throw BER_Decoding_Error("Field length overflow");
+      if(!ber->read_byte(b))
+         throw BER_Decoding_Error("Corrupted length field");
+      length = (length << 8) | b;
+      }
+   return length;
+   }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource* ber, size_t allow_indef)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE), data;
+
+   while(true)
+      {
+      const size_t got = ber->peek(buffer.data(), buffer.size(), data.size());
+      if(got == 0)
+         break;
+
+      data += std::make_pair(buffer.data(), got);
+      }
+
+   DataSource_Memory source(data);
+   data.clear();
+
+   size_t length = 0;
+   while(true)
+      {
+      ASN1_Tag type_tag, class_tag;
+      size_t tag_size = decode_tag(&source, type_tag, class_tag);
+      if(type_tag == NO_OBJECT)
+         break;
+
+      size_t length_size = 0;
+      size_t item_size = decode_length(&source, length_size, allow_indef);
+      source.discard_next(item_size);
+
+      length = BOTAN_CHECKED_ADD(length, item_size);
+      length = BOTAN_CHECKED_ADD(length, tag_size);
+      length = BOTAN_CHECKED_ADD(length, length_size);
+
+      if(type_tag == EOC && class_tag == UNIVERSAL)
+         break;
+      }
+   return length;
+   }
+
+class DataSource_BERObject final : public DataSource
+   {
+   public:
+      size_t read(uint8_t out[], size_t length) override
+         {
+         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+         const size_t got = std::min<size_t>(m_obj.length() - m_offset, length);
+         copy_mem(out, m_obj.bits() + m_offset, got);
+         m_offset += got;
+         return got;
+         }
+
+      size_t peek(uint8_t out[], size_t length, size_t peek_offset) const override
+         {
+         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+         const size_t bytes_left = m_obj.length() - m_offset;
+
+         if(peek_offset >= bytes_left)
+            return 0;
+
+         const size_t got = std::min(bytes_left - peek_offset, length);
+         copy_mem(out, m_obj.bits() + peek_offset, got);
+         return got;
+         }
+
+      bool check_available(size_t n) override
+         {
+         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+         return (n <= (m_obj.length() - m_offset));
+         }
+
+      bool end_of_data() const override
+         {
+         return get_bytes_read() == m_obj.length();
+         }
+
+      size_t get_bytes_read() const override { return m_offset; }
+
+      explicit DataSource_BERObject(BER_Object&& obj) : m_obj(std::move(obj)), m_offset(0) {}
+
+   private:
+      BER_Object m_obj;
+      size_t m_offset;
+   };
+
+}
+
+/*
+* Check if more objects are there
+*/
+bool BER_Decoder::more_items() const
+   {
+   if(m_source->end_of_data() && !m_pushed.is_set())
+      return false;
+   return true;
+   }
+
+/*
+* Verify that no bytes remain in the source
+*/
+BER_Decoder& BER_Decoder::verify_end()
+   {
+   return verify_end("BER_Decoder::verify_end called, but data remains");
+   }
+
+/*
+* Verify that no bytes remain in the source
+*/
+BER_Decoder& BER_Decoder::verify_end(const std::string& err)
+   {
+   if(!m_source->end_of_data() || m_pushed.is_set())
+      throw Decoding_Error(err);
+   return (*this);
+   }
+
+/*
+* Discard all the bytes remaining in the source
+*/
+BER_Decoder& BER_Decoder::discard_remaining()
+   {
+   uint8_t buf;
+   while(m_source->read_byte(buf))
+      {}
+   return (*this);
+   }
+
+/*
+* Return the BER encoding of the next object
+*/
+BER_Object BER_Decoder::get_next_object()
+   {
+   BER_Object next;
+
+   if(m_pushed.is_set())
+      {
+      std::swap(next, m_pushed);
+      return next;
+      }
+
+   for(;;)
+      {
+      ASN1_Tag type_tag, class_tag;
+      decode_tag(m_source, type_tag, class_tag);
+      next.set_tagging(type_tag, class_tag);
+      if(next.is_set() == false) // no more objects
+         return next;
+
+      size_t field_size;
+      const size_t length = decode_length(m_source, field_size, ALLOWED_EOC_NESTINGS);
+      if(!m_source->check_available(length))
+         throw BER_Decoding_Error("Value truncated");
+
+      uint8_t* out = next.mutable_bits(length);
+      if(m_source->read(out, length) != length)
+         throw BER_Decoding_Error("Value truncated");
+
+      if(next.tagging() == EOC)
+         continue;
+      else
+         break;
+      }
+
+   return next;
+   }
+
+/*
+* Push a object back into the stream
+*/
+void BER_Decoder::push_back(const BER_Object& obj)
+   {
+   if(m_pushed.is_set())
+      throw Invalid_State("BER_Decoder: Only one push back is allowed");
+   m_pushed = obj;
+   }
+
+void BER_Decoder::push_back(BER_Object&& obj)
+   {
+   if(m_pushed.is_set())
+      throw Invalid_State("BER_Decoder: Only one push back is allowed");
+   m_pushed = std::move(obj);
+   }
+
+BER_Decoder BER_Decoder::start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   BER_Object obj = get_next_object();
+   obj.assert_is_a(type_tag, ASN1_Tag(class_tag | CONSTRUCTED));
+   return BER_Decoder(std::move(obj), this);
+   }
+
+/*
+* Finish decoding a CONSTRUCTED type
+*/
+BER_Decoder& BER_Decoder::end_cons()
+   {
+   if(!m_parent)
+      throw Invalid_State("BER_Decoder::end_cons called with null parent");
+   if(!m_source->end_of_data())
+      throw Decoding_Error("BER_Decoder::end_cons called with data left");
+   return (*m_parent);
+   }
+
+BER_Decoder::BER_Decoder(BER_Object&& obj, BER_Decoder* parent)
+   {
+   m_data_src.reset(new DataSource_BERObject(std::move(obj)));
+   m_source = m_data_src.get();
+   m_parent = parent;
+   }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(DataSource& src)
+   {
+   m_source = &src;
+   }
+
+/*
+* BER_Decoder Constructor
+ */
+BER_Decoder::BER_Decoder(const uint8_t data[], size_t length)
+   {
+   m_data_src.reset(new DataSource_Memory(data, length));
+   m_source = m_data_src.get();
+   }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const secure_vector<uint8_t>& data)
+   {
+   m_data_src.reset(new DataSource_Memory(data));
+   m_source = m_data_src.get();
+   }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const std::vector<uint8_t>& data)
+   {
+   m_data_src.reset(new DataSource_Memory(data.data(), data.size()));
+   m_source = m_data_src.get();
+   }
+
+/*
+* BER_Decoder Copy Constructor
+*/
+BER_Decoder::BER_Decoder(const BER_Decoder& other)
+   {
+   m_source = other.m_source;
+
+   // take ownership
+   std::swap(m_data_src, other.m_data_src);
+   m_parent = other.m_parent;
+   }
+
+/*
+* Request for an object to decode itself
+*/
+BER_Decoder& BER_Decoder::decode(ASN1_Object& obj,
+                                 ASN1_Tag, ASN1_Tag)
+   {
+   obj.decode_from(*this);
+   return (*this);
+   }
+
+/*
+* Decode a BER encoded NULL
+*/
+BER_Decoder& BER_Decoder::decode_null()
+   {
+   BER_Object obj = get_next_object();
+   obj.assert_is_a(NULL_TAG, UNIVERSAL);
+   if(obj.length() > 0)
+      throw BER_Decoding_Error("NULL object had nonzero size");
+   return (*this);
+   }
+
+BER_Decoder& BER_Decoder::decode_octet_string_bigint(BigInt& out)
+   {
+   secure_vector<uint8_t> out_vec;
+   decode(out_vec, OCTET_STRING);
+   out = BigInt::decode(out_vec.data(), out_vec.size());
+   return (*this);
+   }
+
+/*
+* Decode a BER encoded BOOLEAN
+*/
+BER_Decoder& BER_Decoder::decode(bool& out,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   BER_Object obj = get_next_object();
+   obj.assert_is_a(type_tag, class_tag);
+
+   if(obj.length() != 1)
+      throw BER_Decoding_Error("BER boolean value had invalid size");
+
+   out = (obj.bits()[0]) ? true : false;
+   return (*this);
+   }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(size_t& out,
+                                 ASN1_Tag type_tag,
+                                 ASN1_Tag class_tag)
+   {
+   BigInt integer;
+   decode(integer, type_tag, class_tag);
+
+   if(integer.is_negative())
+      throw BER_Decoding_Error("Decoded small integer value was negative");
+
+   if(integer.bits() > 32)
+      throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+   out = 0;
+   for(size_t i = 0; i != 4; ++i)
+      out = (out << 8) | integer.byte_at(3-i);
+
+   return (*this);
+   }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+uint64_t BER_Decoder::decode_constrained_integer(ASN1_Tag type_tag,
+                                                 ASN1_Tag class_tag,
+                                                 size_t T_bytes)
+   {
+   if(T_bytes > 8)
+      throw BER_Decoding_Error("Can't decode small integer over 8 bytes");
+
+   BigInt integer;
+   decode(integer, type_tag, class_tag);
+
+   if(integer.bits() > 8*T_bytes)
+      throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+   uint64_t out = 0;
+   for(size_t i = 0; i != 8; ++i)
+      out = (out << 8) | integer.byte_at(7-i);
+
+   return out;
+   }
+
+/*
+* Decode a BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(BigInt& out,
+                                 ASN1_Tag type_tag,
+                                 ASN1_Tag class_tag)
+   {
+   BER_Object obj = get_next_object();
+   obj.assert_is_a(type_tag, class_tag);
+
+   if(obj.length() == 0)
+      {
+      out = 0;
+      }
+   else
+      {
+      const bool negative = (obj.bits()[0] & 0x80) ? true : false;
+
+      if(negative)
+         {
+         secure_vector<uint8_t> vec(obj.bits(), obj.bits() + obj.length());
+         for(size_t i = obj.length(); i > 0; --i)
+            if(vec[i-1]--)
+               break;
+         for(size_t i = 0; i != obj.length(); ++i)
+            vec[i] = ~vec[i];
+         out = BigInt(vec.data(), vec.size());
+         out.flip_sign();
+         }
+      else
+         {
+         out = BigInt(obj.bits(), obj.length());
+         }
+      }
+
+   return (*this);
+   }
+
+namespace {
+
+template<typename Alloc>
+void asn1_decode_binary_string(std::vector<uint8_t, Alloc>& buffer,
+                               const BER_Object& obj,
+                               ASN1_Tag real_type,
+                               ASN1_Tag type_tag,
+                               ASN1_Tag class_tag)
+   {
+   obj.assert_is_a(type_tag, class_tag);
+
+   if(real_type == OCTET_STRING)
+      {
+      buffer.assign(obj.bits(), obj.bits() + obj.length());
+      }
+   else
+      {
+      if(obj.length() == 0)
+         throw BER_Decoding_Error("Invalid BIT STRING");
+      if(obj.bits()[0] >= 8)
+         throw BER_Decoding_Error("Bad number of unused bits in BIT STRING");
+
+      buffer.resize(obj.length() - 1);
+
+      if(obj.length() > 1)
+         copy_mem(buffer.data(), obj.bits() + 1, obj.length() - 1);
+      }
+   }
+
+}
+
+/*
+* BER decode a BIT STRING or OCTET STRING
+*/
+BER_Decoder& BER_Decoder::decode(secure_vector<uint8_t>& buffer,
+                                 ASN1_Tag real_type,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+      throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+   asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
+   return (*this);
+   }
+
+BER_Decoder& BER_Decoder::decode(std::vector<uint8_t>& buffer,
+                                 ASN1_Tag real_type,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+      throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+   asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
+   return (*this);
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/ber_dec.h b/third_party/botan/src/lib/asn1/ber_dec.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/ber_dec.h
@@ -0,0 +1,418 @@
+/*
+* BER Decoder
+* (C) 1999-2010,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BER_DECODER_H_
+#define BOTAN_BER_DECODER_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+class BigInt;
+
+/**
+* BER Decoding Object
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Decoder final
+   {
+   public:
+      /**
+      * Set up to BER decode the data in buf of length len
+      */
+      BER_Decoder(const uint8_t buf[], size_t len);
+
+      /**
+      * Set up to BER decode the data in vec
+      */
+      explicit BER_Decoder(const secure_vector<uint8_t>& vec);
+
+      /**
+      * Set up to BER decode the data in vec
+      */
+      explicit BER_Decoder(const std::vector<uint8_t>& vec);
+
+      /**
+      * Set up to BER decode the data in src
+      */
+      explicit BER_Decoder(DataSource& src);
+
+      /**
+      * Set up to BER decode the data in obj
+      */
+      BER_Decoder(const BER_Object& obj) :
+         BER_Decoder(obj.bits(), obj.length()) {}
+
+      /**
+      * Set up to BER decode the data in obj
+      */
+      BER_Decoder(BER_Object&& obj) :
+         BER_Decoder(std::move(obj), nullptr) {}
+
+      BER_Decoder(const BER_Decoder& other);
+
+      BER_Decoder& operator=(const BER_Decoder&) = delete;
+
+      /**
+      * Get the next object in the data stream.
+      * If EOF, returns an object with type NO_OBJECT.
+      */
+      BER_Object get_next_object();
+
+      BER_Decoder& get_next(BER_Object& ber)
+         {
+         ber = get_next_object();
+         return (*this);
+         }
+
+      /**
+      * Push an object back onto the stream. Throws if another
+      * object was previously pushed and has not been subsequently
+      * read out.
+      */
+      void push_back(const BER_Object& obj);
+
+      /**
+      * Push an object back onto the stream. Throws if another
+      * object was previously pushed and has not been subsequently
+      * read out.
+      */
+      void push_back(BER_Object&& obj);
+
+      /**
+      * Return true if there is at least one more item remaining
+      */
+      bool more_items() const;
+
+      /**
+      * Verify the stream is concluded, throws otherwise.
+      * Returns (*this)
+      */
+      BER_Decoder& verify_end();
+
+      /**
+      * Verify the stream is concluded, throws otherwise.
+      * Returns (*this)
+      */
+      BER_Decoder& verify_end(const std::string& err_msg);
+
+      /**
+      * Discard any data that remains unread
+      * Returns (*this)
+      */
+      BER_Decoder& discard_remaining();
+
+      /**
+      * Start decoding a constructed data (sequence or set)
+      */
+      BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL);
+
+      /**
+      * Finish decoding a constructed data, throws if any data remains.
+      * Returns the parent of *this (ie the object on which start_cons was called).
+      */
+      BER_Decoder& end_cons();
+
+      /**
+      * Get next object and copy value to POD type
+      * Asserts value length is equal to POD type sizeof.
+      * Asserts Type tag and optional Class tag according to parameters.
+      * Copy value to POD type (struct, union, C-style array, std::array, etc.).
+      * @param out POD type reference where to copy object value
+      * @param type_tag ASN1_Tag enum to assert type on object read
+      * @param class_tag ASN1_Tag enum to assert class on object read (default: CONTEXT_SPECIFIC)
+      * @return this reference
+      */
+      template <typename T>
+         BER_Decoder& get_next_value(T &out,
+                                     ASN1_Tag type_tag,
+                                     ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+         {
+         static_assert(std::is_pod<T>::value, "Type must be POD");
+
+         BER_Object obj = get_next_object();
+         obj.assert_is_a(type_tag, class_tag);
+
+         if (obj.length() != sizeof(T))
+            throw BER_Decoding_Error(
+                    "Size mismatch. Object value size is " +
+                    std::to_string(obj.length()) +
+                    "; Output type size is " +
+                    std::to_string(sizeof(T)));
+
+         copy_mem(reinterpret_cast<uint8_t*>(&out), obj.bits(), obj.length());
+
+         return (*this);
+         }
+
+      /*
+      * Save all the bytes remaining in the source
+      */
+      template<typename Alloc>
+      BER_Decoder& raw_bytes(std::vector<uint8_t, Alloc>& out)
+         {
+         out.clear();
+         uint8_t buf;
+         while(m_source->read_byte(buf))
+            out.push_back(buf);
+         return (*this);
+         }
+
+      BER_Decoder& decode_null();
+
+      /**
+      * Decode a BER encoded BOOLEAN
+      */
+      BER_Decoder& decode(bool& out)
+         {
+         return decode(out, BOOLEAN, UNIVERSAL);
+         }
+
+      /*
+      * Decode a small BER encoded INTEGER
+      */
+      BER_Decoder& decode(size_t& out)
+         {
+         return decode(out, INTEGER, UNIVERSAL);
+         }
+
+      /*
+      * Decode a BER encoded INTEGER
+      */
+      BER_Decoder& decode(BigInt& out)
+         {
+         return decode(out, INTEGER, UNIVERSAL);
+         }
+
+      std::vector<uint8_t> get_next_octet_string()
+         {
+         std::vector<uint8_t> out_vec;
+         decode(out_vec, OCTET_STRING);
+         return out_vec;
+         }
+
+      /*
+      * BER decode a BIT STRING or OCTET STRING
+      */
+      template<typename Alloc>
+      BER_Decoder& decode(std::vector<uint8_t, Alloc>& out, ASN1_Tag real_type)
+         {
+         return decode(out, real_type, real_type, UNIVERSAL);
+         }
+
+      BER_Decoder& decode(bool& v,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      BER_Decoder& decode(size_t& v,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      BER_Decoder& decode(BigInt& v,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      BER_Decoder& decode(std::vector<uint8_t>& v,
+                          ASN1_Tag real_type,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      BER_Decoder& decode(secure_vector<uint8_t>& v,
+                          ASN1_Tag real_type,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      BER_Decoder& decode(class ASN1_Object& obj,
+                          ASN1_Tag type_tag = NO_OBJECT,
+                          ASN1_Tag class_tag = NO_OBJECT);
+
+      /**
+      * Decode an integer value which is typed as an octet string
+      */
+      BER_Decoder& decode_octet_string_bigint(BigInt& b);
+
+      uint64_t decode_constrained_integer(ASN1_Tag type_tag,
+                                          ASN1_Tag class_tag,
+                                          size_t T_bytes);
+
+      template<typename T> BER_Decoder& decode_integer_type(T& out)
+         {
+         return decode_integer_type<T>(out, INTEGER, UNIVERSAL);
+         }
+
+      template<typename T>
+         BER_Decoder& decode_integer_type(T& out,
+                                          ASN1_Tag type_tag,
+                                          ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+         {
+         out = static_cast<T>(decode_constrained_integer(type_tag, class_tag, sizeof(out)));
+         return (*this);
+         }
+
+      template<typename T>
+         BER_Decoder& decode_optional(T& out,
+                                      ASN1_Tag type_tag,
+                                      ASN1_Tag class_tag,
+                                      const T& default_value = T());
+
+      template<typename T>
+         BER_Decoder& decode_optional_implicit(
+            T& out,
+            ASN1_Tag type_tag,
+            ASN1_Tag class_tag,
+            ASN1_Tag real_type,
+            ASN1_Tag real_class,
+            const T& default_value = T());
+
+      template<typename T>
+         BER_Decoder& decode_list(std::vector<T>& out,
+                                  ASN1_Tag type_tag = SEQUENCE,
+                                  ASN1_Tag class_tag = UNIVERSAL);
+
+      template<typename T>
+         BER_Decoder& decode_and_check(const T& expected,
+                                       const std::string& error_msg)
+         {
+         T actual;
+         decode(actual);
+
+         if(actual != expected)
+            throw Decoding_Error(error_msg);
+
+         return (*this);
+         }
+
+      /*
+      * Decode an OPTIONAL string type
+      */
+      template<typename Alloc>
+      BER_Decoder& decode_optional_string(std::vector<uint8_t, Alloc>& out,
+                                          ASN1_Tag real_type,
+                                          uint16_t type_no,
+                                          ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+         {
+         BER_Object obj = get_next_object();
+
+         ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+         if(obj.is_a(type_tag, class_tag))
+            {
+            if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+               {
+               BER_Decoder(std::move(obj)).decode(out, real_type).verify_end();
+               }
+            else
+               {
+               push_back(std::move(obj));
+               decode(out, real_type, type_tag, class_tag);
+               }
+            }
+         else
+            {
+            out.clear();
+            push_back(std::move(obj));
+            }
+
+         return (*this);
+         }
+
+   private:
+      BER_Decoder(BER_Object&& obj, BER_Decoder* parent);
+
+      BER_Decoder* m_parent = nullptr;
+      BER_Object m_pushed;
+      // either m_data_src.get() or an unowned pointer
+      DataSource* m_source;
+      mutable std::unique_ptr<DataSource> m_data_src;
+   };
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional(T& out,
+                                          ASN1_Tag type_tag,
+                                          ASN1_Tag class_tag,
+                                          const T& default_value)
+   {
+   BER_Object obj = get_next_object();
+
+   if(obj.is_a(type_tag, class_tag))
+      {
+      if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+         {
+         BER_Decoder(std::move(obj)).decode(out).verify_end();
+         }
+      else
+         {
+         push_back(std::move(obj));
+         decode(out, type_tag, class_tag);
+         }
+      }
+   else
+      {
+      out = default_value;
+      push_back(std::move(obj));
+      }
+
+   return (*this);
+   }
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional_implicit(
+   T& out,
+   ASN1_Tag type_tag,
+   ASN1_Tag class_tag,
+   ASN1_Tag real_type,
+   ASN1_Tag real_class,
+   const T& default_value)
+   {
+   BER_Object obj = get_next_object();
+
+   if(obj.is_a(type_tag, class_tag))
+      {
+      obj.set_tagging(real_type, real_class);
+      push_back(std::move(obj));
+      decode(out, real_type, real_class);
+      }
+   else
+      {
+      // Not what we wanted, push it back on the stream
+      out = default_value;
+      push_back(std::move(obj));
+      }
+
+   return (*this);
+   }
+/*
+* Decode a list of homogenously typed values
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_list(std::vector<T>& vec,
+                                      ASN1_Tag type_tag,
+                                      ASN1_Tag class_tag)
+   {
+   BER_Decoder list = start_cons(type_tag, class_tag);
+
+   while(list.more_items())
+      {
+      T value;
+      list.decode(value);
+      vec.push_back(std::move(value));
+      }
+
+   list.end_cons();
+
+   return (*this);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/der_enc.cpp b/third_party/botan/src/lib/asn1/der_enc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/der_enc.cpp
@@ -0,0 +1,405 @@
+/*
+* DER Encoder
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/der_enc.h>
+#include <botan/asn1_obj.h>
+#include <botan/bigint.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*
+* DER encode an ASN.1 type tag
+*/
+void encode_tag(std::vector<uint8_t>& encoded_tag,
+                ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if((class_tag | 0xE0) != 0xE0)
+      throw Encoding_Error("DER_Encoder: Invalid class tag " +
+                           std::to_string(class_tag));
+
+   if(type_tag <= 30)
+      {
+      encoded_tag.push_back(static_cast<uint8_t>(type_tag | class_tag));
+      }
+   else
+      {
+      size_t blocks = high_bit(static_cast<uint32_t>(type_tag)) + 6;
+      blocks = (blocks - (blocks % 7)) / 7;
+
+      BOTAN_ASSERT_NOMSG(blocks > 0);
+
+      encoded_tag.push_back(static_cast<uint8_t>(class_tag | 0x1F));
+      for(size_t i = 0; i != blocks - 1; ++i)
+         encoded_tag.push_back(0x80 | ((type_tag >> 7*(blocks-i-1)) & 0x7F));
+      encoded_tag.push_back(type_tag & 0x7F);
+      }
+   }
+
+/*
+* DER encode an ASN.1 length field
+*/
+void encode_length(std::vector<uint8_t>& encoded_length, size_t length)
+   {
+   if(length <= 127)
+      {
+      encoded_length.push_back(static_cast<uint8_t>(length));
+      }
+   else
+      {
+      const size_t bytes_needed = significant_bytes(length);
+
+      encoded_length.push_back(static_cast<uint8_t>(0x80 | bytes_needed));
+
+      for(size_t i = sizeof(length) - bytes_needed; i < sizeof(length); ++i)
+         encoded_length.push_back(get_byte(i, length));
+      }
+   }
+
+}
+
+DER_Encoder::DER_Encoder(secure_vector<uint8_t>& vec)
+   {
+   m_append_output = [&vec](const uint8_t b[], size_t l)
+      {
+      vec.insert(vec.end(), b, b + l);
+      };
+   }
+
+DER_Encoder::DER_Encoder(std::vector<uint8_t>& vec)
+   {
+   m_append_output = [&vec](const uint8_t b[], size_t l)
+      {
+      vec.insert(vec.end(), b, b + l);
+      };
+   }
+
+/*
+* Push the encoded SEQUENCE/SET to the encoder stream
+*/
+void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der)
+   {
+   const ASN1_Tag real_class_tag = ASN1_Tag(m_class_tag | CONSTRUCTED);
+
+   if(m_type_tag == SET)
+      {
+      std::sort(m_set_contents.begin(), m_set_contents.end());
+      for(size_t i = 0; i != m_set_contents.size(); ++i)
+         m_contents += m_set_contents[i];
+      m_set_contents.clear();
+      }
+
+   der.add_object(m_type_tag, real_class_tag, m_contents.data(), m_contents.size());
+   m_contents.clear();
+   }
+
+/*
+* Add an encoded value to the SEQUENCE/SET
+*/
+void DER_Encoder::DER_Sequence::add_bytes(const uint8_t data[], size_t length)
+   {
+   if(m_type_tag == SET)
+      m_set_contents.push_back(secure_vector<uint8_t>(data, data + length));
+   else
+      m_contents += std::make_pair(data, length);
+   }
+
+void DER_Encoder::DER_Sequence::add_bytes(const uint8_t hdr[], size_t hdr_len,
+                                          const uint8_t val[], size_t val_len)
+   {
+   if(m_type_tag == SET)
+      {
+      secure_vector<uint8_t> m;
+      m.reserve(hdr_len + val_len);
+      m += std::make_pair(hdr, hdr_len);
+      m += std::make_pair(val, val_len);
+      m_set_contents.push_back(std::move(m));
+      }
+   else
+      {
+      m_contents += std::make_pair(hdr, hdr_len);
+      m_contents += std::make_pair(val, val_len);
+      }
+   }
+
+/*
+* Return the type and class taggings
+*/
+ASN1_Tag DER_Encoder::DER_Sequence::tag_of() const
+   {
+   return ASN1_Tag(m_type_tag | m_class_tag);
+   }
+
+/*
+* DER_Sequence Constructor
+*/
+DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Tag t1, ASN1_Tag t2) :
+   m_type_tag(t1), m_class_tag(t2)
+   {
+   }
+
+/*
+* Return the encoded contents
+*/
+secure_vector<uint8_t> DER_Encoder::get_contents()
+   {
+   if(m_subsequences.size() != 0)
+      throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
+
+   if(m_append_output)
+      throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
+
+   secure_vector<uint8_t> output;
+   std::swap(output, m_default_outbuf);
+   return output;
+   }
+
+std::vector<uint8_t> DER_Encoder::get_contents_unlocked()
+   {
+   if(m_subsequences.size() != 0)
+      throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
+
+   if(m_append_output)
+      throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
+
+   std::vector<uint8_t> output(m_default_outbuf.begin(), m_default_outbuf.end());
+   m_default_outbuf.clear();
+   return output;
+   }
+
+/*
+* Start a new ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::start_cons(ASN1_Tag type_tag,
+                                     ASN1_Tag class_tag)
+   {
+   m_subsequences.push_back(DER_Sequence(type_tag, class_tag));
+   return (*this);
+   }
+
+/*
+* Finish the current ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::end_cons()
+   {
+   if(m_subsequences.empty())
+      throw Invalid_State("DER_Encoder::end_cons: No such sequence");
+
+   DER_Sequence last_seq = std::move(m_subsequences[m_subsequences.size()-1]);
+   m_subsequences.pop_back();
+   last_seq.push_contents(*this);
+
+   return (*this);
+   }
+
+/*
+* Start a new ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::start_explicit(uint16_t type_no)
+   {
+   ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+   // This would confuse DER_Sequence
+   if(type_tag == SET)
+      throw Internal_Error("DER_Encoder.start_explicit(SET) not supported");
+
+   return start_cons(type_tag, CONTEXT_SPECIFIC);
+   }
+
+/*
+* Finish the current ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::end_explicit()
+   {
+   return end_cons();
+   }
+
+/*
+* Write raw bytes into the stream
+*/
+DER_Encoder& DER_Encoder::raw_bytes(const uint8_t bytes[], size_t length)
+   {
+   if(m_subsequences.size())
+      {
+      m_subsequences[m_subsequences.size()-1].add_bytes(bytes, length);
+      }
+   else if(m_append_output)
+      {
+      m_append_output(bytes, length);
+      }
+   else
+      {
+      m_default_outbuf += std::make_pair(bytes, length);
+      }
+
+   return (*this);
+   }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                                     const uint8_t rep[], size_t length)
+   {
+   std::vector<uint8_t> hdr;
+   encode_tag(hdr, type_tag, class_tag);
+   encode_length(hdr, length);
+
+   if(m_subsequences.size())
+      {
+      m_subsequences[m_subsequences.size()-1].add_bytes(hdr.data(), hdr.size(), rep, length);
+      }
+   else if(m_append_output)
+      {
+      m_append_output(hdr.data(), hdr.size());
+      m_append_output(rep, length);
+      }
+   else
+      {
+      m_default_outbuf += hdr;
+      m_default_outbuf += std::make_pair(rep, length);
+      }
+
+   return (*this);
+   }
+
+/*
+* Encode a NULL object
+*/
+DER_Encoder& DER_Encoder::encode_null()
+   {
+   return add_object(NULL_TAG, UNIVERSAL, nullptr, 0);
+   }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true)
+   {
+   return encode(is_true, BOOLEAN, UNIVERSAL);
+   }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n)
+   {
+   return encode(BigInt(n), INTEGER, UNIVERSAL);
+   }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n)
+   {
+   return encode(n, INTEGER, UNIVERSAL);
+   }
+
+/*
+* Encode this object
+*/
+DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
+                                 ASN1_Tag real_type)
+   {
+   return encode(bytes, length, real_type, real_type, UNIVERSAL);
+   }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   uint8_t val = is_true ? 0xFF : 0x00;
+   return add_object(type_tag, class_tag, &val, 1);
+   }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   return encode(BigInt(n), type_tag, class_tag);
+   }
+
+/*
+* DER encode an INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if(n == 0)
+      return add_object(type_tag, class_tag, 0);
+
+   const size_t extra_zero = (n.bits() % 8 == 0) ? 1 : 0;
+   secure_vector<uint8_t> contents(extra_zero + n.bytes());
+   n.binary_encode(&contents[extra_zero]);
+   if(n < 0)
+      {
+      for(size_t i = 0; i != contents.size(); ++i)
+         contents[i] = ~contents[i];
+      for(size_t i = contents.size(); i > 0; --i)
+         if(++contents[i-1])
+            break;
+      }
+
+   return add_object(type_tag, class_tag, contents);
+   }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
+                                 ASN1_Tag real_type,
+                                 ASN1_Tag type_tag, ASN1_Tag class_tag)
+   {
+   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+      throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string");
+
+   if(real_type == BIT_STRING)
+      {
+      secure_vector<uint8_t> encoded;
+      encoded.push_back(0);
+      encoded += std::make_pair(bytes, length);
+      return add_object(type_tag, class_tag, encoded);
+      }
+   else
+      return add_object(type_tag, class_tag, bytes, length);
+   }
+
+DER_Encoder& DER_Encoder::encode(const ASN1_Object& obj)
+   {
+   obj.encode_into(*this);
+   return (*this);
+   }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                                     const std::string& rep_str)
+   {
+   const uint8_t* rep = cast_char_ptr_to_uint8(rep_str.data());
+   const size_t rep_len = rep_str.size();
+   return add_object(type_tag, class_tag, rep, rep_len);
+   }
+
+/*
+* Write the encoding of the byte
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag,
+                                     ASN1_Tag class_tag, uint8_t rep)
+   {
+   return add_object(type_tag, class_tag, &rep, 1);
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/der_enc.h b/third_party/botan/src/lib/asn1/der_enc.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/der_enc.h
@@ -0,0 +1,228 @@
+/*
+* DER Encoder
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DER_ENCODER_H_
+#define BOTAN_DER_ENCODER_H_
+
+#include <botan/asn1_obj.h>
+#include <vector>
+#include <functional>
+
+namespace Botan {
+
+class BigInt;
+class ASN1_Object;
+
+/**
+* General DER Encoding Object
+*/
+class BOTAN_PUBLIC_API(2,0) DER_Encoder final
+   {
+   public:
+      typedef std::function<void (const uint8_t[], size_t)> append_fn;
+
+      /**
+      * DER encode, writing to an internal buffer
+      * Use get_contents or get_contents_unlocked to read the results
+      * after all encoding is completed.
+      */
+      DER_Encoder() = default;
+
+      /**
+      * DER encode, writing to @param vec
+      * If this constructor is used, get_contents* may not be called.
+      */
+      DER_Encoder(secure_vector<uint8_t>& vec);
+
+      /**
+      * DER encode, writing to @param vec
+      * If this constructor is used, get_contents* may not be called.
+      */
+      DER_Encoder(std::vector<uint8_t>& vec);
+
+      /**
+      * DER encode, calling append to write output
+      * If this constructor is used, get_contents* may not be called.
+      */
+      DER_Encoder(append_fn append) : m_append_output(append) {}
+
+      secure_vector<uint8_t> get_contents();
+
+      /**
+      * Return the encoded contents as a std::vector
+      *
+      * If using this function, instead pass a std::vector to the
+      * contructor of DER_Encoder where the output will be placed. This
+      * avoids several unecessary copies.
+      */
+      std::vector<uint8_t> BOTAN_DEPRECATED("Use DER_Encoder(vector) instead") get_contents_unlocked();
+
+      DER_Encoder& start_cons(ASN1_Tag type_tag,
+                              ASN1_Tag class_tag = UNIVERSAL);
+      DER_Encoder& end_cons();
+
+      DER_Encoder& start_explicit(uint16_t type_tag);
+      DER_Encoder& end_explicit();
+
+      /**
+      * Insert raw bytes directly into the output stream
+      */
+      DER_Encoder& raw_bytes(const uint8_t val[], size_t len);
+
+      template<typename Alloc>
+      DER_Encoder& raw_bytes(const std::vector<uint8_t, Alloc>& val)
+         {
+         return raw_bytes(val.data(), val.size());
+         }
+
+      DER_Encoder& encode_null();
+      DER_Encoder& encode(bool b);
+      DER_Encoder& encode(size_t s);
+      DER_Encoder& encode(const BigInt& n);
+      DER_Encoder& encode(const uint8_t val[], size_t len, ASN1_Tag real_type);
+
+      template<typename Alloc>
+      DER_Encoder& encode(const std::vector<uint8_t, Alloc>& vec, ASN1_Tag real_type)
+         {
+         return encode(vec.data(), vec.size(), real_type);
+         }
+
+      DER_Encoder& encode(bool b,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      DER_Encoder& encode(size_t s,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      DER_Encoder& encode(const BigInt& n,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      DER_Encoder& encode(const uint8_t v[], size_t len,
+                          ASN1_Tag real_type,
+                          ASN1_Tag type_tag,
+                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+      template<typename Alloc>
+      DER_Encoder& encode(const std::vector<uint8_t, Alloc>& bytes,
+                          ASN1_Tag real_type,
+                          ASN1_Tag type_tag, ASN1_Tag class_tag)
+         {
+         return encode(bytes.data(), bytes.size(),
+                       real_type, type_tag, class_tag);
+         }
+
+      template<typename T>
+      DER_Encoder& encode_optional(const T& value, const T& default_value)
+         {
+         if(value != default_value)
+            encode(value);
+         return (*this);
+         }
+
+      template<typename T>
+      DER_Encoder& encode_list(const std::vector<T>& values)
+         {
+         for(size_t i = 0; i != values.size(); ++i)
+            encode(values[i]);
+         return (*this);
+         }
+
+      /*
+      * Request for an object to encode itself to this stream
+      */
+      DER_Encoder& encode(const ASN1_Object& obj);
+
+      /*
+      * Conditionally write some values to the stream
+      */
+      DER_Encoder& encode_if(bool pred, DER_Encoder& enc)
+         {
+         if(pred)
+            return raw_bytes(enc.get_contents());
+         return (*this);
+         }
+
+      DER_Encoder& encode_if(bool pred, const ASN1_Object& obj)
+         {
+         if(pred)
+            encode(obj);
+         return (*this);
+         }
+
+      DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                              const uint8_t rep[], size_t length);
+
+      DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                              const std::vector<uint8_t>& rep)
+         {
+         return add_object(type_tag, class_tag, rep.data(), rep.size());
+         }
+
+      DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                              const secure_vector<uint8_t>& rep)
+         {
+         return add_object(type_tag, class_tag, rep.data(), rep.size());
+         }
+
+      DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                              const std::string& str);
+
+      DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+                              uint8_t val);
+
+   private:
+      class DER_Sequence final
+         {
+         public:
+            ASN1_Tag tag_of() const;
+
+            void push_contents(DER_Encoder& der);
+
+            void add_bytes(const uint8_t val[], size_t len);
+
+            void add_bytes(const uint8_t hdr[], size_t hdr_len,
+                           const uint8_t val[], size_t val_len);
+
+            DER_Sequence(ASN1_Tag, ASN1_Tag);
+
+            DER_Sequence(DER_Sequence&& seq)
+               {
+               std::swap(m_type_tag, seq.m_type_tag);
+               std::swap(m_class_tag, seq.m_class_tag);
+               std::swap(m_contents, seq.m_contents);
+               std::swap(m_set_contents, seq.m_set_contents);
+               }
+
+            DER_Sequence& operator=(DER_Sequence&& seq)
+               {
+               std::swap(m_type_tag, seq.m_type_tag);
+               std::swap(m_class_tag, seq.m_class_tag);
+               std::swap(m_contents, seq.m_contents);
+               std::swap(m_set_contents, seq.m_set_contents);
+               return (*this);
+               }
+
+            DER_Sequence(const DER_Sequence& seq) = default;
+
+            DER_Sequence& operator=(const DER_Sequence& seq) = default;
+
+         private:
+            ASN1_Tag m_type_tag, m_class_tag;
+            secure_vector<uint8_t> m_contents;
+            std::vector< secure_vector<uint8_t> > m_set_contents;
+         };
+
+      append_fn m_append_output;
+      secure_vector<uint8_t> m_default_outbuf;
+      std::vector<DER_Sequence> m_subsequences;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/asn1/oid_maps.cpp b/third_party/botan/src/lib/asn1/oid_maps.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/oid_maps.cpp
@@ -0,0 +1,510 @@
+/*
+* OID maps
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/oids.h>
+#include <unordered_map>
+
+namespace Botan {
+
+std::unordered_map<std::string, std::string> OIDS::load_oid2str_map()
+   {
+   return std::unordered_map<std::string,std::string>{
+      { "0.3.4401.5.3.1.9.26", "Camellia-192/GCM" },
+      { "0.3.4401.5.3.1.9.46", "Camellia-256/GCM" },
+      { "0.3.4401.5.3.1.9.6", "Camellia-128/GCM" },
+      { "0.4.0.127.0.15.1.1.13.0", "XMSS" },
+      { "1.0.14888.3.0.5", "ECKCDSA" },
+      { "1.2.156.10197.1.104.100", "SM4/OCB" },
+      { "1.2.156.10197.1.104.2", "SM4/CBC" },
+      { "1.2.156.10197.1.104.8", "SM4/GCM" },
+      { "1.2.156.10197.1.301", "sm2p256v1" },
+      { "1.2.156.10197.1.301.1", "SM2" },
+      { "1.2.156.10197.1.301.2", "SM2_Kex" },
+      { "1.2.156.10197.1.301.3", "SM2_Enc" },
+      { "1.2.156.10197.1.401", "SM3" },
+      { "1.2.156.10197.1.501", "SM2_Sig/SM3" },
+      { "1.2.156.10197.1.504", "RSA/EMSA3(SM3)" },
+      { "1.2.250.1.223.101.256.1", "frp256v1" },
+      { "1.2.392.200011.61.1.1.1.2", "Camellia-128/CBC" },
+      { "1.2.392.200011.61.1.1.1.3", "Camellia-192/CBC" },
+      { "1.2.392.200011.61.1.1.1.4", "Camellia-256/CBC" },
+      { "1.2.410.200004.1.100.4.3", "ECKCDSA/EMSA1(SHA-1)" },
+      { "1.2.410.200004.1.100.4.4", "ECKCDSA/EMSA1(SHA-224)" },
+      { "1.2.410.200004.1.100.4.5", "ECKCDSA/EMSA1(SHA-256)" },
+      { "1.2.410.200004.1.4", "SEED/CBC" },
+      { "1.2.643.100.1", "GOST.OGRN" },
+      { "1.2.643.100.111", "GOST.SubjectSigningTool" },
+      { "1.2.643.100.112", "GOST.IssuerSigningTool" },
+      { "1.2.643.2.2.19", "GOST-34.10" },
+      { "1.2.643.2.2.3", "GOST-34.10/EMSA1(GOST-R-34.11-94)" },
+      { "1.2.643.2.2.35.1", "gost_256A" },
+      { "1.2.643.2.2.36.0", "gost_256A" },
+      { "1.2.643.3.131.1.1", "GOST.INN" },
+      { "1.2.643.7.1.1.1.1", "GOST-34.10-2012-256" },
+      { "1.2.643.7.1.1.1.2", "GOST-34.10-2012-512" },
+      { "1.2.643.7.1.1.2.2", "Streebog-256" },
+      { "1.2.643.7.1.1.2.3", "Streebog-512" },
+      { "1.2.643.7.1.1.3.2", "GOST-34.10-2012-256/EMSA1(Streebog-256)" },
+      { "1.2.643.7.1.1.3.3", "GOST-34.10-2012-512/EMSA1(Streebog-512)" },
+      { "1.2.643.7.1.2.1.1.1", "gost_256A" },
+      { "1.2.643.7.1.2.1.1.2", "gost_256B" },
+      { "1.2.643.7.1.2.1.2.1", "gost_512A" },
+      { "1.2.643.7.1.2.1.2.2", "gost_512B" },
+      { "1.2.840.10040.4.1", "DSA" },
+      { "1.2.840.10040.4.3", "DSA/EMSA1(SHA-160)" },
+      { "1.2.840.10045.2.1", "ECDSA" },
+      { "1.2.840.10045.3.1.1", "secp192r1" },
+      { "1.2.840.10045.3.1.2", "x962_p192v2" },
+      { "1.2.840.10045.3.1.3", "x962_p192v3" },
+      { "1.2.840.10045.3.1.4", "x962_p239v1" },
+      { "1.2.840.10045.3.1.5", "x962_p239v2" },
+      { "1.2.840.10045.3.1.6", "x962_p239v3" },
+      { "1.2.840.10045.3.1.7", "secp256r1" },
+      { "1.2.840.10045.4.1", "ECDSA/EMSA1(SHA-160)" },
+      { "1.2.840.10045.4.3.1", "ECDSA/EMSA1(SHA-224)" },
+      { "1.2.840.10045.4.3.2", "ECDSA/EMSA1(SHA-256)" },
+      { "1.2.840.10045.4.3.3", "ECDSA/EMSA1(SHA-384)" },
+      { "1.2.840.10045.4.3.4", "ECDSA/EMSA1(SHA-512)" },
+      { "1.2.840.10046.2.1", "DH" },
+      { "1.2.840.113533.7.66.10", "CAST-128/CBC" },
+      { "1.2.840.113533.7.66.15", "KeyWrap.CAST-128" },
+      { "1.2.840.113549.1.1.1", "RSA" },
+      { "1.2.840.113549.1.1.10", "RSA/EMSA4" },
+      { "1.2.840.113549.1.1.11", "RSA/EMSA3(SHA-256)" },
+      { "1.2.840.113549.1.1.12", "RSA/EMSA3(SHA-384)" },
+      { "1.2.840.113549.1.1.13", "RSA/EMSA3(SHA-512)" },
+      { "1.2.840.113549.1.1.14", "RSA/EMSA3(SHA-224)" },
+      { "1.2.840.113549.1.1.16", "RSA/EMSA3(SHA-512-256)" },
+      { "1.2.840.113549.1.1.4", "RSA/EMSA3(MD5)" },
+      { "1.2.840.113549.1.1.5", "RSA/EMSA3(SHA-160)" },
+      { "1.2.840.113549.1.1.7", "RSA/OAEP" },
+      { "1.2.840.113549.1.1.8", "MGF1" },
+      { "1.2.840.113549.1.5.12", "PKCS5.PBKDF2" },
+      { "1.2.840.113549.1.5.13", "PBE-PKCS5v20" },
+      { "1.2.840.113549.1.9.1", "PKCS9.EmailAddress" },
+      { "1.2.840.113549.1.9.14", "PKCS9.ExtensionRequest" },
+      { "1.2.840.113549.1.9.16.3.18", "ChaCha20Poly1305" },
+      { "1.2.840.113549.1.9.16.3.6", "KeyWrap.TripleDES" },
+      { "1.2.840.113549.1.9.16.3.8", "Compression.Zlib" },
+      { "1.2.840.113549.1.9.2", "PKCS9.UnstructuredName" },
+      { "1.2.840.113549.1.9.3", "PKCS9.ContentType" },
+      { "1.2.840.113549.1.9.4", "PKCS9.MessageDigest" },
+      { "1.2.840.113549.1.9.7", "PKCS9.ChallengePassword" },
+      { "1.2.840.113549.2.10", "HMAC(SHA-384)" },
+      { "1.2.840.113549.2.11", "HMAC(SHA-512)" },
+      { "1.2.840.113549.2.13", "HMAC(SHA-512-256)" },
+      { "1.2.840.113549.2.5", "MD5" },
+      { "1.2.840.113549.2.7", "HMAC(SHA-160)" },
+      { "1.2.840.113549.2.8", "HMAC(SHA-224)" },
+      { "1.2.840.113549.2.9", "HMAC(SHA-256)" },
+      { "1.2.840.113549.3.7", "TripleDES/CBC" },
+      { "1.3.101.110", "Curve25519" },
+      { "1.3.101.112", "Ed25519" },
+      { "1.3.132.0.10", "secp256k1" },
+      { "1.3.132.0.30", "secp160r2" },
+      { "1.3.132.0.31", "secp192k1" },
+      { "1.3.132.0.32", "secp224k1" },
+      { "1.3.132.0.33", "secp224r1" },
+      { "1.3.132.0.34", "secp384r1" },
+      { "1.3.132.0.35", "secp521r1" },
+      { "1.3.132.0.8", "secp160r1" },
+      { "1.3.132.0.9", "secp160k1" },
+      { "1.3.132.1.12", "ECDH" },
+      { "1.3.14.3.2.26", "SHA-160" },
+      { "1.3.14.3.2.7", "DES/CBC" },
+      { "1.3.36.3.2.1", "RIPEMD-160" },
+      { "1.3.36.3.3.1.2", "RSA/EMSA3(RIPEMD-160)" },
+      { "1.3.36.3.3.2.5.2.1", "ECGDSA" },
+      { "1.3.36.3.3.2.5.4.1", "ECGDSA/EMSA1(RIPEMD-160)" },
+      { "1.3.36.3.3.2.5.4.2", "ECGDSA/EMSA1(SHA-160)" },
+      { "1.3.36.3.3.2.5.4.3", "ECGDSA/EMSA1(SHA-224)" },
+      { "1.3.36.3.3.2.5.4.4", "ECGDSA/EMSA1(SHA-256)" },
+      { "1.3.36.3.3.2.5.4.5", "ECGDSA/EMSA1(SHA-384)" },
+      { "1.3.36.3.3.2.5.4.6", "ECGDSA/EMSA1(SHA-512)" },
+      { "1.3.36.3.3.2.8.1.1.1", "brainpool160r1" },
+      { "1.3.36.3.3.2.8.1.1.11", "brainpool384r1" },
+      { "1.3.36.3.3.2.8.1.1.13", "brainpool512r1" },
+      { "1.3.36.3.3.2.8.1.1.3", "brainpool192r1" },
+      { "1.3.36.3.3.2.8.1.1.5", "brainpool224r1" },
+      { "1.3.36.3.3.2.8.1.1.7", "brainpool256r1" },
+      { "1.3.36.3.3.2.8.1.1.9", "brainpool320r1" },
+      { "1.3.6.1.4.1.11591.12.2", "Tiger(24,3)" },
+      { "1.3.6.1.4.1.11591.15.1", "OpenPGP.Ed25519" },
+      { "1.3.6.1.4.1.11591.4.11", "Scrypt" },
+      { "1.3.6.1.4.1.25258.1.3", "McEliece" },
+      { "1.3.6.1.4.1.25258.1.5", "XMSS-draft6" },
+      { "1.3.6.1.4.1.25258.1.6.1", "GOST-34.10-2012-256/EMSA1(SHA-256)" },
+      { "1.3.6.1.4.1.25258.1.8", "XMSS-draft12" },
+      { "1.3.6.1.4.1.25258.3.1", "Serpent/CBC" },
+      { "1.3.6.1.4.1.25258.3.101", "Serpent/GCM" },
+      { "1.3.6.1.4.1.25258.3.102", "Twofish/GCM" },
+      { "1.3.6.1.4.1.25258.3.2", "Threefish-512/CBC" },
+      { "1.3.6.1.4.1.25258.3.2.1", "AES-128/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.2", "AES-192/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.3", "AES-256/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.4", "Serpent/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.5", "Twofish/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.6", "Camellia-128/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.7", "Camellia-192/OCB" },
+      { "1.3.6.1.4.1.25258.3.2.8", "Camellia-256/OCB" },
+      { "1.3.6.1.4.1.25258.3.3", "Twofish/CBC" },
+      { "1.3.6.1.4.1.25258.3.4.1", "AES-128/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.2", "AES-192/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.3", "AES-256/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.4", "Serpent/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.5", "Twofish/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.6", "Camellia-128/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.7", "Camellia-192/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.8", "Camellia-256/SIV" },
+      { "1.3.6.1.4.1.25258.3.4.9", "SM4/SIV" },
+      { "1.3.6.1.4.1.3029.1.2.1", "ElGamal" },
+      { "1.3.6.1.4.1.3029.1.5.1", "OpenPGP.Curve25519" },
+      { "1.3.6.1.4.1.311.20.2.2", "Microsoft SmartcardLogon" },
+      { "1.3.6.1.4.1.311.20.2.3", "Microsoft UPN" },
+      { "1.3.6.1.4.1.8301.3.1.2.9.0.38", "secp521r1" },
+      { "1.3.6.1.5.5.7.1.1", "PKIX.AuthorityInformationAccess" },
+      { "1.3.6.1.5.5.7.3.1", "PKIX.ServerAuth" },
+      { "1.3.6.1.5.5.7.3.2", "PKIX.ClientAuth" },
+      { "1.3.6.1.5.5.7.3.3", "PKIX.CodeSigning" },
+      { "1.3.6.1.5.5.7.3.4", "PKIX.EmailProtection" },
+      { "1.3.6.1.5.5.7.3.5", "PKIX.IPsecEndSystem" },
+      { "1.3.6.1.5.5.7.3.6", "PKIX.IPsecTunnel" },
+      { "1.3.6.1.5.5.7.3.7", "PKIX.IPsecUser" },
+      { "1.3.6.1.5.5.7.3.8", "PKIX.TimeStamping" },
+      { "1.3.6.1.5.5.7.3.9", "PKIX.OCSPSigning" },
+      { "1.3.6.1.5.5.7.48.1", "PKIX.OCSP" },
+      { "1.3.6.1.5.5.7.48.1.1", "PKIX.OCSP.BasicResponse" },
+      { "1.3.6.1.5.5.7.48.2", "PKIX.CertificateAuthorityIssuers" },
+      { "1.3.6.1.5.5.7.8.5", "PKIX.XMPPAddr" },
+      { "2.16.840.1.101.3.4.1.2", "AES-128/CBC" },
+      { "2.16.840.1.101.3.4.1.22", "AES-192/CBC" },
+      { "2.16.840.1.101.3.4.1.25", "KeyWrap.AES-192" },
+      { "2.16.840.1.101.3.4.1.26", "AES-192/GCM" },
+      { "2.16.840.1.101.3.4.1.27", "AES-192/CCM" },
+      { "2.16.840.1.101.3.4.1.42", "AES-256/CBC" },
+      { "2.16.840.1.101.3.4.1.45", "KeyWrap.AES-256" },
+      { "2.16.840.1.101.3.4.1.46", "AES-256/GCM" },
+      { "2.16.840.1.101.3.4.1.47", "AES-256/CCM" },
+      { "2.16.840.1.101.3.4.1.5", "KeyWrap.AES-128" },
+      { "2.16.840.1.101.3.4.1.6", "AES-128/GCM" },
+      { "2.16.840.1.101.3.4.1.7", "AES-128/CCM" },
+      { "2.16.840.1.101.3.4.2.1", "SHA-256" },
+      { "2.16.840.1.101.3.4.2.10", "SHA-3(512)" },
+      { "2.16.840.1.101.3.4.2.11", "SHAKE-128" },
+      { "2.16.840.1.101.3.4.2.12", "SHAKE-256" },
+      { "2.16.840.1.101.3.4.2.2", "SHA-384" },
+      { "2.16.840.1.101.3.4.2.3", "SHA-512" },
+      { "2.16.840.1.101.3.4.2.4", "SHA-224" },
+      { "2.16.840.1.101.3.4.2.6", "SHA-512-256" },
+      { "2.16.840.1.101.3.4.2.7", "SHA-3(224)" },
+      { "2.16.840.1.101.3.4.2.8", "SHA-3(256)" },
+      { "2.16.840.1.101.3.4.2.9", "SHA-3(384)" },
+      { "2.16.840.1.101.3.4.3.1", "DSA/EMSA1(SHA-224)" },
+      { "2.16.840.1.101.3.4.3.10", "ECDSA/EMSA1(SHA-3(256))" },
+      { "2.16.840.1.101.3.4.3.11", "ECDSA/EMSA1(SHA-3(384))" },
+      { "2.16.840.1.101.3.4.3.12", "ECDSA/EMSA1(SHA-3(512))" },
+      { "2.16.840.1.101.3.4.3.13", "RSA/EMSA3(SHA-3(224))" },
+      { "2.16.840.1.101.3.4.3.14", "RSA/EMSA3(SHA-3(256))" },
+      { "2.16.840.1.101.3.4.3.15", "RSA/EMSA3(SHA-3(384))" },
+      { "2.16.840.1.101.3.4.3.16", "RSA/EMSA3(SHA-3(512))" },
+      { "2.16.840.1.101.3.4.3.2", "DSA/EMSA1(SHA-256)" },
+      { "2.16.840.1.101.3.4.3.3", "DSA/EMSA1(SHA-384)" },
+      { "2.16.840.1.101.3.4.3.4", "DSA/EMSA1(SHA-512)" },
+      { "2.16.840.1.101.3.4.3.5", "DSA/EMSA1(SHA-3(224))" },
+      { "2.16.840.1.101.3.4.3.6", "DSA/EMSA1(SHA-3(256))" },
+      { "2.16.840.1.101.3.4.3.7", "DSA/EMSA1(SHA-3(384))" },
+      { "2.16.840.1.101.3.4.3.8", "DSA/EMSA1(SHA-3(512))" },
+      { "2.16.840.1.101.3.4.3.9", "ECDSA/EMSA1(SHA-3(224))" },
+      { "2.16.840.1.113730.1.13", "Certificate Comment" },
+      { "2.5.29.14", "X509v3.SubjectKeyIdentifier" },
+      { "2.5.29.15", "X509v3.KeyUsage" },
+      { "2.5.29.16", "X509v3.PrivateKeyUsagePeriod" },
+      { "2.5.29.17", "X509v3.SubjectAlternativeName" },
+      { "2.5.29.18", "X509v3.IssuerAlternativeName" },
+      { "2.5.29.19", "X509v3.BasicConstraints" },
+      { "2.5.29.20", "X509v3.CRLNumber" },
+      { "2.5.29.21", "X509v3.ReasonCode" },
+      { "2.5.29.23", "X509v3.HoldInstructionCode" },
+      { "2.5.29.24", "X509v3.InvalidityDate" },
+      { "2.5.29.28", "X509v3.CRLIssuingDistributionPoint" },
+      { "2.5.29.30", "X509v3.NameConstraints" },
+      { "2.5.29.31", "X509v3.CRLDistributionPoints" },
+      { "2.5.29.32", "X509v3.CertificatePolicies" },
+      { "2.5.29.32.0", "X509v3.AnyPolicy" },
+      { "2.5.29.35", "X509v3.AuthorityKeyIdentifier" },
+      { "2.5.29.36", "X509v3.PolicyConstraints" },
+      { "2.5.29.37", "X509v3.ExtendedKeyUsage" },
+      { "2.5.4.10", "X520.Organization" },
+      { "2.5.4.11", "X520.OrganizationalUnit" },
+      { "2.5.4.12", "X520.Title" },
+      { "2.5.4.3", "X520.CommonName" },
+      { "2.5.4.4", "X520.Surname" },
+      { "2.5.4.42", "X520.GivenName" },
+      { "2.5.4.43", "X520.Initials" },
+      { "2.5.4.44", "X520.GenerationalQualifier" },
+      { "2.5.4.46", "X520.DNQualifier" },
+      { "2.5.4.5", "X520.SerialNumber" },
+      { "2.5.4.6", "X520.Country" },
+      { "2.5.4.65", "X520.Pseudonym" },
+      { "2.5.4.7", "X520.Locality" },
+      { "2.5.4.8", "X520.State" },
+      { "2.5.4.9", "X520.StreetAddress" },
+      { "2.5.8.1.1", "RSA" }
+      };
+   }
+
+std::unordered_map<std::string, OID> OIDS::load_str2oid_map()
+   {
+   return std::unordered_map<std::string,OID>{
+      { "AES-128/CBC", OID({2,16,840,1,101,3,4,1,2}) },
+      { "AES-128/CCM", OID({2,16,840,1,101,3,4,1,7}) },
+      { "AES-128/GCM", OID({2,16,840,1,101,3,4,1,6}) },
+      { "AES-128/OCB", OID({1,3,6,1,4,1,25258,3,2,1}) },
+      { "AES-128/SIV", OID({1,3,6,1,4,1,25258,3,4,1}) },
+      { "AES-192/CBC", OID({2,16,840,1,101,3,4,1,22}) },
+      { "AES-192/CCM", OID({2,16,840,1,101,3,4,1,27}) },
+      { "AES-192/GCM", OID({2,16,840,1,101,3,4,1,26}) },
+      { "AES-192/OCB", OID({1,3,6,1,4,1,25258,3,2,2}) },
+      { "AES-192/SIV", OID({1,3,6,1,4,1,25258,3,4,2}) },
+      { "AES-256/CBC", OID({2,16,840,1,101,3,4,1,42}) },
+      { "AES-256/CCM", OID({2,16,840,1,101,3,4,1,47}) },
+      { "AES-256/GCM", OID({2,16,840,1,101,3,4,1,46}) },
+      { "AES-256/OCB", OID({1,3,6,1,4,1,25258,3,2,3}) },
+      { "AES-256/SIV", OID({1,3,6,1,4,1,25258,3,4,3}) },
+      { "CAST-128/CBC", OID({1,2,840,113533,7,66,10}) },
+      { "Camellia-128/CBC", OID({1,2,392,200011,61,1,1,1,2}) },
+      { "Camellia-128/GCM", OID({0,3,4401,5,3,1,9,6}) },
+      { "Camellia-128/OCB", OID({1,3,6,1,4,1,25258,3,2,6}) },
+      { "Camellia-128/SIV", OID({1,3,6,1,4,1,25258,3,4,6}) },
+      { "Camellia-192/CBC", OID({1,2,392,200011,61,1,1,1,3}) },
+      { "Camellia-192/GCM", OID({0,3,4401,5,3,1,9,26}) },
+      { "Camellia-192/OCB", OID({1,3,6,1,4,1,25258,3,2,7}) },
+      { "Camellia-192/SIV", OID({1,3,6,1,4,1,25258,3,4,7}) },
+      { "Camellia-256/CBC", OID({1,2,392,200011,61,1,1,1,4}) },
+      { "Camellia-256/GCM", OID({0,3,4401,5,3,1,9,46}) },
+      { "Camellia-256/OCB", OID({1,3,6,1,4,1,25258,3,2,8}) },
+      { "Camellia-256/SIV", OID({1,3,6,1,4,1,25258,3,4,8}) },
+      { "Certificate Comment", OID({2,16,840,1,113730,1,13}) },
+      { "ChaCha20Poly1305", OID({1,2,840,113549,1,9,16,3,18}) },
+      { "Compression.Zlib", OID({1,2,840,113549,1,9,16,3,8}) },
+      { "Curve25519", OID({1,3,101,110}) },
+      { "DES/CBC", OID({1,3,14,3,2,7}) },
+      { "DH", OID({1,2,840,10046,2,1}) },
+      { "DSA", OID({1,2,840,10040,4,1}) },
+      { "DSA/EMSA1(SHA-160)", OID({1,2,840,10040,4,3}) },
+      { "DSA/EMSA1(SHA-224)", OID({2,16,840,1,101,3,4,3,1}) },
+      { "DSA/EMSA1(SHA-256)", OID({2,16,840,1,101,3,4,3,2}) },
+      { "DSA/EMSA1(SHA-3(224))", OID({2,16,840,1,101,3,4,3,5}) },
+      { "DSA/EMSA1(SHA-3(256))", OID({2,16,840,1,101,3,4,3,6}) },
+      { "DSA/EMSA1(SHA-3(384))", OID({2,16,840,1,101,3,4,3,7}) },
+      { "DSA/EMSA1(SHA-3(512))", OID({2,16,840,1,101,3,4,3,8}) },
+      { "DSA/EMSA1(SHA-384)", OID({2,16,840,1,101,3,4,3,3}) },
+      { "DSA/EMSA1(SHA-512)", OID({2,16,840,1,101,3,4,3,4}) },
+      { "ECDH", OID({1,3,132,1,12}) },
+      { "ECDSA", OID({1,2,840,10045,2,1}) },
+      { "ECDSA/EMSA1(SHA-160)", OID({1,2,840,10045,4,1}) },
+      { "ECDSA/EMSA1(SHA-224)", OID({1,2,840,10045,4,3,1}) },
+      { "ECDSA/EMSA1(SHA-256)", OID({1,2,840,10045,4,3,2}) },
+      { "ECDSA/EMSA1(SHA-3(224))", OID({2,16,840,1,101,3,4,3,9}) },
+      { "ECDSA/EMSA1(SHA-3(256))", OID({2,16,840,1,101,3,4,3,10}) },
+      { "ECDSA/EMSA1(SHA-3(384))", OID({2,16,840,1,101,3,4,3,11}) },
+      { "ECDSA/EMSA1(SHA-3(512))", OID({2,16,840,1,101,3,4,3,12}) },
+      { "ECDSA/EMSA1(SHA-384)", OID({1,2,840,10045,4,3,3}) },
+      { "ECDSA/EMSA1(SHA-512)", OID({1,2,840,10045,4,3,4}) },
+      { "ECGDSA", OID({1,3,36,3,3,2,5,2,1}) },
+      { "ECGDSA/EMSA1(RIPEMD-160)", OID({1,3,36,3,3,2,5,4,1}) },
+      { "ECGDSA/EMSA1(SHA-160)", OID({1,3,36,3,3,2,5,4,2}) },
+      { "ECGDSA/EMSA1(SHA-224)", OID({1,3,36,3,3,2,5,4,3}) },
+      { "ECGDSA/EMSA1(SHA-256)", OID({1,3,36,3,3,2,5,4,4}) },
+      { "ECGDSA/EMSA1(SHA-384)", OID({1,3,36,3,3,2,5,4,5}) },
+      { "ECGDSA/EMSA1(SHA-512)", OID({1,3,36,3,3,2,5,4,6}) },
+      { "ECKCDSA", OID({1,0,14888,3,0,5}) },
+      { "ECKCDSA/EMSA1(SHA-1)", OID({1,2,410,200004,1,100,4,3}) },
+      { "ECKCDSA/EMSA1(SHA-224)", OID({1,2,410,200004,1,100,4,4}) },
+      { "ECKCDSA/EMSA1(SHA-256)", OID({1,2,410,200004,1,100,4,5}) },
+      { "Ed25519", OID({1,3,101,112}) },
+      { "ElGamal", OID({1,3,6,1,4,1,3029,1,2,1}) },
+      { "GOST-34.10", OID({1,2,643,2,2,19}) },
+      { "GOST-34.10-2012-256", OID({1,2,643,7,1,1,1,1}) },
+      { "GOST-34.10-2012-256/EMSA1(SHA-256)", OID({1,3,6,1,4,1,25258,1,6,1}) },
+      { "GOST-34.10-2012-256/EMSA1(Streebog-256)", OID({1,2,643,7,1,1,3,2}) },
+      { "GOST-34.10-2012-512", OID({1,2,643,7,1,1,1,2}) },
+      { "GOST-34.10-2012-512/EMSA1(Streebog-512)", OID({1,2,643,7,1,1,3,3}) },
+      { "GOST-34.10/EMSA1(GOST-R-34.11-94)", OID({1,2,643,2,2,3}) },
+      { "GOST.INN", OID({1,2,643,3,131,1,1}) },
+      { "GOST.IssuerSigningTool", OID({1,2,643,100,112}) },
+      { "GOST.OGRN", OID({1,2,643,100,1}) },
+      { "GOST.SubjectSigningTool", OID({1,2,643,100,111}) },
+      { "HMAC(SHA-160)", OID({1,2,840,113549,2,7}) },
+      { "HMAC(SHA-224)", OID({1,2,840,113549,2,8}) },
+      { "HMAC(SHA-256)", OID({1,2,840,113549,2,9}) },
+      { "HMAC(SHA-384)", OID({1,2,840,113549,2,10}) },
+      { "HMAC(SHA-512)", OID({1,2,840,113549,2,11}) },
+      { "HMAC(SHA-512-256)", OID({1,2,840,113549,2,13}) },
+      { "KeyWrap.AES-128", OID({2,16,840,1,101,3,4,1,5}) },
+      { "KeyWrap.AES-192", OID({2,16,840,1,101,3,4,1,25}) },
+      { "KeyWrap.AES-256", OID({2,16,840,1,101,3,4,1,45}) },
+      { "KeyWrap.CAST-128", OID({1,2,840,113533,7,66,15}) },
+      { "KeyWrap.TripleDES", OID({1,2,840,113549,1,9,16,3,6}) },
+      { "MD5", OID({1,2,840,113549,2,5}) },
+      { "MGF1", OID({1,2,840,113549,1,1,8}) },
+      { "McEliece", OID({1,3,6,1,4,1,25258,1,3}) },
+      { "Microsoft SmartcardLogon", OID({1,3,6,1,4,1,311,20,2,2}) },
+      { "Microsoft UPN", OID({1,3,6,1,4,1,311,20,2,3}) },
+      { "OpenPGP.Curve25519", OID({1,3,6,1,4,1,3029,1,5,1}) },
+      { "OpenPGP.Ed25519", OID({1,3,6,1,4,1,11591,15,1}) },
+      { "PBE-PKCS5v20", OID({1,2,840,113549,1,5,13}) },
+      { "PBES2", OID({1,2,840,113549,1,5,13}) },
+      { "PKCS5.PBKDF2", OID({1,2,840,113549,1,5,12}) },
+      { "PKCS9.ChallengePassword", OID({1,2,840,113549,1,9,7}) },
+      { "PKCS9.ContentType", OID({1,2,840,113549,1,9,3}) },
+      { "PKCS9.EmailAddress", OID({1,2,840,113549,1,9,1}) },
+      { "PKCS9.ExtensionRequest", OID({1,2,840,113549,1,9,14}) },
+      { "PKCS9.MessageDigest", OID({1,2,840,113549,1,9,4}) },
+      { "PKCS9.UnstructuredName", OID({1,2,840,113549,1,9,2}) },
+      { "PKIX.AuthorityInformationAccess", OID({1,3,6,1,5,5,7,1,1}) },
+      { "PKIX.CertificateAuthorityIssuers", OID({1,3,6,1,5,5,7,48,2}) },
+      { "PKIX.ClientAuth", OID({1,3,6,1,5,5,7,3,2}) },
+      { "PKIX.CodeSigning", OID({1,3,6,1,5,5,7,3,3}) },
+      { "PKIX.EmailProtection", OID({1,3,6,1,5,5,7,3,4}) },
+      { "PKIX.IPsecEndSystem", OID({1,3,6,1,5,5,7,3,5}) },
+      { "PKIX.IPsecTunnel", OID({1,3,6,1,5,5,7,3,6}) },
+      { "PKIX.IPsecUser", OID({1,3,6,1,5,5,7,3,7}) },
+      { "PKIX.OCSP", OID({1,3,6,1,5,5,7,48,1}) },
+      { "PKIX.OCSP.BasicResponse", OID({1,3,6,1,5,5,7,48,1,1}) },
+      { "PKIX.OCSPSigning", OID({1,3,6,1,5,5,7,3,9}) },
+      { "PKIX.ServerAuth", OID({1,3,6,1,5,5,7,3,1}) },
+      { "PKIX.TimeStamping", OID({1,3,6,1,5,5,7,3,8}) },
+      { "PKIX.XMPPAddr", OID({1,3,6,1,5,5,7,8,5}) },
+      { "RIPEMD-160", OID({1,3,36,3,2,1}) },
+      { "RSA", OID({1,2,840,113549,1,1,1}) },
+      { "RSA/EMSA3(MD5)", OID({1,2,840,113549,1,1,4}) },
+      { "RSA/EMSA3(RIPEMD-160)", OID({1,3,36,3,3,1,2}) },
+      { "RSA/EMSA3(SHA-160)", OID({1,2,840,113549,1,1,5}) },
+      { "RSA/EMSA3(SHA-224)", OID({1,2,840,113549,1,1,14}) },
+      { "RSA/EMSA3(SHA-256)", OID({1,2,840,113549,1,1,11}) },
+      { "RSA/EMSA3(SHA-3(224))", OID({2,16,840,1,101,3,4,3,13}) },
+      { "RSA/EMSA3(SHA-3(256))", OID({2,16,840,1,101,3,4,3,14}) },
+      { "RSA/EMSA3(SHA-3(384))", OID({2,16,840,1,101,3,4,3,15}) },
+      { "RSA/EMSA3(SHA-3(512))", OID({2,16,840,1,101,3,4,3,16}) },
+      { "RSA/EMSA3(SHA-384)", OID({1,2,840,113549,1,1,12}) },
+      { "RSA/EMSA3(SHA-512)", OID({1,2,840,113549,1,1,13}) },
+      { "RSA/EMSA3(SHA-512-256)", OID({1,2,840,113549,1,1,16}) },
+      { "RSA/EMSA3(SM3)", OID({1,2,156,10197,1,504}) },
+      { "RSA/EMSA4", OID({1,2,840,113549,1,1,10}) },
+      { "RSA/OAEP", OID({1,2,840,113549,1,1,7}) },
+      { "SEED/CBC", OID({1,2,410,200004,1,4}) },
+      { "SHA-160", OID({1,3,14,3,2,26}) },
+      { "SHA-224", OID({2,16,840,1,101,3,4,2,4}) },
+      { "SHA-256", OID({2,16,840,1,101,3,4,2,1}) },
+      { "SHA-3(224)", OID({2,16,840,1,101,3,4,2,7}) },
+      { "SHA-3(256)", OID({2,16,840,1,101,3,4,2,8}) },
+      { "SHA-3(384)", OID({2,16,840,1,101,3,4,2,9}) },
+      { "SHA-3(512)", OID({2,16,840,1,101,3,4,2,10}) },
+      { "SHA-384", OID({2,16,840,1,101,3,4,2,2}) },
+      { "SHA-512", OID({2,16,840,1,101,3,4,2,3}) },
+      { "SHA-512-256", OID({2,16,840,1,101,3,4,2,6}) },
+      { "SHAKE-128", OID({2,16,840,1,101,3,4,2,11}) },
+      { "SHAKE-256", OID({2,16,840,1,101,3,4,2,12}) },
+      { "SM2", OID({1,2,156,10197,1,301,1}) },
+      { "SM2_Enc", OID({1,2,156,10197,1,301,3}) },
+      { "SM2_Kex", OID({1,2,156,10197,1,301,2}) },
+      { "SM2_Sig", OID({1,2,156,10197,1,301,1}) },
+      { "SM2_Sig/SM3", OID({1,2,156,10197,1,501}) },
+      { "SM3", OID({1,2,156,10197,1,401}) },
+      { "SM4/CBC", OID({1,2,156,10197,1,104,2}) },
+      { "SM4/GCM", OID({1,2,156,10197,1,104,8}) },
+      { "SM4/OCB", OID({1,2,156,10197,1,104,100}) },
+      { "SM4/SIV", OID({1,3,6,1,4,1,25258,3,4,9}) },
+      { "Scrypt", OID({1,3,6,1,4,1,11591,4,11}) },
+      { "Serpent/CBC", OID({1,3,6,1,4,1,25258,3,1}) },
+      { "Serpent/GCM", OID({1,3,6,1,4,1,25258,3,101}) },
+      { "Serpent/OCB", OID({1,3,6,1,4,1,25258,3,2,4}) },
+      { "Serpent/SIV", OID({1,3,6,1,4,1,25258,3,4,4}) },
+      { "Streebog-256", OID({1,2,643,7,1,1,2,2}) },
+      { "Streebog-512", OID({1,2,643,7,1,1,2,3}) },
+      { "Threefish-512/CBC", OID({1,3,6,1,4,1,25258,3,2}) },
+      { "Tiger(24,3)", OID({1,3,6,1,4,1,11591,12,2}) },
+      { "TripleDES/CBC", OID({1,2,840,113549,3,7}) },
+      { "Twofish/CBC", OID({1,3,6,1,4,1,25258,3,3}) },
+      { "Twofish/GCM", OID({1,3,6,1,4,1,25258,3,102}) },
+      { "Twofish/OCB", OID({1,3,6,1,4,1,25258,3,2,5}) },
+      { "Twofish/SIV", OID({1,3,6,1,4,1,25258,3,4,5}) },
+      { "X509v3.AnyPolicy", OID({2,5,29,32,0}) },
+      { "X509v3.AuthorityKeyIdentifier", OID({2,5,29,35}) },
+      { "X509v3.BasicConstraints", OID({2,5,29,19}) },
+      { "X509v3.CRLDistributionPoints", OID({2,5,29,31}) },
+      { "X509v3.CRLIssuingDistributionPoint", OID({2,5,29,28}) },
+      { "X509v3.CRLNumber", OID({2,5,29,20}) },
+      { "X509v3.CertificatePolicies", OID({2,5,29,32}) },
+      { "X509v3.ExtendedKeyUsage", OID({2,5,29,37}) },
+      { "X509v3.HoldInstructionCode", OID({2,5,29,23}) },
+      { "X509v3.InvalidityDate", OID({2,5,29,24}) },
+      { "X509v3.IssuerAlternativeName", OID({2,5,29,18}) },
+      { "X509v3.KeyUsage", OID({2,5,29,15}) },
+      { "X509v3.NameConstraints", OID({2,5,29,30}) },
+      { "X509v3.PolicyConstraints", OID({2,5,29,36}) },
+      { "X509v3.PrivateKeyUsagePeriod", OID({2,5,29,16}) },
+      { "X509v3.ReasonCode", OID({2,5,29,21}) },
+      { "X509v3.SubjectAlternativeName", OID({2,5,29,17}) },
+      { "X509v3.SubjectKeyIdentifier", OID({2,5,29,14}) },
+      { "X520.CommonName", OID({2,5,4,3}) },
+      { "X520.Country", OID({2,5,4,6}) },
+      { "X520.DNQualifier", OID({2,5,4,46}) },
+      { "X520.GenerationalQualifier", OID({2,5,4,44}) },
+      { "X520.GivenName", OID({2,5,4,42}) },
+      { "X520.Initials", OID({2,5,4,43}) },
+      { "X520.Locality", OID({2,5,4,7}) },
+      { "X520.Organization", OID({2,5,4,10}) },
+      { "X520.OrganizationalUnit", OID({2,5,4,11}) },
+      { "X520.Pseudonym", OID({2,5,4,65}) },
+      { "X520.SerialNumber", OID({2,5,4,5}) },
+      { "X520.State", OID({2,5,4,8}) },
+      { "X520.StreetAddress", OID({2,5,4,9}) },
+      { "X520.Surname", OID({2,5,4,4}) },
+      { "X520.Title", OID({2,5,4,12}) },
+      { "XMSS", OID({0,4,0,127,0,15,1,1,13,0}) },
+      { "XMSS-draft12", OID({1,3,6,1,4,1,25258,1,8}) },
+      { "XMSS-draft6", OID({1,3,6,1,4,1,25258,1,5}) },
+      { "brainpool160r1", OID({1,3,36,3,3,2,8,1,1,1}) },
+      { "brainpool192r1", OID({1,3,36,3,3,2,8,1,1,3}) },
+      { "brainpool224r1", OID({1,3,36,3,3,2,8,1,1,5}) },
+      { "brainpool256r1", OID({1,3,36,3,3,2,8,1,1,7}) },
+      { "brainpool320r1", OID({1,3,36,3,3,2,8,1,1,9}) },
+      { "brainpool384r1", OID({1,3,36,3,3,2,8,1,1,11}) },
+      { "brainpool512r1", OID({1,3,36,3,3,2,8,1,1,13}) },
+      { "frp256v1", OID({1,2,250,1,223,101,256,1}) },
+      { "gost_256A", OID({1,2,643,7,1,2,1,1,1}) },
+      { "gost_256B", OID({1,2,643,7,1,2,1,1,2}) },
+      { "gost_512A", OID({1,2,643,7,1,2,1,2,1}) },
+      { "gost_512B", OID({1,2,643,7,1,2,1,2,2}) },
+      { "secp160k1", OID({1,3,132,0,9}) },
+      { "secp160r1", OID({1,3,132,0,8}) },
+      { "secp160r2", OID({1,3,132,0,30}) },
+      { "secp192k1", OID({1,3,132,0,31}) },
+      { "secp192r1", OID({1,2,840,10045,3,1,1}) },
+      { "secp224k1", OID({1,3,132,0,32}) },
+      { "secp224r1", OID({1,3,132,0,33}) },
+      { "secp256k1", OID({1,3,132,0,10}) },
+      { "secp256r1", OID({1,2,840,10045,3,1,7}) },
+      { "secp384r1", OID({1,3,132,0,34}) },
+      { "secp521r1", OID({1,3,132,0,35}) },
+      { "sm2p256v1", OID({1,2,156,10197,1,301}) },
+      { "x962_p192v2", OID({1,2,840,10045,3,1,2}) },
+      { "x962_p192v3", OID({1,2,840,10045,3,1,3}) },
+      { "x962_p239v1", OID({1,2,840,10045,3,1,4}) },
+      { "x962_p239v2", OID({1,2,840,10045,3,1,5}) },
+      { "x962_p239v3", OID({1,2,840,10045,3,1,6}) }
+      };
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/asn1/oids.cpp b/third_party/botan/src/lib/asn1/oids.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/oids.cpp
@@ -0,0 +1,134 @@
+/*
+* OID Registry
+* (C) 1999-2008,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/oids.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+namespace {
+
+class OID_Map final
+   {
+   public:
+      void add_oid(const OID& oid, const std::string& str)
+         {
+         add_str2oid(oid, str);
+         add_oid2str(oid, str);
+         }
+
+      void add_str2oid(const OID& oid, const std::string& str)
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         auto i = m_str2oid.find(str);
+         if(i == m_str2oid.end())
+            m_str2oid.insert(std::make_pair(str, oid));
+         }
+
+      void add_oid2str(const OID& oid, const std::string& str)
+         {
+         const std::string oid_str = oid.to_string();
+         lock_guard_type<mutex_type> lock(m_mutex);
+         auto i = m_oid2str.find(oid_str);
+         if(i == m_oid2str.end())
+            m_oid2str.insert(std::make_pair(oid_str, str));
+         }
+
+      std::string oid2str(const OID& oid)
+         {
+         const std::string oid_str = oid.to_string();
+
+         lock_guard_type<mutex_type> lock(m_mutex);
+
+         auto i = m_oid2str.find(oid_str);
+         if(i != m_oid2str.end())
+            return i->second;
+
+         return "";
+         }
+
+      OID str2oid(const std::string& str)
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         auto i = m_str2oid.find(str);
+         if(i != m_str2oid.end())
+            return i->second;
+
+         return OID();
+         }
+
+      bool have_oid(const std::string& str)
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         return m_str2oid.find(str) != m_str2oid.end();
+         }
+
+      static OID_Map& global_registry()
+         {
+         static OID_Map g_map;
+         return g_map;
+         }
+
+   private:
+
+      OID_Map()
+         {
+         m_str2oid = OIDS::load_str2oid_map();
+         m_oid2str = OIDS::load_oid2str_map();
+         }
+
+      mutex_type m_mutex;
+      std::unordered_map<std::string, OID> m_str2oid;
+      std::unordered_map<std::string, std::string> m_oid2str;
+   };
+
+}
+
+void OIDS::add_oid(const OID& oid, const std::string& name)
+   {
+   OID_Map::global_registry().add_oid(oid, name);
+   }
+
+void OIDS::add_oidstr(const char* oidstr, const char* name)
+   {
+   add_oid(OID(oidstr), name);
+   }
+
+void OIDS::add_oid2str(const OID& oid, const std::string& name)
+   {
+   OID_Map::global_registry().add_oid2str(oid, name);
+   }
+
+void OIDS::add_str2oid(const OID& oid, const std::string& name)
+   {
+   OID_Map::global_registry().add_str2oid(oid, name);
+   }
+
+std::string OIDS::oid2str_or_empty(const OID& oid)
+   {
+   return OID_Map::global_registry().oid2str(oid);
+   }
+
+OID OIDS::str2oid_or_empty(const std::string& name)
+   {
+   return OID_Map::global_registry().str2oid(name);
+   }
+
+std::string OIDS::oid2str_or_throw(const OID& oid)
+   {
+   const std::string s = OIDS::oid2str_or_empty(oid);
+   if(s.empty())
+      throw Lookup_Error("No name associated with OID " + oid.to_string());
+   return s;
+   }
+
+bool OIDS::have_oid(const std::string& name)
+   {
+   return OID_Map::global_registry().have_oid(name);
+   }
+
+}
diff --git a/third_party/botan/src/lib/asn1/oids.h b/third_party/botan/src/lib/asn1/oids.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/asn1/oids.h
@@ -0,0 +1,98 @@
+/*
+* OID Registry
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OIDS_H_
+#define BOTAN_OIDS_H_
+
+#include <botan/asn1_oid.h>
+#include <unordered_map>
+
+namespace Botan {
+
+namespace OIDS {
+
+/**
+* Register an OID to string mapping.
+* @param oid the oid to register
+* @param name the name to be associated with the oid
+*/
+BOTAN_UNSTABLE_API void add_oid(const OID& oid, const std::string& name);
+
+BOTAN_UNSTABLE_API void add_oid2str(const OID& oid, const std::string& name);
+BOTAN_UNSTABLE_API void add_str2oid(const OID& oid, const std::string& name);
+
+BOTAN_UNSTABLE_API void add_oidstr(const char* oidstr, const char* name);
+
+std::unordered_map<std::string, std::string> load_oid2str_map();
+std::unordered_map<std::string, OID> load_str2oid_map();
+
+/**
+* Resolve an OID
+* @param oid the OID to look up
+* @return name associated with this OID, or an empty string
+*/
+BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID& oid);
+
+/**
+* Find the OID to a name. The lookup will be performed in the
+* general OID section of the configuration.
+* @param name the name to resolve
+* @return OID associated with the specified name
+*/
+BOTAN_UNSTABLE_API OID str2oid_or_empty(const std::string& name);
+
+BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID& oid);
+
+/**
+* See if an OID exists in the internal table.
+* @param oid the oid to check for
+* @return true if the oid is registered
+*/
+BOTAN_UNSTABLE_API bool BOTAN_DEPRECATED("Just lookup the value instead") have_oid(const std::string& oid);
+
+/**
+* Tests whether the specified OID stands for the specified name.
+* @param oid the OID to check
+* @param name the name to check
+* @return true if the specified OID stands for the specified name
+*/
+inline bool BOTAN_DEPRECATED("Use oid == OID::from_string(name)") name_of(const OID& oid, const std::string& name)
+   {
+   return (oid == str2oid_or_empty(name));
+   }
+
+/**
+* Prefer oid2str_or_empty
+*/
+inline std::string lookup(const OID& oid)
+   {
+   return oid2str_or_empty(oid);
+   }
+
+/**
+* Prefer str2oid_or_empty
+*/
+inline OID lookup(const std::string& name)
+   {
+   return str2oid_or_empty(name);
+   }
+
+inline std::string BOTAN_DEPRECATED("Use oid2str_or_empty") oid2str(const OID& oid)
+   {
+   return oid2str_or_empty(oid);
+   }
+
+inline OID BOTAN_DEPRECATED("Use str2oid_or_empty") str2oid(const std::string& name)
+   {
+   return str2oid_or_empty(name);
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/botan.h b/third_party/botan/src/lib/base/botan.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/botan.h
@@ -0,0 +1,41 @@
+/*
+* A vague catch all include file for Botan
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BOTAN_H_
+#define BOTAN_BOTAN_H_
+
+/*
+* There is no real reason for this header to exist beyond historical
+* reasons. The application should instead include the specific header
+* files that define the interfaces it intends to use.
+*
+* This header file will be removed in Botan 3.x
+*/
+
+#include <botan/lookup.h>
+#include <botan/version.h>
+#include <botan/parsing.h>
+#include <botan/init.h>
+#include <botan/rng.h>
+#include <botan/secmem.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+  #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_FILTERS)
+  #include <botan/filters.h>
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+  #include <botan/x509_key.h>
+  #include <botan/pkcs8.h>
+#endif
+
+BOTAN_DEPRECATED_HEADER(botan.h)
+
+#endif
diff --git a/third_party/botan/src/lib/base/buf_comp.cpp b/third_party/botan/src/lib/base/buf_comp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/buf_comp.cpp
@@ -0,0 +1,54 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/buf_comp.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Buffered_Computation::update_be(uint16_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_be(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+void Buffered_Computation::update_be(uint32_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_be(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+void Buffered_Computation::update_be(uint64_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_be(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+void Buffered_Computation::update_le(uint16_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_le(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+void Buffered_Computation::update_le(uint32_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_le(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+void Buffered_Computation::update_le(uint64_t val)
+   {
+   uint8_t inb[sizeof(val)];
+   store_le(val, inb);
+   add_data(inb, sizeof(inb));
+   }
+
+}
diff --git a/third_party/botan/src/lib/base/buf_comp.h b/third_party/botan/src/lib/base/buf_comp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/buf_comp.h
@@ -0,0 +1,178 @@
+/*
+* Buffered Computation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BUFFERED_COMPUTATION_H_
+#define BOTAN_BUFFERED_COMPUTATION_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* This class represents any kind of computation which uses an internal
+* state, such as hash functions or MACs
+*/
+class BOTAN_PUBLIC_API(2,0) Buffered_Computation
+   {
+   public:
+      /**
+      * @return length of the output of this function in bytes
+      */
+      virtual size_t output_length() const = 0;
+
+      /**
+      * Add new input to process.
+      * @param in the input to process as a byte array
+      * @param length of param in in bytes
+      */
+      void update(const uint8_t in[], size_t length) { add_data(in, length); }
+
+      /**
+      * Add new input to process.
+      * @param in the input to process as a secure_vector
+      */
+      void update(const secure_vector<uint8_t>& in)
+         {
+         add_data(in.data(), in.size());
+         }
+
+      /**
+      * Add new input to process.
+      * @param in the input to process as a std::vector
+      */
+      void update(const std::vector<uint8_t>& in)
+         {
+         add_data(in.data(), in.size());
+         }
+
+      void update_be(uint16_t val);
+      void update_be(uint32_t val);
+      void update_be(uint64_t val);
+
+      void update_le(uint16_t val);
+      void update_le(uint32_t val);
+      void update_le(uint64_t val);
+
+      /**
+      * Add new input to process.
+      * @param str the input to process as a std::string. Will be interpreted
+      * as a byte array based on the strings encoding.
+      */
+      void update(const std::string& str)
+         {
+         add_data(cast_char_ptr_to_uint8(str.data()), str.size());
+         }
+
+      /**
+      * Process a single byte.
+      * @param in the byte to process
+      */
+      void update(uint8_t in) { add_data(&in, 1); }
+
+      /**
+      * Complete the computation and retrieve the
+      * final result.
+      * @param out The byte array to be filled with the result.
+      * Must be of length output_length()
+      */
+      void final(uint8_t out[]) { final_result(out); }
+
+      /**
+      * Complete the computation and retrieve the
+      * final result.
+      * @return secure_vector holding the result
+      */
+      secure_vector<uint8_t> final()
+         {
+         secure_vector<uint8_t> output(output_length());
+         final_result(output.data());
+         return output;
+         }
+
+      std::vector<uint8_t> final_stdvec()
+         {
+         std::vector<uint8_t> output(output_length());
+         final_result(output.data());
+         return output;
+         }
+
+      template<typename Alloc>
+         void final(std::vector<uint8_t, Alloc>& out)
+         {
+         out.resize(output_length());
+         final_result(out.data());
+         }
+
+      /**
+      * Update and finalize computation. Does the same as calling update()
+      * and final() consecutively.
+      * @param in the input to process as a byte array
+      * @param length the length of the byte array
+      * @result the result of the call to final()
+      */
+      secure_vector<uint8_t> process(const uint8_t in[], size_t length)
+         {
+         add_data(in, length);
+         return final();
+         }
+
+      /**
+      * Update and finalize computation. Does the same as calling update()
+      * and final() consecutively.
+      * @param in the input to process
+      * @result the result of the call to final()
+      */
+      secure_vector<uint8_t> process(const secure_vector<uint8_t>& in)
+         {
+         add_data(in.data(), in.size());
+         return final();
+         }
+
+      /**
+      * Update and finalize computation. Does the same as calling update()
+      * and final() consecutively.
+      * @param in the input to process
+      * @result the result of the call to final()
+      */
+      secure_vector<uint8_t> process(const std::vector<uint8_t>& in)
+         {
+         add_data(in.data(), in.size());
+         return final();
+         }
+
+      /**
+      * Update and finalize computation. Does the same as calling update()
+      * and final() consecutively.
+      * @param in the input to process as a string
+      * @result the result of the call to final()
+      */
+      secure_vector<uint8_t> process(const std::string& in)
+         {
+         update(in);
+         return final();
+         }
+
+      virtual ~Buffered_Computation() = default;
+   private:
+      /**
+      * Add more data to the computation
+      * @param input is an input buffer
+      * @param length is the length of input in bytes
+      */
+      virtual void add_data(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * Write the final output to out
+      * @param out is an output buffer of output_length()
+      */
+      virtual void final_result(uint8_t out[]) = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/init.h b/third_party/botan/src/lib/base/init.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/init.h
@@ -0,0 +1,35 @@
+/*
+* Library Initialization
+* (C) 1999-2008,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LIBRARY_INITIALIZER_H_
+#define BOTAN_LIBRARY_INITIALIZER_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+BOTAN_DEPRECATED_HEADER(init.h)
+
+/*
+* Previously botan had state whose lifetime had to be explicitly
+* managed by the application. As of 1.11.14 this is no longer the
+* case, and this class is no longer needed and kept only for backwards
+* compatibility.
+*/
+class BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("LibraryInitializer is no longer required") LibraryInitializer final
+   {
+   public:
+      explicit LibraryInitializer(const std::string& /*ignored*/ = "") { }
+
+      static void initialize(const std::string& /*ignored*/ = "") {}
+      static void deinitialize() {}
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/key_spec.h b/third_party/botan/src/lib/base/key_spec.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/key_spec.h
@@ -0,0 +1,100 @@
+/*
+* Symmetric Key Length Specification
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEY_LEN_SPECIFICATION_H_
+#define BOTAN_KEY_LEN_SPECIFICATION_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Represents the length requirements on an algorithm key
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Length_Specification final
+   {
+   public:
+      /**
+      * Constructor for fixed length keys
+      * @param keylen the supported key length
+      */
+      explicit Key_Length_Specification(size_t keylen) :
+         m_min_keylen(keylen),
+         m_max_keylen(keylen),
+         m_keylen_mod(1)
+         {
+         }
+
+      /**
+      * Constructor for variable length keys
+      * @param min_k the smallest supported key length
+      * @param max_k the largest supported key length
+      * @param k_mod the number of bytes the key must be a multiple of
+      */
+      Key_Length_Specification(size_t min_k,
+                               size_t max_k,
+                               size_t k_mod = 1) :
+         m_min_keylen(min_k),
+         m_max_keylen(max_k ? max_k : min_k),
+         m_keylen_mod(k_mod)
+         {
+         }
+
+      /**
+      * @param length is a key length in bytes
+      * @return true iff this length is a valid length for this algo
+      */
+      bool valid_keylength(size_t length) const
+         {
+         return ((length >= m_min_keylen) &&
+                 (length <= m_max_keylen) &&
+                 (length % m_keylen_mod == 0));
+         }
+
+      /**
+      * @return minimum key length in bytes
+      */
+      size_t minimum_keylength() const
+         {
+         return m_min_keylen;
+         }
+
+      /**
+      * @return maximum key length in bytes
+      */
+      size_t maximum_keylength() const
+         {
+         return m_max_keylen;
+         }
+
+      /**
+      * @return key length multiple in bytes
+      */
+      size_t keylength_multiple() const
+         {
+         return m_keylen_mod;
+         }
+
+      /*
+      * Multiplies all length requirements with the given factor
+      * @param n the multiplication factor
+      * @return a key length specification multiplied by the factor
+      */
+      Key_Length_Specification multiple(size_t n) const
+         {
+         return Key_Length_Specification(n * m_min_keylen,
+                                         n * m_max_keylen,
+                                         n * m_keylen_mod);
+         }
+
+   private:
+      size_t m_min_keylen, m_max_keylen, m_keylen_mod;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/lookup.h b/third_party/botan/src/lib/base/lookup.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/lookup.h
@@ -0,0 +1,179 @@
+/*
+* Algorithm Lookup
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LOOKUP_H_
+#define BOTAN_LOOKUP_H_
+
+#include <botan/build.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <vector>
+#include <memory>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+   #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+   #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+   #include <botan/mac.h>
+#endif
+
+namespace Botan {
+
+BOTAN_DEPRECATED_HEADER(lookup.h)
+
+/*
+* As of 1.11.26 this header is deprecated. Instead use the calls T::create and
+* T::providers (as demonstrated in the implementation below).
+*/
+
+/*
+* Get an algorithm object
+*  NOTE: these functions create and return new objects, letting the
+* caller assume ownership of them
+*/
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+/**
+* Block cipher factory method.
+*
+* @param algo_spec the name of the desired block cipher
+* @param provider the provider to use
+* @return pointer to the block cipher object
+*/
+BOTAN_DEPRECATED("Use BlockCipher::create")
+inline BlockCipher* get_block_cipher(const std::string& algo_spec,
+                                     const std::string& provider = "")
+   {
+   return BlockCipher::create(algo_spec, provider).release();
+   }
+
+BOTAN_DEPRECATED("Use BlockCipher::create_or_throw")
+inline std::unique_ptr<BlockCipher> make_block_cipher(const std::string& algo_spec,
+                                                      const std::string& provider = "")
+   {
+   return BlockCipher::create_or_throw(algo_spec, provider);
+   }
+
+BOTAN_DEPRECATED("Use BlockCipher::providers")
+inline std::vector<std::string> get_block_cipher_providers(const std::string& algo_spec)
+   {
+   return BlockCipher::providers(algo_spec);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+/**
+* Stream cipher factory method.
+*
+* @param algo_spec the name of the desired stream cipher
+* @param provider the provider to use
+* @return pointer to the stream cipher object
+*/
+BOTAN_DEPRECATED("Use StreamCipher::create")
+inline StreamCipher* get_stream_cipher(const std::string& algo_spec,
+                                       const std::string& provider = "")
+   {
+   return StreamCipher::create(algo_spec, provider).release();
+   }
+
+BOTAN_DEPRECATED("Use StreamCipher::create_or_throw")
+inline std::unique_ptr<StreamCipher> make_stream_cipher(const std::string& algo_spec,
+                                                        const std::string& provider = "")
+   {
+   return StreamCipher::create_or_throw(algo_spec, provider);
+   }
+
+BOTAN_DEPRECATED("Use StreamCipher::providers")
+inline std::vector<std::string> get_stream_cipher_providers(const std::string& algo_spec)
+   {
+   return StreamCipher::providers(algo_spec);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+/**
+* Hash function factory method.
+*
+* @param algo_spec the name of the desired hash function
+* @param provider the provider to use
+* @return pointer to the hash function object
+*/
+BOTAN_DEPRECATED("Use HashFunction::create")
+inline HashFunction* get_hash_function(const std::string& algo_spec,
+                                       const std::string& provider = "")
+   {
+   return HashFunction::create(algo_spec, provider).release();
+   }
+
+BOTAN_DEPRECATED("Use HashFunction::create_or_throw")
+inline std::unique_ptr<HashFunction> make_hash_function(const std::string& algo_spec,
+                                                        const std::string& provider = "")
+   {
+   return HashFunction::create_or_throw(algo_spec, provider);
+   }
+
+BOTAN_DEPRECATED("Use HashFunction::create")
+inline HashFunction* get_hash(const std::string& algo_spec,
+                              const std::string& provider = "")
+   {
+   return HashFunction::create(algo_spec, provider).release();
+   }
+
+BOTAN_DEPRECATED("Use HashFunction::providers")
+inline std::vector<std::string> get_hash_function_providers(const std::string& algo_spec)
+   {
+   return HashFunction::providers(algo_spec);
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+/**
+* MAC factory method.
+*
+* @param algo_spec the name of the desired MAC
+* @param provider the provider to use
+* @return pointer to the MAC object
+*/
+BOTAN_DEPRECATED("MessageAuthenticationCode::create")
+inline MessageAuthenticationCode* get_mac(const std::string& algo_spec,
+                                             const std::string& provider = "")
+   {
+   return MessageAuthenticationCode::create(algo_spec, provider).release();
+   }
+
+BOTAN_DEPRECATED("MessageAuthenticationCode::create_or_throw")
+inline std::unique_ptr<MessageAuthenticationCode> make_message_auth(const std::string& algo_spec,
+                                                                       const std::string& provider = "")
+   {
+   return MessageAuthenticationCode::create(algo_spec, provider);
+   }
+
+BOTAN_DEPRECATED("MessageAuthenticationCode::providers")
+inline std::vector<std::string> get_mac_providers(const std::string& algo_spec)
+   {
+   return MessageAuthenticationCode::providers(algo_spec);
+   }
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/scan_name.cpp b/third_party/botan/src/lib/base/scan_name.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/scan_name.cpp
@@ -0,0 +1,146 @@
+/*
+* SCAN Name Abstraction
+* (C) 2008-2009,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+std::string make_arg(const std::vector<std::pair<size_t, std::string>>& name, size_t start)
+   {
+   std::string output = name[start].second;
+   size_t level = name[start].first;
+
+   size_t paren_depth = 0;
+
+   for(size_t i = start + 1; i != name.size(); ++i)
+      {
+      if(name[i].first <= name[start].first)
+         break;
+
+      if(name[i].first > level)
+         {
+         output += "(" + name[i].second;
+         ++paren_depth;
+         }
+      else if(name[i].first < level)
+         {
+         output += ")," + name[i].second;
+         --paren_depth;
+         }
+      else
+         {
+         if(output[output.size() - 1] != '(')
+            output += ",";
+         output += name[i].second;
+         }
+
+      level = name[i].first;
+      }
+
+   for(size_t i = 0; i != paren_depth; ++i)
+      output += ")";
+
+   return output;
+   }
+
+}
+
+SCAN_Name::SCAN_Name(const char* algo_spec) : SCAN_Name(std::string(algo_spec))
+   {
+   }
+
+SCAN_Name::SCAN_Name(std::string algo_spec) : m_orig_algo_spec(algo_spec), m_alg_name(), m_args(), m_mode_info()
+   {
+   if(algo_spec.size() == 0)
+      throw Invalid_Argument("Expected algorithm name, got empty string");
+
+   std::vector<std::pair<size_t, std::string>> name;
+   size_t level = 0;
+   std::pair<size_t, std::string> accum = std::make_pair(level, "");
+
+   const std::string decoding_error = "Bad SCAN name '" + algo_spec + "': ";
+
+   for(size_t i = 0; i != algo_spec.size(); ++i)
+      {
+      char c = algo_spec[i];
+
+      if(c == '/' || c == ',' || c == '(' || c == ')')
+         {
+         if(c == '(')
+            ++level;
+         else if(c == ')')
+            {
+            if(level == 0)
+               throw Decoding_Error(decoding_error + "Mismatched parens");
+            --level;
+            }
+
+         if(c == '/' && level > 0)
+            accum.second.push_back(c);
+         else
+            {
+            if(accum.second != "")
+               name.push_back(accum);
+            accum = std::make_pair(level, "");
+            }
+         }
+      else
+         accum.second.push_back(c);
+      }
+
+   if(accum.second != "")
+      name.push_back(accum);
+
+   if(level != 0)
+      throw Decoding_Error(decoding_error + "Missing close paren");
+
+   if(name.size() == 0)
+      throw Decoding_Error(decoding_error + "Empty name");
+
+   m_alg_name = name[0].second;
+
+   bool in_modes = false;
+
+   for(size_t i = 1; i != name.size(); ++i)
+      {
+      if(name[i].first == 0)
+         {
+         m_mode_info.push_back(make_arg(name, i));
+         in_modes = true;
+         }
+      else if(name[i].first == 1 && !in_modes)
+         m_args.push_back(make_arg(name, i));
+      }
+   }
+
+std::string SCAN_Name::arg(size_t i) const
+   {
+   if(i >= arg_count())
+      throw Invalid_Argument("SCAN_Name::arg " + std::to_string(i) +
+                             " out of range for '" + to_string() + "'");
+   return m_args[i];
+   }
+
+std::string SCAN_Name::arg(size_t i, const std::string& def_value) const
+   {
+   if(i >= arg_count())
+      return def_value;
+   return m_args[i];
+   }
+
+size_t SCAN_Name::arg_as_integer(size_t i, size_t def_value) const
+   {
+   if(i >= arg_count())
+      return def_value;
+   return to_u32bit(m_args[i]);
+   }
+
+}
diff --git a/third_party/botan/src/lib/base/scan_name.h b/third_party/botan/src/lib/base/scan_name.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/scan_name.h
@@ -0,0 +1,124 @@
+/*
+* SCAN Name Abstraction
+* (C) 2008,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SCAN_NAME_H_
+#define BOTAN_SCAN_NAME_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(scan_name.h)
+
+namespace Botan {
+
+/**
+A class encapsulating a SCAN name (similar to JCE conventions)
+http://www.users.zetnet.co.uk/hopwood/crypto/scan/
+*/
+class BOTAN_PUBLIC_API(2,0) SCAN_Name final
+   {
+   public:
+      /**
+      * Create a SCAN_Name
+      * @param algo_spec A SCAN-format name
+      */
+      explicit SCAN_Name(const char* algo_spec);
+
+      /**
+      * Create a SCAN_Name
+      * @param algo_spec A SCAN-format name
+      */
+      explicit SCAN_Name(std::string algo_spec);
+
+      /**
+      * @return original input string
+      */
+      const std::string& to_string() const { return m_orig_algo_spec; }
+
+      BOTAN_DEPRECATED("Use SCAN_Name::to_string") const std::string& as_string() const
+         {
+         return this->to_string();
+         }
+
+      /**
+      * @return algorithm name
+      */
+      const std::string& algo_name() const { return m_alg_name; }
+
+      /**
+      * @return number of arguments
+      */
+      size_t arg_count() const { return m_args.size(); }
+
+      /**
+      * @param lower is the lower bound
+      * @param upper is the upper bound
+      * @return if the number of arguments is between lower and upper
+      */
+      bool arg_count_between(size_t lower, size_t upper) const
+         { return ((arg_count() >= lower) && (arg_count() <= upper)); }
+
+      /**
+      * @param i which argument
+      * @return ith argument
+      */
+      std::string arg(size_t i) const;
+
+      /**
+      * @param i which argument
+      * @param def_value the default value
+      * @return ith argument or the default value
+      */
+      std::string arg(size_t i, const std::string& def_value) const;
+
+      /**
+      * @param i which argument
+      * @param def_value the default value
+      * @return ith argument as an integer, or the default value
+      */
+      size_t arg_as_integer(size_t i, size_t def_value) const;
+
+      /**
+      * @return cipher mode (if any)
+      */
+      std::string cipher_mode() const
+         { return (m_mode_info.size() >= 1) ? m_mode_info[0] : ""; }
+
+      /**
+      * @return cipher mode padding (if any)
+      */
+      std::string cipher_mode_pad() const
+         { return (m_mode_info.size() >= 2) ? m_mode_info[1] : ""; }
+
+   private:
+      std::string m_orig_algo_spec;
+      std::string m_alg_name;
+      std::vector<std::string> m_args;
+      std::vector<std::string> m_mode_info;
+   };
+
+// This is unrelated but it is convenient to stash it here
+template<typename T>
+std::vector<std::string> probe_providers_of(const std::string& algo_spec,
+                                            const std::vector<std::string>& possible)
+   {
+   std::vector<std::string> providers;
+   for(auto&& prov : possible)
+      {
+      std::unique_ptr<T> o(T::create(algo_spec, prov));
+      if(o)
+         {
+         providers.push_back(prov); // available
+         }
+      }
+   return providers;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/secmem.h b/third_party/botan/src/lib/base/secmem.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/secmem.h
@@ -0,0 +1,179 @@
+/*
+* Secure Memory Buffers
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SECURE_MEMORY_BUFFERS_H_
+#define BOTAN_SECURE_MEMORY_BUFFERS_H_
+
+#include <botan/types.h> // IWYU pragma: export
+#include <botan/mem_ops.h> // IWYU pragma: export
+#include <vector> // IWYU pragma: export
+#include <algorithm>
+#include <deque>
+#include <type_traits>
+
+namespace Botan {
+
+template<typename T>
+class secure_allocator
+   {
+   public:
+      /*
+      * Assert exists to prevent someone from doing something that will
+      * probably crash anyway (like secure_vector<non_POD_t> where ~non_POD_t
+      * deletes a member pointer which was zeroed before it ran).
+      * MSVC in debug mode uses non-integral proxy types in container types
+      * like std::vector, thus we disable the check there.
+      */
+#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
+      static_assert(std::is_integral<T>::value, "secure_allocator supports only integer types");
+#endif
+
+      typedef T          value_type;
+      typedef std::size_t size_type;
+
+      secure_allocator() noexcept = default;
+      secure_allocator(const secure_allocator&) noexcept = default;
+      secure_allocator& operator=(const secure_allocator&) noexcept = default;
+      ~secure_allocator() noexcept = default;
+
+      template<typename U>
+      secure_allocator(const secure_allocator<U>&) noexcept {}
+
+      T* allocate(std::size_t n)
+         {
+         return static_cast<T*>(allocate_memory(n, sizeof(T)));
+         }
+
+      void deallocate(T* p, std::size_t n)
+         {
+         deallocate_memory(p, n, sizeof(T));
+         }
+   };
+
+template<typename T, typename U> inline bool
+operator==(const secure_allocator<T>&, const secure_allocator<U>&)
+   { return true; }
+
+template<typename T, typename U> inline bool
+operator!=(const secure_allocator<T>&, const secure_allocator<U>&)
+   { return false; }
+
+template<typename T> using secure_vector = std::vector<T, secure_allocator<T>>;
+template<typename T> using secure_deque = std::deque<T, secure_allocator<T>>;
+
+// For better compatibility with 1.10 API
+template<typename T> using SecureVector = secure_vector<T>;
+
+template<typename T>
+std::vector<T> unlock(const secure_vector<T>& in)
+   {
+   std::vector<T> out(in.size());
+   copy_mem(out.data(), in.data(), in.size());
+   return out;
+   }
+
+template<typename T, typename Alloc>
+size_t buffer_insert(std::vector<T, Alloc>& buf,
+                     size_t buf_offset,
+                     const T input[],
+                     size_t input_length)
+   {
+   BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
+   const size_t to_copy = std::min(input_length, buf.size() - buf_offset);
+   if(to_copy > 0)
+      {
+      copy_mem(&buf[buf_offset], input, to_copy);
+      }
+   return to_copy;
+   }
+
+template<typename T, typename Alloc, typename Alloc2>
+size_t buffer_insert(std::vector<T, Alloc>& buf,
+                     size_t buf_offset,
+                     const std::vector<T, Alloc2>& input)
+   {
+   BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
+   const size_t to_copy = std::min(input.size(), buf.size() - buf_offset);
+   if(to_copy > 0)
+      {
+      copy_mem(&buf[buf_offset], input.data(), to_copy);
+      }
+   return to_copy;
+   }
+
+template<typename T, typename Alloc, typename Alloc2>
+std::vector<T, Alloc>&
+operator+=(std::vector<T, Alloc>& out,
+           const std::vector<T, Alloc2>& in)
+   {
+   const size_t copy_offset = out.size();
+   out.resize(out.size() + in.size());
+   if(in.size() > 0)
+      {
+      copy_mem(&out[copy_offset], in.data(), in.size());
+      }
+   return out;
+   }
+
+template<typename T, typename Alloc>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out, T in)
+   {
+   out.push_back(in);
+   return out;
+   }
+
+template<typename T, typename Alloc, typename L>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out,
+                                  const std::pair<const T*, L>& in)
+   {
+   const size_t copy_offset = out.size();
+   out.resize(out.size() + in.second);
+   if(in.second > 0)
+      {
+      copy_mem(&out[copy_offset], in.first, in.second);
+      }
+   return out;
+   }
+
+template<typename T, typename Alloc, typename L>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out,
+                                  const std::pair<T*, L>& in)
+   {
+   const size_t copy_offset = out.size();
+   out.resize(out.size() + in.second);
+   if(in.second > 0)
+      {
+      copy_mem(&out[copy_offset], in.first, in.second);
+      }
+   return out;
+   }
+
+/**
+* Zeroise the values; length remains unchanged
+* @param vec the vector to zeroise
+*/
+template<typename T, typename Alloc>
+void zeroise(std::vector<T, Alloc>& vec)
+   {
+   clear_mem(vec.data(), vec.size());
+   }
+
+/**
+* Zeroise the values then free the memory
+* @param vec the vector to zeroise and free
+*/
+template<typename T, typename Alloc>
+void zap(std::vector<T, Alloc>& vec)
+   {
+   zeroise(vec);
+   vec.clear();
+   vec.shrink_to_fit();
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/sym_algo.cpp b/third_party/botan/src/lib/base/sym_algo.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/sym_algo.cpp
@@ -0,0 +1,24 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sym_algo.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+void SymmetricAlgorithm::throw_key_not_set_error() const
+   {
+   throw Key_Not_Set(name());
+   }
+
+void SymmetricAlgorithm::set_key(const uint8_t key[], size_t length)
+   {
+   if(!valid_keylength(length))
+      throw Invalid_Key_Length(name(), length);
+   key_schedule(key, length);
+   }
+
+}
diff --git a/third_party/botan/src/lib/base/sym_algo.h b/third_party/botan/src/lib/base/sym_algo.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/sym_algo.h
@@ -0,0 +1,108 @@
+/*
+* Symmetric Algorithm Base Class
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYMMETRIC_ALGORITHM_H_
+#define BOTAN_SYMMETRIC_ALGORITHM_H_
+
+#include <botan/key_spec.h>
+#include <botan/symkey.h>
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* This class represents a symmetric algorithm object.
+*/
+class BOTAN_PUBLIC_API(2,0) SymmetricAlgorithm
+   {
+   public:
+      virtual ~SymmetricAlgorithm() = default;
+
+      /**
+      * Reset the state.
+      */
+      virtual void clear() = 0;
+
+      /**
+      * @return object describing limits on key size
+      */
+      virtual Key_Length_Specification key_spec() const = 0;
+
+      /**
+      * @return maximum allowed key length
+      */
+      size_t maximum_keylength() const
+         {
+         return key_spec().maximum_keylength();
+         }
+
+      /**
+      * @return minimum allowed key length
+      */
+      size_t minimum_keylength() const
+         {
+         return key_spec().minimum_keylength();
+         }
+
+      /**
+      * Check whether a given key length is valid for this algorithm.
+      * @param length the key length to be checked.
+      * @return true if the key length is valid.
+      */
+      bool valid_keylength(size_t length) const
+         {
+         return key_spec().valid_keylength(length);
+         }
+
+      /**
+      * Set the symmetric key of this object.
+      * @param key the SymmetricKey to be set.
+      */
+      void set_key(const SymmetricKey& key)
+         {
+         set_key(key.begin(), key.length());
+         }
+
+      template<typename Alloc>
+      void set_key(const std::vector<uint8_t, Alloc>& key)
+         {
+         set_key(key.data(), key.size());
+         }
+
+      /**
+      * Set the symmetric key of this object.
+      * @param key the to be set as a byte array.
+      * @param length in bytes of key param
+      */
+      void set_key(const uint8_t key[], size_t length);
+
+      /**
+      * @return the algorithm name
+      */
+      virtual std::string name() const = 0;
+
+   protected:
+      void verify_key_set(bool cond) const
+         {
+         if(cond == false)
+            throw_key_not_set_error();
+         }
+
+   private:
+      void throw_key_not_set_error() const;
+
+      /**
+      * Run the key schedule
+      * @param key the key
+      * @param length of key
+      */
+      virtual void key_schedule(const uint8_t key[], size_t length) = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/base/symkey.cpp b/third_party/botan/src/lib/base/symkey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/symkey.cpp
@@ -0,0 +1,134 @@
+/*
+* OctetString
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/symkey.h>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Create an OctetString from RNG output
+*/
+OctetString::OctetString(RandomNumberGenerator& rng,
+                         size_t len)
+   {
+   rng.random_vec(m_data, len);
+   }
+
+/*
+* Create an OctetString from a hex string
+*/
+OctetString::OctetString(const std::string& hex_string)
+   {
+   if(!hex_string.empty())
+      {
+      m_data.resize(1 + hex_string.length() / 2);
+      m_data.resize(hex_decode(m_data.data(), hex_string));
+      }
+   }
+
+/*
+* Create an OctetString from a byte string
+*/
+OctetString::OctetString(const uint8_t in[], size_t n)
+   {
+   m_data.assign(in, in + n);
+   }
+
+/*
+* Set the parity of each key byte to odd
+*/
+void OctetString::set_odd_parity()
+   {
+   const uint8_t ODD_PARITY[256] = {
+      0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, 0x08, 0x08, 0x0B, 0x0B,
+      0x0D, 0x0D, 0x0E, 0x0E, 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16,
+      0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F, 0x20, 0x20, 0x23, 0x23,
+      0x25, 0x25, 0x26, 0x26, 0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F,
+      0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, 0x38, 0x38, 0x3B, 0x3B,
+      0x3D, 0x3D, 0x3E, 0x3E, 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46,
+      0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F, 0x51, 0x51, 0x52, 0x52,
+      0x54, 0x54, 0x57, 0x57, 0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E,
+      0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, 0x68, 0x68, 0x6B, 0x6B,
+      0x6D, 0x6D, 0x6E, 0x6E, 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76,
+      0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F, 0x80, 0x80, 0x83, 0x83,
+      0x85, 0x85, 0x86, 0x86, 0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F,
+      0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, 0x98, 0x98, 0x9B, 0x9B,
+      0x9D, 0x9D, 0x9E, 0x9E, 0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7,
+      0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE, 0xB0, 0xB0, 0xB3, 0xB3,
+      0xB5, 0xB5, 0xB6, 0xB6, 0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF,
+      0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, 0xC8, 0xC8, 0xCB, 0xCB,
+      0xCD, 0xCD, 0xCE, 0xCE, 0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6,
+      0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF, 0xE0, 0xE0, 0xE3, 0xE3,
+      0xE5, 0xE5, 0xE6, 0xE6, 0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF,
+      0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7, 0xF8, 0xF8, 0xFB, 0xFB,
+      0xFD, 0xFD, 0xFE, 0xFE };
+
+   for(size_t j = 0; j != m_data.size(); ++j)
+      m_data[j] = ODD_PARITY[m_data[j]];
+   }
+
+/*
+* Hex encode an OctetString
+*/
+std::string OctetString::to_string() const
+   {
+   return hex_encode(m_data.data(), m_data.size());
+   }
+
+/*
+* XOR Operation for OctetStrings
+*/
+OctetString& OctetString::operator^=(const OctetString& k)
+   {
+   if(&k == this) { zeroise(m_data); return (*this); }
+   xor_buf(m_data.data(), k.begin(), std::min(length(), k.length()));
+   return (*this);
+   }
+
+/*
+* Equality Operation for OctetStrings
+*/
+bool operator==(const OctetString& s1, const OctetString& s2)
+   {
+   return (s1.bits_of() == s2.bits_of());
+   }
+
+/*
+* Unequality Operation for OctetStrings
+*/
+bool operator!=(const OctetString& s1, const OctetString& s2)
+   {
+   return !(s1 == s2);
+   }
+
+/*
+* Append Operation for OctetStrings
+*/
+OctetString operator+(const OctetString& k1, const OctetString& k2)
+   {
+   secure_vector<uint8_t> out;
+   out += k1.bits_of();
+   out += k2.bits_of();
+   return OctetString(out);
+   }
+
+/*
+* XOR Operation for OctetStrings
+*/
+OctetString operator^(const OctetString& k1, const OctetString& k2)
+   {
+   secure_vector<uint8_t> out(std::max(k1.length(), k2.length()));
+
+   copy_mem(out.data(), k1.begin(), k1.length());
+   xor_buf(out.data(), k2.begin(), k2.length());
+   return OctetString(out);
+   }
+
+}
diff --git a/third_party/botan/src/lib/base/symkey.h b/third_party/botan/src/lib/base/symkey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/base/symkey.h
@@ -0,0 +1,150 @@
+/*
+* OctetString
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYMKEY_H_
+#define BOTAN_SYMKEY_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Octet String
+*/
+class BOTAN_PUBLIC_API(2,0) OctetString final
+   {
+   public:
+      /**
+      * @return size of this octet string in bytes
+      */
+      size_t length() const { return m_data.size(); }
+      size_t size() const { return m_data.size(); }
+
+      /**
+      * @return this object as a secure_vector<uint8_t>
+      */
+      secure_vector<uint8_t> bits_of() const { return m_data; }
+
+      /**
+      * @return start of this string
+      */
+      const uint8_t* begin() const { return m_data.data(); }
+
+      /**
+      * @return end of this string
+      */
+      const uint8_t* end() const   { return begin() + m_data.size(); }
+
+      /**
+      * @return this encoded as hex
+      */
+      std::string to_string() const;
+
+      std::string BOTAN_DEPRECATED("Use OctetString::to_string") as_string() const
+         {
+         return this->to_string();
+         }
+
+      /**
+      * XOR the contents of another octet string into this one
+      * @param other octet string
+      * @return reference to this
+      */
+      OctetString& operator^=(const OctetString& other);
+
+      /**
+      * Force to have odd parity
+      */
+      void set_odd_parity();
+
+      /**
+      * Create a new OctetString
+      * @param str is a hex encoded string
+      */
+      explicit OctetString(const std::string& str = "");
+
+      /**
+      * Create a new random OctetString
+      * @param rng is a random number generator
+      * @param len is the desired length in bytes
+      */
+      OctetString(class RandomNumberGenerator& rng, size_t len);
+
+      /**
+      * Create a new OctetString
+      * @param in is an array
+      * @param len is the length of in in bytes
+      */
+      OctetString(const uint8_t in[], size_t len);
+
+      /**
+      * Create a new OctetString
+      * @param in a bytestring
+      */
+      OctetString(const secure_vector<uint8_t>& in) : m_data(in) {}
+
+      /**
+      * Create a new OctetString
+      * @param in a bytestring
+      */
+      OctetString(const std::vector<uint8_t>& in) : m_data(in.begin(), in.end()) {}
+
+   private:
+      secure_vector<uint8_t> m_data;
+   };
+
+/**
+* Compare two strings
+* @param x an octet string
+* @param y an octet string
+* @return if x is equal to y
+*/
+BOTAN_PUBLIC_API(2,0) bool operator==(const OctetString& x,
+                          const OctetString& y);
+
+/**
+* Compare two strings
+* @param x an octet string
+* @param y an octet string
+* @return if x is not equal to y
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const OctetString& x,
+                          const OctetString& y);
+
+/**
+* Concatenate two strings
+* @param x an octet string
+* @param y an octet string
+* @return x concatenated with y
+*/
+BOTAN_PUBLIC_API(2,0) OctetString operator+(const OctetString& x,
+                                const OctetString& y);
+
+/**
+* XOR two strings
+* @param x an octet string
+* @param y an octet string
+* @return x XORed with y
+*/
+BOTAN_PUBLIC_API(2,0) OctetString operator^(const OctetString& x,
+                                const OctetString& y);
+
+
+/**
+* Alternate name for octet string showing intent to use as a key
+*/
+using SymmetricKey = OctetString;
+
+/**
+* Alternate name for octet string showing intent to use as an IV
+*/
+using InitializationVector = OctetString;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/aes/aes.cpp b/third_party/botan/src/lib/block/aes/aes.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes.cpp
@@ -0,0 +1,1017 @@
+/*
+* (C) 1999-2010,2015,2017,2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/rotate.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+   #define BOTAN_HAS_HW_AES_SUPPORT
+#endif
+
+/*
+* One of three AES implementation strategies are used to get a constant time
+* implementation which is immune to common cache/timing based side channels:
+*
+* - If AES hardware support is available (AES-NI, POWER8, Aarch64) use that
+*
+* - If 128-bit SIMD with byte shuffles are available (SSSE3, NEON, or Altivec),
+*   use the vperm technique published by Mike Hamburg at CHES 2009.
+*
+* - If no hardware or SIMD support, fall back to a constant time bitsliced
+*   implementation. This uses 32-bit words resulting in 2 blocks being processed
+*   in parallel. Moving to 4 blocks (with 64-bit words) would approximately
+*   double performance on 64-bit CPUs. Likewise moving to 128 bit SIMD would
+*   again approximately double performance vs 64-bit. However the assumption is
+*   that most 64-bit CPUs either have hardware AES or SIMD shuffle support and
+*   that the majority of users falling back to this code will be 32-bit cores.
+*   If this assumption proves to be unsound, the bitsliced code can easily be
+*   extended to operate on either 32 or 64 bit words depending on the native
+*   wordsize of the target processor.
+*
+* Useful references
+*
+* - "Accelerating AES with Vector Permute Instructions" Mike Hamburg
+*   https://www.shiftleft.org/papers/vector_aes/vector_aes.pdf
+*
+* - "Faster and Timing-Attack Resistant AES-GCM" Käsper and Schwabe
+*   https://eprint.iacr.org/2009/129.pdf
+*
+* - "A new combinational logic minimization technique with applications to cryptology."
+*   Boyar and Peralta https://eprint.iacr.org/2009/191.pdf
+*
+* - "A depth-16 circuit for the AES S-box" Boyar and Peralta
+*    https://eprint.iacr.org/2011/332.pdf
+*
+* - "A Very Compact S-box for AES" Canright
+*   https://www.iacr.org/archive/ches2005/032.pdf
+*   https://core.ac.uk/download/pdf/36694529.pdf (extended)
+*/
+
+namespace {
+
+/*
+This is an AES sbox circuit which can execute in bitsliced mode up to 32x in
+parallel.
+
+The circuit is from the "Circuit Minimization Team" group
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/CMT.html
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/SLP_AES_113.txt
+
+This circuit has size 113 and depth 27. In software it is much faster than
+circuits which are considered faster for hardware purposes (where circuit depth
+is the critical constraint), because unlike in hardware, on common CPUs we can
+only execute - at best - 3 or 4 logic operations per cycle. So a smaller circuit
+is superior. On an x86-64 machine this circuit is about 15% faster than the
+circuit of size 128 and depth 16 given in "A depth-16 circuit for the AES S-box".
+
+Another circuit for AES Sbox of size 102 and depth 24 is describted in "New
+Circuit Minimization Techniques for Smaller and Faster AES SBoxes"
+[https://eprint.iacr.org/2019/802] however it relies on "non-standard" gates
+like MUX, NOR, NAND, etc and so in practice in bitsliced software, its size is
+actually a bit larger than this circuit, as few CPUs have such instructions and
+otherwise they must be emulated using a sequence of available bit operations.
+*/
+void AES_SBOX(uint32_t V[8])
+   {
+   const uint32_t U0 = V[0];
+   const uint32_t U1 = V[1];
+   const uint32_t U2 = V[2];
+   const uint32_t U3 = V[3];
+   const uint32_t U4 = V[4];
+   const uint32_t U5 = V[5];
+   const uint32_t U6 = V[6];
+   const uint32_t U7 = V[7];
+
+   const uint32_t y14 = U3 ^ U5;
+   const uint32_t y13 = U0 ^ U6;
+   const uint32_t y9 = U0 ^ U3;
+   const uint32_t y8 = U0 ^ U5;
+   const uint32_t t0 = U1 ^ U2;
+   const uint32_t y1 = t0 ^ U7;
+   const uint32_t y4 = y1 ^ U3;
+   const uint32_t y12 = y13 ^ y14;
+   const uint32_t y2 = y1 ^ U0;
+   const uint32_t y5 = y1 ^ U6;
+   const uint32_t y3 = y5 ^ y8;
+   const uint32_t t1 = U4 ^ y12;
+   const uint32_t y15 = t1 ^ U5;
+   const uint32_t y20 = t1 ^ U1;
+   const uint32_t y6 = y15 ^ U7;
+   const uint32_t y10 = y15 ^ t0;
+   const uint32_t y11 = y20 ^ y9;
+   const uint32_t y7 = U7 ^ y11;
+   const uint32_t y17 = y10 ^ y11;
+   const uint32_t y19 = y10 ^ y8;
+   const uint32_t y16 = t0 ^ y11;
+   const uint32_t y21 = y13 ^ y16;
+   const uint32_t y18 = U0 ^ y16;
+   const uint32_t t2 = y12 & y15;
+   const uint32_t t3 = y3 & y6;
+   const uint32_t t4 = t3 ^ t2;
+   const uint32_t t5 = y4 & U7;
+   const uint32_t t6 = t5 ^ t2;
+   const uint32_t t7 = y13 & y16;
+   const uint32_t t8 = y5 & y1;
+   const uint32_t t9 = t8 ^ t7;
+   const uint32_t t10 = y2 & y7;
+   const uint32_t t11 = t10 ^ t7;
+   const uint32_t t12 = y9 & y11;
+   const uint32_t t13 = y14 & y17;
+   const uint32_t t14 = t13 ^ t12;
+   const uint32_t t15 = y8 & y10;
+   const uint32_t t16 = t15 ^ t12;
+   const uint32_t t17 = t4 ^ y20;
+   const uint32_t t18 = t6 ^ t16;
+   const uint32_t t19 = t9 ^ t14;
+   const uint32_t t20 = t11 ^ t16;
+   const uint32_t t21 = t17 ^ t14;
+   const uint32_t t22 = t18 ^ y19;
+   const uint32_t t23 = t19 ^ y21;
+   const uint32_t t24 = t20 ^ y18;
+   const uint32_t t25 = t21 ^ t22;
+   const uint32_t t26 = t21 & t23;
+   const uint32_t t27 = t24 ^ t26;
+   const uint32_t t28 = t25 & t27;
+   const uint32_t t29 = t28 ^ t22;
+   const uint32_t t30 = t23 ^ t24;
+   const uint32_t t31 = t22 ^ t26;
+   const uint32_t t32 = t31 & t30;
+   const uint32_t t33 = t32 ^ t24;
+   const uint32_t t34 = t23 ^ t33;
+   const uint32_t t35 = t27 ^ t33;
+   const uint32_t t36 = t24 & t35;
+   const uint32_t t37 = t36 ^ t34;
+   const uint32_t t38 = t27 ^ t36;
+   const uint32_t t39 = t29 & t38;
+   const uint32_t t40 = t25 ^ t39;
+   const uint32_t t41 = t40 ^ t37;
+   const uint32_t t42 = t29 ^ t33;
+   const uint32_t t43 = t29 ^ t40;
+   const uint32_t t44 = t33 ^ t37;
+   const uint32_t t45 = t42 ^ t41;
+   const uint32_t z0 = t44 & y15;
+   const uint32_t z1 = t37 & y6;
+   const uint32_t z2 = t33 & U7;
+   const uint32_t z3 = t43 & y16;
+   const uint32_t z4 = t40 & y1;
+   const uint32_t z5 = t29 & y7;
+   const uint32_t z6 = t42 & y11;
+   const uint32_t z7 = t45 & y17;
+   const uint32_t z8 = t41 & y10;
+   const uint32_t z9 = t44 & y12;
+   const uint32_t z10 = t37 & y3;
+   const uint32_t z11 = t33 & y4;
+   const uint32_t z12 = t43 & y13;
+   const uint32_t z13 = t40 & y5;
+   const uint32_t z14 = t29 & y2;
+   const uint32_t z15 = t42 & y9;
+   const uint32_t z16 = t45 & y14;
+   const uint32_t z17 = t41 & y8;
+   const uint32_t tc1 = z15 ^ z16;
+   const uint32_t tc2 = z10 ^ tc1;
+   const uint32_t tc3 = z9 ^ tc2;
+   const uint32_t tc4 = z0 ^ z2;
+   const uint32_t tc5 = z1 ^ z0;
+   const uint32_t tc6 = z3 ^ z4;
+   const uint32_t tc7 = z12 ^ tc4;
+   const uint32_t tc8 = z7 ^ tc6;
+   const uint32_t tc9 = z8 ^ tc7;
+   const uint32_t tc10 = tc8 ^ tc9;
+   const uint32_t tc11 = tc6 ^ tc5;
+   const uint32_t tc12 = z3 ^ z5;
+   const uint32_t tc13 = z13 ^ tc1;
+   const uint32_t tc14 = tc4 ^ tc12;
+   const uint32_t S3 = tc3 ^ tc11;
+   const uint32_t tc16 = z6 ^ tc8;
+   const uint32_t tc17 = z14 ^ tc10;
+   const uint32_t tc18 = ~tc13 ^ tc14;
+   const uint32_t S7 = z12 ^ tc18;
+   const uint32_t tc20 = z15 ^ tc16;
+   const uint32_t tc21 = tc2 ^ z11;
+   const uint32_t S0 = tc3 ^ tc16;
+   const uint32_t S6 = tc10 ^ tc18;
+   const uint32_t S4 = tc14 ^ S3;
+   const uint32_t S1 = ~(S3 ^ tc16);
+   const uint32_t tc26 = tc17 ^ tc20;
+   const uint32_t S2 = ~(tc26 ^ z17);
+   const uint32_t S5 = tc21 ^ tc17;
+
+   V[0] = S0;
+   V[1] = S1;
+   V[2] = S2;
+   V[3] = S3;
+   V[4] = S4;
+   V[5] = S5;
+   V[6] = S6;
+   V[7] = S7;
+   }
+
+/*
+A circuit for inverse AES Sbox of size 121 and depth 21 from
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/CMT.html
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/Sinv.txt
+*/
+void AES_INV_SBOX(uint32_t V[8])
+   {
+   const uint32_t U0 = V[0];
+   const uint32_t U1 = V[1];
+   const uint32_t U2 = V[2];
+   const uint32_t U3 = V[3];
+   const uint32_t U4 = V[4];
+   const uint32_t U5 = V[5];
+   const uint32_t U6 = V[6];
+   const uint32_t U7 = V[7];
+
+   const uint32_t Y0 = U0 ^ U3;
+   const uint32_t Y2 = ~(U1 ^ U3);
+   const uint32_t Y4 = U0 ^ Y2;
+   const uint32_t RTL0 = U6 ^ U7;
+   const uint32_t Y1 = Y2 ^ RTL0;
+   const uint32_t Y7 = ~(U2 ^ Y1);
+   const uint32_t RTL1 = U3 ^ U4;
+   const uint32_t Y6 = ~(U7 ^ RTL1);
+   const uint32_t Y3 = Y1 ^ RTL1;
+   const uint32_t RTL2 = ~(U0 ^ U2);
+   const uint32_t Y5 = U5 ^ RTL2;
+   const uint32_t sa1 = Y0 ^ Y2;
+   const uint32_t sa0 = Y1 ^ Y3;
+   const uint32_t sb1 = Y4 ^ Y6;
+   const uint32_t sb0 = Y5 ^ Y7;
+   const uint32_t ah = Y0 ^ Y1;
+   const uint32_t al = Y2 ^ Y3;
+   const uint32_t aa = sa0 ^ sa1;
+   const uint32_t bh = Y4 ^ Y5;
+   const uint32_t bl = Y6 ^ Y7;
+   const uint32_t bb = sb0 ^ sb1;
+   const uint32_t ab20 = sa0 ^ sb0;
+   const uint32_t ab22 = al ^ bl;
+   const uint32_t ab23 = Y3 ^ Y7;
+   const uint32_t ab21 = sa1 ^ sb1;
+   const uint32_t abcd1 = ah & bh;
+   const uint32_t rr1 = Y0 & Y4;
+   const uint32_t ph11 = ab20 ^ abcd1;
+   const uint32_t t01 = Y1 & Y5;
+   const uint32_t ph01 = t01 ^ abcd1;
+   const uint32_t abcd2 = al & bl;
+   const uint32_t r1 = Y2 & Y6;
+   const uint32_t pl11 = ab22 ^ abcd2;
+   const uint32_t r2 = Y3 & Y7;
+   const uint32_t pl01 = r2 ^ abcd2;
+   const uint32_t r3 = sa0 & sb0;
+   const uint32_t vr1 = aa & bb;
+   const uint32_t pr1 = vr1 ^ r3;
+   const uint32_t wr1 = sa1 & sb1;
+   const uint32_t qr1 = wr1 ^ r3;
+   const uint32_t ab0 = ph11 ^ rr1;
+   const uint32_t ab1 = ph01 ^ ab21;
+   const uint32_t ab2 = pl11 ^ r1;
+   const uint32_t ab3 = pl01 ^ qr1;
+   const uint32_t cp1 = ab0 ^ pr1;
+   const uint32_t cp2 = ab1 ^ qr1;
+   const uint32_t cp3 = ab2 ^ pr1;
+   const uint32_t cp4 = ab3 ^ ab23;
+   const uint32_t tinv1 = cp3 ^ cp4;
+   const uint32_t tinv2 = cp3 & cp1;
+   const uint32_t tinv3 = cp2 ^ tinv2;
+   const uint32_t tinv4 = cp1 ^ cp2;
+   const uint32_t tinv5 = cp4 ^ tinv2;
+   const uint32_t tinv6 = tinv5 & tinv4;
+   const uint32_t tinv7 = tinv3 & tinv1;
+   const uint32_t d2 = cp4 ^ tinv7;
+   const uint32_t d0 = cp2 ^ tinv6;
+   const uint32_t tinv8 = cp1 & cp4;
+   const uint32_t tinv9 = tinv4 & tinv8;
+   const uint32_t tinv10 = tinv4 ^ tinv2;
+   const uint32_t d1 = tinv9 ^ tinv10;
+   const uint32_t tinv11 = cp2 & cp3;
+   const uint32_t tinv12 = tinv1 & tinv11;
+   const uint32_t tinv13 = tinv1 ^ tinv2;
+   const uint32_t d3 = tinv12 ^ tinv13;
+   const uint32_t sd1 = d1 ^ d3;
+   const uint32_t sd0 = d0 ^ d2;
+   const uint32_t dl = d0 ^ d1;
+   const uint32_t dh = d2 ^ d3;
+   const uint32_t dd = sd0 ^ sd1;
+   const uint32_t abcd3 = dh & bh;
+   const uint32_t rr2 = d3 & Y4;
+   const uint32_t t02 = d2 & Y5;
+   const uint32_t abcd4 = dl & bl;
+   const uint32_t r4 = d1 & Y6;
+   const uint32_t r5 = d0 & Y7;
+   const uint32_t r6 = sd0 & sb0;
+   const uint32_t vr2 = dd & bb;
+   const uint32_t wr2 = sd1 & sb1;
+   const uint32_t abcd5 = dh & ah;
+   const uint32_t r7 = d3 & Y0;
+   const uint32_t r8 = d2 & Y1;
+   const uint32_t abcd6 = dl & al;
+   const uint32_t r9 = d1 & Y2;
+   const uint32_t r10 = d0 & Y3;
+   const uint32_t r11 = sd0 & sa0;
+   const uint32_t vr3 = dd & aa;
+   const uint32_t wr3 = sd1 & sa1;
+   const uint32_t ph12 = rr2 ^ abcd3;
+   const uint32_t ph02 = t02 ^ abcd3;
+   const uint32_t pl12 = r4 ^ abcd4;
+   const uint32_t pl02 = r5 ^ abcd4;
+   const uint32_t pr2 = vr2 ^ r6;
+   const uint32_t qr2 = wr2 ^ r6;
+   const uint32_t p0 = ph12 ^ pr2;
+   const uint32_t p1 = ph02 ^ qr2;
+   const uint32_t p2 = pl12 ^ pr2;
+   const uint32_t p3 = pl02 ^ qr2;
+   const uint32_t ph13 = r7 ^ abcd5;
+   const uint32_t ph03 = r8 ^ abcd5;
+   const uint32_t pl13 = r9 ^ abcd6;
+   const uint32_t pl03 = r10 ^ abcd6;
+   const uint32_t pr3 = vr3 ^ r11;
+   const uint32_t qr3 = wr3 ^ r11;
+   const uint32_t p4 = ph13 ^ pr3;
+   const uint32_t S7 = ph03 ^ qr3;
+   const uint32_t p6 = pl13 ^ pr3;
+   const uint32_t p7 = pl03 ^ qr3;
+   const uint32_t S3 = p1 ^ p6;
+   const uint32_t S6 = p2 ^ p6;
+   const uint32_t S0 = p3 ^ p6;
+   const uint32_t X11 = p0 ^ p2;
+   const uint32_t S5 = S0 ^ X11;
+   const uint32_t X13 = p4 ^ p7;
+   const uint32_t X14 = X11 ^ X13;
+   const uint32_t S1 = S3 ^ X14;
+   const uint32_t X16 = p1 ^ S7;
+   const uint32_t S2 = X14 ^ X16;
+   const uint32_t X18 = p0 ^ p4;
+   const uint32_t X19 = S5 ^ X16;
+   const uint32_t S4 = X18 ^ X19;
+
+   V[0] = S0;
+   V[1] = S1;
+   V[2] = S2;
+   V[3] = S3;
+   V[4] = S4;
+   V[5] = S5;
+   V[6] = S6;
+   V[7] = S7;
+   }
+
+inline void bit_transpose(uint32_t B[8])
+   {
+   swap_bits<uint32_t>(B[1], B[0], 0x55555555, 1);
+   swap_bits<uint32_t>(B[3], B[2], 0x55555555, 1);
+   swap_bits<uint32_t>(B[5], B[4], 0x55555555, 1);
+   swap_bits<uint32_t>(B[7], B[6], 0x55555555, 1);
+
+   swap_bits<uint32_t>(B[2], B[0], 0x33333333, 2);
+   swap_bits<uint32_t>(B[3], B[1], 0x33333333, 2);
+   swap_bits<uint32_t>(B[6], B[4], 0x33333333, 2);
+   swap_bits<uint32_t>(B[7], B[5], 0x33333333, 2);
+
+   swap_bits<uint32_t>(B[4], B[0], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[5], B[1], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[6], B[2], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[7], B[3], 0x0F0F0F0F, 4);
+   }
+
+inline void ks_expand(uint32_t B[8], const uint32_t K[], size_t r)
+   {
+   /*
+   This is bit_transpose of K[r..r+4] || K[r..r+4], we can save some computation
+   due to knowing the first and second halves are the same data.
+   */
+   for(size_t i = 0; i != 4; ++i)
+      B[i] = K[r + i];
+
+   swap_bits<uint32_t>(B[1], B[0], 0x55555555, 1);
+   swap_bits<uint32_t>(B[3], B[2], 0x55555555, 1);
+
+   swap_bits<uint32_t>(B[2], B[0], 0x33333333, 2);
+   swap_bits<uint32_t>(B[3], B[1], 0x33333333, 2);
+
+   B[4] = B[0];
+   B[5] = B[1];
+   B[6] = B[2];
+   B[7] = B[3];
+
+   swap_bits<uint32_t>(B[4], B[0], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[5], B[1], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[6], B[2], 0x0F0F0F0F, 4);
+   swap_bits<uint32_t>(B[7], B[3], 0x0F0F0F0F, 4);
+   }
+
+inline void shift_rows(uint32_t B[8])
+   {
+   // 3 0 1 2 7 4 5 6 10 11 8 9 14 15 12 13 17 18 19 16 21 22 23 20 24 25 26 27 28 29 30 31
+#if defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+   for(size_t i = 0; i != 8; i += 2)
+      {
+      uint64_t x = (static_cast<uint64_t>(B[i]) << 32) | B[i+1];
+      x = bit_permute_step<uint64_t>(x, 0x0022331100223311, 2);
+      x = bit_permute_step<uint64_t>(x, 0x0055005500550055, 1);
+      B[i] = static_cast<uint32_t>(x >> 32);
+      B[i+1] = static_cast<uint32_t>(x);
+      }
+#else
+   for(size_t i = 0; i != 8; ++i)
+      {
+      uint32_t x = B[i];
+      x = bit_permute_step<uint32_t>(x, 0x00223311, 2);
+      x = bit_permute_step<uint32_t>(x, 0x00550055, 1);
+      B[i] = x;
+      }
+#endif
+   }
+
+inline void inv_shift_rows(uint32_t B[8])
+   {
+   // Inverse of shift_rows, just inverting the steps
+
+#if defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+   for(size_t i = 0; i != 8; i += 2)
+      {
+      uint64_t x = (static_cast<uint64_t>(B[i]) << 32) | B[i+1];
+      x = bit_permute_step<uint64_t>(x, 0x0055005500550055, 1);
+      x = bit_permute_step<uint64_t>(x, 0x0022331100223311, 2);
+      B[i] = static_cast<uint32_t>(x >> 32);
+      B[i+1] = static_cast<uint32_t>(x);
+      }
+#else
+   for(size_t i = 0; i != 8; ++i)
+      {
+      uint32_t x = B[i];
+      x = bit_permute_step<uint32_t>(x, 0x00550055, 1);
+      x = bit_permute_step<uint32_t>(x, 0x00223311, 2);
+      B[i] = x;
+      }
+#endif
+   }
+
+inline void mix_columns(uint32_t B[8])
+   {
+   // carry high bits in B[0] to positions in 0x1b == 0b11011
+   const uint32_t X2[8] = {
+      B[1],
+      B[2],
+      B[3],
+      B[4] ^ B[0],
+      B[5] ^ B[0],
+      B[6],
+      B[7] ^ B[0],
+      B[0],
+   };
+
+   for(size_t i = 0; i != 8; i++)
+      {
+      const uint32_t X3 = B[i] ^ X2[i];
+      B[i] = X2[i] ^ rotr<8>(B[i]) ^ rotr<16>(B[i]) ^ rotr<24>(X3);
+      }
+   }
+
+void inv_mix_columns(uint32_t B[8])
+   {
+   /*
+   OpenSSL's bsaes implementation credits Jussi Kivilinna with the lovely
+   matrix decomposition
+
+   | 0e 0b 0d 09 |   | 02 03 01 01 |   | 05 00 04 00 |
+   | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+   | 0d 09 0e 0b |   | 01 01 02 03 |   | 04 00 05 00 |
+   | 0b 0d 09 0e |   | 03 01 01 02 |   | 00 04 00 05 |
+
+   Notice the first component is simply the MixColumns matrix. So we can
+   multiply first by (05,00,04,00) then perform MixColumns to get the equivalent
+   of InvMixColumn.
+   */
+   const uint32_t X4[8] = {
+      B[2],
+      B[3],
+      B[4] ^ B[0],
+      B[5] ^ B[0] ^ B[1],
+      B[6] ^ B[1],
+      B[7] ^ B[0],
+      B[0] ^ B[1],
+      B[1],
+   };
+
+   for(size_t i = 0; i != 8; i++)
+      {
+      const uint32_t X5 = X4[i] ^ B[i];
+      B[i] = X5 ^ rotr<16>(X4[i]);
+      }
+
+   mix_columns(B);
+   }
+
+/*
+* AES Encryption
+*/
+void aes_encrypt_n(const uint8_t in[], uint8_t out[],
+                   size_t blocks,
+                   const secure_vector<uint32_t>& EK)
+   {
+   BOTAN_ASSERT(EK.size() == 44 || EK.size() == 52 || EK.size() == 60, "Key was set");
+
+   const size_t rounds = (EK.size() - 4) / 4;
+
+   uint32_t KS[13*8] = { 0 }; // actual maximum is (rounds - 1) * 8
+   for(size_t i = 0; i < rounds - 1; i += 1)
+      {
+      ks_expand(&KS[8*i], EK.data(), 4*i + 4);
+      }
+
+   const size_t BLOCK_SIZE = 16;
+   const size_t BITSLICED_BLOCKS = 8*sizeof(uint32_t) / BLOCK_SIZE;
+
+   while(blocks > 0)
+      {
+      const size_t this_loop = std::min(blocks, BITSLICED_BLOCKS);
+
+      uint32_t B[8] = { 0 };
+
+      load_be(B, in, this_loop*4);
+
+      for(size_t i = 0; i != 8; ++i)
+         B[i] ^= EK[i % 4];
+
+      bit_transpose(B);
+
+      for(size_t r = 0; r != rounds - 1; ++r)
+         {
+         AES_SBOX(B);
+         shift_rows(B);
+         mix_columns(B);
+
+         for(size_t i = 0; i != 8; ++i)
+            B[i] ^= KS[8*r + i];
+         }
+
+      // Final round:
+      AES_SBOX(B);
+      shift_rows(B);
+      bit_transpose(B);
+
+      for(size_t i = 0; i != 8; ++i)
+         B[i] ^= EK[4*rounds + i % 4];
+
+      copy_out_be(out, this_loop*4*sizeof(uint32_t), B);
+
+      in += this_loop * BLOCK_SIZE;
+      out += this_loop * BLOCK_SIZE;
+      blocks -= this_loop;
+      }
+   }
+
+/*
+* AES Decryption
+*/
+void aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks,
+                   const secure_vector<uint32_t>& DK)
+   {
+   BOTAN_ASSERT(DK.size() == 44 || DK.size() == 52 || DK.size() == 60, "Key was set");
+
+   const size_t rounds = (DK.size() - 4) / 4;
+
+   uint32_t KS[13*8] = { 0 }; // actual maximum is (rounds - 1) * 8
+   for(size_t i = 0; i < rounds - 1; i += 1)
+      {
+      ks_expand(&KS[8*i], DK.data(), 4*i + 4);
+      }
+
+   const size_t BLOCK_SIZE = 16;
+   const size_t BITSLICED_BLOCKS = 8*sizeof(uint32_t) / BLOCK_SIZE;
+
+   while(blocks > 0)
+      {
+      const size_t this_loop = std::min(blocks, BITSLICED_BLOCKS);
+
+      uint32_t B[8] = { 0 };
+
+      load_be(B, in, this_loop*4);
+
+      for(size_t i = 0; i != 8; ++i)
+         B[i] ^= DK[i % 4];
+
+      bit_transpose(B);
+
+      for(size_t r = 0; r != rounds - 1; ++r)
+         {
+         AES_INV_SBOX(B);
+         inv_shift_rows(B);
+         inv_mix_columns(B);
+
+         for(size_t i = 0; i != 8; ++i)
+            B[i] ^= KS[8*r + i];
+         }
+
+      // Final round:
+      AES_INV_SBOX(B);
+      inv_shift_rows(B);
+      bit_transpose(B);
+
+      for(size_t i = 0; i != 8; ++i)
+         B[i] ^= DK[4*rounds + i % 4];
+
+      copy_out_be(out, this_loop*4*sizeof(uint32_t), B);
+
+      in += this_loop * BLOCK_SIZE;
+      out += this_loop * BLOCK_SIZE;
+      blocks -= this_loop;
+      }
+   }
+
+inline uint32_t xtime32(uint32_t s)
+   {
+   const uint32_t lo_bit = 0x01010101;
+   const uint32_t mask = 0x7F7F7F7F;
+   const uint32_t poly = 0x1B;
+
+   return ((s & mask) << 1) ^ (((s >> 7) & lo_bit) * poly);
+   }
+
+inline uint32_t InvMixColumn(uint32_t s1)
+   {
+   const uint32_t s2 = xtime32(s1);
+   const uint32_t s4 = xtime32(s2);
+   const uint32_t s8 = xtime32(s4);
+   const uint32_t s9 = s8 ^ s1;
+   const uint32_t s11 = s9 ^ s2;
+   const uint32_t s13 = s9 ^ s4;
+   const uint32_t s14 = s8 ^ s4 ^ s2;
+
+   return s14 ^ rotr<8>(s9) ^ rotr<16>(s13) ^ rotr<24>(s11);
+   }
+
+void InvMixColumn_x4(uint32_t x[4])
+   {
+   x[0] = InvMixColumn(x[0]);
+   x[1] = InvMixColumn(x[1]);
+   x[2] = InvMixColumn(x[2]);
+   x[3] = InvMixColumn(x[3]);
+   }
+
+uint32_t SE_word(uint32_t x)
+   {
+   uint32_t I[8] = { 0 };
+
+   for(size_t i = 0; i != 8; ++i)
+      I[i] = (x >> (7-i)) & 0x01010101;
+
+   AES_SBOX(I);
+
+   x = 0;
+
+   for(size_t i = 0; i != 8; ++i)
+      x |= ((I[i] & 0x01010101) << (7-i));
+
+   return x;
+   }
+
+void aes_key_schedule(const uint8_t key[], size_t length,
+                      secure_vector<uint32_t>& EK,
+                      secure_vector<uint32_t>& DK,
+                      bool bswap_keys = false)
+   {
+   static const uint32_t RC[10] = {
+      0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+      0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 };
+
+   const size_t X = length / 4;
+
+   // Can't happen, but make static analyzers happy
+   BOTAN_ASSERT_NOMSG(X == 4 || X == 6 || X == 8);
+
+   const size_t rounds = (length / 4) + 6;
+
+   // Help the optimizer
+   BOTAN_ASSERT_NOMSG(rounds == 10 || rounds == 12 || rounds == 14);
+
+   CT::poison(key, length);
+
+   EK.resize(length + 28);
+   DK.resize(length + 28);
+
+   for(size_t i = 0; i != X; ++i)
+      EK[i] = load_be<uint32_t>(key, i);
+
+   for(size_t i = X; i < 4*(rounds+1); i += X)
+      {
+      EK[i] = EK[i-X] ^ RC[(i-X)/X] ^ rotl<8>(SE_word(EK[i-1]));
+
+      for(size_t j = 1; j != X && (i+j) < EK.size(); ++j)
+         {
+         EK[i+j] = EK[i+j-X];
+
+         if(X == 8 && j == 4)
+            EK[i+j] ^= SE_word(EK[i+j-1]);
+         else
+            EK[i+j] ^= EK[i+j-1];
+         }
+      }
+
+   for(size_t i = 0; i != 4*(rounds+1); i += 4)
+      {
+      DK[i  ] = EK[4*rounds - i  ];
+      DK[i+1] = EK[4*rounds - i+1];
+      DK[i+2] = EK[4*rounds - i+2];
+      DK[i+3] = EK[4*rounds - i+3];
+      }
+
+   for(size_t i = 4; i != 4*rounds; i += 4)
+      {
+      InvMixColumn_x4(&DK[i]);
+      }
+
+   if(bswap_keys)
+      {
+      // HW AES on little endian needs the subkeys to be byte reversed
+      for(size_t i = 0; i != EK.size(); ++i)
+         EK[i] = reverse_bytes(EK[i]);
+      for(size_t i = 0; i != DK.size(); ++i)
+         DK[i] = reverse_bytes(DK[i]);
+      }
+
+   CT::unpoison(EK.data(), EK.size());
+   CT::unpoison(DK.data(), DK.size());
+   CT::unpoison(key, length);
+   }
+
+size_t aes_parallelism()
+   {
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return 4; // pipelined
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return 2; // pipelined
+      }
+#endif
+
+   // bitsliced:
+   return 2;
+   }
+
+const char* aes_provider()
+   {
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return "cpu";
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return "vperm";
+      }
+#endif
+
+   return "base";
+   }
+
+}
+
+std::string AES_128::provider() const { return aes_provider(); }
+std::string AES_192::provider() const { return aes_provider(); }
+std::string AES_256::provider() const { return aes_provider(); }
+
+size_t AES_128::parallelism() const { return aes_parallelism(); }
+size_t AES_192::parallelism() const { return aes_parallelism(); }
+size_t AES_256::parallelism() const { return aes_parallelism(); }
+
+void AES_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_encrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_encrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_encrypt_n(in, out, blocks, m_EK);
+   }
+
+void AES_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_decrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_decrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_decrypt_n(in, out, blocks, m_DK);
+   }
+
+void AES_128::key_schedule(const uint8_t key[], size_t length)
+   {
+#if defined(BOTAN_HAS_AES_NI)
+   if(CPUID::has_aes_ni())
+      {
+      return aesni_key_schedule(key, length);
+      }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_key_schedule(key, length);
+      }
+#endif
+
+   aes_key_schedule(key, length, m_EK, m_DK);
+   }
+
+void AES_128::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+void AES_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_encrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_encrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_encrypt_n(in, out, blocks, m_EK);
+   }
+
+void AES_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_decrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_decrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_decrypt_n(in, out, blocks, m_DK);
+   }
+
+void AES_192::key_schedule(const uint8_t key[], size_t length)
+   {
+#if defined(BOTAN_HAS_AES_NI)
+   if(CPUID::has_aes_ni())
+      {
+      return aesni_key_schedule(key, length);
+      }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_key_schedule(key, length);
+      }
+#endif
+
+   aes_key_schedule(key, length, m_EK, m_DK);
+   }
+
+void AES_192::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+void AES_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_encrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_encrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_encrypt_n(in, out, blocks, m_EK);
+   }
+
+void AES_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return hw_aes_decrypt_n(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_decrypt_n(in, out, blocks);
+      }
+#endif
+
+   aes_decrypt_n(in, out, blocks, m_DK);
+   }
+
+void AES_256::key_schedule(const uint8_t key[], size_t length)
+   {
+#if defined(BOTAN_HAS_AES_NI)
+   if(CPUID::has_aes_ni())
+      {
+      return aesni_key_schedule(key, length);
+      }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+   if(CPUID::has_hw_aes())
+      {
+      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return vperm_key_schedule(key, length);
+      }
+#endif
+
+   aes_key_schedule(key, length, m_EK, m_DK);
+   }
+
+void AES_256::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/aes/aes.h b/third_party/botan/src/lib/block/aes/aes.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes.h
@@ -0,0 +1,131 @@
+/*
+* AES
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AES_H_
+#define BOTAN_AES_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(aes.h)
+
+namespace Botan {
+
+/**
+* AES-128
+*/
+class BOTAN_PUBLIC_API(2,0) AES_128 final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+      std::string name() const override { return "AES-128"; }
+      BlockCipher* clone() const override { return new AES_128; }
+      size_t parallelism() const override;
+
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+#if defined(BOTAN_HAS_AES_VPERM)
+      void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+      void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+      void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      secure_vector<uint32_t> m_EK, m_DK;
+   };
+
+/**
+* AES-192
+*/
+class BOTAN_PUBLIC_API(2,0) AES_192 final : public Block_Cipher_Fixed_Params<16, 24>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+      std::string name() const override { return "AES-192"; }
+      BlockCipher* clone() const override { return new AES_192; }
+      size_t parallelism() const override;
+
+   private:
+#if defined(BOTAN_HAS_AES_VPERM)
+      void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+      void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+      void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint32_t> m_EK, m_DK;
+   };
+
+/**
+* AES-256
+*/
+class BOTAN_PUBLIC_API(2,0) AES_256 final : public Block_Cipher_Fixed_Params<16, 32>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+
+      std::string name() const override { return "AES-256"; }
+      BlockCipher* clone() const override { return new AES_256; }
+      size_t parallelism() const override;
+
+   private:
+#if defined(BOTAN_HAS_AES_VPERM)
+      void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+      void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+      void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint32_t> m_EK, m_DK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp b/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp
@@ -0,0 +1,484 @@
+/*
+* AES using ARMv8
+* Contributed by Jeffrey Walton
+*
+* Further changes
+* (C) 2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+#define AES_ENC_4_ROUNDS(K)                \
+   do                                      \
+      {                                    \
+      B0 = vaesmcq_u8(vaeseq_u8(B0, K));   \
+      B1 = vaesmcq_u8(vaeseq_u8(B1, K));   \
+      B2 = vaesmcq_u8(vaeseq_u8(B2, K));   \
+      B3 = vaesmcq_u8(vaeseq_u8(B3, K));   \
+      } while(0)
+
+#define AES_ENC_4_LAST_ROUNDS(K, K2)       \
+   do                                      \
+      {                                    \
+      B0 = veorq_u8(vaeseq_u8(B0, K), K2); \
+      B1 = veorq_u8(vaeseq_u8(B1, K), K2); \
+      B2 = veorq_u8(vaeseq_u8(B2, K), K2); \
+      B3 = veorq_u8(vaeseq_u8(B3, K), K2); \
+      } while(0)
+
+#define AES_DEC_4_ROUNDS(K)                \
+   do                                      \
+      {                                    \
+      B0 = vaesimcq_u8(vaesdq_u8(B0, K));  \
+      B1 = vaesimcq_u8(vaesdq_u8(B1, K));  \
+      B2 = vaesimcq_u8(vaesdq_u8(B2, K));  \
+      B3 = vaesimcq_u8(vaesdq_u8(B3, K));  \
+      } while(0)
+
+#define AES_DEC_4_LAST_ROUNDS(K, K2)       \
+   do                                      \
+      {                                    \
+      B0 = veorq_u8(vaesdq_u8(B0, K), K2); \
+      B1 = veorq_u8(vaesdq_u8(B1, K), K2); \
+      B2 = veorq_u8(vaesdq_u8(B2, K), K2); \
+      B3 = veorq_u8(vaesdq_u8(B3, K), K2); \
+      } while(0)
+
+/*
+* AES-128 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_ENC_4_ROUNDS(K0);
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_LAST_ROUNDS(K9, K10);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesmcq_u8(vaeseq_u8(B, K0));
+      B = vaesmcq_u8(vaeseq_u8(B, K1));
+      B = vaesmcq_u8(vaeseq_u8(B, K2));
+      B = vaesmcq_u8(vaeseq_u8(B, K3));
+      B = vaesmcq_u8(vaeseq_u8(B, K4));
+      B = vaesmcq_u8(vaeseq_u8(B, K5));
+      B = vaesmcq_u8(vaeseq_u8(B, K6));
+      B = vaesmcq_u8(vaeseq_u8(B, K7));
+      B = vaesmcq_u8(vaeseq_u8(B, K8));
+      B = veorq_u8(vaeseq_u8(B, K9), K10);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+/*
+* AES-128 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_DEC_4_ROUNDS(K0);
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_LAST_ROUNDS(K9, K10);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesimcq_u8(vaesdq_u8(B, K0));
+      B = vaesimcq_u8(vaesdq_u8(B, K1));
+      B = vaesimcq_u8(vaesdq_u8(B, K2));
+      B = vaesimcq_u8(vaesdq_u8(B, K3));
+      B = vaesimcq_u8(vaesdq_u8(B, K4));
+      B = vaesimcq_u8(vaesdq_u8(B, K5));
+      B = vaesimcq_u8(vaesdq_u8(B, K6));
+      B = vaesimcq_u8(vaesdq_u8(B, K7));
+      B = vaesimcq_u8(vaesdq_u8(B, K8));
+      B = veorq_u8(vaesdq_u8(B, K9), K10);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+/*
+* AES-192 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+   const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+   const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_ENC_4_ROUNDS(K0);
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_ROUNDS(K9);
+      AES_ENC_4_ROUNDS(K10);
+      AES_ENC_4_LAST_ROUNDS(K11, K12);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesmcq_u8(vaeseq_u8(B, K0));
+      B = vaesmcq_u8(vaeseq_u8(B, K1));
+      B = vaesmcq_u8(vaeseq_u8(B, K2));
+      B = vaesmcq_u8(vaeseq_u8(B, K3));
+      B = vaesmcq_u8(vaeseq_u8(B, K4));
+      B = vaesmcq_u8(vaeseq_u8(B, K5));
+      B = vaesmcq_u8(vaeseq_u8(B, K6));
+      B = vaesmcq_u8(vaeseq_u8(B, K7));
+      B = vaesmcq_u8(vaeseq_u8(B, K8));
+      B = vaesmcq_u8(vaeseq_u8(B, K9));
+      B = vaesmcq_u8(vaeseq_u8(B, K10));
+      B = veorq_u8(vaeseq_u8(B, K11), K12);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+/*
+* AES-192 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+   const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+   const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_DEC_4_ROUNDS(K0);
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_ROUNDS(K9);
+      AES_DEC_4_ROUNDS(K10);
+      AES_DEC_4_LAST_ROUNDS(K11, K12);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesimcq_u8(vaesdq_u8(B, K0));
+      B = vaesimcq_u8(vaesdq_u8(B, K1));
+      B = vaesimcq_u8(vaesdq_u8(B, K2));
+      B = vaesimcq_u8(vaesdq_u8(B, K3));
+      B = vaesimcq_u8(vaesdq_u8(B, K4));
+      B = vaesimcq_u8(vaesdq_u8(B, K5));
+      B = vaesimcq_u8(vaesdq_u8(B, K6));
+      B = vaesimcq_u8(vaesdq_u8(B, K7));
+      B = vaesimcq_u8(vaesdq_u8(B, K8));
+      B = vaesimcq_u8(vaesdq_u8(B, K9));
+      B = vaesimcq_u8(vaesdq_u8(B, K10));
+      B = veorq_u8(vaesdq_u8(B, K11), K12);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+/*
+* AES-256 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+   const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+   const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+   const uint8x16_t K13 = vld1q_u8(skey + 13*16);
+   const uint8x16_t K14 = vld1q_u8(skey + 14*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_ENC_4_ROUNDS(K0);
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_ROUNDS(K9);
+      AES_ENC_4_ROUNDS(K10);
+      AES_ENC_4_ROUNDS(K11);
+      AES_ENC_4_ROUNDS(K12);
+      AES_ENC_4_LAST_ROUNDS(K13, K14);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesmcq_u8(vaeseq_u8(B, K0));
+      B = vaesmcq_u8(vaeseq_u8(B, K1));
+      B = vaesmcq_u8(vaeseq_u8(B, K2));
+      B = vaesmcq_u8(vaeseq_u8(B, K3));
+      B = vaesmcq_u8(vaeseq_u8(B, K4));
+      B = vaesmcq_u8(vaeseq_u8(B, K5));
+      B = vaesmcq_u8(vaeseq_u8(B, K6));
+      B = vaesmcq_u8(vaeseq_u8(B, K7));
+      B = vaesmcq_u8(vaeseq_u8(B, K8));
+      B = vaesmcq_u8(vaeseq_u8(B, K9));
+      B = vaesmcq_u8(vaeseq_u8(B, K10));
+      B = vaesmcq_u8(vaeseq_u8(B, K11));
+      B = vaesmcq_u8(vaeseq_u8(B, K12));
+      B = veorq_u8(vaeseq_u8(B, K13), K14);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+/*
+* AES-256 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+   const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+   const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+   const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+   const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+   const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+   const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+   const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+   const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+   const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+   const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+   const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+   const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+   const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+   const uint8x16_t K13 = vld1q_u8(skey + 13*16);
+   const uint8x16_t K14 = vld1q_u8(skey + 14*16);
+
+   while(blocks >= 4)
+      {
+      uint8x16_t B0 = vld1q_u8(in);
+      uint8x16_t B1 = vld1q_u8(in+16);
+      uint8x16_t B2 = vld1q_u8(in+32);
+      uint8x16_t B3 = vld1q_u8(in+48);
+
+      AES_DEC_4_ROUNDS(K0);
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_ROUNDS(K9);
+      AES_DEC_4_ROUNDS(K10);
+      AES_DEC_4_ROUNDS(K11);
+      AES_DEC_4_ROUNDS(K12);
+      AES_DEC_4_LAST_ROUNDS(K13, K14);
+
+      vst1q_u8(out, B0);
+      vst1q_u8(out+16, B1);
+      vst1q_u8(out+32, B2);
+      vst1q_u8(out+48, B3);
+
+      in += 16*4;
+      out += 16*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint8x16_t B = vld1q_u8(in+16*i);
+      B = vaesimcq_u8(vaesdq_u8(B, K0));
+      B = vaesimcq_u8(vaesdq_u8(B, K1));
+      B = vaesimcq_u8(vaesdq_u8(B, K2));
+      B = vaesimcq_u8(vaesdq_u8(B, K3));
+      B = vaesimcq_u8(vaesdq_u8(B, K4));
+      B = vaesimcq_u8(vaesdq_u8(B, K5));
+      B = vaesimcq_u8(vaesdq_u8(B, K6));
+      B = vaesimcq_u8(vaesdq_u8(B, K7));
+      B = vaesimcq_u8(vaesdq_u8(B, K8));
+      B = vaesimcq_u8(vaesdq_u8(B, K9));
+      B = vaesimcq_u8(vaesdq_u8(B, K10));
+      B = vaesimcq_u8(vaesdq_u8(B, K11));
+      B = vaesimcq_u8(vaesdq_u8(B, K12));
+      B = veorq_u8(vaesdq_u8(B, K13), K14);
+      vst1q_u8(out+16*i, B);
+      }
+   }
+
+#undef AES_ENC_4_ROUNDS
+#undef AES_ENC_4_LAST_ROUNDS
+#undef AES_DEC_4_ROUNDS
+#undef AES_DEC_4_LAST_ROUNDS
+
+}
diff --git a/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp b/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp
@@ -0,0 +1,780 @@
+/*
+* AES using AES-NI instructions
+* (C) 2009,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <wmmintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("ssse3")
+__m128i aes_128_key_expansion(__m128i key, __m128i key_with_rcon)
+   {
+   key_with_rcon = _mm_shuffle_epi32(key_with_rcon, _MM_SHUFFLE(3,3,3,3));
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   return _mm_xor_si128(key, key_with_rcon);
+   }
+
+BOTAN_FUNC_ISA("ssse3")
+void aes_192_key_expansion(__m128i* K1, __m128i* K2, __m128i key2_with_rcon,
+                           uint32_t out[], bool last)
+   {
+   __m128i key1 = *K1;
+   __m128i key2 = *K2;
+
+   key2_with_rcon  = _mm_shuffle_epi32(key2_with_rcon, _MM_SHUFFLE(1,1,1,1));
+   key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+   key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+   key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+   key1 = _mm_xor_si128(key1, key2_with_rcon);
+
+   *K1 = key1;
+   _mm_storeu_si128(reinterpret_cast<__m128i*>(out), key1);
+
+   if(last)
+      return;
+
+   key2 = _mm_xor_si128(key2, _mm_slli_si128(key2, 4));
+   key2 = _mm_xor_si128(key2, _mm_shuffle_epi32(key1, _MM_SHUFFLE(3,3,3,3)));
+
+   *K2 = key2;
+   out[4] = _mm_cvtsi128_si32(key2);
+   out[5] = _mm_cvtsi128_si32(_mm_srli_si128(key2, 4));
+   }
+
+/*
+* The second half of the AES-256 key expansion (other half same as AES-128)
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+__m128i aes_256_key_expansion(__m128i key, __m128i key2)
+   {
+   __m128i key_with_rcon = _mm_aeskeygenassist_si128(key2, 0x00);
+   key_with_rcon = _mm_shuffle_epi32(key_with_rcon, _MM_SHUFFLE(2,2,2,2));
+
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+   return _mm_xor_si128(key, key_with_rcon);
+   }
+
+}
+
+#define AES_ENC_4_ROUNDS(K)                     \
+   do                                           \
+      {                                         \
+      B0 = _mm_aesenc_si128(B0, K);             \
+      B1 = _mm_aesenc_si128(B1, K);             \
+      B2 = _mm_aesenc_si128(B2, K);             \
+      B3 = _mm_aesenc_si128(B3, K);             \
+      } while(0)
+
+#define AES_ENC_4_LAST_ROUNDS(K)                \
+   do                                           \
+      {                                         \
+      B0 = _mm_aesenclast_si128(B0, K);         \
+      B1 = _mm_aesenclast_si128(B1, K);         \
+      B2 = _mm_aesenclast_si128(B2, K);         \
+      B3 = _mm_aesenclast_si128(B3, K);         \
+      } while(0)
+
+#define AES_DEC_4_ROUNDS(K)                     \
+   do                                           \
+      {                                         \
+      B0 = _mm_aesdec_si128(B0, K);             \
+      B1 = _mm_aesdec_si128(B1, K);             \
+      B2 = _mm_aesdec_si128(B2, K);             \
+      B3 = _mm_aesdec_si128(B3, K);             \
+      } while(0)
+
+#define AES_DEC_4_LAST_ROUNDS(K)                \
+   do                                           \
+      {                                         \
+      B0 = _mm_aesdeclast_si128(B0, K);         \
+      B1 = _mm_aesdeclast_si128(B1, K);         \
+      B2 = _mm_aesdeclast_si128(B2, K);         \
+      B3 = _mm_aesdeclast_si128(B3, K);         \
+      } while(0)
+
+/*
+* AES-128 Encryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_ROUNDS(K9);
+      AES_ENC_4_LAST_ROUNDS(K10);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesenc_si128(B, K1);
+      B = _mm_aesenc_si128(B, K2);
+      B = _mm_aesenc_si128(B, K3);
+      B = _mm_aesenc_si128(B, K4);
+      B = _mm_aesenc_si128(B, K5);
+      B = _mm_aesenc_si128(B, K6);
+      B = _mm_aesenc_si128(B, K7);
+      B = _mm_aesenc_si128(B, K8);
+      B = _mm_aesenc_si128(B, K9);
+      B = _mm_aesenclast_si128(B, K10);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-128 Decryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_ROUNDS(K9);
+      AES_DEC_4_LAST_ROUNDS(K10);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesdec_si128(B, K1);
+      B = _mm_aesdec_si128(B, K2);
+      B = _mm_aesdec_si128(B, K3);
+      B = _mm_aesdec_si128(B, K4);
+      B = _mm_aesdec_si128(B, K5);
+      B = _mm_aesdec_si128(B, K6);
+      B = _mm_aesdec_si128(B, K7);
+      B = _mm_aesdec_si128(B, K8);
+      B = _mm_aesdec_si128(B, K9);
+      B = _mm_aesdeclast_si128(B, K10);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-128 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_128::aesni_key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(44);
+   m_DK.resize(44);
+
+   #define AES_128_key_exp(K, RCON) \
+      aes_128_key_expansion(K, _mm_aeskeygenassist_si128(K, RCON))
+
+   const __m128i K0  = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+   const __m128i K1  = AES_128_key_exp(K0, 0x01);
+   const __m128i K2  = AES_128_key_exp(K1, 0x02);
+   const __m128i K3  = AES_128_key_exp(K2, 0x04);
+   const __m128i K4  = AES_128_key_exp(K3, 0x08);
+   const __m128i K5  = AES_128_key_exp(K4, 0x10);
+   const __m128i K6  = AES_128_key_exp(K5, 0x20);
+   const __m128i K7  = AES_128_key_exp(K6, 0x40);
+   const __m128i K8  = AES_128_key_exp(K7, 0x80);
+   const __m128i K9  = AES_128_key_exp(K8, 0x1B);
+   const __m128i K10 = AES_128_key_exp(K9, 0x36);
+
+   __m128i* EK_mm = reinterpret_cast<__m128i*>(m_EK.data());
+   _mm_storeu_si128(EK_mm     , K0);
+   _mm_storeu_si128(EK_mm +  1, K1);
+   _mm_storeu_si128(EK_mm +  2, K2);
+   _mm_storeu_si128(EK_mm +  3, K3);
+   _mm_storeu_si128(EK_mm +  4, K4);
+   _mm_storeu_si128(EK_mm +  5, K5);
+   _mm_storeu_si128(EK_mm +  6, K6);
+   _mm_storeu_si128(EK_mm +  7, K7);
+   _mm_storeu_si128(EK_mm +  8, K8);
+   _mm_storeu_si128(EK_mm +  9, K9);
+   _mm_storeu_si128(EK_mm + 10, K10);
+
+   // Now generate decryption keys
+
+   __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+   _mm_storeu_si128(DK_mm     , K10);
+   _mm_storeu_si128(DK_mm +  1, _mm_aesimc_si128(K9));
+   _mm_storeu_si128(DK_mm +  2, _mm_aesimc_si128(K8));
+   _mm_storeu_si128(DK_mm +  3, _mm_aesimc_si128(K7));
+   _mm_storeu_si128(DK_mm +  4, _mm_aesimc_si128(K6));
+   _mm_storeu_si128(DK_mm +  5, _mm_aesimc_si128(K5));
+   _mm_storeu_si128(DK_mm +  6, _mm_aesimc_si128(K4));
+   _mm_storeu_si128(DK_mm +  7, _mm_aesimc_si128(K3));
+   _mm_storeu_si128(DK_mm +  8, _mm_aesimc_si128(K2));
+   _mm_storeu_si128(DK_mm +  9, _mm_aesimc_si128(K1));
+   _mm_storeu_si128(DK_mm + 10, K0);
+   }
+
+/*
+* AES-192 Encryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+   const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+   const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_ROUNDS(K9);
+      AES_ENC_4_ROUNDS(K10);
+      AES_ENC_4_ROUNDS(K11);
+      AES_ENC_4_LAST_ROUNDS(K12);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesenc_si128(B, K1);
+      B = _mm_aesenc_si128(B, K2);
+      B = _mm_aesenc_si128(B, K3);
+      B = _mm_aesenc_si128(B, K4);
+      B = _mm_aesenc_si128(B, K5);
+      B = _mm_aesenc_si128(B, K6);
+      B = _mm_aesenc_si128(B, K7);
+      B = _mm_aesenc_si128(B, K8);
+      B = _mm_aesenc_si128(B, K9);
+      B = _mm_aesenc_si128(B, K10);
+      B = _mm_aesenc_si128(B, K11);
+      B = _mm_aesenclast_si128(B, K12);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-192 Decryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+   const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+   const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_ROUNDS(K9);
+      AES_DEC_4_ROUNDS(K10);
+      AES_DEC_4_ROUNDS(K11);
+      AES_DEC_4_LAST_ROUNDS(K12);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesdec_si128(B, K1);
+      B = _mm_aesdec_si128(B, K2);
+      B = _mm_aesdec_si128(B, K3);
+      B = _mm_aesdec_si128(B, K4);
+      B = _mm_aesdec_si128(B, K5);
+      B = _mm_aesdec_si128(B, K6);
+      B = _mm_aesdec_si128(B, K7);
+      B = _mm_aesdec_si128(B, K8);
+      B = _mm_aesdec_si128(B, K9);
+      B = _mm_aesdec_si128(B, K10);
+      B = _mm_aesdec_si128(B, K11);
+      B = _mm_aesdeclast_si128(B, K12);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-192 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_192::aesni_key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(52);
+   m_DK.resize(52);
+
+   __m128i K0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+   __m128i K1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key + 8));
+   K1 = _mm_srli_si128(K1, 8);
+
+   load_le(m_EK.data(), key, 6);
+
+   #define AES_192_key_exp(RCON, EK_OFF)                         \
+     aes_192_key_expansion(&K0, &K1,                             \
+                           _mm_aeskeygenassist_si128(K1, RCON),  \
+                           &m_EK[EK_OFF], EK_OFF == 48)
+
+   AES_192_key_exp(0x01, 6);
+   AES_192_key_exp(0x02, 12);
+   AES_192_key_exp(0x04, 18);
+   AES_192_key_exp(0x08, 24);
+   AES_192_key_exp(0x10, 30);
+   AES_192_key_exp(0x20, 36);
+   AES_192_key_exp(0x40, 42);
+   AES_192_key_exp(0x80, 48);
+
+   #undef AES_192_key_exp
+
+   // Now generate decryption keys
+   const __m128i* EK_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+   __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+   _mm_storeu_si128(DK_mm     , _mm_loadu_si128(EK_mm + 12));
+   _mm_storeu_si128(DK_mm +  1, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 11)));
+   _mm_storeu_si128(DK_mm +  2, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 10)));
+   _mm_storeu_si128(DK_mm +  3, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 9)));
+   _mm_storeu_si128(DK_mm +  4, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 8)));
+   _mm_storeu_si128(DK_mm +  5, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 7)));
+   _mm_storeu_si128(DK_mm +  6, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 6)));
+   _mm_storeu_si128(DK_mm +  7, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 5)));
+   _mm_storeu_si128(DK_mm +  8, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 4)));
+   _mm_storeu_si128(DK_mm +  9, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 3)));
+   _mm_storeu_si128(DK_mm + 10, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 2)));
+   _mm_storeu_si128(DK_mm + 11, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 1)));
+   _mm_storeu_si128(DK_mm + 12, _mm_loadu_si128(EK_mm + 0));
+   }
+
+/*
+* AES-256 Encryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+   const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+   const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+   const __m128i K13 = _mm_loadu_si128(key_mm + 13);
+   const __m128i K14 = _mm_loadu_si128(key_mm + 14);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_ENC_4_ROUNDS(K1);
+      AES_ENC_4_ROUNDS(K2);
+      AES_ENC_4_ROUNDS(K3);
+      AES_ENC_4_ROUNDS(K4);
+      AES_ENC_4_ROUNDS(K5);
+      AES_ENC_4_ROUNDS(K6);
+      AES_ENC_4_ROUNDS(K7);
+      AES_ENC_4_ROUNDS(K8);
+      AES_ENC_4_ROUNDS(K9);
+      AES_ENC_4_ROUNDS(K10);
+      AES_ENC_4_ROUNDS(K11);
+      AES_ENC_4_ROUNDS(K12);
+      AES_ENC_4_ROUNDS(K13);
+      AES_ENC_4_LAST_ROUNDS(K14);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesenc_si128(B, K1);
+      B = _mm_aesenc_si128(B, K2);
+      B = _mm_aesenc_si128(B, K3);
+      B = _mm_aesenc_si128(B, K4);
+      B = _mm_aesenc_si128(B, K5);
+      B = _mm_aesenc_si128(B, K6);
+      B = _mm_aesenc_si128(B, K7);
+      B = _mm_aesenc_si128(B, K8);
+      B = _mm_aesenc_si128(B, K9);
+      B = _mm_aesenc_si128(B, K10);
+      B = _mm_aesenc_si128(B, K11);
+      B = _mm_aesenc_si128(B, K12);
+      B = _mm_aesenc_si128(B, K13);
+      B = _mm_aesenclast_si128(B, K14);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-256 Decryption
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+   const __m128i K0  = _mm_loadu_si128(key_mm);
+   const __m128i K1  = _mm_loadu_si128(key_mm + 1);
+   const __m128i K2  = _mm_loadu_si128(key_mm + 2);
+   const __m128i K3  = _mm_loadu_si128(key_mm + 3);
+   const __m128i K4  = _mm_loadu_si128(key_mm + 4);
+   const __m128i K5  = _mm_loadu_si128(key_mm + 5);
+   const __m128i K6  = _mm_loadu_si128(key_mm + 6);
+   const __m128i K7  = _mm_loadu_si128(key_mm + 7);
+   const __m128i K8  = _mm_loadu_si128(key_mm + 8);
+   const __m128i K9  = _mm_loadu_si128(key_mm + 9);
+   const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+   const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+   const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+   const __m128i K13 = _mm_loadu_si128(key_mm + 13);
+   const __m128i K14 = _mm_loadu_si128(key_mm + 14);
+
+   while(blocks >= 4)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm + 0);
+      __m128i B1 = _mm_loadu_si128(in_mm + 1);
+      __m128i B2 = _mm_loadu_si128(in_mm + 2);
+      __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+      B0 = _mm_xor_si128(B0, K0);
+      B1 = _mm_xor_si128(B1, K0);
+      B2 = _mm_xor_si128(B2, K0);
+      B3 = _mm_xor_si128(B3, K0);
+
+      AES_DEC_4_ROUNDS(K1);
+      AES_DEC_4_ROUNDS(K2);
+      AES_DEC_4_ROUNDS(K3);
+      AES_DEC_4_ROUNDS(K4);
+      AES_DEC_4_ROUNDS(K5);
+      AES_DEC_4_ROUNDS(K6);
+      AES_DEC_4_ROUNDS(K7);
+      AES_DEC_4_ROUNDS(K8);
+      AES_DEC_4_ROUNDS(K9);
+      AES_DEC_4_ROUNDS(K10);
+      AES_DEC_4_ROUNDS(K11);
+      AES_DEC_4_ROUNDS(K12);
+      AES_DEC_4_ROUNDS(K13);
+      AES_DEC_4_LAST_ROUNDS(K14);
+
+      _mm_storeu_si128(out_mm + 0, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+      _mm_storeu_si128(out_mm + 2, B2);
+      _mm_storeu_si128(out_mm + 3, B3);
+
+      blocks -= 4;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m128i B = _mm_loadu_si128(in_mm + i);
+
+      B = _mm_xor_si128(B, K0);
+
+      B = _mm_aesdec_si128(B, K1);
+      B = _mm_aesdec_si128(B, K2);
+      B = _mm_aesdec_si128(B, K3);
+      B = _mm_aesdec_si128(B, K4);
+      B = _mm_aesdec_si128(B, K5);
+      B = _mm_aesdec_si128(B, K6);
+      B = _mm_aesdec_si128(B, K7);
+      B = _mm_aesdec_si128(B, K8);
+      B = _mm_aesdec_si128(B, K9);
+      B = _mm_aesdec_si128(B, K10);
+      B = _mm_aesdec_si128(B, K11);
+      B = _mm_aesdec_si128(B, K12);
+      B = _mm_aesdec_si128(B, K13);
+      B = _mm_aesdeclast_si128(B, K14);
+
+      _mm_storeu_si128(out_mm + i, B);
+      }
+   }
+
+/*
+* AES-256 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_256::aesni_key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(60);
+   m_DK.resize(60);
+
+   const __m128i K0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+   const __m128i K1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key + 16));
+
+   const __m128i K2 = aes_128_key_expansion(K0, _mm_aeskeygenassist_si128(K1, 0x01));
+   const __m128i K3 = aes_256_key_expansion(K1, K2);
+
+   const __m128i K4 = aes_128_key_expansion(K2, _mm_aeskeygenassist_si128(K3, 0x02));
+   const __m128i K5 = aes_256_key_expansion(K3, K4);
+
+   const __m128i K6 = aes_128_key_expansion(K4, _mm_aeskeygenassist_si128(K5, 0x04));
+   const __m128i K7 = aes_256_key_expansion(K5, K6);
+
+   const __m128i K8 = aes_128_key_expansion(K6, _mm_aeskeygenassist_si128(K7, 0x08));
+   const __m128i K9 = aes_256_key_expansion(K7, K8);
+
+   const __m128i K10 = aes_128_key_expansion(K8, _mm_aeskeygenassist_si128(K9, 0x10));
+   const __m128i K11 = aes_256_key_expansion(K9, K10);
+
+   const __m128i K12 = aes_128_key_expansion(K10, _mm_aeskeygenassist_si128(K11, 0x20));
+   const __m128i K13 = aes_256_key_expansion(K11, K12);
+
+   const __m128i K14 = aes_128_key_expansion(K12, _mm_aeskeygenassist_si128(K13, 0x40));
+
+   __m128i* EK_mm = reinterpret_cast<__m128i*>(m_EK.data());
+   _mm_storeu_si128(EK_mm     , K0);
+   _mm_storeu_si128(EK_mm +  1, K1);
+   _mm_storeu_si128(EK_mm +  2, K2);
+   _mm_storeu_si128(EK_mm +  3, K3);
+   _mm_storeu_si128(EK_mm +  4, K4);
+   _mm_storeu_si128(EK_mm +  5, K5);
+   _mm_storeu_si128(EK_mm +  6, K6);
+   _mm_storeu_si128(EK_mm +  7, K7);
+   _mm_storeu_si128(EK_mm +  8, K8);
+   _mm_storeu_si128(EK_mm +  9, K9);
+   _mm_storeu_si128(EK_mm + 10, K10);
+   _mm_storeu_si128(EK_mm + 11, K11);
+   _mm_storeu_si128(EK_mm + 12, K12);
+   _mm_storeu_si128(EK_mm + 13, K13);
+   _mm_storeu_si128(EK_mm + 14, K14);
+
+   // Now generate decryption keys
+   __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+   _mm_storeu_si128(DK_mm     , K14);
+   _mm_storeu_si128(DK_mm +  1, _mm_aesimc_si128(K13));
+   _mm_storeu_si128(DK_mm +  2, _mm_aesimc_si128(K12));
+   _mm_storeu_si128(DK_mm +  3, _mm_aesimc_si128(K11));
+   _mm_storeu_si128(DK_mm +  4, _mm_aesimc_si128(K10));
+   _mm_storeu_si128(DK_mm +  5, _mm_aesimc_si128(K9));
+   _mm_storeu_si128(DK_mm +  6, _mm_aesimc_si128(K8));
+   _mm_storeu_si128(DK_mm +  7, _mm_aesimc_si128(K7));
+   _mm_storeu_si128(DK_mm +  8, _mm_aesimc_si128(K6));
+   _mm_storeu_si128(DK_mm +  9, _mm_aesimc_si128(K5));
+   _mm_storeu_si128(DK_mm + 10, _mm_aesimc_si128(K4));
+   _mm_storeu_si128(DK_mm + 11, _mm_aesimc_si128(K3));
+   _mm_storeu_si128(DK_mm + 12, _mm_aesimc_si128(K2));
+   _mm_storeu_si128(DK_mm + 13, _mm_aesimc_si128(K1));
+   _mm_storeu_si128(DK_mm + 14, K0);
+   }
+
+#undef AES_ENC_4_ROUNDS
+#undef AES_ENC_4_LAST_ROUNDS
+#undef AES_DEC_4_ROUNDS
+#undef AES_DEC_4_LAST_ROUNDS
+
+}
diff --git a/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp b/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp
@@ -0,0 +1,529 @@
+/*
+* AES using POWER8/POWER9 crypto extensions
+*
+* Contributed by Jeffrey Walton
+*
+* Further changes
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/cpuid.h>
+
+#include <altivec.h>
+#undef vector
+#undef bool
+
+namespace Botan {
+
+typedef __vector unsigned long long Altivec64x2;
+typedef __vector unsigned int Altivec32x4;
+typedef __vector unsigned char Altivec8x16;
+
+namespace {
+
+inline Altivec8x16 reverse_vec(Altivec8x16 src)
+   {
+   if(CPUID::is_little_endian())
+      {
+      const Altivec8x16 mask = {15,14,13,12, 11,10,9,8, 7,6,5,4, 3,2,1,0};
+      const Altivec8x16 zero = {0};
+      return vec_perm(src, zero, mask);
+      }
+   else
+      {
+      return src;
+      }
+   }
+
+inline Altivec64x2 load_key(const uint32_t key[])
+   {
+   return (Altivec64x2)reverse_vec((Altivec8x16)vec_vsx_ld(0, key));;
+   }
+
+inline Altivec64x2 load_block(const uint8_t src[])
+   {
+   return (Altivec64x2)reverse_vec(vec_vsx_ld(0, src));
+   }
+
+inline void store_block(Altivec64x2 src, uint8_t dest[])
+   {
+   vec_vsx_st(reverse_vec((Altivec8x16)src), 0, dest);
+   }
+
+inline void store_blocks(Altivec64x2 B0, Altivec64x2 B1,
+                         Altivec64x2 B2, Altivec64x2 B3,
+                         uint8_t out[])
+   {
+   store_block(B0, out);
+   store_block(B1, out+16);
+   store_block(B2, out+16*2);
+   store_block(B3, out+16*3);
+   }
+
+#define AES_XOR_4(B0, B1, B2, B3, K) do {      \
+   B0 = vec_xor(B0, K);                        \
+   B1 = vec_xor(B1, K);                        \
+   B2 = vec_xor(B2, K);                        \
+   B3 = vec_xor(B3, K);                        \
+   } while(0)
+
+#define AES_ENCRYPT_4(B0, B1, B2, B3, K) do {                   \
+   B0 = __builtin_crypto_vcipher(B0, K);                        \
+   B1 = __builtin_crypto_vcipher(B1, K);                        \
+   B2 = __builtin_crypto_vcipher(B2, K);                        \
+   B3 = __builtin_crypto_vcipher(B3, K);                        \
+   } while(0)
+
+#define AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K) do {              \
+   B0 = __builtin_crypto_vcipherlast(B0, K);                    \
+   B1 = __builtin_crypto_vcipherlast(B1, K);                    \
+   B2 = __builtin_crypto_vcipherlast(B2, K);                    \
+   B3 = __builtin_crypto_vcipherlast(B3, K);                    \
+   } while(0)
+
+#define AES_DECRYPT_4(B0, B1, B2, B3, K) do {                   \
+   B0 = __builtin_crypto_vncipher(B0, K);                       \
+   B1 = __builtin_crypto_vncipher(B1, K);                       \
+   B2 = __builtin_crypto_vncipher(B2, K);                       \
+   B3 = __builtin_crypto_vncipher(B3, K);                       \
+   } while(0)
+
+#define AES_DECRYPT_4_LAST(B0, B1, B2, B3, K) do {              \
+   B0 = __builtin_crypto_vncipherlast(B0, K);                   \
+   B1 = __builtin_crypto_vncipherlast(B1, K);                   \
+   B2 = __builtin_crypto_vncipherlast(B2, K);                   \
+   B3 = __builtin_crypto_vncipherlast(B3, K);                   \
+   } while(0)
+
+}
+
+BOTAN_FUNC_ISA("crypto")
+void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[0]);
+   const Altivec64x2 K1  = load_key(&m_EK[4]);
+   const Altivec64x2 K2  = load_key(&m_EK[8]);
+   const Altivec64x2 K3  = load_key(&m_EK[12]);
+   const Altivec64x2 K4  = load_key(&m_EK[16]);
+   const Altivec64x2 K5  = load_key(&m_EK[20]);
+   const Altivec64x2 K6  = load_key(&m_EK[24]);
+   const Altivec64x2 K7  = load_key(&m_EK[28]);
+   const Altivec64x2 K8  = load_key(&m_EK[32]);
+   const Altivec64x2 K9  = load_key(&m_EK[36]);
+   const Altivec64x2 K10 = load_key(&m_EK[40]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K1);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K2);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K3);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K4);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K5);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K6);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K7);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K8);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K9);
+      AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K10);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vcipher(B, K1);
+      B = __builtin_crypto_vcipher(B, K2);
+      B = __builtin_crypto_vcipher(B, K3);
+      B = __builtin_crypto_vcipher(B, K4);
+      B = __builtin_crypto_vcipher(B, K5);
+      B = __builtin_crypto_vcipher(B, K6);
+      B = __builtin_crypto_vcipher(B, K7);
+      B = __builtin_crypto_vcipher(B, K8);
+      B = __builtin_crypto_vcipher(B, K9);
+      B = __builtin_crypto_vcipherlast(B, K10);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+BOTAN_FUNC_ISA("crypto")
+void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[40]);
+   const Altivec64x2 K1  = load_key(&m_EK[36]);
+   const Altivec64x2 K2  = load_key(&m_EK[32]);
+   const Altivec64x2 K3  = load_key(&m_EK[28]);
+   const Altivec64x2 K4  = load_key(&m_EK[24]);
+   const Altivec64x2 K5  = load_key(&m_EK[20]);
+   const Altivec64x2 K6  = load_key(&m_EK[16]);
+   const Altivec64x2 K7  = load_key(&m_EK[12]);
+   const Altivec64x2 K8  = load_key(&m_EK[8]);
+   const Altivec64x2 K9  = load_key(&m_EK[4]);
+   const Altivec64x2 K10 = load_key(&m_EK[0]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_DECRYPT_4(B0, B1, B2, B3, K1);
+      AES_DECRYPT_4(B0, B1, B2, B3, K2);
+      AES_DECRYPT_4(B0, B1, B2, B3, K3);
+      AES_DECRYPT_4(B0, B1, B2, B3, K4);
+      AES_DECRYPT_4(B0, B1, B2, B3, K5);
+      AES_DECRYPT_4(B0, B1, B2, B3, K6);
+      AES_DECRYPT_4(B0, B1, B2, B3, K7);
+      AES_DECRYPT_4(B0, B1, B2, B3, K8);
+      AES_DECRYPT_4(B0, B1, B2, B3, K9);
+      AES_DECRYPT_4_LAST(B0, B1, B2, B3, K10);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vncipher(B, K1);
+      B = __builtin_crypto_vncipher(B, K2);
+      B = __builtin_crypto_vncipher(B, K3);
+      B = __builtin_crypto_vncipher(B, K4);
+      B = __builtin_crypto_vncipher(B, K5);
+      B = __builtin_crypto_vncipher(B, K6);
+      B = __builtin_crypto_vncipher(B, K7);
+      B = __builtin_crypto_vncipher(B, K8);
+      B = __builtin_crypto_vncipher(B, K9);
+      B = __builtin_crypto_vncipherlast(B, K10);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+BOTAN_FUNC_ISA("crypto")
+void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[0]);
+   const Altivec64x2 K1  = load_key(&m_EK[4]);
+   const Altivec64x2 K2  = load_key(&m_EK[8]);
+   const Altivec64x2 K3  = load_key(&m_EK[12]);
+   const Altivec64x2 K4  = load_key(&m_EK[16]);
+   const Altivec64x2 K5  = load_key(&m_EK[20]);
+   const Altivec64x2 K6  = load_key(&m_EK[24]);
+   const Altivec64x2 K7  = load_key(&m_EK[28]);
+   const Altivec64x2 K8  = load_key(&m_EK[32]);
+   const Altivec64x2 K9  = load_key(&m_EK[36]);
+   const Altivec64x2 K10 = load_key(&m_EK[40]);
+   const Altivec64x2 K11 = load_key(&m_EK[44]);
+   const Altivec64x2 K12 = load_key(&m_EK[48]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K1);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K2);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K3);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K4);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K5);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K6);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K7);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K8);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K9);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K10);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K11);
+      AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K12);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vcipher(B, K1);
+      B = __builtin_crypto_vcipher(B, K2);
+      B = __builtin_crypto_vcipher(B, K3);
+      B = __builtin_crypto_vcipher(B, K4);
+      B = __builtin_crypto_vcipher(B, K5);
+      B = __builtin_crypto_vcipher(B, K6);
+      B = __builtin_crypto_vcipher(B, K7);
+      B = __builtin_crypto_vcipher(B, K8);
+      B = __builtin_crypto_vcipher(B, K9);
+      B = __builtin_crypto_vcipher(B, K10);
+      B = __builtin_crypto_vcipher(B, K11);
+      B = __builtin_crypto_vcipherlast(B, K12);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+BOTAN_FUNC_ISA("crypto")
+void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[48]);
+   const Altivec64x2 K1  = load_key(&m_EK[44]);
+   const Altivec64x2 K2  = load_key(&m_EK[40]);
+   const Altivec64x2 K3  = load_key(&m_EK[36]);
+   const Altivec64x2 K4  = load_key(&m_EK[32]);
+   const Altivec64x2 K5  = load_key(&m_EK[28]);
+   const Altivec64x2 K6  = load_key(&m_EK[24]);
+   const Altivec64x2 K7  = load_key(&m_EK[20]);
+   const Altivec64x2 K8  = load_key(&m_EK[16]);
+   const Altivec64x2 K9  = load_key(&m_EK[12]);
+   const Altivec64x2 K10 = load_key(&m_EK[8]);
+   const Altivec64x2 K11 = load_key(&m_EK[4]);
+   const Altivec64x2 K12 = load_key(&m_EK[0]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_DECRYPT_4(B0, B1, B2, B3, K1);
+      AES_DECRYPT_4(B0, B1, B2, B3, K2);
+      AES_DECRYPT_4(B0, B1, B2, B3, K3);
+      AES_DECRYPT_4(B0, B1, B2, B3, K4);
+      AES_DECRYPT_4(B0, B1, B2, B3, K5);
+      AES_DECRYPT_4(B0, B1, B2, B3, K6);
+      AES_DECRYPT_4(B0, B1, B2, B3, K7);
+      AES_DECRYPT_4(B0, B1, B2, B3, K8);
+      AES_DECRYPT_4(B0, B1, B2, B3, K9);
+      AES_DECRYPT_4(B0, B1, B2, B3, K10);
+      AES_DECRYPT_4(B0, B1, B2, B3, K11);
+      AES_DECRYPT_4_LAST(B0, B1, B2, B3, K12);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vncipher(B, K1);
+      B = __builtin_crypto_vncipher(B, K2);
+      B = __builtin_crypto_vncipher(B, K3);
+      B = __builtin_crypto_vncipher(B, K4);
+      B = __builtin_crypto_vncipher(B, K5);
+      B = __builtin_crypto_vncipher(B, K6);
+      B = __builtin_crypto_vncipher(B, K7);
+      B = __builtin_crypto_vncipher(B, K8);
+      B = __builtin_crypto_vncipher(B, K9);
+      B = __builtin_crypto_vncipher(B, K10);
+      B = __builtin_crypto_vncipher(B, K11);
+      B = __builtin_crypto_vncipherlast(B, K12);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+BOTAN_FUNC_ISA("crypto")
+void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[0]);
+   const Altivec64x2 K1  = load_key(&m_EK[4]);
+   const Altivec64x2 K2  = load_key(&m_EK[8]);
+   const Altivec64x2 K3  = load_key(&m_EK[12]);
+   const Altivec64x2 K4  = load_key(&m_EK[16]);
+   const Altivec64x2 K5  = load_key(&m_EK[20]);
+   const Altivec64x2 K6  = load_key(&m_EK[24]);
+   const Altivec64x2 K7  = load_key(&m_EK[28]);
+   const Altivec64x2 K8  = load_key(&m_EK[32]);
+   const Altivec64x2 K9  = load_key(&m_EK[36]);
+   const Altivec64x2 K10 = load_key(&m_EK[40]);
+   const Altivec64x2 K11 = load_key(&m_EK[44]);
+   const Altivec64x2 K12 = load_key(&m_EK[48]);
+   const Altivec64x2 K13 = load_key(&m_EK[52]);
+   const Altivec64x2 K14 = load_key(&m_EK[56]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K1);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K2);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K3);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K4);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K5);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K6);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K7);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K8);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K9);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K10);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K11);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K12);
+      AES_ENCRYPT_4(B0, B1, B2, B3, K13);
+      AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K14);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vcipher(B, K1);
+      B = __builtin_crypto_vcipher(B, K2);
+      B = __builtin_crypto_vcipher(B, K3);
+      B = __builtin_crypto_vcipher(B, K4);
+      B = __builtin_crypto_vcipher(B, K5);
+      B = __builtin_crypto_vcipher(B, K6);
+      B = __builtin_crypto_vcipher(B, K7);
+      B = __builtin_crypto_vcipher(B, K8);
+      B = __builtin_crypto_vcipher(B, K9);
+      B = __builtin_crypto_vcipher(B, K10);
+      B = __builtin_crypto_vcipher(B, K11);
+      B = __builtin_crypto_vcipher(B, K12);
+      B = __builtin_crypto_vcipher(B, K13);
+      B = __builtin_crypto_vcipherlast(B, K14);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+BOTAN_FUNC_ISA("crypto")
+void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const Altivec64x2 K0  = load_key(&m_EK[56]);
+   const Altivec64x2 K1  = load_key(&m_EK[52]);
+   const Altivec64x2 K2  = load_key(&m_EK[48]);
+   const Altivec64x2 K3  = load_key(&m_EK[44]);
+   const Altivec64x2 K4  = load_key(&m_EK[40]);
+   const Altivec64x2 K5  = load_key(&m_EK[36]);
+   const Altivec64x2 K6  = load_key(&m_EK[32]);
+   const Altivec64x2 K7  = load_key(&m_EK[28]);
+   const Altivec64x2 K8  = load_key(&m_EK[24]);
+   const Altivec64x2 K9  = load_key(&m_EK[20]);
+   const Altivec64x2 K10 = load_key(&m_EK[16]);
+   const Altivec64x2 K11 = load_key(&m_EK[12]);
+   const Altivec64x2 K12 = load_key(&m_EK[8]);
+   const Altivec64x2 K13 = load_key(&m_EK[4]);
+   const Altivec64x2 K14 = load_key(&m_EK[0]);
+
+   while(blocks >= 4)
+      {
+      Altivec64x2 B0 = load_block(in);
+      Altivec64x2 B1 = load_block(in+16);
+      Altivec64x2 B2 = load_block(in+16*2);
+      Altivec64x2 B3 = load_block(in+16*3);
+
+      AES_XOR_4(B0, B1, B2, B3, K0);
+      AES_DECRYPT_4(B0, B1, B2, B3, K1);
+      AES_DECRYPT_4(B0, B1, B2, B3, K2);
+      AES_DECRYPT_4(B0, B1, B2, B3, K3);
+      AES_DECRYPT_4(B0, B1, B2, B3, K4);
+      AES_DECRYPT_4(B0, B1, B2, B3, K5);
+      AES_DECRYPT_4(B0, B1, B2, B3, K6);
+      AES_DECRYPT_4(B0, B1, B2, B3, K7);
+      AES_DECRYPT_4(B0, B1, B2, B3, K8);
+      AES_DECRYPT_4(B0, B1, B2, B3, K9);
+      AES_DECRYPT_4(B0, B1, B2, B3, K10);
+      AES_DECRYPT_4(B0, B1, B2, B3, K11);
+      AES_DECRYPT_4(B0, B1, B2, B3, K12);
+      AES_DECRYPT_4(B0, B1, B2, B3, K13);
+      AES_DECRYPT_4_LAST(B0, B1, B2, B3, K14);
+
+      store_blocks(B0, B1, B2, B3, out);
+
+      out += 4*16;
+      in += 4*16;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      Altivec64x2 B = load_block(in);
+
+      B = vec_xor(B, K0);
+      B = __builtin_crypto_vncipher(B, K1);
+      B = __builtin_crypto_vncipher(B, K2);
+      B = __builtin_crypto_vncipher(B, K3);
+      B = __builtin_crypto_vncipher(B, K4);
+      B = __builtin_crypto_vncipher(B, K5);
+      B = __builtin_crypto_vncipher(B, K6);
+      B = __builtin_crypto_vncipher(B, K7);
+      B = __builtin_crypto_vncipher(B, K8);
+      B = __builtin_crypto_vncipher(B, K9);
+      B = __builtin_crypto_vncipher(B, K10);
+      B = __builtin_crypto_vncipher(B, K11);
+      B = __builtin_crypto_vncipher(B, K12);
+      B = __builtin_crypto_vncipher(B, K13);
+      B = __builtin_crypto_vncipherlast(B, K14);
+
+      store_block(B, out);
+
+      out += 16;
+      in += 16;
+      }
+   }
+
+#undef AES_XOR_4
+#undef AES_ENCRYPT_4
+#undef AES_ENCRYPT_4_LAST
+#undef AES_DECRYPT_4
+#undef AES_DECRYPT_4_LAST
+
+}
diff --git a/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp b/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp
@@ -0,0 +1,627 @@
+/*
+* AES using vector permutes (SSSE3, NEON)
+* (C) 2010,2016,2019 Jack Lloyd
+*
+* Based on public domain x86-64 assembly written by Mike Hamburg,
+* described in "Accelerating AES with Vector Permute Instructions"
+* (CHES 2009). His original code is available at
+* https://crypto.stanford.edu/vpaes/
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/simd_32.h>
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+  #include <tmmintrin.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) shuffle(SIMD_4x32 a, SIMD_4x32 b)
+   {
+#if defined(BOTAN_SIMD_USE_SSE2)
+   return SIMD_4x32(_mm_shuffle_epi8(a.raw(), b.raw()));
+#elif defined(BOTAN_SIMD_USE_NEON)
+   const uint8x16_t tbl = vreinterpretq_u8_u32(a.raw());
+   const uint8x16_t idx = vreinterpretq_u8_u32(b.raw());
+
+#if defined(BOTAN_TARGET_ARCH_IS_ARM32)
+   const uint8x8x2_t tbl2 = { vget_low_u8(tbl), vget_high_u8(tbl) };
+
+   return SIMD_4x32(vreinterpretq_u32_u8(
+                       vcombine_u8(vtbl2_u8(tbl2, vget_low_u8(idx)),
+                                   vtbl2_u8(tbl2, vget_high_u8(idx)))));
+
+#else
+   return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(tbl, idx)));
+#endif
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+   const auto zero = vec_splat_s8(0x00);
+   const auto mask = vec_cmplt((__vector signed char)b.raw(), zero);
+   const auto r = vec_perm((__vector signed char)a.raw(), (__vector signed char)a.raw(), (__vector unsigned char)b.raw());
+   return SIMD_4x32((__vector unsigned int)vec_sel(r, zero, mask));
+
+#else
+   #error "No shuffle implementation available"
+#endif
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) alignr8(SIMD_4x32 a, SIMD_4x32 b)
+   {
+#if defined(BOTAN_SIMD_USE_SSE2)
+   return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 8));
+#elif defined(BOTAN_SIMD_USE_NEON)
+   return SIMD_4x32(vextq_u32(b.raw(), a.raw(), 2));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+   const __vector unsigned char mask = {8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23};
+   return SIMD_4x32(vec_perm(b.raw(), a.raw(), mask));
+#else
+   #error "No alignr8 implementation available"
+#endif
+   }
+
+const SIMD_4x32 k_ipt1 = SIMD_4x32(0x5A2A7000, 0xC2B2E898, 0x52227808, 0xCABAE090);
+const SIMD_4x32 k_ipt2 = SIMD_4x32(0x317C4D00, 0x4C01307D, 0xB0FDCC81, 0xCD80B1FC);
+
+const SIMD_4x32 k_inv1 = SIMD_4x32(0x0D080180, 0x0E05060F, 0x0A0B0C02, 0x04070309);
+const SIMD_4x32 k_inv2 = SIMD_4x32(0x0F0B0780, 0x01040A06, 0x02050809, 0x030D0E0C);
+
+const SIMD_4x32 sb1u = SIMD_4x32(0xCB503E00, 0xB19BE18F, 0x142AF544, 0xA5DF7A6E);
+const SIMD_4x32 sb1t = SIMD_4x32(0xFAE22300, 0x3618D415, 0x0D2ED9EF, 0x3BF7CCC1);
+const SIMD_4x32 sbou = SIMD_4x32(0x6FBDC700, 0xD0D26D17, 0xC502A878, 0x15AABF7A);
+const SIMD_4x32 sbot = SIMD_4x32(0x5FBB6A00, 0xCFE474A5, 0x412B35FA, 0x8E1E90D1);
+
+const SIMD_4x32 sboud = SIMD_4x32(0x7EF94000, 0x1387EA53, 0xD4943E2D, 0xC7AA6DB9);
+const SIMD_4x32 sbotd = SIMD_4x32(0x93441D00, 0x12D7560F, 0xD8C58E9C, 0xCA4B8159);
+
+const SIMD_4x32 mc_forward[4] = {
+   SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D),
+   SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201),
+   SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605),
+   SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09)
+};
+
+const SIMD_4x32 vperm_sr[4] = {
+   SIMD_4x32(0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C),
+   SIMD_4x32(0x0F0A0500, 0x030E0904, 0x07020D08, 0x0B06010C),
+   SIMD_4x32(0x0B020900, 0x0F060D04, 0x030A0108, 0x070E050C),
+   SIMD_4x32(0x070A0D00, 0x0B0E0104, 0x0F020508, 0x0306090C),
+};
+
+const SIMD_4x32 rcon[10] = {
+   SIMD_4x32(0x00000070, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x0000002A, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x00000098, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x00000008, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x0000004D, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x0000007C, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x0000007D, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x00000081, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x0000001F, 0x00000000, 0x00000000, 0x00000000),
+   SIMD_4x32(0x00000083, 0x00000000, 0x00000000, 0x00000000),
+};
+
+const SIMD_4x32 sb2u = SIMD_4x32(0x0B712400, 0xE27A93C6, 0xBC982FCD, 0x5EB7E955);
+const SIMD_4x32 sb2t = SIMD_4x32(0x0AE12900, 0x69EB8840, 0xAB82234A, 0xC2A163C8);
+
+const SIMD_4x32 k_dipt1 = SIMD_4x32(0x0B545F00, 0x0F505B04, 0x114E451A, 0x154A411E);
+const SIMD_4x32 k_dipt2 = SIMD_4x32(0x60056500, 0x86E383E6, 0xF491F194, 0x12771772);
+
+const SIMD_4x32 sb9u = SIMD_4x32(0x9A86D600, 0x851C0353, 0x4F994CC9, 0xCAD51F50);
+const SIMD_4x32 sb9t = SIMD_4x32(0xECD74900, 0xC03B1789, 0xB2FBA565, 0x725E2C9E);
+
+const SIMD_4x32 sbeu = SIMD_4x32(0x26D4D000, 0x46F29296, 0x64B4F6B0, 0x22426004);
+const SIMD_4x32 sbet = SIMD_4x32(0xFFAAC100, 0x0C55A6CD, 0x98593E32, 0x9467F36B);
+
+const SIMD_4x32 sbdu = SIMD_4x32(0xE6B1A200, 0x7D57CCDF, 0x882A4439, 0xF56E9B13);
+const SIMD_4x32 sbdt = SIMD_4x32(0x24C6CB00, 0x3CE2FAF7, 0x15DEEFD3, 0x2931180D);
+
+const SIMD_4x32 sbbu = SIMD_4x32(0x96B44200, 0xD0226492, 0xB0F2D404, 0x602646F6);
+const SIMD_4x32 sbbt = SIMD_4x32(0xCD596700, 0xC19498A6, 0x3255AA6B, 0xF3FF0C3E);
+
+const SIMD_4x32 mcx[4] = {
+   SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09),
+   SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605),
+   SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201),
+   SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D),
+};
+
+const SIMD_4x32 mc_backward[4] = {
+   SIMD_4x32(0x02010003, 0x06050407, 0x0A09080B, 0x0E0D0C0F),
+   SIMD_4x32(0x0E0D0C0F, 0x02010003, 0x06050407, 0x0A09080B),
+   SIMD_4x32(0x0A09080B, 0x0E0D0C0F, 0x02010003, 0x06050407),
+   SIMD_4x32(0x06050407, 0x0A09080B, 0x0E0D0C0F, 0x02010003),
+};
+
+const SIMD_4x32 lo_nibs_mask = SIMD_4x32::splat_u8(0x0F);
+
+inline SIMD_4x32 low_nibs(SIMD_4x32 x)
+   {
+   return lo_nibs_mask & x;
+   }
+
+inline SIMD_4x32 high_nibs(SIMD_4x32 x)
+   {
+   return (x.shr<4>() & lo_nibs_mask);
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_first_round(SIMD_4x32 B, SIMD_4x32 K)
+   {
+   return shuffle(k_ipt1, low_nibs(B)) ^ shuffle(k_ipt2, high_nibs(B)) ^ K;
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_round(SIMD_4x32 B, SIMD_4x32 K, size_t r)
+   {
+   const SIMD_4x32 Bh = high_nibs(B);
+   SIMD_4x32 Bl = low_nibs(B);
+   const SIMD_4x32 t2 = shuffle(k_inv2, Bl);
+   Bl ^= Bh;
+
+   const SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh));
+   const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl));
+
+   const SIMD_4x32 t7 = shuffle(sb1t, t6) ^ shuffle(sb1u, t5) ^ K;
+   const SIMD_4x32 t8 = shuffle(sb2t, t6) ^ shuffle(sb2u, t5) ^ shuffle(t7, mc_forward[r % 4]);
+
+   return shuffle(t8, mc_forward[r % 4]) ^ shuffle(t7, mc_backward[r % 4]) ^ t8;
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r)
+   {
+   const SIMD_4x32 Bh = high_nibs(B);
+   SIMD_4x32 Bl = low_nibs(B);
+   const SIMD_4x32 t2 = shuffle(k_inv2, Bl);
+   Bl ^= Bh;
+
+   const SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh));
+   const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl));
+
+   return shuffle(shuffle(sbou, t5) ^ shuffle(sbot, t6) ^ K, vperm_sr[r % 4]);
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_first_round(SIMD_4x32 B, SIMD_4x32 K)
+   {
+   return shuffle(k_dipt1, low_nibs(B)) ^ shuffle(k_dipt2, high_nibs(B)) ^ K;
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_round(SIMD_4x32 B, SIMD_4x32 K, size_t r)
+   {
+   const SIMD_4x32 Bh = high_nibs(B);
+   B = low_nibs(B);
+   const SIMD_4x32 t2 = shuffle(k_inv2, B);
+
+   B ^= Bh;
+
+   const SIMD_4x32 t5 = B ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh));
+   const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, B));
+
+   const SIMD_4x32 mc = mcx[(r-1)%4];
+
+   const SIMD_4x32 t8 = shuffle(sb9t, t6) ^ shuffle(sb9u, t5) ^ K;
+   const SIMD_4x32 t9 = shuffle(t8, mc) ^ shuffle(sbdu, t5) ^ shuffle(sbdt, t6);
+   const SIMD_4x32 t12 = shuffle(t9, mc) ^ shuffle(sbbu, t5) ^ shuffle(sbbt, t6);
+   return shuffle(t12, mc) ^ shuffle(sbeu, t5) ^ shuffle(sbet, t6);
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r)
+   {
+   const uint32_t which_sr = ((((r - 1) << 4) ^ 48) & 48) / 16;
+
+   const SIMD_4x32 Bh = high_nibs(B);
+   B = low_nibs(B);
+   const SIMD_4x32 t2 = shuffle(k_inv2, B);
+
+   B ^= Bh;
+
+   const SIMD_4x32 t5 = B ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh));
+   const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, B));
+
+   const SIMD_4x32 x = shuffle(sboud, t5) ^ shuffle(sbotd, t6) ^ K;
+   return shuffle(x, vperm_sr[which_sr]);
+   }
+
+void BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+   vperm_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks,
+                        const SIMD_4x32 K[], size_t rounds)
+   {
+   CT::poison(in, blocks * 16);
+
+   const size_t blocks2 = blocks - (blocks % 2);
+
+   for(size_t i = 0; i != blocks2; i += 2)
+      {
+      SIMD_4x32 B0 = SIMD_4x32::load_le(in + i*16);
+      SIMD_4x32 B1 = SIMD_4x32::load_le(in + (i+1)*16);
+
+      B0 = aes_enc_first_round(B0, K[0]);
+      B1 = aes_enc_first_round(B1, K[0]);
+
+      for(size_t r = 1; r != rounds; ++r)
+         {
+         B0 = aes_enc_round(B0, K[r], r);
+         B1 = aes_enc_round(B1, K[r], r);
+         }
+
+      B0 = aes_enc_last_round(B0, K[rounds], rounds);
+      B1 = aes_enc_last_round(B1, K[rounds], rounds);
+
+      B0.store_le(out + i*16);
+      B1.store_le(out + (i+1)*16);
+      }
+
+   for(size_t i = blocks2; i < blocks; ++i)
+      {
+      SIMD_4x32 B = SIMD_4x32::load_le(in + i*16); // ???
+
+      B = aes_enc_first_round(B, K[0]);
+
+      for(size_t r = 1; r != rounds; ++r)
+         {
+         B = aes_enc_round(B, K[r], r);
+         }
+
+      B = aes_enc_last_round(B, K[rounds], rounds);
+      B.store_le(out + i*16);
+      }
+
+   CT::unpoison(in,  blocks * 16);
+   CT::unpoison(out, blocks * 16);
+   }
+
+void BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+   vperm_decrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks,
+                        const SIMD_4x32 K[], size_t rounds)
+   {
+   CT::poison(in, blocks * 16);
+
+   const size_t blocks2 = blocks - (blocks % 2);
+
+   for(size_t i = 0; i != blocks2; i += 2)
+      {
+      SIMD_4x32 B0 = SIMD_4x32::load_le(in + i*16);
+      SIMD_4x32 B1 = SIMD_4x32::load_le(in + (i+1)*16);
+
+      B0 = aes_dec_first_round(B0, K[0]);
+      B1 = aes_dec_first_round(B1, K[0]);
+
+      for(size_t r = 1; r != rounds; ++r)
+         {
+         B0 = aes_dec_round(B0, K[r], r);
+         B1 = aes_dec_round(B1, K[r], r);
+         }
+
+      B0 = aes_dec_last_round(B0, K[rounds], rounds);
+      B1 = aes_dec_last_round(B1, K[rounds], rounds);
+
+      B0.store_le(out + i*16);
+      B1.store_le(out + (i+1)*16);
+      }
+
+   for(size_t i = blocks2; i < blocks; ++i)
+      {
+      SIMD_4x32 B = SIMD_4x32::load_le(in + i*16); // ???
+
+      B = aes_dec_first_round(B, K[0]);
+
+      for(size_t r = 1; r != rounds; ++r)
+         {
+         B = aes_dec_round(B, K[r], r);
+         }
+
+      B = aes_dec_last_round(B, K[rounds], rounds);
+      B.store_le(out + i*16);
+      }
+
+   CT::unpoison(in,  blocks * 16);
+   CT::unpoison(out, blocks * 16);
+   }
+
+}
+
+void AES_128::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[11] = {
+      SIMD_4x32(&m_EK[4* 0]), SIMD_4x32(&m_EK[4* 1]), SIMD_4x32(&m_EK[4* 2]),
+      SIMD_4x32(&m_EK[4* 3]), SIMD_4x32(&m_EK[4* 4]), SIMD_4x32(&m_EK[4* 5]),
+      SIMD_4x32(&m_EK[4* 6]), SIMD_4x32(&m_EK[4* 7]), SIMD_4x32(&m_EK[4* 8]),
+      SIMD_4x32(&m_EK[4* 9]), SIMD_4x32(&m_EK[4*10]),
+   };
+
+   return vperm_encrypt_blocks(in, out, blocks, K, 10);
+   }
+
+void AES_128::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[11] = {
+      SIMD_4x32(&m_DK[4* 0]), SIMD_4x32(&m_DK[4* 1]), SIMD_4x32(&m_DK[4* 2]),
+      SIMD_4x32(&m_DK[4* 3]), SIMD_4x32(&m_DK[4* 4]), SIMD_4x32(&m_DK[4* 5]),
+      SIMD_4x32(&m_DK[4* 6]), SIMD_4x32(&m_DK[4* 7]), SIMD_4x32(&m_DK[4* 8]),
+      SIMD_4x32(&m_DK[4* 9]), SIMD_4x32(&m_DK[4*10]),
+   };
+
+   return vperm_decrypt_blocks(in, out, blocks, K, 10);
+   }
+
+void AES_192::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[13] = {
+      SIMD_4x32(&m_EK[4* 0]), SIMD_4x32(&m_EK[4* 1]), SIMD_4x32(&m_EK[4* 2]),
+      SIMD_4x32(&m_EK[4* 3]), SIMD_4x32(&m_EK[4* 4]), SIMD_4x32(&m_EK[4* 5]),
+      SIMD_4x32(&m_EK[4* 6]), SIMD_4x32(&m_EK[4* 7]), SIMD_4x32(&m_EK[4* 8]),
+      SIMD_4x32(&m_EK[4* 9]), SIMD_4x32(&m_EK[4*10]), SIMD_4x32(&m_EK[4*11]),
+      SIMD_4x32(&m_EK[4*12]),
+   };
+
+   return vperm_encrypt_blocks(in, out, blocks, K, 12);
+   }
+
+void AES_192::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[13] = {
+      SIMD_4x32(&m_DK[4* 0]), SIMD_4x32(&m_DK[4* 1]), SIMD_4x32(&m_DK[4* 2]),
+      SIMD_4x32(&m_DK[4* 3]), SIMD_4x32(&m_DK[4* 4]), SIMD_4x32(&m_DK[4* 5]),
+      SIMD_4x32(&m_DK[4* 6]), SIMD_4x32(&m_DK[4* 7]), SIMD_4x32(&m_DK[4* 8]),
+      SIMD_4x32(&m_DK[4* 9]), SIMD_4x32(&m_DK[4*10]), SIMD_4x32(&m_DK[4*11]),
+      SIMD_4x32(&m_DK[4*12]),
+   };
+
+   return vperm_decrypt_blocks(in, out, blocks, K, 12);
+   }
+
+void AES_256::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[15] = {
+      SIMD_4x32(&m_EK[4* 0]), SIMD_4x32(&m_EK[4* 1]), SIMD_4x32(&m_EK[4* 2]),
+      SIMD_4x32(&m_EK[4* 3]), SIMD_4x32(&m_EK[4* 4]), SIMD_4x32(&m_EK[4* 5]),
+      SIMD_4x32(&m_EK[4* 6]), SIMD_4x32(&m_EK[4* 7]), SIMD_4x32(&m_EK[4* 8]),
+      SIMD_4x32(&m_EK[4* 9]), SIMD_4x32(&m_EK[4*10]), SIMD_4x32(&m_EK[4*11]),
+      SIMD_4x32(&m_EK[4*12]), SIMD_4x32(&m_EK[4*13]), SIMD_4x32(&m_EK[4*14]),
+   };
+
+   return vperm_encrypt_blocks(in, out, blocks, K, 14);
+   }
+
+void AES_256::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const SIMD_4x32 K[15] = {
+      SIMD_4x32(&m_DK[4* 0]), SIMD_4x32(&m_DK[4* 1]), SIMD_4x32(&m_DK[4* 2]),
+      SIMD_4x32(&m_DK[4* 3]), SIMD_4x32(&m_DK[4* 4]), SIMD_4x32(&m_DK[4* 5]),
+      SIMD_4x32(&m_DK[4* 6]), SIMD_4x32(&m_DK[4* 7]), SIMD_4x32(&m_DK[4* 8]),
+      SIMD_4x32(&m_DK[4* 9]), SIMD_4x32(&m_DK[4*10]), SIMD_4x32(&m_DK[4*11]),
+      SIMD_4x32(&m_DK[4*12]), SIMD_4x32(&m_DK[4*13]), SIMD_4x32(&m_DK[4*14]),
+   };
+
+   return vperm_decrypt_blocks(in, out, blocks, K, 14);
+   }
+
+namespace {
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+   aes_schedule_transform(SIMD_4x32 input,
+                          SIMD_4x32 table_1,
+                          SIMD_4x32 table_2)
+   {
+   return shuffle(table_1, low_nibs(input)) ^ shuffle(table_2, high_nibs(input));
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle(SIMD_4x32 k, uint8_t round_no)
+   {
+   const SIMD_4x32 mc_forward0(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D);
+
+   SIMD_4x32 t = shuffle(k ^ SIMD_4x32::splat_u8(0x5B), mc_forward0);
+   SIMD_4x32 t2 = t;
+   t = shuffle(t, mc_forward0);
+   t2 = t ^ t2 ^ shuffle(t, mc_forward0);
+   return shuffle(t2, vperm_sr[round_no % 4]);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_dec(SIMD_4x32 k, uint8_t round_no)
+   {
+   const SIMD_4x32 mc_forward0(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D);
+
+   const SIMD_4x32 dsk[8] = {
+      SIMD_4x32(0x7ED9A700, 0xB6116FC8, 0x82255BFC, 0x4AED9334),
+      SIMD_4x32(0x27143300, 0x45765162, 0xE9DAFDCE, 0x8BB89FAC),
+      SIMD_4x32(0xCCA86400, 0x27438FEB, 0xADC90561, 0x4622EE8A),
+      SIMD_4x32(0x4F92DD00, 0x815C13CE, 0xBD602FF2, 0x73AEE13C),
+      SIMD_4x32(0x01C6C700, 0x03C4C502, 0xFA3D3CFB, 0xF83F3EF9),
+      SIMD_4x32(0x38CFF700, 0xEE1921D6, 0x7384BC4B, 0xA5526A9D),
+      SIMD_4x32(0x53732000, 0xE3C390B0, 0x10306343, 0xA080D3F3),
+      SIMD_4x32(0x036982E8, 0xA0CA214B, 0x8CE60D67, 0x2F45AEC4),
+   };
+
+   SIMD_4x32 t = aes_schedule_transform(k, dsk[0], dsk[1]);
+   SIMD_4x32 output = shuffle(t, mc_forward0);
+
+   t = aes_schedule_transform(t, dsk[2], dsk[3]);
+   output = shuffle(t ^ output, mc_forward0);
+
+   t = aes_schedule_transform(t, dsk[4], dsk[5]);
+   output = shuffle(t ^ output, mc_forward0);
+
+   t = aes_schedule_transform(t, dsk[6], dsk[7]);
+   output = shuffle(t ^ output, mc_forward0);
+
+   return shuffle(output, vperm_sr[round_no % 4]);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_last(SIMD_4x32 k, uint8_t round_no)
+   {
+   const SIMD_4x32 out_tr1(0xD6B66000, 0xFF9F4929, 0xDEBE6808, 0xF7974121);
+   const SIMD_4x32 out_tr2(0x50BCEC00, 0x01EDBD51, 0xB05C0CE0, 0xE10D5DB1);
+
+   k = shuffle(k, vperm_sr[round_no % 4]);
+   k ^= SIMD_4x32::splat_u8(0x5B);
+   return aes_schedule_transform(k, out_tr1, out_tr2);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_last_dec(SIMD_4x32 k)
+   {
+   const SIMD_4x32 deskew1(0x47A4E300, 0x07E4A340, 0x5DBEF91A, 0x1DFEB95A);
+   const SIMD_4x32 deskew2(0x83EA6900, 0x5F36B5DC, 0xF49D1E77, 0x2841C2AB);
+
+   k ^= SIMD_4x32::splat_u8(0x5B);
+   return aes_schedule_transform(k, deskew1, deskew2);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_round(SIMD_4x32 input1, SIMD_4x32 input2)
+   {
+   SIMD_4x32 smeared = input2 ^ input2.shift_elems_left<1>();
+   smeared ^= smeared.shift_elems_left<2>();
+   smeared ^= SIMD_4x32::splat_u8(0x5B);
+
+   const SIMD_4x32 Bh = high_nibs(input1);
+   SIMD_4x32 Bl = low_nibs(input1);
+
+   const SIMD_4x32 t2 = shuffle(k_inv2, Bl);
+
+   Bl ^= Bh;
+
+   SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh));
+   SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl));
+
+   return smeared ^ shuffle(sb1u, t5) ^ shuffle(sb1t, t6);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_round(SIMD_4x32 rc, SIMD_4x32 input1, SIMD_4x32 input2)
+   {
+   // This byte shuffle is equivalent to alignr<1>(shuffle32(input1, (3,3,3,3)));
+   const SIMD_4x32 shuffle3333_15 = SIMD_4x32::splat(0x0C0F0E0D);
+   return aes_schedule_round(shuffle(input1, shuffle3333_15), input2 ^ rc);
+   }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_192_smear(SIMD_4x32 x, SIMD_4x32 y)
+   {
+   const SIMD_4x32 shuffle3332 =
+      SIMD_4x32(0x0B0A0908, 0x0F0E0D0C, 0x0F0E0D0C, 0x0F0E0D0C);
+   const SIMD_4x32 shuffle2000 =
+      SIMD_4x32(0x03020100, 0x03020100, 0x03020100, 0x0B0A0908);
+
+   const SIMD_4x32 zero_top_half(0, 0, 0xFFFFFFFF, 0xFFFFFFFF);
+   y &= zero_top_half;
+   return y ^ shuffle(x, shuffle3332) ^ shuffle(y, shuffle2000);
+   }
+
+}
+
+void AES_128::vperm_key_schedule(const uint8_t keyb[], size_t)
+   {
+   m_EK.resize(11*4);
+   m_DK.resize(11*4);
+
+   SIMD_4x32 key = SIMD_4x32::load_le(keyb);
+
+   shuffle(key, vperm_sr[2]).store_le(&m_DK[4*10]);
+
+   key = aes_schedule_transform(key, k_ipt1, k_ipt2);
+   key.store_le(&m_EK[0]);
+
+   for(size_t i = 1; i != 10; ++i)
+      {
+      key = aes_schedule_round(rcon[i-1], key, key);
+
+      aes_schedule_mangle(key, (12-i) % 4).store_le(&m_EK[4*i]);
+
+      aes_schedule_mangle_dec(key, (10-i)%4).store_le(&m_DK[4*(10-i)]);
+      }
+
+   key = aes_schedule_round(rcon[9], key, key);
+   aes_schedule_mangle_last(key, 2).store_le(&m_EK[4*10]);
+   aes_schedule_mangle_last_dec(key).store_le(&m_DK[0]);
+   }
+
+void AES_192::vperm_key_schedule(const uint8_t keyb[], size_t)
+   {
+   m_EK.resize(13*4);
+   m_DK.resize(13*4);
+
+   SIMD_4x32 key1 = SIMD_4x32::load_le(keyb);
+   SIMD_4x32 key2 = SIMD_4x32::load_le(keyb + 8);
+
+   shuffle(key1, vperm_sr[0]).store_le(&m_DK[12*4]);
+
+   key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2);
+   key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2);
+
+   key1.store_le(&m_EK[0]);
+
+   for(size_t i = 0; i != 4; ++i)
+      {
+      // key2 with 8 high bytes masked off
+      SIMD_4x32 t = key2;
+      key2 = aes_schedule_round(rcon[2*i], key2, key1);
+      const SIMD_4x32 key2t = alignr8(key2, t);
+      aes_schedule_mangle(key2t, (i+3)%4).store_le(&m_EK[4*(3*i+1)]);
+      aes_schedule_mangle_dec(key2t, (i+3)%4).store_le(&m_DK[4*(11-3*i)]);
+
+      t = aes_schedule_192_smear(key2, t);
+
+      aes_schedule_mangle(t, (i+2)%4).store_le(&m_EK[4*(3*i+2)]);
+      aes_schedule_mangle_dec(t, (i+2)%4).store_le(&m_DK[4*(10-3*i)]);
+
+      key2 = aes_schedule_round(rcon[2*i+1], t, key2);
+
+      if(i == 3)
+         {
+         aes_schedule_mangle_last(key2, (i+1)%4).store_le(&m_EK[4*(3*i+3)]);
+         aes_schedule_mangle_last_dec(key2).store_le(&m_DK[4*(9-3*i)]);
+         }
+      else
+         {
+         aes_schedule_mangle(key2, (i+1)%4).store_le(&m_EK[4*(3*i+3)]);
+         aes_schedule_mangle_dec(key2, (i+1)%4).store_le(&m_DK[4*(9-3*i)]);
+         }
+
+      key1 = key2;
+      key2 = aes_schedule_192_smear(key2, t);
+      }
+   }
+
+void AES_256::vperm_key_schedule(const uint8_t keyb[], size_t)
+   {
+   m_EK.resize(15*4);
+   m_DK.resize(15*4);
+
+   SIMD_4x32 key1 = SIMD_4x32::load_le(keyb);
+   SIMD_4x32 key2 = SIMD_4x32::load_le(keyb + 16);
+
+   shuffle(key1, vperm_sr[2]).store_le(&m_DK[4*14]);
+
+   key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2);
+   key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2);
+
+   key1.store_le(&m_EK[0]);
+   aes_schedule_mangle(key2, 3).store_le(&m_EK[4]);
+
+   aes_schedule_mangle_dec(key2, 1).store_le(&m_DK[4*13]);
+
+   const SIMD_4x32 shuffle3333 = SIMD_4x32::splat(0x0F0E0D0C);
+
+   for(size_t i = 2; i != 14; i += 2)
+      {
+      const SIMD_4x32 k_t = key2;
+      key1 = key2 = aes_schedule_round(rcon[(i/2)-1], key2, key1);
+
+      aes_schedule_mangle(key2, i % 4).store_le(&m_EK[4*i]);
+      aes_schedule_mangle_dec(key2, (i+2)%4).store_le(&m_DK[4*(14-i)]);
+
+      key2 = aes_schedule_round(shuffle(key2, shuffle3333), k_t);
+
+      aes_schedule_mangle(key2, (i-1)%4).store_le(&m_EK[4*(i+1)]);
+      aes_schedule_mangle_dec(key2, (i+1)%4).store_le(&m_DK[4*(13-i)]);
+      }
+
+   key2 = aes_schedule_round(rcon[6], key2, key1);
+
+   aes_schedule_mangle_last(key2, 2).store_le(&m_EK[4*14]);
+   aes_schedule_mangle_last_dec(key2).store_le(&m_DK[0]);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/aria/aria.cpp b/third_party/botan/src/lib/block/aria/aria.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aria/aria.cpp
@@ -0,0 +1,506 @@
+/*
+* ARIA
+* Adapted for Botan by Jeffrey Walton, public domain
+*
+* Further changes
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*
+* This ARIA implementation is based on the 32-bit implementation by Aaram Yun from the
+* National Security Research Institute, KOREA. Aaram Yun's implementation is based on
+* the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from
+* the Korea Internet & Security Agency website.
+* <A HREF="https://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
+* <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
+* Internet & Security Agency homepage</A>
+*/
+
+#include <botan/aria.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+namespace ARIA_F {
+
+alignas(16)
+const uint32_t S1[256]={
+   0x00636363,0x007c7c7c,0x00777777,0x007b7b7b,0x00f2f2f2,0x006b6b6b,0x006f6f6f,0x00c5c5c5,
+   0x00303030,0x00010101,0x00676767,0x002b2b2b,0x00fefefe,0x00d7d7d7,0x00ababab,0x00767676,
+   0x00cacaca,0x00828282,0x00c9c9c9,0x007d7d7d,0x00fafafa,0x00595959,0x00474747,0x00f0f0f0,
+   0x00adadad,0x00d4d4d4,0x00a2a2a2,0x00afafaf,0x009c9c9c,0x00a4a4a4,0x00727272,0x00c0c0c0,
+   0x00b7b7b7,0x00fdfdfd,0x00939393,0x00262626,0x00363636,0x003f3f3f,0x00f7f7f7,0x00cccccc,
+   0x00343434,0x00a5a5a5,0x00e5e5e5,0x00f1f1f1,0x00717171,0x00d8d8d8,0x00313131,0x00151515,
+   0x00040404,0x00c7c7c7,0x00232323,0x00c3c3c3,0x00181818,0x00969696,0x00050505,0x009a9a9a,
+   0x00070707,0x00121212,0x00808080,0x00e2e2e2,0x00ebebeb,0x00272727,0x00b2b2b2,0x00757575,
+   0x00090909,0x00838383,0x002c2c2c,0x001a1a1a,0x001b1b1b,0x006e6e6e,0x005a5a5a,0x00a0a0a0,
+   0x00525252,0x003b3b3b,0x00d6d6d6,0x00b3b3b3,0x00292929,0x00e3e3e3,0x002f2f2f,0x00848484,
+   0x00535353,0x00d1d1d1,0x00000000,0x00ededed,0x00202020,0x00fcfcfc,0x00b1b1b1,0x005b5b5b,
+   0x006a6a6a,0x00cbcbcb,0x00bebebe,0x00393939,0x004a4a4a,0x004c4c4c,0x00585858,0x00cfcfcf,
+   0x00d0d0d0,0x00efefef,0x00aaaaaa,0x00fbfbfb,0x00434343,0x004d4d4d,0x00333333,0x00858585,
+   0x00454545,0x00f9f9f9,0x00020202,0x007f7f7f,0x00505050,0x003c3c3c,0x009f9f9f,0x00a8a8a8,
+   0x00515151,0x00a3a3a3,0x00404040,0x008f8f8f,0x00929292,0x009d9d9d,0x00383838,0x00f5f5f5,
+   0x00bcbcbc,0x00b6b6b6,0x00dadada,0x00212121,0x00101010,0x00ffffff,0x00f3f3f3,0x00d2d2d2,
+   0x00cdcdcd,0x000c0c0c,0x00131313,0x00ececec,0x005f5f5f,0x00979797,0x00444444,0x00171717,
+   0x00c4c4c4,0x00a7a7a7,0x007e7e7e,0x003d3d3d,0x00646464,0x005d5d5d,0x00191919,0x00737373,
+   0x00606060,0x00818181,0x004f4f4f,0x00dcdcdc,0x00222222,0x002a2a2a,0x00909090,0x00888888,
+   0x00464646,0x00eeeeee,0x00b8b8b8,0x00141414,0x00dedede,0x005e5e5e,0x000b0b0b,0x00dbdbdb,
+   0x00e0e0e0,0x00323232,0x003a3a3a,0x000a0a0a,0x00494949,0x00060606,0x00242424,0x005c5c5c,
+   0x00c2c2c2,0x00d3d3d3,0x00acacac,0x00626262,0x00919191,0x00959595,0x00e4e4e4,0x00797979,
+   0x00e7e7e7,0x00c8c8c8,0x00373737,0x006d6d6d,0x008d8d8d,0x00d5d5d5,0x004e4e4e,0x00a9a9a9,
+   0x006c6c6c,0x00565656,0x00f4f4f4,0x00eaeaea,0x00656565,0x007a7a7a,0x00aeaeae,0x00080808,
+   0x00bababa,0x00787878,0x00252525,0x002e2e2e,0x001c1c1c,0x00a6a6a6,0x00b4b4b4,0x00c6c6c6,
+   0x00e8e8e8,0x00dddddd,0x00747474,0x001f1f1f,0x004b4b4b,0x00bdbdbd,0x008b8b8b,0x008a8a8a,
+   0x00707070,0x003e3e3e,0x00b5b5b5,0x00666666,0x00484848,0x00030303,0x00f6f6f6,0x000e0e0e,
+   0x00616161,0x00353535,0x00575757,0x00b9b9b9,0x00868686,0x00c1c1c1,0x001d1d1d,0x009e9e9e,
+   0x00e1e1e1,0x00f8f8f8,0x00989898,0x00111111,0x00696969,0x00d9d9d9,0x008e8e8e,0x00949494,
+   0x009b9b9b,0x001e1e1e,0x00878787,0x00e9e9e9,0x00cecece,0x00555555,0x00282828,0x00dfdfdf,
+   0x008c8c8c,0x00a1a1a1,0x00898989,0x000d0d0d,0x00bfbfbf,0x00e6e6e6,0x00424242,0x00686868,
+   0x00414141,0x00999999,0x002d2d2d,0x000f0f0f,0x00b0b0b0,0x00545454,0x00bbbbbb,0x00161616
+};
+
+alignas(16)
+const uint32_t S2[256]={
+   0xe200e2e2,0x4e004e4e,0x54005454,0xfc00fcfc,0x94009494,0xc200c2c2,0x4a004a4a,0xcc00cccc,
+   0x62006262,0x0d000d0d,0x6a006a6a,0x46004646,0x3c003c3c,0x4d004d4d,0x8b008b8b,0xd100d1d1,
+   0x5e005e5e,0xfa00fafa,0x64006464,0xcb00cbcb,0xb400b4b4,0x97009797,0xbe00bebe,0x2b002b2b,
+   0xbc00bcbc,0x77007777,0x2e002e2e,0x03000303,0xd300d3d3,0x19001919,0x59005959,0xc100c1c1,
+   0x1d001d1d,0x06000606,0x41004141,0x6b006b6b,0x55005555,0xf000f0f0,0x99009999,0x69006969,
+   0xea00eaea,0x9c009c9c,0x18001818,0xae00aeae,0x63006363,0xdf00dfdf,0xe700e7e7,0xbb00bbbb,
+   0x00000000,0x73007373,0x66006666,0xfb00fbfb,0x96009696,0x4c004c4c,0x85008585,0xe400e4e4,
+   0x3a003a3a,0x09000909,0x45004545,0xaa00aaaa,0x0f000f0f,0xee00eeee,0x10001010,0xeb00ebeb,
+   0x2d002d2d,0x7f007f7f,0xf400f4f4,0x29002929,0xac00acac,0xcf00cfcf,0xad00adad,0x91009191,
+   0x8d008d8d,0x78007878,0xc800c8c8,0x95009595,0xf900f9f9,0x2f002f2f,0xce00cece,0xcd00cdcd,
+   0x08000808,0x7a007a7a,0x88008888,0x38003838,0x5c005c5c,0x83008383,0x2a002a2a,0x28002828,
+   0x47004747,0xdb00dbdb,0xb800b8b8,0xc700c7c7,0x93009393,0xa400a4a4,0x12001212,0x53005353,
+   0xff00ffff,0x87008787,0x0e000e0e,0x31003131,0x36003636,0x21002121,0x58005858,0x48004848,
+   0x01000101,0x8e008e8e,0x37003737,0x74007474,0x32003232,0xca00caca,0xe900e9e9,0xb100b1b1,
+   0xb700b7b7,0xab00abab,0x0c000c0c,0xd700d7d7,0xc400c4c4,0x56005656,0x42004242,0x26002626,
+   0x07000707,0x98009898,0x60006060,0xd900d9d9,0xb600b6b6,0xb900b9b9,0x11001111,0x40004040,
+   0xec00ecec,0x20002020,0x8c008c8c,0xbd00bdbd,0xa000a0a0,0xc900c9c9,0x84008484,0x04000404,
+   0x49004949,0x23002323,0xf100f1f1,0x4f004f4f,0x50005050,0x1f001f1f,0x13001313,0xdc00dcdc,
+   0xd800d8d8,0xc000c0c0,0x9e009e9e,0x57005757,0xe300e3e3,0xc300c3c3,0x7b007b7b,0x65006565,
+   0x3b003b3b,0x02000202,0x8f008f8f,0x3e003e3e,0xe800e8e8,0x25002525,0x92009292,0xe500e5e5,
+   0x15001515,0xdd00dddd,0xfd00fdfd,0x17001717,0xa900a9a9,0xbf00bfbf,0xd400d4d4,0x9a009a9a,
+   0x7e007e7e,0xc500c5c5,0x39003939,0x67006767,0xfe00fefe,0x76007676,0x9d009d9d,0x43004343,
+   0xa700a7a7,0xe100e1e1,0xd000d0d0,0xf500f5f5,0x68006868,0xf200f2f2,0x1b001b1b,0x34003434,
+   0x70007070,0x05000505,0xa300a3a3,0x8a008a8a,0xd500d5d5,0x79007979,0x86008686,0xa800a8a8,
+   0x30003030,0xc600c6c6,0x51005151,0x4b004b4b,0x1e001e1e,0xa600a6a6,0x27002727,0xf600f6f6,
+   0x35003535,0xd200d2d2,0x6e006e6e,0x24002424,0x16001616,0x82008282,0x5f005f5f,0xda00dada,
+   0xe600e6e6,0x75007575,0xa200a2a2,0xef00efef,0x2c002c2c,0xb200b2b2,0x1c001c1c,0x9f009f9f,
+   0x5d005d5d,0x6f006f6f,0x80008080,0x0a000a0a,0x72007272,0x44004444,0x9b009b9b,0x6c006c6c,
+   0x90009090,0x0b000b0b,0x5b005b5b,0x33003333,0x7d007d7d,0x5a005a5a,0x52005252,0xf300f3f3,
+   0x61006161,0xa100a1a1,0xf700f7f7,0xb000b0b0,0xd600d6d6,0x3f003f3f,0x7c007c7c,0x6d006d6d,
+   0xed00eded,0x14001414,0xe000e0e0,0xa500a5a5,0x3d003d3d,0x22002222,0xb300b3b3,0xf800f8f8,
+   0x89008989,0xde00dede,0x71007171,0x1a001a1a,0xaf00afaf,0xba00baba,0xb500b5b5,0x81008181
+};
+
+alignas(16)
+const uint32_t X1[256]={
+   0x52520052,0x09090009,0x6a6a006a,0xd5d500d5,0x30300030,0x36360036,0xa5a500a5,0x38380038,
+   0xbfbf00bf,0x40400040,0xa3a300a3,0x9e9e009e,0x81810081,0xf3f300f3,0xd7d700d7,0xfbfb00fb,
+   0x7c7c007c,0xe3e300e3,0x39390039,0x82820082,0x9b9b009b,0x2f2f002f,0xffff00ff,0x87870087,
+   0x34340034,0x8e8e008e,0x43430043,0x44440044,0xc4c400c4,0xdede00de,0xe9e900e9,0xcbcb00cb,
+   0x54540054,0x7b7b007b,0x94940094,0x32320032,0xa6a600a6,0xc2c200c2,0x23230023,0x3d3d003d,
+   0xeeee00ee,0x4c4c004c,0x95950095,0x0b0b000b,0x42420042,0xfafa00fa,0xc3c300c3,0x4e4e004e,
+   0x08080008,0x2e2e002e,0xa1a100a1,0x66660066,0x28280028,0xd9d900d9,0x24240024,0xb2b200b2,
+   0x76760076,0x5b5b005b,0xa2a200a2,0x49490049,0x6d6d006d,0x8b8b008b,0xd1d100d1,0x25250025,
+   0x72720072,0xf8f800f8,0xf6f600f6,0x64640064,0x86860086,0x68680068,0x98980098,0x16160016,
+   0xd4d400d4,0xa4a400a4,0x5c5c005c,0xcccc00cc,0x5d5d005d,0x65650065,0xb6b600b6,0x92920092,
+   0x6c6c006c,0x70700070,0x48480048,0x50500050,0xfdfd00fd,0xeded00ed,0xb9b900b9,0xdada00da,
+   0x5e5e005e,0x15150015,0x46460046,0x57570057,0xa7a700a7,0x8d8d008d,0x9d9d009d,0x84840084,
+   0x90900090,0xd8d800d8,0xabab00ab,0x00000000,0x8c8c008c,0xbcbc00bc,0xd3d300d3,0x0a0a000a,
+   0xf7f700f7,0xe4e400e4,0x58580058,0x05050005,0xb8b800b8,0xb3b300b3,0x45450045,0x06060006,
+   0xd0d000d0,0x2c2c002c,0x1e1e001e,0x8f8f008f,0xcaca00ca,0x3f3f003f,0x0f0f000f,0x02020002,
+   0xc1c100c1,0xafaf00af,0xbdbd00bd,0x03030003,0x01010001,0x13130013,0x8a8a008a,0x6b6b006b,
+   0x3a3a003a,0x91910091,0x11110011,0x41410041,0x4f4f004f,0x67670067,0xdcdc00dc,0xeaea00ea,
+   0x97970097,0xf2f200f2,0xcfcf00cf,0xcece00ce,0xf0f000f0,0xb4b400b4,0xe6e600e6,0x73730073,
+   0x96960096,0xacac00ac,0x74740074,0x22220022,0xe7e700e7,0xadad00ad,0x35350035,0x85850085,
+   0xe2e200e2,0xf9f900f9,0x37370037,0xe8e800e8,0x1c1c001c,0x75750075,0xdfdf00df,0x6e6e006e,
+   0x47470047,0xf1f100f1,0x1a1a001a,0x71710071,0x1d1d001d,0x29290029,0xc5c500c5,0x89890089,
+   0x6f6f006f,0xb7b700b7,0x62620062,0x0e0e000e,0xaaaa00aa,0x18180018,0xbebe00be,0x1b1b001b,
+   0xfcfc00fc,0x56560056,0x3e3e003e,0x4b4b004b,0xc6c600c6,0xd2d200d2,0x79790079,0x20200020,
+   0x9a9a009a,0xdbdb00db,0xc0c000c0,0xfefe00fe,0x78780078,0xcdcd00cd,0x5a5a005a,0xf4f400f4,
+   0x1f1f001f,0xdddd00dd,0xa8a800a8,0x33330033,0x88880088,0x07070007,0xc7c700c7,0x31310031,
+   0xb1b100b1,0x12120012,0x10100010,0x59590059,0x27270027,0x80800080,0xecec00ec,0x5f5f005f,
+   0x60600060,0x51510051,0x7f7f007f,0xa9a900a9,0x19190019,0xb5b500b5,0x4a4a004a,0x0d0d000d,
+   0x2d2d002d,0xe5e500e5,0x7a7a007a,0x9f9f009f,0x93930093,0xc9c900c9,0x9c9c009c,0xefef00ef,
+   0xa0a000a0,0xe0e000e0,0x3b3b003b,0x4d4d004d,0xaeae00ae,0x2a2a002a,0xf5f500f5,0xb0b000b0,
+   0xc8c800c8,0xebeb00eb,0xbbbb00bb,0x3c3c003c,0x83830083,0x53530053,0x99990099,0x61610061,
+   0x17170017,0x2b2b002b,0x04040004,0x7e7e007e,0xbaba00ba,0x77770077,0xd6d600d6,0x26260026,
+   0xe1e100e1,0x69690069,0x14140014,0x63630063,0x55550055,0x21210021,0x0c0c000c,0x7d7d007d
+};
+
+alignas(16)
+const uint32_t X2[256]={
+   0x30303000,0x68686800,0x99999900,0x1b1b1b00,0x87878700,0xb9b9b900,0x21212100,0x78787800,
+   0x50505000,0x39393900,0xdbdbdb00,0xe1e1e100,0x72727200,0x09090900,0x62626200,0x3c3c3c00,
+   0x3e3e3e00,0x7e7e7e00,0x5e5e5e00,0x8e8e8e00,0xf1f1f100,0xa0a0a000,0xcccccc00,0xa3a3a300,
+   0x2a2a2a00,0x1d1d1d00,0xfbfbfb00,0xb6b6b600,0xd6d6d600,0x20202000,0xc4c4c400,0x8d8d8d00,
+   0x81818100,0x65656500,0xf5f5f500,0x89898900,0xcbcbcb00,0x9d9d9d00,0x77777700,0xc6c6c600,
+   0x57575700,0x43434300,0x56565600,0x17171700,0xd4d4d400,0x40404000,0x1a1a1a00,0x4d4d4d00,
+   0xc0c0c000,0x63636300,0x6c6c6c00,0xe3e3e300,0xb7b7b700,0xc8c8c800,0x64646400,0x6a6a6a00,
+   0x53535300,0xaaaaaa00,0x38383800,0x98989800,0x0c0c0c00,0xf4f4f400,0x9b9b9b00,0xededed00,
+   0x7f7f7f00,0x22222200,0x76767600,0xafafaf00,0xdddddd00,0x3a3a3a00,0x0b0b0b00,0x58585800,
+   0x67676700,0x88888800,0x06060600,0xc3c3c300,0x35353500,0x0d0d0d00,0x01010100,0x8b8b8b00,
+   0x8c8c8c00,0xc2c2c200,0xe6e6e600,0x5f5f5f00,0x02020200,0x24242400,0x75757500,0x93939300,
+   0x66666600,0x1e1e1e00,0xe5e5e500,0xe2e2e200,0x54545400,0xd8d8d800,0x10101000,0xcecece00,
+   0x7a7a7a00,0xe8e8e800,0x08080800,0x2c2c2c00,0x12121200,0x97979700,0x32323200,0xababab00,
+   0xb4b4b400,0x27272700,0x0a0a0a00,0x23232300,0xdfdfdf00,0xefefef00,0xcacaca00,0xd9d9d900,
+   0xb8b8b800,0xfafafa00,0xdcdcdc00,0x31313100,0x6b6b6b00,0xd1d1d100,0xadadad00,0x19191900,
+   0x49494900,0xbdbdbd00,0x51515100,0x96969600,0xeeeeee00,0xe4e4e400,0xa8a8a800,0x41414100,
+   0xdadada00,0xffffff00,0xcdcdcd00,0x55555500,0x86868600,0x36363600,0xbebebe00,0x61616100,
+   0x52525200,0xf8f8f800,0xbbbbbb00,0x0e0e0e00,0x82828200,0x48484800,0x69696900,0x9a9a9a00,
+   0xe0e0e000,0x47474700,0x9e9e9e00,0x5c5c5c00,0x04040400,0x4b4b4b00,0x34343400,0x15151500,
+   0x79797900,0x26262600,0xa7a7a700,0xdedede00,0x29292900,0xaeaeae00,0x92929200,0xd7d7d700,
+   0x84848400,0xe9e9e900,0xd2d2d200,0xbababa00,0x5d5d5d00,0xf3f3f300,0xc5c5c500,0xb0b0b000,
+   0xbfbfbf00,0xa4a4a400,0x3b3b3b00,0x71717100,0x44444400,0x46464600,0x2b2b2b00,0xfcfcfc00,
+   0xebebeb00,0x6f6f6f00,0xd5d5d500,0xf6f6f600,0x14141400,0xfefefe00,0x7c7c7c00,0x70707000,
+   0x5a5a5a00,0x7d7d7d00,0xfdfdfd00,0x2f2f2f00,0x18181800,0x83838300,0x16161600,0xa5a5a500,
+   0x91919100,0x1f1f1f00,0x05050500,0x95959500,0x74747400,0xa9a9a900,0xc1c1c100,0x5b5b5b00,
+   0x4a4a4a00,0x85858500,0x6d6d6d00,0x13131300,0x07070700,0x4f4f4f00,0x4e4e4e00,0x45454500,
+   0xb2b2b200,0x0f0f0f00,0xc9c9c900,0x1c1c1c00,0xa6a6a600,0xbcbcbc00,0xececec00,0x73737300,
+   0x90909000,0x7b7b7b00,0xcfcfcf00,0x59595900,0x8f8f8f00,0xa1a1a100,0xf9f9f900,0x2d2d2d00,
+   0xf2f2f200,0xb1b1b100,0x00000000,0x94949400,0x37373700,0x9f9f9f00,0xd0d0d000,0x2e2e2e00,
+   0x9c9c9c00,0x6e6e6e00,0x28282800,0x3f3f3f00,0x80808000,0xf0f0f000,0x3d3d3d00,0xd3d3d300,
+   0x25252500,0x8a8a8a00,0xb5b5b500,0xe7e7e700,0x42424200,0xb3b3b300,0xc7c7c700,0xeaeaea00,
+   0xf7f7f700,0x4c4c4c00,0x11111100,0x33333300,0x03030300,0xa2a2a200,0xacacac00,0x60606000
+};
+
+inline void ARIA_FO(uint32_t& T0, uint32_t& T1, uint32_t& T2, uint32_t& T3)
+   {
+   T0 = S1[get_byte(0,T0)] ^ S2[get_byte(1,T0)] ^ X1[get_byte(2,T0)] ^ X2[get_byte(3,T0)];
+   T1 = S1[get_byte(0,T1)] ^ S2[get_byte(1,T1)] ^ X1[get_byte(2,T1)] ^ X2[get_byte(3,T1)];
+   T2 = S1[get_byte(0,T2)] ^ S2[get_byte(1,T2)] ^ X1[get_byte(2,T2)] ^ X2[get_byte(3,T2)];
+   T3 = S1[get_byte(0,T3)] ^ S2[get_byte(1,T3)] ^ X1[get_byte(2,T3)] ^ X2[get_byte(3,T3)];
+
+   T1 ^= T2;
+   T2 ^= T3; T0 ^= T1;
+   T3 ^= T1; T2 ^= T0;
+   T1 ^= T2;
+
+   T1 = ((T1 << 8) & 0xFF00FF00) | ((T1 >> 8) & 0x00FF00FF);
+   T2 = rotr<16>(T2);
+   T3 = reverse_bytes(T3);
+
+   T1 ^= T2;
+   T2 ^= T3; T0 ^= T1;
+   T3 ^= T1; T2 ^= T0;
+   T1 ^= T2;
+   }
+
+inline void ARIA_FE(uint32_t& T0, uint32_t& T1, uint32_t& T2, uint32_t& T3)
+   {
+   T0 = X1[get_byte(0,T0)] ^ X2[get_byte(1,T0)] ^ S1[get_byte(2,T0)] ^ S2[get_byte(3,T0)];
+   T1 = X1[get_byte(0,T1)] ^ X2[get_byte(1,T1)] ^ S1[get_byte(2,T1)] ^ S2[get_byte(3,T1)];
+   T2 = X1[get_byte(0,T2)] ^ X2[get_byte(1,T2)] ^ S1[get_byte(2,T2)] ^ S2[get_byte(3,T2)];
+   T3 = X1[get_byte(0,T3)] ^ X2[get_byte(1,T3)] ^ S1[get_byte(2,T3)] ^ S2[get_byte(3,T3)];
+
+   T1 ^= T2;
+   T2 ^= T3; T0 ^= T1;
+   T3 ^= T1; T2 ^= T0;
+   T1 ^= T2;
+
+   T3 = ((T3 << 8) & 0xFF00FF00) | ((T3 >> 8) & 0x00FF00FF);
+   T0 = rotr<16>(T0);
+   T1 = reverse_bytes(T1);
+
+   T1 ^= T2;
+   T2 ^= T3; T0 ^= T1;
+   T3 ^= T1; T2 ^= T0;
+   T1 ^= T2;
+   }
+
+/*
+* ARIA encryption and decryption
+*/
+void transform(const uint8_t in[], uint8_t out[], size_t blocks,
+               const secure_vector<uint32_t>& KS)
+   {
+   /*
+   * Hit every cache line of S1, S2, X1, X2
+   *
+   * The initializer of Z ensures Z == 0xFFFFFFFF for any cache line
+   * size that is a power of 2 and <= 512
+   */
+   const size_t cache_line_size = CPUID::cache_line_size();
+
+   volatile uint32_t Z = 0x11101010;
+   for(size_t i = 0; i < 256; i += cache_line_size / sizeof(uint32_t))
+      {
+      Z |= S1[i] | S2[i] | X1[i] | X2[i];
+      }
+
+   const size_t ROUNDS = (KS.size() / 4) - 1;
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t t0, t1, t2, t3;
+      load_be(in + 16*i, t0, t1, t2, t3);
+
+      t0 &= Z;
+
+      for(size_t r = 0; r < ROUNDS; r += 2)
+         {
+         t0 ^= KS[4*r];
+         t1 ^= KS[4*r+1];
+         t2 ^= KS[4*r+2];
+         t3 ^= KS[4*r+3];
+         ARIA_FO(t0,t1,t2,t3);
+
+         t0 ^= KS[4*r+4];
+         t1 ^= KS[4*r+5];
+         t2 ^= KS[4*r+6];
+         t3 ^= KS[4*r+7];
+
+         if(r != ROUNDS-2)
+            ARIA_FE(t0,t1,t2,t3);
+         }
+
+      out[16*i+ 0] = static_cast<uint8_t>(X1[get_byte(0,t0)]   ) ^ get_byte(0, KS[4*ROUNDS]);
+      out[16*i+ 1] = static_cast<uint8_t>(X2[get_byte(1,t0)]>>8) ^ get_byte(1, KS[4*ROUNDS]);
+      out[16*i+ 2] = static_cast<uint8_t>(S1[get_byte(2,t0)]   ) ^ get_byte(2, KS[4*ROUNDS]);
+      out[16*i+ 3] = static_cast<uint8_t>(S2[get_byte(3,t0)]   ) ^ get_byte(3, KS[4*ROUNDS]);
+      out[16*i+ 4] = static_cast<uint8_t>(X1[get_byte(0,t1)]   ) ^ get_byte(0, KS[4*ROUNDS+1]);
+      out[16*i+ 5] = static_cast<uint8_t>(X2[get_byte(1,t1)]>>8) ^ get_byte(1, KS[4*ROUNDS+1]);
+      out[16*i+ 6] = static_cast<uint8_t>(S1[get_byte(2,t1)]   ) ^ get_byte(2, KS[4*ROUNDS+1]);
+      out[16*i+ 7] = static_cast<uint8_t>(S2[get_byte(3,t1)]   ) ^ get_byte(3, KS[4*ROUNDS+1]);
+      out[16*i+ 8] = static_cast<uint8_t>(X1[get_byte(0,t2)]   ) ^ get_byte(0, KS[4*ROUNDS+2]);
+      out[16*i+ 9] = static_cast<uint8_t>(X2[get_byte(1,t2)]>>8) ^ get_byte(1, KS[4*ROUNDS+2]);
+      out[16*i+10] = static_cast<uint8_t>(S1[get_byte(2,t2)]   ) ^ get_byte(2, KS[4*ROUNDS+2]);
+      out[16*i+11] = static_cast<uint8_t>(S2[get_byte(3,t2)]   ) ^ get_byte(3, KS[4*ROUNDS+2]);
+      out[16*i+12] = static_cast<uint8_t>(X1[get_byte(0,t3)]   ) ^ get_byte(0, KS[4*ROUNDS+3]);
+      out[16*i+13] = static_cast<uint8_t>(X2[get_byte(1,t3)]>>8) ^ get_byte(1, KS[4*ROUNDS+3]);
+      out[16*i+14] = static_cast<uint8_t>(S1[get_byte(2,t3)]   ) ^ get_byte(2, KS[4*ROUNDS+3]);
+      out[16*i+15] = static_cast<uint8_t>(S2[get_byte(3,t3)]   ) ^ get_byte(3, KS[4*ROUNDS+3]);
+      }
+   }
+
+// n-bit right shift of Y XORed to X
+template<size_t N>
+inline void ARIA_ROL128(const uint32_t X[4], const uint32_t Y[4], uint32_t KS[4])
+   {
+   // MSVC is not generating a "rotate immediate". Constify to help it along.
+   static const size_t Q = 4 - (N / 32);
+   static const size_t R = N % 32;
+   static_assert(R > 0 && R < 32, "Rotation in range for type");
+   KS[0] = (X[0]) ^ ((Y[(Q  )%4])>>R) ^ ((Y[(Q+3)%4])<<(32-R));
+   KS[1] = (X[1]) ^ ((Y[(Q+1)%4])>>R) ^ ((Y[(Q  )%4])<<(32-R));
+   KS[2] = (X[2]) ^ ((Y[(Q+2)%4])>>R) ^ ((Y[(Q+1)%4])<<(32-R));
+   KS[3] = (X[3]) ^ ((Y[(Q+3)%4])>>R) ^ ((Y[(Q+2)%4])<<(32-R));
+   }
+
+/*
+* ARIA Key Schedule
+*/
+void key_schedule(secure_vector<uint32_t>& ERK,
+                  secure_vector<uint32_t>& DRK,
+                  const uint8_t key[], size_t length)
+   {
+   const uint32_t KRK[3][4] = {
+      {0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0},
+      {0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0},
+      {0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e}
+   };
+
+   const size_t CK0 = (length / 8) - 2;
+   const size_t CK1 = (CK0 + 1) % 3;
+   const size_t CK2 = (CK1 + 1) % 3;
+
+   uint32_t w0[4];
+   uint32_t w1[4];
+   uint32_t w2[4];
+   uint32_t w3[4];
+
+   w0[0] = load_be<uint32_t>(key,0);
+   w0[1] = load_be<uint32_t>(key,1);
+   w0[2] = load_be<uint32_t>(key,2);
+   w0[3] = load_be<uint32_t>(key,3);
+
+   w1[0] = w0[0] ^ KRK[CK0][0];
+   w1[1] = w0[1] ^ KRK[CK0][1];
+   w1[2] = w0[2] ^ KRK[CK0][2];
+   w1[3] = w0[3] ^ KRK[CK0][3];
+
+   ARIA_FO(w1[0], w1[1], w1[2], w1[3]);
+
+   if(length == 24 || length == 32)
+      {
+      w1[0] ^= load_be<uint32_t>(key,4);
+      w1[1] ^= load_be<uint32_t>(key,5);
+      }
+   if(length == 32)
+      {
+      w1[2] ^= load_be<uint32_t>(key,6);
+      w1[3] ^= load_be<uint32_t>(key,7);
+      }
+
+   w2[0] = w1[0] ^ KRK[CK1][0];
+   w2[1] = w1[1] ^ KRK[CK1][1];
+   w2[2] = w1[2] ^ KRK[CK1][2];
+   w2[3] = w1[3] ^ KRK[CK1][3];
+
+   ARIA_FE(w2[0], w2[1], w2[2], w2[3]);
+
+   w2[0] ^= w0[0];
+   w2[1] ^= w0[1];
+   w2[2] ^= w0[2];
+   w2[3] ^= w0[3];
+
+   w3[0] = w2[0] ^ KRK[CK2][0];
+   w3[1] = w2[1] ^ KRK[CK2][1];
+   w3[2] = w2[2] ^ KRK[CK2][2];
+   w3[3] = w2[3] ^ KRK[CK2][3];
+
+   ARIA_FO(w3[0], w3[1], w3[2], w3[3]);
+
+   w3[0] ^= w1[0];
+   w3[1] ^= w1[1];
+   w3[2] ^= w1[2];
+   w3[3] ^= w1[3];
+
+   if(length == 16)
+      ERK.resize(4*13);
+   else if(length == 24)
+      ERK.resize(4*15);
+   else if(length == 32)
+      ERK.resize(4*17);
+
+   ARIA_ROL128<19>(w0, w1, &ERK[ 0]);
+   ARIA_ROL128<19>(w1, w2, &ERK[ 4]);
+   ARIA_ROL128<19>(w2, w3, &ERK[ 8]);
+   ARIA_ROL128<19>(w3, w0, &ERK[12]);
+   ARIA_ROL128<31>(w0, w1, &ERK[16]);
+   ARIA_ROL128<31>(w1, w2, &ERK[20]);
+   ARIA_ROL128<31>(w2, w3, &ERK[24]);
+   ARIA_ROL128<31>(w3, w0, &ERK[28]);
+   ARIA_ROL128<67>(w0, w1, &ERK[32]);
+   ARIA_ROL128<67>(w1, w2, &ERK[36]);
+   ARIA_ROL128<67>(w2, w3, &ERK[40]);
+   ARIA_ROL128<67>(w3, w0, &ERK[44]);
+   ARIA_ROL128<97>(w0, w1, &ERK[48]);
+
+   if(length == 24 || length == 32)
+      {
+      ARIA_ROL128<97>(w1, w2, &ERK[52]);
+      ARIA_ROL128<97>(w2, w3, &ERK[56]);
+
+      if(length == 32)
+         {
+         ARIA_ROL128< 97>(w3, w0, &ERK[60]);
+         ARIA_ROL128<109>(w0, w1, &ERK[64]);
+         }
+      }
+
+   // Now create the decryption key schedule
+   DRK.resize(ERK.size());
+
+   for(size_t i = 0; i != DRK.size(); i += 4)
+      {
+      DRK[i  ] = ERK[ERK.size()-4-i];
+      DRK[i+1] = ERK[ERK.size()-3-i];
+      DRK[i+2] = ERK[ERK.size()-2-i];
+      DRK[i+3] = ERK[ERK.size()-1-i];
+      }
+
+   for(size_t i = 4; i != DRK.size() - 4; i += 4)
+      {
+      for(size_t j = 0; j != 4; ++j)
+         {
+         DRK[i+j] = rotr<8>(DRK[i+j]) ^
+                    rotr<16>(DRK[i+j]) ^
+                    rotr<24>(DRK[i+j]);
+         }
+
+      DRK[i+1] ^= DRK[i+2]; DRK[i+2] ^= DRK[i+3];
+      DRK[i+0] ^= DRK[i+1]; DRK[i+3] ^= DRK[i+1];
+      DRK[i+2] ^= DRK[i+0]; DRK[i+1] ^= DRK[i+2];
+
+      DRK[i+1] = ((DRK[i+1] << 8) & 0xFF00FF00) | ((DRK[i+1] >> 8) & 0x00FF00FF);
+      DRK[i+2] = rotr<16>(DRK[i+2]);
+      DRK[i+3] = reverse_bytes(DRK[i+3]);
+
+      DRK[i+1] ^= DRK[i+2]; DRK[i+2] ^= DRK[i+3];
+      DRK[i+0] ^= DRK[i+1]; DRK[i+3] ^= DRK[i+1];
+      DRK[i+2] ^= DRK[i+0]; DRK[i+1] ^= DRK[i+2];
+      }
+   }
+
+}
+
+}
+
+void ARIA_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_ERK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_ERK);
+   }
+
+void ARIA_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_ERK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_ERK);
+   }
+
+void ARIA_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_ERK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_ERK);
+   }
+
+void ARIA_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DRK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_DRK);
+   }
+
+void ARIA_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DRK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_DRK);
+   }
+
+void ARIA_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DRK.size() > 0);
+   ARIA_F::transform(in, out, blocks, m_DRK);
+   }
+
+void ARIA_128::key_schedule(const uint8_t key[], size_t length)
+   {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+   }
+
+void ARIA_192::key_schedule(const uint8_t key[], size_t length)
+   {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+   }
+
+void ARIA_256::key_schedule(const uint8_t key[], size_t length)
+   {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+   }
+
+void ARIA_128::clear()
+   {
+   zap(m_ERK);
+   zap(m_DRK);
+   }
+
+void ARIA_192::clear()
+   {
+   zap(m_ERK);
+   zap(m_DRK);
+   }
+
+void ARIA_256::clear()
+   {
+   zap(m_ERK);
+   zap(m_DRK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/aria/aria.h b/third_party/botan/src/lib/block/aria/aria.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/aria/aria.h
@@ -0,0 +1,84 @@
+/*
+* ARIA
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*
+* This ARIA implementation is based on the 32-bit implementation by Aaram Yun from the
+* National Security Research Institute, KOREA. Aaram Yun's implementation is based on
+* the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from
+* the Korea Internet & Security Agency website.
+* <A HREF="https://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
+* <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
+* Internet & Security Agency homepage</A>
+*/
+
+#ifndef BOTAN_ARIA_H_
+#define BOTAN_ARIA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(aria.h)
+
+namespace Botan {
+
+/**
+* ARIA-128
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_128 final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "ARIA-128"; }
+      BlockCipher* clone() const override { return new ARIA_128; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      // Encryption and Decryption round keys.
+      secure_vector<uint32_t> m_ERK, m_DRK;
+   };
+
+/**
+* ARIA-192
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_192 final : public Block_Cipher_Fixed_Params<16, 24>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "ARIA-192"; }
+      BlockCipher* clone() const override { return new ARIA_192; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      // Encryption and Decryption round keys.
+      secure_vector<uint32_t> m_ERK, m_DRK;
+   };
+
+/**
+* ARIA-256
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_256 final : public Block_Cipher_Fixed_Params<16, 32>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "ARIA-256"; }
+      BlockCipher* clone() const override { return new ARIA_256; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      // Encryption and Decryption round keys.
+      secure_vector<uint32_t> m_ERK, m_DRK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/block_cipher.cpp b/third_party/botan/src/lib/block/block_cipher.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/block_cipher.cpp
@@ -0,0 +1,363 @@
+/*
+* Block Ciphers
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/block_cipher.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_AES)
+  #include <botan/aes.h>
+#endif
+
+#if defined(BOTAN_HAS_ARIA)
+  #include <botan/aria.h>
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH)
+  #include <botan/blowfish.h>
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA)
+  #include <botan/camellia.h>
+#endif
+
+#if defined(BOTAN_HAS_CAST_128)
+  #include <botan/cast128.h>
+#endif
+
+#if defined(BOTAN_HAS_CAST_256)
+  #include <botan/cast256.h>
+#endif
+
+#if defined(BOTAN_HAS_CASCADE)
+  #include <botan/cascade.h>
+#endif
+
+#if defined(BOTAN_HAS_DES)
+  #include <botan/des.h>
+  #include <botan/desx.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_28147_89)
+  #include <botan/gost_28147.h>
+#endif
+
+#if defined(BOTAN_HAS_IDEA)
+  #include <botan/idea.h>
+#endif
+
+#if defined(BOTAN_HAS_KASUMI)
+  #include <botan/kasumi.h>
+#endif
+
+#if defined(BOTAN_HAS_LION)
+  #include <botan/lion.h>
+#endif
+
+#if defined(BOTAN_HAS_MISTY1)
+  #include <botan/misty1.h>
+#endif
+
+#if defined(BOTAN_HAS_NOEKEON)
+  #include <botan/noekeon.h>
+#endif
+
+#if defined(BOTAN_HAS_SEED)
+  #include <botan/seed.h>
+#endif
+
+#if defined(BOTAN_HAS_SERPENT)
+  #include <botan/serpent.h>
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2)
+  #include <botan/shacal2.h>
+#endif
+
+#if defined(BOTAN_HAS_SM4)
+  #include <botan/sm4.h>
+#endif
+
+#if defined(BOTAN_HAS_TWOFISH)
+  #include <botan/twofish.h>
+#endif
+
+#if defined(BOTAN_HAS_THREEFISH_512)
+  #include <botan/threefish_512.h>
+#endif
+
+#if defined(BOTAN_HAS_XTEA)
+  #include <botan/xtea.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+  #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<BlockCipher>
+BlockCipher::create(const std::string& algo,
+                    const std::string& provider)
+   {
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+   if(provider.empty() || provider == "commoncrypto")
+      {
+      if(auto bc = make_commoncrypto_block_cipher(algo))
+         return bc;
+
+      if(!provider.empty())
+         return nullptr;
+      }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider.empty() || provider == "openssl")
+      {
+      if(auto bc = make_openssl_block_cipher(algo))
+         return bc;
+
+      if(!provider.empty())
+         return nullptr;
+      }
+#endif
+
+   // TODO: CryptoAPI
+   // TODO: /dev/crypto
+
+   // Only base providers from here on out
+   if(provider.empty() == false && provider != "base")
+      return nullptr;
+
+#if defined(BOTAN_HAS_AES)
+   if(algo == "AES-128")
+      {
+      return std::unique_ptr<BlockCipher>(new AES_128);
+      }
+
+   if(algo == "AES-192")
+      {
+      return std::unique_ptr<BlockCipher>(new AES_192);
+      }
+
+   if(algo == "AES-256")
+      {
+      return std::unique_ptr<BlockCipher>(new AES_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ARIA)
+   if(algo == "ARIA-128")
+      {
+      return std::unique_ptr<BlockCipher>(new ARIA_128);
+      }
+
+   if(algo == "ARIA-192")
+      {
+      return std::unique_ptr<BlockCipher>(new ARIA_192);
+      }
+
+   if(algo == "ARIA-256")
+      {
+      return std::unique_ptr<BlockCipher>(new ARIA_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT)
+   if(algo == "Serpent")
+      {
+      return std::unique_ptr<BlockCipher>(new Serpent);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2)
+   if(algo == "SHACAL2")
+      {
+      return std::unique_ptr<BlockCipher>(new SHACAL2);
+      }
+#endif
+
+#if defined(BOTAN_HAS_TWOFISH)
+   if(algo == "Twofish")
+      {
+      return std::unique_ptr<BlockCipher>(new Twofish);
+      }
+#endif
+
+#if defined(BOTAN_HAS_THREEFISH_512)
+   if(algo == "Threefish-512")
+      {
+      return std::unique_ptr<BlockCipher>(new Threefish_512);
+      }
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH)
+   if(algo == "Blowfish")
+      {
+      return std::unique_ptr<BlockCipher>(new Blowfish);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA)
+   if(algo == "Camellia-128")
+      {
+      return std::unique_ptr<BlockCipher>(new Camellia_128);
+      }
+
+   if(algo == "Camellia-192")
+      {
+      return std::unique_ptr<BlockCipher>(new Camellia_192);
+      }
+
+   if(algo == "Camellia-256")
+      {
+      return std::unique_ptr<BlockCipher>(new Camellia_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_DES)
+   if(algo == "DES")
+      {
+      return std::unique_ptr<BlockCipher>(new DES);
+      }
+
+   if(algo == "DESX")
+      {
+      return std::unique_ptr<BlockCipher>(new DESX);
+      }
+
+   if(algo == "TripleDES" || algo == "3DES" || algo == "DES-EDE")
+      {
+      return std::unique_ptr<BlockCipher>(new TripleDES);
+      }
+#endif
+
+#if defined(BOTAN_HAS_NOEKEON)
+   if(algo == "Noekeon")
+      {
+      return std::unique_ptr<BlockCipher>(new Noekeon);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CAST_128)
+   if(algo == "CAST-128" || algo == "CAST5")
+      {
+      return std::unique_ptr<BlockCipher>(new CAST_128);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CAST_256)
+   if(algo == "CAST-256")
+      {
+      return std::unique_ptr<BlockCipher>(new CAST_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_IDEA)
+   if(algo == "IDEA")
+      {
+      return std::unique_ptr<BlockCipher>(new IDEA);
+      }
+#endif
+
+#if defined(BOTAN_HAS_KASUMI)
+   if(algo == "KASUMI")
+      {
+      return std::unique_ptr<BlockCipher>(new KASUMI);
+      }
+#endif
+
+#if defined(BOTAN_HAS_MISTY1)
+   if(algo == "MISTY1")
+      {
+      return std::unique_ptr<BlockCipher>(new MISTY1);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SEED)
+   if(algo == "SEED")
+      {
+      return std::unique_ptr<BlockCipher>(new SEED);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SM4)
+   if(algo == "SM4")
+      {
+      return std::unique_ptr<BlockCipher>(new SM4);
+      }
+#endif
+
+#if defined(BOTAN_HAS_XTEA)
+   if(algo == "XTEA")
+      {
+      return std::unique_ptr<BlockCipher>(new XTEA);
+      }
+#endif
+
+   const SCAN_Name req(algo);
+
+#if defined(BOTAN_HAS_GOST_28147_89)
+   if(req.algo_name() == "GOST-28147-89")
+      {
+      return std::unique_ptr<BlockCipher>(new GOST_28147_89(req.arg(0, "R3411_94_TestParam")));
+      }
+#endif
+
+#if defined(BOTAN_HAS_CASCADE)
+   if(req.algo_name() == "Cascade" && req.arg_count() == 2)
+      {
+      std::unique_ptr<BlockCipher> c1(BlockCipher::create(req.arg(0)));
+      std::unique_ptr<BlockCipher> c2(BlockCipher::create(req.arg(1)));
+
+      if(c1 && c2)
+         return std::unique_ptr<BlockCipher>(new Cascade_Cipher(c1.release(), c2.release()));
+      }
+#endif
+
+#if defined(BOTAN_HAS_LION)
+   if(req.algo_name() == "Lion" && req.arg_count_between(2, 3))
+      {
+      std::unique_ptr<HashFunction> hash(HashFunction::create(req.arg(0)));
+      std::unique_ptr<StreamCipher> stream(StreamCipher::create(req.arg(1)));
+
+      if(hash && stream)
+         {
+         const size_t block_size = req.arg_as_integer(2, 1024);
+         return std::unique_ptr<BlockCipher>(new Lion(hash.release(), stream.release(), block_size));
+         }
+      }
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<BlockCipher>
+BlockCipher::create_or_throw(const std::string& algo,
+                             const std::string& provider)
+   {
+   if(auto bc = BlockCipher::create(algo, provider))
+      {
+      return bc;
+      }
+   throw Lookup_Error("Block cipher", algo, provider);
+   }
+
+std::vector<std::string> BlockCipher::providers(const std::string& algo)
+   {
+   return probe_providers_of<BlockCipher>(algo, { "base", "openssl", "commoncrypto" });
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/block_cipher.h b/third_party/botan/src/lib/block/block_cipher.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/block_cipher.h
@@ -0,0 +1,254 @@
+/*
+* Block Cipher Base Class
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLOCK_CIPHER_H_
+#define BOTAN_BLOCK_CIPHER_H_
+
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+/**
+* This class represents a block cipher object.
+*/
+class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
+   {
+   public:
+
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to choose
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<BlockCipher>
+         create(const std::string& algo_spec,
+                const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name, or throw if the
+      * algo/provider combination cannot be found. If provider is
+      * empty then best available is chosen.
+      */
+      static std::unique_ptr<BlockCipher>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      * @param algo_spec algorithm name
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * @return block size of this algorithm
+      */
+      virtual size_t block_size() const = 0;
+
+      /**
+      * @return native parallelism of this cipher in blocks
+      */
+      virtual size_t parallelism() const { return 1; }
+
+      /**
+      * @return prefererred parallelism of this cipher in bytes
+      */
+      size_t parallel_bytes() const
+         {
+         return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT;
+         }
+
+      /**
+      * @return provider information about this implementation. Default is "base",
+      * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+      */
+      virtual std::string provider() const { return "base"; }
+
+      /**
+      * Encrypt a block.
+      * @param in The plaintext block to be encrypted as a byte array.
+      * Must be of length block_size().
+      * @param out The byte array designated to hold the encrypted block.
+      * Must be of length block_size().
+      */
+      void encrypt(const uint8_t in[], uint8_t out[]) const
+         { encrypt_n(in, out, 1); }
+
+      /**
+      * Decrypt a block.
+      * @param in The ciphertext block to be decypted as a byte array.
+      * Must be of length block_size().
+      * @param out The byte array designated to hold the decrypted block.
+      * Must be of length block_size().
+      */
+      void decrypt(const uint8_t in[], uint8_t out[]) const
+         { decrypt_n(in, out, 1); }
+
+      /**
+      * Encrypt a block.
+      * @param block the plaintext block to be encrypted
+      * Must be of length block_size(). Will hold the result when the function
+      * has finished.
+      */
+      void encrypt(uint8_t block[]) const { encrypt_n(block, block, 1); }
+
+      /**
+      * Decrypt a block.
+      * @param block the ciphertext block to be decrypted
+      * Must be of length block_size(). Will hold the result when the function
+      * has finished.
+      */
+      void decrypt(uint8_t block[]) const { decrypt_n(block, block, 1); }
+
+      /**
+      * Encrypt one or more blocks
+      * @param block the input/output buffer (multiple of block_size())
+      */
+      template<typename Alloc>
+      void encrypt(std::vector<uint8_t, Alloc>& block) const
+         {
+         return encrypt_n(block.data(), block.data(), block.size() / block_size());
+         }
+
+      /**
+      * Decrypt one or more blocks
+      * @param block the input/output buffer (multiple of block_size())
+      */
+      template<typename Alloc>
+      void decrypt(std::vector<uint8_t, Alloc>& block) const
+         {
+         return decrypt_n(block.data(), block.data(), block.size() / block_size());
+         }
+
+      /**
+      * Encrypt one or more blocks
+      * @param in the input buffer (multiple of block_size())
+      * @param out the output buffer (same size as in)
+      */
+      template<typename Alloc, typename Alloc2>
+      void encrypt(const std::vector<uint8_t, Alloc>& in,
+                   std::vector<uint8_t, Alloc2>& out) const
+         {
+         return encrypt_n(in.data(), out.data(), in.size() / block_size());
+         }
+
+      /**
+      * Decrypt one or more blocks
+      * @param in the input buffer (multiple of block_size())
+      * @param out the output buffer (same size as in)
+      */
+      template<typename Alloc, typename Alloc2>
+      void decrypt(const std::vector<uint8_t, Alloc>& in,
+                   std::vector<uint8_t, Alloc2>& out) const
+         {
+         return decrypt_n(in.data(), out.data(), in.size() / block_size());
+         }
+
+      /**
+      * Encrypt one or more blocks
+      * @param in the input buffer (multiple of block_size())
+      * @param out the output buffer (same size as in)
+      * @param blocks the number of blocks to process
+      */
+      virtual void encrypt_n(const uint8_t in[], uint8_t out[],
+                             size_t blocks) const = 0;
+
+      /**
+      * Decrypt one or more blocks
+      * @param in the input buffer (multiple of block_size())
+      * @param out the output buffer (same size as in)
+      * @param blocks the number of blocks to process
+      */
+      virtual void decrypt_n(const uint8_t in[], uint8_t out[],
+                             size_t blocks) const = 0;
+
+      virtual void encrypt_n_xex(uint8_t data[],
+                                 const uint8_t mask[],
+                                 size_t blocks) const
+         {
+         const size_t BS = block_size();
+         xor_buf(data, mask, blocks * BS);
+         encrypt_n(data, data, blocks);
+         xor_buf(data, mask, blocks * BS);
+         }
+
+      virtual void decrypt_n_xex(uint8_t data[],
+                                 const uint8_t mask[],
+                                 size_t blocks) const
+         {
+         const size_t BS = block_size();
+         xor_buf(data, mask, blocks * BS);
+         decrypt_n(data, data, blocks);
+         xor_buf(data, mask, blocks * BS);
+         }
+
+      /**
+      * @return new object representing the same algorithm as *this
+      */
+      virtual BlockCipher* clone() const = 0;
+
+      virtual ~BlockCipher() = default;
+   };
+
+/**
+* Tweakable block ciphers allow setting a tweak which is a non-keyed
+* value which affects the encryption/decryption operation.
+*/
+class BOTAN_PUBLIC_API(2,8) Tweakable_Block_Cipher : public BlockCipher
+   {
+   public:
+      /**
+      * Set the tweak value. This must be called after setting a key. The value
+      * persists until either set_tweak, set_key, or clear is called.
+      * Different algorithms support different tweak length(s). If called with
+      * an unsupported length, Invalid_Argument will be thrown.
+      */
+      virtual void set_tweak(const uint8_t tweak[], size_t len) = 0;
+   };
+
+/**
+* Represents a block cipher with a single fixed block size
+*/
+template<size_t BS, size_t KMIN, size_t KMAX = 0, size_t KMOD = 1, typename BaseClass = BlockCipher>
+class Block_Cipher_Fixed_Params : public BaseClass
+   {
+   public:
+      enum { BLOCK_SIZE = BS };
+      size_t block_size() const final override { return BS; }
+
+      // override to take advantage of compile time constant block size
+      void encrypt_n_xex(uint8_t data[],
+                         const uint8_t mask[],
+                         size_t blocks) const final override
+         {
+         xor_buf(data, mask, blocks * BS);
+         this->encrypt_n(data, data, blocks);
+         xor_buf(data, mask, blocks * BS);
+         }
+
+      void decrypt_n_xex(uint8_t data[],
+                         const uint8_t mask[],
+                         size_t blocks) const final override
+         {
+         xor_buf(data, mask, blocks * BS);
+         this->decrypt_n(data, data, blocks);
+         xor_buf(data, mask, blocks * BS);
+         }
+
+      Key_Length_Specification key_spec() const final override
+         {
+         return Key_Length_Specification(KMIN, KMAX, KMOD);
+         }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/blowfish/blowfish.cpp b/third_party/botan/src/lib/block/blowfish/blowfish.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/blowfish/blowfish.cpp
@@ -0,0 +1,456 @@
+/*
+* Blowfish
+* (C) 1999-2011,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blowfish.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+const uint32_t P_INIT[18] = {
+   0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
+   0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+   0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B };
+
+const uint32_t S_INIT[1024] = {
+   0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,
+   0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+   0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
+   0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+   0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,
+   0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+   0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6,
+   0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+   0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
+   0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+   0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1,
+   0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+   0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,
+   0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+   0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
+   0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+   0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,
+   0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+   0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B,
+   0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+   0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
+   0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+   0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A,
+   0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+   0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760,
+   0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+   0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
+   0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+   0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33,
+   0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+   0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0,
+   0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+   0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
+   0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+   0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705,
+   0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+   0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E,
+   0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+   0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
+   0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+   0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F,
+   0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+   0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A, 0x4B7A70E9, 0xB5B32944,
+   0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+   0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29,
+   0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+   0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26,
+   0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+   0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C,
+   0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+   0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6,
+   0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+   0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F,
+   0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+   0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810,
+   0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+   0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA,
+   0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+   0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55,
+   0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+   0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1,
+   0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+   0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78,
+   0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+   0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883,
+   0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+   0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170,
+   0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+   0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7,
+   0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+   0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099,
+   0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+   0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263,
+   0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+   0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3,
+   0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+   0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7,
+   0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+   0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D,
+   0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+   0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460,
+   0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+   0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484,
+   0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+   0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A,
+   0xE6E39F2B, 0xDB83ADF7, 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+   0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A,
+   0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+   0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785,
+   0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+   0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900,
+   0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+   0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9,
+   0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+   0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397,
+   0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+   0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9,
+   0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+   0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F,
+   0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+   0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E,
+   0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+   0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD,
+   0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+   0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8,
+   0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+   0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C,
+   0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+   0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B,
+   0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+   0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386,
+   0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+   0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0,
+   0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+   0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2,
+   0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+   0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770,
+   0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+   0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C,
+   0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+   0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA,
+   0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+   0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63,
+   0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+   0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9,
+   0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+   0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4,
+   0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0,
+   0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742,
+   0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+   0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
+   0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+   0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A,
+   0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+   0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1,
+   0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+   0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
+   0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+   0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6,
+   0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+   0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA,
+   0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+   0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
+   0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+   0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE,
+   0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+   0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD,
+   0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+   0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
+   0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+   0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC,
+   0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+   0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC,
+   0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+   0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
+   0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+   0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A,
+   0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+   0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B,
+   0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+   0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
+   0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+   0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623,
+   0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+   0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A,
+   0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+   0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
+   0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+   0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C,
+   0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+   0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 };
+
+inline uint32_t BFF(uint32_t X, const secure_vector<uint32_t>& S)
+   {
+   return ((S[    get_byte(0, X)]  + S[256+get_byte(1, X)]) ^
+            S[512+get_byte(2, X)]) + S[768+get_byte(3, X)];
+   }
+
+}
+
+/*
+* Blowfish Encryption
+*/
+void Blowfish::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_S.empty() == false);
+
+   while(blocks >= 4)
+      {
+      uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+      load_be(in, L0, R0, L1, R1, L2, R2, L3, R3);
+
+      for(size_t r = 0; r != 16; r += 2)
+         {
+         L0 ^= m_P[r];
+         L1 ^= m_P[r];
+         L2 ^= m_P[r];
+         L3 ^= m_P[r];
+         R0 ^= BFF(L0, m_S);
+         R1 ^= BFF(L1, m_S);
+         R2 ^= BFF(L2, m_S);
+         R3 ^= BFF(L3, m_S);
+
+         R0 ^= m_P[r+1];
+         R1 ^= m_P[r+1];
+         R2 ^= m_P[r+1];
+         R3 ^= m_P[r+1];
+         L0 ^= BFF(R0, m_S);
+         L1 ^= BFF(R1, m_S);
+         L2 ^= BFF(R2, m_S);
+         L3 ^= BFF(R3, m_S);
+         }
+
+      L0 ^= m_P[16]; R0 ^= m_P[17];
+      L1 ^= m_P[16]; R1 ^= m_P[17];
+      L2 ^= m_P[16]; R2 ^= m_P[17];
+      L3 ^= m_P[16]; R3 ^= m_P[17];
+
+      store_be(out, R0, L0, R1, L1, R2, L2, R3, L3);
+
+      in += 4*BLOCK_SIZE;
+      out += 4*BLOCK_SIZE;
+      blocks -= 4;
+      }
+
+   while(blocks)
+      {
+      uint32_t L, R;
+      load_be(in, L, R);
+
+      for(size_t r = 0; r != 16; r += 2)
+         {
+         L ^= m_P[r];
+         R ^= BFF(L, m_S);
+
+         R ^= m_P[r+1];
+         L ^= BFF(R, m_S);
+         }
+
+      L ^= m_P[16]; R ^= m_P[17];
+
+      store_be(out, R, L);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      blocks--;
+      }
+   }
+
+/*
+* Blowfish Decryption
+*/
+void Blowfish::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_S.empty() == false);
+
+   while(blocks >= 4)
+      {
+      uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+      load_be(in, L0, R0, L1, R1, L2, R2, L3, R3);
+
+      for(size_t r = 17; r != 1; r -= 2)
+         {
+         L0 ^= m_P[r];
+         L1 ^= m_P[r];
+         L2 ^= m_P[r];
+         L3 ^= m_P[r];
+         R0 ^= BFF(L0, m_S);
+         R1 ^= BFF(L1, m_S);
+         R2 ^= BFF(L2, m_S);
+         R3 ^= BFF(L3, m_S);
+
+         R0 ^= m_P[r-1];
+         R1 ^= m_P[r-1];
+         R2 ^= m_P[r-1];
+         R3 ^= m_P[r-1];
+
+         L0 ^= BFF(R0, m_S);
+         L1 ^= BFF(R1, m_S);
+         L2 ^= BFF(R2, m_S);
+         L3 ^= BFF(R3, m_S);
+         }
+
+      L0 ^= m_P[1]; R0 ^= m_P[0];
+      L1 ^= m_P[1]; R1 ^= m_P[0];
+      L2 ^= m_P[1]; R2 ^= m_P[0];
+      L3 ^= m_P[1]; R3 ^= m_P[0];
+
+      store_be(out, R0, L0, R1, L1, R2, L2, R3, L3);
+
+      in += 4*BLOCK_SIZE;
+      out += 4*BLOCK_SIZE;
+      blocks -= 4;
+      }
+
+   while(blocks)
+      {
+      uint32_t L, R;
+      load_be(in, L, R);
+
+      for(size_t r = 17; r != 1; r -= 2)
+         {
+         L ^= m_P[r];
+         R ^= BFF(L, m_S);
+
+         R ^= m_P[r-1];
+         L ^= BFF(R, m_S);
+         }
+
+      L ^= m_P[1]; R ^= m_P[0];
+
+      store_be(out, R, L);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      blocks--;
+      }
+   }
+
+/*
+* Blowfish Key Schedule
+*/
+void Blowfish::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_P.resize(18);
+   copy_mem(m_P.data(), P_INIT, 18);
+
+   m_S.resize(1024);
+   copy_mem(m_S.data(), S_INIT, 1024);
+
+   key_expansion(key, length, nullptr, 0);
+   }
+
+void Blowfish::key_expansion(const uint8_t key[],
+                             size_t length,
+                             const uint8_t salt[],
+                             size_t salt_length)
+   {
+   BOTAN_ASSERT_NOMSG(salt_length % 4 == 0);
+
+   for(size_t i = 0, j = 0; i != 18; ++i, j += 4)
+      m_P[i] ^= make_uint32(key[(j  ) % length], key[(j+1) % length],
+                            key[(j+2) % length], key[(j+3) % length]);
+
+   const size_t P_salt_offset = (salt_length > 0) ? 18 % (salt_length / 4) : 0;
+
+   uint32_t L = 0, R = 0;
+   generate_sbox(m_P, L, R, salt, salt_length, 0);
+   generate_sbox(m_S, L, R, salt, salt_length, P_salt_offset);
+   }
+
+/*
+* Modified key schedule used for bcrypt password hashing
+*/
+void Blowfish::salted_set_key(const uint8_t key[], size_t length,
+                              const uint8_t salt[], size_t salt_length,
+                              size_t workfactor, bool salt_first)
+   {
+   BOTAN_ARG_CHECK(salt_length > 0 && salt_length % 4 == 0,
+                   "Invalid salt length for Blowfish salted key schedule");
+
+   if(length > 72)
+      {
+      // Truncate longer passwords to the 72 char bcrypt limit
+      length = 72;
+      }
+
+   m_P.resize(18);
+   copy_mem(m_P.data(), P_INIT, 18);
+
+   m_S.resize(1024);
+   copy_mem(m_S.data(), S_INIT, 1024);
+   key_expansion(key, length, salt, salt_length);
+
+   if(workfactor > 0)
+      {
+      const size_t rounds = static_cast<size_t>(1) << workfactor;
+
+      for(size_t r = 0; r != rounds; ++r)
+         {
+         if(salt_first)
+            {
+            key_expansion(salt, salt_length, nullptr, 0);
+            key_expansion(key, length, nullptr, 0);
+            }
+         else
+            {
+            key_expansion(key, length, nullptr, 0);
+            key_expansion(salt, salt_length, nullptr, 0);
+            }
+         }
+      }
+   }
+
+/*
+* Generate one of the Sboxes
+*/
+void Blowfish::generate_sbox(secure_vector<uint32_t>& box,
+                             uint32_t& L, uint32_t& R,
+                             const uint8_t salt[],
+                             size_t salt_length,
+                             size_t salt_off) const
+   {
+   for(size_t i = 0; i != box.size(); i += 2)
+      {
+      if(salt_length > 0)
+         {
+         L ^= load_be<uint32_t>(salt, (i + salt_off) % (salt_length / 4));
+         R ^= load_be<uint32_t>(salt, (i + salt_off + 1) % (salt_length / 4));
+         }
+
+      for(size_t r = 0; r != 16; r += 2)
+         {
+         L ^= m_P[r];
+         R ^= BFF(L, m_S);
+
+         R ^= m_P[r+1];
+         L ^= BFF(R, m_S);
+         }
+
+      uint32_t T = R; R = L ^ m_P[16]; L = T ^ m_P[17];
+      box[i] = L;
+      box[i+1] = R;
+      }
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Blowfish::clear()
+   {
+   zap(m_P);
+   zap(m_S);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/blowfish/blowfish.h b/third_party/botan/src/lib/block/blowfish/blowfish.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/blowfish/blowfish.h
@@ -0,0 +1,62 @@
+/*
+* Blowfish
+* (C) 1999-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLOWFISH_H_
+#define BOTAN_BLOWFISH_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blowfish.h)
+
+namespace Botan {
+
+/**
+* Blowfish
+*/
+class BOTAN_PUBLIC_API(2,0) Blowfish final : public Block_Cipher_Fixed_Params<8, 1, 56>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      /**
+      * Modified EKSBlowfish key schedule, used for bcrypt password hashing
+      */
+      void salted_set_key(const uint8_t key[], size_t key_length,
+                          const uint8_t salt[], size_t salt_length,
+                          const size_t workfactor, bool salt_first = false);
+
+      BOTAN_DEPRECATED("Use Blowfish::salted_set_key taking salt length")
+      void eks_key_schedule(const uint8_t key[], size_t key_length,
+                            const uint8_t salt[16], size_t workfactor)
+         {
+         salted_set_key(key, key_length, salt, 16, workfactor);
+         }
+
+      void clear() override;
+      std::string name() const override { return "Blowfish"; }
+      BlockCipher* clone() const override { return new Blowfish; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      void key_expansion(const uint8_t key[],
+                         size_t key_length,
+                         const uint8_t salt[],
+                         size_t salt_length);
+
+      void generate_sbox(secure_vector<uint32_t>& box,
+                         uint32_t& L, uint32_t& R,
+                         const uint8_t salt[],
+                         size_t salt_length,
+                         size_t salt_off) const;
+
+      secure_vector<uint32_t> m_S, m_P;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/camellia/camellia.cpp b/third_party/botan/src/lib/block/camellia/camellia.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/camellia/camellia.cpp
@@ -0,0 +1,924 @@
+/*
+* Camellia
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/camellia.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+const uint64_t Camellia_SBOX1[256] = {
+0x7070700070000070, 0x8282820082000082, 0x2C2C2C002C00002C, 0xECECEC00EC0000EC,
+0xB3B3B300B30000B3, 0x2727270027000027, 0xC0C0C000C00000C0, 0xE5E5E500E50000E5,
+0xE4E4E400E40000E4, 0x8585850085000085, 0x5757570057000057, 0x3535350035000035,
+0xEAEAEA00EA0000EA, 0x0C0C0C000C00000C, 0xAEAEAE00AE0000AE, 0x4141410041000041,
+0x2323230023000023, 0xEFEFEF00EF0000EF, 0x6B6B6B006B00006B, 0x9393930093000093,
+0x4545450045000045, 0x1919190019000019, 0xA5A5A500A50000A5, 0x2121210021000021,
+0xEDEDED00ED0000ED, 0x0E0E0E000E00000E, 0x4F4F4F004F00004F, 0x4E4E4E004E00004E,
+0x1D1D1D001D00001D, 0x6565650065000065, 0x9292920092000092, 0xBDBDBD00BD0000BD,
+0x8686860086000086, 0xB8B8B800B80000B8, 0xAFAFAF00AF0000AF, 0x8F8F8F008F00008F,
+0x7C7C7C007C00007C, 0xEBEBEB00EB0000EB, 0x1F1F1F001F00001F, 0xCECECE00CE0000CE,
+0x3E3E3E003E00003E, 0x3030300030000030, 0xDCDCDC00DC0000DC, 0x5F5F5F005F00005F,
+0x5E5E5E005E00005E, 0xC5C5C500C50000C5, 0x0B0B0B000B00000B, 0x1A1A1A001A00001A,
+0xA6A6A600A60000A6, 0xE1E1E100E10000E1, 0x3939390039000039, 0xCACACA00CA0000CA,
+0xD5D5D500D50000D5, 0x4747470047000047, 0x5D5D5D005D00005D, 0x3D3D3D003D00003D,
+0xD9D9D900D90000D9, 0x0101010001000001, 0x5A5A5A005A00005A, 0xD6D6D600D60000D6,
+0x5151510051000051, 0x5656560056000056, 0x6C6C6C006C00006C, 0x4D4D4D004D00004D,
+0x8B8B8B008B00008B, 0x0D0D0D000D00000D, 0x9A9A9A009A00009A, 0x6666660066000066,
+0xFBFBFB00FB0000FB, 0xCCCCCC00CC0000CC, 0xB0B0B000B00000B0, 0x2D2D2D002D00002D,
+0x7474740074000074, 0x1212120012000012, 0x2B2B2B002B00002B, 0x2020200020000020,
+0xF0F0F000F00000F0, 0xB1B1B100B10000B1, 0x8484840084000084, 0x9999990099000099,
+0xDFDFDF00DF0000DF, 0x4C4C4C004C00004C, 0xCBCBCB00CB0000CB, 0xC2C2C200C20000C2,
+0x3434340034000034, 0x7E7E7E007E00007E, 0x7676760076000076, 0x0505050005000005,
+0x6D6D6D006D00006D, 0xB7B7B700B70000B7, 0xA9A9A900A90000A9, 0x3131310031000031,
+0xD1D1D100D10000D1, 0x1717170017000017, 0x0404040004000004, 0xD7D7D700D70000D7,
+0x1414140014000014, 0x5858580058000058, 0x3A3A3A003A00003A, 0x6161610061000061,
+0xDEDEDE00DE0000DE, 0x1B1B1B001B00001B, 0x1111110011000011, 0x1C1C1C001C00001C,
+0x3232320032000032, 0x0F0F0F000F00000F, 0x9C9C9C009C00009C, 0x1616160016000016,
+0x5353530053000053, 0x1818180018000018, 0xF2F2F200F20000F2, 0x2222220022000022,
+0xFEFEFE00FE0000FE, 0x4444440044000044, 0xCFCFCF00CF0000CF, 0xB2B2B200B20000B2,
+0xC3C3C300C30000C3, 0xB5B5B500B50000B5, 0x7A7A7A007A00007A, 0x9191910091000091,
+0x2424240024000024, 0x0808080008000008, 0xE8E8E800E80000E8, 0xA8A8A800A80000A8,
+0x6060600060000060, 0xFCFCFC00FC0000FC, 0x6969690069000069, 0x5050500050000050,
+0xAAAAAA00AA0000AA, 0xD0D0D000D00000D0, 0xA0A0A000A00000A0, 0x7D7D7D007D00007D,
+0xA1A1A100A10000A1, 0x8989890089000089, 0x6262620062000062, 0x9797970097000097,
+0x5454540054000054, 0x5B5B5B005B00005B, 0x1E1E1E001E00001E, 0x9595950095000095,
+0xE0E0E000E00000E0, 0xFFFFFF00FF0000FF, 0x6464640064000064, 0xD2D2D200D20000D2,
+0x1010100010000010, 0xC4C4C400C40000C4, 0x0000000000000000, 0x4848480048000048,
+0xA3A3A300A30000A3, 0xF7F7F700F70000F7, 0x7575750075000075, 0xDBDBDB00DB0000DB,
+0x8A8A8A008A00008A, 0x0303030003000003, 0xE6E6E600E60000E6, 0xDADADA00DA0000DA,
+0x0909090009000009, 0x3F3F3F003F00003F, 0xDDDDDD00DD0000DD, 0x9494940094000094,
+0x8787870087000087, 0x5C5C5C005C00005C, 0x8383830083000083, 0x0202020002000002,
+0xCDCDCD00CD0000CD, 0x4A4A4A004A00004A, 0x9090900090000090, 0x3333330033000033,
+0x7373730073000073, 0x6767670067000067, 0xF6F6F600F60000F6, 0xF3F3F300F30000F3,
+0x9D9D9D009D00009D, 0x7F7F7F007F00007F, 0xBFBFBF00BF0000BF, 0xE2E2E200E20000E2,
+0x5252520052000052, 0x9B9B9B009B00009B, 0xD8D8D800D80000D8, 0x2626260026000026,
+0xC8C8C800C80000C8, 0x3737370037000037, 0xC6C6C600C60000C6, 0x3B3B3B003B00003B,
+0x8181810081000081, 0x9696960096000096, 0x6F6F6F006F00006F, 0x4B4B4B004B00004B,
+0x1313130013000013, 0xBEBEBE00BE0000BE, 0x6363630063000063, 0x2E2E2E002E00002E,
+0xE9E9E900E90000E9, 0x7979790079000079, 0xA7A7A700A70000A7, 0x8C8C8C008C00008C,
+0x9F9F9F009F00009F, 0x6E6E6E006E00006E, 0xBCBCBC00BC0000BC, 0x8E8E8E008E00008E,
+0x2929290029000029, 0xF5F5F500F50000F5, 0xF9F9F900F90000F9, 0xB6B6B600B60000B6,
+0x2F2F2F002F00002F, 0xFDFDFD00FD0000FD, 0xB4B4B400B40000B4, 0x5959590059000059,
+0x7878780078000078, 0x9898980098000098, 0x0606060006000006, 0x6A6A6A006A00006A,
+0xE7E7E700E70000E7, 0x4646460046000046, 0x7171710071000071, 0xBABABA00BA0000BA,
+0xD4D4D400D40000D4, 0x2525250025000025, 0xABABAB00AB0000AB, 0x4242420042000042,
+0x8888880088000088, 0xA2A2A200A20000A2, 0x8D8D8D008D00008D, 0xFAFAFA00FA0000FA,
+0x7272720072000072, 0x0707070007000007, 0xB9B9B900B90000B9, 0x5555550055000055,
+0xF8F8F800F80000F8, 0xEEEEEE00EE0000EE, 0xACACAC00AC0000AC, 0x0A0A0A000A00000A,
+0x3636360036000036, 0x4949490049000049, 0x2A2A2A002A00002A, 0x6868680068000068,
+0x3C3C3C003C00003C, 0x3838380038000038, 0xF1F1F100F10000F1, 0xA4A4A400A40000A4,
+0x4040400040000040, 0x2828280028000028, 0xD3D3D300D30000D3, 0x7B7B7B007B00007B,
+0xBBBBBB00BB0000BB, 0xC9C9C900C90000C9, 0x4343430043000043, 0xC1C1C100C10000C1,
+0x1515150015000015, 0xE3E3E300E30000E3, 0xADADAD00AD0000AD, 0xF4F4F400F40000F4,
+0x7777770077000077, 0xC7C7C700C70000C7, 0x8080800080000080, 0x9E9E9E009E00009E };
+
+const uint64_t Camellia_SBOX2[256] = {
+0x00E0E0E0E0E00000, 0x0005050505050000, 0x0058585858580000, 0x00D9D9D9D9D90000,
+0x0067676767670000, 0x004E4E4E4E4E0000, 0x0081818181810000, 0x00CBCBCBCBCB0000,
+0x00C9C9C9C9C90000, 0x000B0B0B0B0B0000, 0x00AEAEAEAEAE0000, 0x006A6A6A6A6A0000,
+0x00D5D5D5D5D50000, 0x0018181818180000, 0x005D5D5D5D5D0000, 0x0082828282820000,
+0x0046464646460000, 0x00DFDFDFDFDF0000, 0x00D6D6D6D6D60000, 0x0027272727270000,
+0x008A8A8A8A8A0000, 0x0032323232320000, 0x004B4B4B4B4B0000, 0x0042424242420000,
+0x00DBDBDBDBDB0000, 0x001C1C1C1C1C0000, 0x009E9E9E9E9E0000, 0x009C9C9C9C9C0000,
+0x003A3A3A3A3A0000, 0x00CACACACACA0000, 0x0025252525250000, 0x007B7B7B7B7B0000,
+0x000D0D0D0D0D0000, 0x0071717171710000, 0x005F5F5F5F5F0000, 0x001F1F1F1F1F0000,
+0x00F8F8F8F8F80000, 0x00D7D7D7D7D70000, 0x003E3E3E3E3E0000, 0x009D9D9D9D9D0000,
+0x007C7C7C7C7C0000, 0x0060606060600000, 0x00B9B9B9B9B90000, 0x00BEBEBEBEBE0000,
+0x00BCBCBCBCBC0000, 0x008B8B8B8B8B0000, 0x0016161616160000, 0x0034343434340000,
+0x004D4D4D4D4D0000, 0x00C3C3C3C3C30000, 0x0072727272720000, 0x0095959595950000,
+0x00ABABABABAB0000, 0x008E8E8E8E8E0000, 0x00BABABABABA0000, 0x007A7A7A7A7A0000,
+0x00B3B3B3B3B30000, 0x0002020202020000, 0x00B4B4B4B4B40000, 0x00ADADADADAD0000,
+0x00A2A2A2A2A20000, 0x00ACACACACAC0000, 0x00D8D8D8D8D80000, 0x009A9A9A9A9A0000,
+0x0017171717170000, 0x001A1A1A1A1A0000, 0x0035353535350000, 0x00CCCCCCCCCC0000,
+0x00F7F7F7F7F70000, 0x0099999999990000, 0x0061616161610000, 0x005A5A5A5A5A0000,
+0x00E8E8E8E8E80000, 0x0024242424240000, 0x0056565656560000, 0x0040404040400000,
+0x00E1E1E1E1E10000, 0x0063636363630000, 0x0009090909090000, 0x0033333333330000,
+0x00BFBFBFBFBF0000, 0x0098989898980000, 0x0097979797970000, 0x0085858585850000,
+0x0068686868680000, 0x00FCFCFCFCFC0000, 0x00ECECECECEC0000, 0x000A0A0A0A0A0000,
+0x00DADADADADA0000, 0x006F6F6F6F6F0000, 0x0053535353530000, 0x0062626262620000,
+0x00A3A3A3A3A30000, 0x002E2E2E2E2E0000, 0x0008080808080000, 0x00AFAFAFAFAF0000,
+0x0028282828280000, 0x00B0B0B0B0B00000, 0x0074747474740000, 0x00C2C2C2C2C20000,
+0x00BDBDBDBDBD0000, 0x0036363636360000, 0x0022222222220000, 0x0038383838380000,
+0x0064646464640000, 0x001E1E1E1E1E0000, 0x0039393939390000, 0x002C2C2C2C2C0000,
+0x00A6A6A6A6A60000, 0x0030303030300000, 0x00E5E5E5E5E50000, 0x0044444444440000,
+0x00FDFDFDFDFD0000, 0x0088888888880000, 0x009F9F9F9F9F0000, 0x0065656565650000,
+0x0087878787870000, 0x006B6B6B6B6B0000, 0x00F4F4F4F4F40000, 0x0023232323230000,
+0x0048484848480000, 0x0010101010100000, 0x00D1D1D1D1D10000, 0x0051515151510000,
+0x00C0C0C0C0C00000, 0x00F9F9F9F9F90000, 0x00D2D2D2D2D20000, 0x00A0A0A0A0A00000,
+0x0055555555550000, 0x00A1A1A1A1A10000, 0x0041414141410000, 0x00FAFAFAFAFA0000,
+0x0043434343430000, 0x0013131313130000, 0x00C4C4C4C4C40000, 0x002F2F2F2F2F0000,
+0x00A8A8A8A8A80000, 0x00B6B6B6B6B60000, 0x003C3C3C3C3C0000, 0x002B2B2B2B2B0000,
+0x00C1C1C1C1C10000, 0x00FFFFFFFFFF0000, 0x00C8C8C8C8C80000, 0x00A5A5A5A5A50000,
+0x0020202020200000, 0x0089898989890000, 0x0000000000000000, 0x0090909090900000,
+0x0047474747470000, 0x00EFEFEFEFEF0000, 0x00EAEAEAEAEA0000, 0x00B7B7B7B7B70000,
+0x0015151515150000, 0x0006060606060000, 0x00CDCDCDCDCD0000, 0x00B5B5B5B5B50000,
+0x0012121212120000, 0x007E7E7E7E7E0000, 0x00BBBBBBBBBB0000, 0x0029292929290000,
+0x000F0F0F0F0F0000, 0x00B8B8B8B8B80000, 0x0007070707070000, 0x0004040404040000,
+0x009B9B9B9B9B0000, 0x0094949494940000, 0x0021212121210000, 0x0066666666660000,
+0x00E6E6E6E6E60000, 0x00CECECECECE0000, 0x00EDEDEDEDED0000, 0x00E7E7E7E7E70000,
+0x003B3B3B3B3B0000, 0x00FEFEFEFEFE0000, 0x007F7F7F7F7F0000, 0x00C5C5C5C5C50000,
+0x00A4A4A4A4A40000, 0x0037373737370000, 0x00B1B1B1B1B10000, 0x004C4C4C4C4C0000,
+0x0091919191910000, 0x006E6E6E6E6E0000, 0x008D8D8D8D8D0000, 0x0076767676760000,
+0x0003030303030000, 0x002D2D2D2D2D0000, 0x00DEDEDEDEDE0000, 0x0096969696960000,
+0x0026262626260000, 0x007D7D7D7D7D0000, 0x00C6C6C6C6C60000, 0x005C5C5C5C5C0000,
+0x00D3D3D3D3D30000, 0x00F2F2F2F2F20000, 0x004F4F4F4F4F0000, 0x0019191919190000,
+0x003F3F3F3F3F0000, 0x00DCDCDCDCDC0000, 0x0079797979790000, 0x001D1D1D1D1D0000,
+0x0052525252520000, 0x00EBEBEBEBEB0000, 0x00F3F3F3F3F30000, 0x006D6D6D6D6D0000,
+0x005E5E5E5E5E0000, 0x00FBFBFBFBFB0000, 0x0069696969690000, 0x00B2B2B2B2B20000,
+0x00F0F0F0F0F00000, 0x0031313131310000, 0x000C0C0C0C0C0000, 0x00D4D4D4D4D40000,
+0x00CFCFCFCFCF0000, 0x008C8C8C8C8C0000, 0x00E2E2E2E2E20000, 0x0075757575750000,
+0x00A9A9A9A9A90000, 0x004A4A4A4A4A0000, 0x0057575757570000, 0x0084848484840000,
+0x0011111111110000, 0x0045454545450000, 0x001B1B1B1B1B0000, 0x00F5F5F5F5F50000,
+0x00E4E4E4E4E40000, 0x000E0E0E0E0E0000, 0x0073737373730000, 0x00AAAAAAAAAA0000,
+0x00F1F1F1F1F10000, 0x00DDDDDDDDDD0000, 0x0059595959590000, 0x0014141414140000,
+0x006C6C6C6C6C0000, 0x0092929292920000, 0x0054545454540000, 0x00D0D0D0D0D00000,
+0x0078787878780000, 0x0070707070700000, 0x00E3E3E3E3E30000, 0x0049494949490000,
+0x0080808080800000, 0x0050505050500000, 0x00A7A7A7A7A70000, 0x00F6F6F6F6F60000,
+0x0077777777770000, 0x0093939393930000, 0x0086868686860000, 0x0083838383830000,
+0x002A2A2A2A2A0000, 0x00C7C7C7C7C70000, 0x005B5B5B5B5B0000, 0x00E9E9E9E9E90000,
+0x00EEEEEEEEEE0000, 0x008F8F8F8F8F0000, 0x0001010101010000, 0x003D3D3D3D3D0000 };
+
+const uint64_t Camellia_SBOX3[256] = {
+0x3800383800383800, 0x4100414100414100, 0x1600161600161600, 0x7600767600767600,
+0xD900D9D900D9D900, 0x9300939300939300, 0x6000606000606000, 0xF200F2F200F2F200,
+0x7200727200727200, 0xC200C2C200C2C200, 0xAB00ABAB00ABAB00, 0x9A009A9A009A9A00,
+0x7500757500757500, 0x0600060600060600, 0x5700575700575700, 0xA000A0A000A0A000,
+0x9100919100919100, 0xF700F7F700F7F700, 0xB500B5B500B5B500, 0xC900C9C900C9C900,
+0xA200A2A200A2A200, 0x8C008C8C008C8C00, 0xD200D2D200D2D200, 0x9000909000909000,
+0xF600F6F600F6F600, 0x0700070700070700, 0xA700A7A700A7A700, 0x2700272700272700,
+0x8E008E8E008E8E00, 0xB200B2B200B2B200, 0x4900494900494900, 0xDE00DEDE00DEDE00,
+0x4300434300434300, 0x5C005C5C005C5C00, 0xD700D7D700D7D700, 0xC700C7C700C7C700,
+0x3E003E3E003E3E00, 0xF500F5F500F5F500, 0x8F008F8F008F8F00, 0x6700676700676700,
+0x1F001F1F001F1F00, 0x1800181800181800, 0x6E006E6E006E6E00, 0xAF00AFAF00AFAF00,
+0x2F002F2F002F2F00, 0xE200E2E200E2E200, 0x8500858500858500, 0x0D000D0D000D0D00,
+0x5300535300535300, 0xF000F0F000F0F000, 0x9C009C9C009C9C00, 0x6500656500656500,
+0xEA00EAEA00EAEA00, 0xA300A3A300A3A300, 0xAE00AEAE00AEAE00, 0x9E009E9E009E9E00,
+0xEC00ECEC00ECEC00, 0x8000808000808000, 0x2D002D2D002D2D00, 0x6B006B6B006B6B00,
+0xA800A8A800A8A800, 0x2B002B2B002B2B00, 0x3600363600363600, 0xA600A6A600A6A600,
+0xC500C5C500C5C500, 0x8600868600868600, 0x4D004D4D004D4D00, 0x3300333300333300,
+0xFD00FDFD00FDFD00, 0x6600666600666600, 0x5800585800585800, 0x9600969600969600,
+0x3A003A3A003A3A00, 0x0900090900090900, 0x9500959500959500, 0x1000101000101000,
+0x7800787800787800, 0xD800D8D800D8D800, 0x4200424200424200, 0xCC00CCCC00CCCC00,
+0xEF00EFEF00EFEF00, 0x2600262600262600, 0xE500E5E500E5E500, 0x6100616100616100,
+0x1A001A1A001A1A00, 0x3F003F3F003F3F00, 0x3B003B3B003B3B00, 0x8200828200828200,
+0xB600B6B600B6B600, 0xDB00DBDB00DBDB00, 0xD400D4D400D4D400, 0x9800989800989800,
+0xE800E8E800E8E800, 0x8B008B8B008B8B00, 0x0200020200020200, 0xEB00EBEB00EBEB00,
+0x0A000A0A000A0A00, 0x2C002C2C002C2C00, 0x1D001D1D001D1D00, 0xB000B0B000B0B000,
+0x6F006F6F006F6F00, 0x8D008D8D008D8D00, 0x8800888800888800, 0x0E000E0E000E0E00,
+0x1900191900191900, 0x8700878700878700, 0x4E004E4E004E4E00, 0x0B000B0B000B0B00,
+0xA900A9A900A9A900, 0x0C000C0C000C0C00, 0x7900797900797900, 0x1100111100111100,
+0x7F007F7F007F7F00, 0x2200222200222200, 0xE700E7E700E7E700, 0x5900595900595900,
+0xE100E1E100E1E100, 0xDA00DADA00DADA00, 0x3D003D3D003D3D00, 0xC800C8C800C8C800,
+0x1200121200121200, 0x0400040400040400, 0x7400747400747400, 0x5400545400545400,
+0x3000303000303000, 0x7E007E7E007E7E00, 0xB400B4B400B4B400, 0x2800282800282800,
+0x5500555500555500, 0x6800686800686800, 0x5000505000505000, 0xBE00BEBE00BEBE00,
+0xD000D0D000D0D000, 0xC400C4C400C4C400, 0x3100313100313100, 0xCB00CBCB00CBCB00,
+0x2A002A2A002A2A00, 0xAD00ADAD00ADAD00, 0x0F000F0F000F0F00, 0xCA00CACA00CACA00,
+0x7000707000707000, 0xFF00FFFF00FFFF00, 0x3200323200323200, 0x6900696900696900,
+0x0800080800080800, 0x6200626200626200, 0x0000000000000000, 0x2400242400242400,
+0xD100D1D100D1D100, 0xFB00FBFB00FBFB00, 0xBA00BABA00BABA00, 0xED00EDED00EDED00,
+0x4500454500454500, 0x8100818100818100, 0x7300737300737300, 0x6D006D6D006D6D00,
+0x8400848400848400, 0x9F009F9F009F9F00, 0xEE00EEEE00EEEE00, 0x4A004A4A004A4A00,
+0xC300C3C300C3C300, 0x2E002E2E002E2E00, 0xC100C1C100C1C100, 0x0100010100010100,
+0xE600E6E600E6E600, 0x2500252500252500, 0x4800484800484800, 0x9900999900999900,
+0xB900B9B900B9B900, 0xB300B3B300B3B300, 0x7B007B7B007B7B00, 0xF900F9F900F9F900,
+0xCE00CECE00CECE00, 0xBF00BFBF00BFBF00, 0xDF00DFDF00DFDF00, 0x7100717100717100,
+0x2900292900292900, 0xCD00CDCD00CDCD00, 0x6C006C6C006C6C00, 0x1300131300131300,
+0x6400646400646400, 0x9B009B9B009B9B00, 0x6300636300636300, 0x9D009D9D009D9D00,
+0xC000C0C000C0C000, 0x4B004B4B004B4B00, 0xB700B7B700B7B700, 0xA500A5A500A5A500,
+0x8900898900898900, 0x5F005F5F005F5F00, 0xB100B1B100B1B100, 0x1700171700171700,
+0xF400F4F400F4F400, 0xBC00BCBC00BCBC00, 0xD300D3D300D3D300, 0x4600464600464600,
+0xCF00CFCF00CFCF00, 0x3700373700373700, 0x5E005E5E005E5E00, 0x4700474700474700,
+0x9400949400949400, 0xFA00FAFA00FAFA00, 0xFC00FCFC00FCFC00, 0x5B005B5B005B5B00,
+0x9700979700979700, 0xFE00FEFE00FEFE00, 0x5A005A5A005A5A00, 0xAC00ACAC00ACAC00,
+0x3C003C3C003C3C00, 0x4C004C4C004C4C00, 0x0300030300030300, 0x3500353500353500,
+0xF300F3F300F3F300, 0x2300232300232300, 0xB800B8B800B8B800, 0x5D005D5D005D5D00,
+0x6A006A6A006A6A00, 0x9200929200929200, 0xD500D5D500D5D500, 0x2100212100212100,
+0x4400444400444400, 0x5100515100515100, 0xC600C6C600C6C600, 0x7D007D7D007D7D00,
+0x3900393900393900, 0x8300838300838300, 0xDC00DCDC00DCDC00, 0xAA00AAAA00AAAA00,
+0x7C007C7C007C7C00, 0x7700777700777700, 0x5600565600565600, 0x0500050500050500,
+0x1B001B1B001B1B00, 0xA400A4A400A4A400, 0x1500151500151500, 0x3400343400343400,
+0x1E001E1E001E1E00, 0x1C001C1C001C1C00, 0xF800F8F800F8F800, 0x5200525200525200,
+0x2000202000202000, 0x1400141400141400, 0xE900E9E900E9E900, 0xBD00BDBD00BDBD00,
+0xDD00DDDD00DDDD00, 0xE400E4E400E4E400, 0xA100A1A100A1A100, 0xE000E0E000E0E000,
+0x8A008A8A008A8A00, 0xF100F1F100F1F100, 0xD600D6D600D6D600, 0x7A007A7A007A7A00,
+0xBB00BBBB00BBBB00, 0xE300E3E300E3E300, 0x4000404000404000, 0x4F004F4F004F4F00 };
+
+const uint64_t Camellia_SBOX4[256] = {
+0x7070007000007070, 0x2C2C002C00002C2C, 0xB3B300B30000B3B3, 0xC0C000C00000C0C0,
+0xE4E400E40000E4E4, 0x5757005700005757, 0xEAEA00EA0000EAEA, 0xAEAE00AE0000AEAE,
+0x2323002300002323, 0x6B6B006B00006B6B, 0x4545004500004545, 0xA5A500A50000A5A5,
+0xEDED00ED0000EDED, 0x4F4F004F00004F4F, 0x1D1D001D00001D1D, 0x9292009200009292,
+0x8686008600008686, 0xAFAF00AF0000AFAF, 0x7C7C007C00007C7C, 0x1F1F001F00001F1F,
+0x3E3E003E00003E3E, 0xDCDC00DC0000DCDC, 0x5E5E005E00005E5E, 0x0B0B000B00000B0B,
+0xA6A600A60000A6A6, 0x3939003900003939, 0xD5D500D50000D5D5, 0x5D5D005D00005D5D,
+0xD9D900D90000D9D9, 0x5A5A005A00005A5A, 0x5151005100005151, 0x6C6C006C00006C6C,
+0x8B8B008B00008B8B, 0x9A9A009A00009A9A, 0xFBFB00FB0000FBFB, 0xB0B000B00000B0B0,
+0x7474007400007474, 0x2B2B002B00002B2B, 0xF0F000F00000F0F0, 0x8484008400008484,
+0xDFDF00DF0000DFDF, 0xCBCB00CB0000CBCB, 0x3434003400003434, 0x7676007600007676,
+0x6D6D006D00006D6D, 0xA9A900A90000A9A9, 0xD1D100D10000D1D1, 0x0404000400000404,
+0x1414001400001414, 0x3A3A003A00003A3A, 0xDEDE00DE0000DEDE, 0x1111001100001111,
+0x3232003200003232, 0x9C9C009C00009C9C, 0x5353005300005353, 0xF2F200F20000F2F2,
+0xFEFE00FE0000FEFE, 0xCFCF00CF0000CFCF, 0xC3C300C30000C3C3, 0x7A7A007A00007A7A,
+0x2424002400002424, 0xE8E800E80000E8E8, 0x6060006000006060, 0x6969006900006969,
+0xAAAA00AA0000AAAA, 0xA0A000A00000A0A0, 0xA1A100A10000A1A1, 0x6262006200006262,
+0x5454005400005454, 0x1E1E001E00001E1E, 0xE0E000E00000E0E0, 0x6464006400006464,
+0x1010001000001010, 0x0000000000000000, 0xA3A300A30000A3A3, 0x7575007500007575,
+0x8A8A008A00008A8A, 0xE6E600E60000E6E6, 0x0909000900000909, 0xDDDD00DD0000DDDD,
+0x8787008700008787, 0x8383008300008383, 0xCDCD00CD0000CDCD, 0x9090009000009090,
+0x7373007300007373, 0xF6F600F60000F6F6, 0x9D9D009D00009D9D, 0xBFBF00BF0000BFBF,
+0x5252005200005252, 0xD8D800D80000D8D8, 0xC8C800C80000C8C8, 0xC6C600C60000C6C6,
+0x8181008100008181, 0x6F6F006F00006F6F, 0x1313001300001313, 0x6363006300006363,
+0xE9E900E90000E9E9, 0xA7A700A70000A7A7, 0x9F9F009F00009F9F, 0xBCBC00BC0000BCBC,
+0x2929002900002929, 0xF9F900F90000F9F9, 0x2F2F002F00002F2F, 0xB4B400B40000B4B4,
+0x7878007800007878, 0x0606000600000606, 0xE7E700E70000E7E7, 0x7171007100007171,
+0xD4D400D40000D4D4, 0xABAB00AB0000ABAB, 0x8888008800008888, 0x8D8D008D00008D8D,
+0x7272007200007272, 0xB9B900B90000B9B9, 0xF8F800F80000F8F8, 0xACAC00AC0000ACAC,
+0x3636003600003636, 0x2A2A002A00002A2A, 0x3C3C003C00003C3C, 0xF1F100F10000F1F1,
+0x4040004000004040, 0xD3D300D30000D3D3, 0xBBBB00BB0000BBBB, 0x4343004300004343,
+0x1515001500001515, 0xADAD00AD0000ADAD, 0x7777007700007777, 0x8080008000008080,
+0x8282008200008282, 0xECEC00EC0000ECEC, 0x2727002700002727, 0xE5E500E50000E5E5,
+0x8585008500008585, 0x3535003500003535, 0x0C0C000C00000C0C, 0x4141004100004141,
+0xEFEF00EF0000EFEF, 0x9393009300009393, 0x1919001900001919, 0x2121002100002121,
+0x0E0E000E00000E0E, 0x4E4E004E00004E4E, 0x6565006500006565, 0xBDBD00BD0000BDBD,
+0xB8B800B80000B8B8, 0x8F8F008F00008F8F, 0xEBEB00EB0000EBEB, 0xCECE00CE0000CECE,
+0x3030003000003030, 0x5F5F005F00005F5F, 0xC5C500C50000C5C5, 0x1A1A001A00001A1A,
+0xE1E100E10000E1E1, 0xCACA00CA0000CACA, 0x4747004700004747, 0x3D3D003D00003D3D,
+0x0101000100000101, 0xD6D600D60000D6D6, 0x5656005600005656, 0x4D4D004D00004D4D,
+0x0D0D000D00000D0D, 0x6666006600006666, 0xCCCC00CC0000CCCC, 0x2D2D002D00002D2D,
+0x1212001200001212, 0x2020002000002020, 0xB1B100B10000B1B1, 0x9999009900009999,
+0x4C4C004C00004C4C, 0xC2C200C20000C2C2, 0x7E7E007E00007E7E, 0x0505000500000505,
+0xB7B700B70000B7B7, 0x3131003100003131, 0x1717001700001717, 0xD7D700D70000D7D7,
+0x5858005800005858, 0x6161006100006161, 0x1B1B001B00001B1B, 0x1C1C001C00001C1C,
+0x0F0F000F00000F0F, 0x1616001600001616, 0x1818001800001818, 0x2222002200002222,
+0x4444004400004444, 0xB2B200B20000B2B2, 0xB5B500B50000B5B5, 0x9191009100009191,
+0x0808000800000808, 0xA8A800A80000A8A8, 0xFCFC00FC0000FCFC, 0x5050005000005050,
+0xD0D000D00000D0D0, 0x7D7D007D00007D7D, 0x8989008900008989, 0x9797009700009797,
+0x5B5B005B00005B5B, 0x9595009500009595, 0xFFFF00FF0000FFFF, 0xD2D200D20000D2D2,
+0xC4C400C40000C4C4, 0x4848004800004848, 0xF7F700F70000F7F7, 0xDBDB00DB0000DBDB,
+0x0303000300000303, 0xDADA00DA0000DADA, 0x3F3F003F00003F3F, 0x9494009400009494,
+0x5C5C005C00005C5C, 0x0202000200000202, 0x4A4A004A00004A4A, 0x3333003300003333,
+0x6767006700006767, 0xF3F300F30000F3F3, 0x7F7F007F00007F7F, 0xE2E200E20000E2E2,
+0x9B9B009B00009B9B, 0x2626002600002626, 0x3737003700003737, 0x3B3B003B00003B3B,
+0x9696009600009696, 0x4B4B004B00004B4B, 0xBEBE00BE0000BEBE, 0x2E2E002E00002E2E,
+0x7979007900007979, 0x8C8C008C00008C8C, 0x6E6E006E00006E6E, 0x8E8E008E00008E8E,
+0xF5F500F50000F5F5, 0xB6B600B60000B6B6, 0xFDFD00FD0000FDFD, 0x5959005900005959,
+0x9898009800009898, 0x6A6A006A00006A6A, 0x4646004600004646, 0xBABA00BA0000BABA,
+0x2525002500002525, 0x4242004200004242, 0xA2A200A20000A2A2, 0xFAFA00FA0000FAFA,
+0x0707000700000707, 0x5555005500005555, 0xEEEE00EE0000EEEE, 0x0A0A000A00000A0A,
+0x4949004900004949, 0x6868006800006868, 0x3838003800003838, 0xA4A400A40000A4A4,
+0x2828002800002828, 0x7B7B007B00007B7B, 0xC9C900C90000C9C9, 0xC1C100C10000C1C1,
+0xE3E300E30000E3E3, 0xF4F400F40000F4F4, 0xC7C700C70000C7C7, 0x9E9E009E00009E9E };
+
+const uint64_t Camellia_SBOX5[256] = {
+0x00E0E0E000E0E0E0, 0x0005050500050505, 0x0058585800585858, 0x00D9D9D900D9D9D9,
+0x0067676700676767, 0x004E4E4E004E4E4E, 0x0081818100818181, 0x00CBCBCB00CBCBCB,
+0x00C9C9C900C9C9C9, 0x000B0B0B000B0B0B, 0x00AEAEAE00AEAEAE, 0x006A6A6A006A6A6A,
+0x00D5D5D500D5D5D5, 0x0018181800181818, 0x005D5D5D005D5D5D, 0x0082828200828282,
+0x0046464600464646, 0x00DFDFDF00DFDFDF, 0x00D6D6D600D6D6D6, 0x0027272700272727,
+0x008A8A8A008A8A8A, 0x0032323200323232, 0x004B4B4B004B4B4B, 0x0042424200424242,
+0x00DBDBDB00DBDBDB, 0x001C1C1C001C1C1C, 0x009E9E9E009E9E9E, 0x009C9C9C009C9C9C,
+0x003A3A3A003A3A3A, 0x00CACACA00CACACA, 0x0025252500252525, 0x007B7B7B007B7B7B,
+0x000D0D0D000D0D0D, 0x0071717100717171, 0x005F5F5F005F5F5F, 0x001F1F1F001F1F1F,
+0x00F8F8F800F8F8F8, 0x00D7D7D700D7D7D7, 0x003E3E3E003E3E3E, 0x009D9D9D009D9D9D,
+0x007C7C7C007C7C7C, 0x0060606000606060, 0x00B9B9B900B9B9B9, 0x00BEBEBE00BEBEBE,
+0x00BCBCBC00BCBCBC, 0x008B8B8B008B8B8B, 0x0016161600161616, 0x0034343400343434,
+0x004D4D4D004D4D4D, 0x00C3C3C300C3C3C3, 0x0072727200727272, 0x0095959500959595,
+0x00ABABAB00ABABAB, 0x008E8E8E008E8E8E, 0x00BABABA00BABABA, 0x007A7A7A007A7A7A,
+0x00B3B3B300B3B3B3, 0x0002020200020202, 0x00B4B4B400B4B4B4, 0x00ADADAD00ADADAD,
+0x00A2A2A200A2A2A2, 0x00ACACAC00ACACAC, 0x00D8D8D800D8D8D8, 0x009A9A9A009A9A9A,
+0x0017171700171717, 0x001A1A1A001A1A1A, 0x0035353500353535, 0x00CCCCCC00CCCCCC,
+0x00F7F7F700F7F7F7, 0x0099999900999999, 0x0061616100616161, 0x005A5A5A005A5A5A,
+0x00E8E8E800E8E8E8, 0x0024242400242424, 0x0056565600565656, 0x0040404000404040,
+0x00E1E1E100E1E1E1, 0x0063636300636363, 0x0009090900090909, 0x0033333300333333,
+0x00BFBFBF00BFBFBF, 0x0098989800989898, 0x0097979700979797, 0x0085858500858585,
+0x0068686800686868, 0x00FCFCFC00FCFCFC, 0x00ECECEC00ECECEC, 0x000A0A0A000A0A0A,
+0x00DADADA00DADADA, 0x006F6F6F006F6F6F, 0x0053535300535353, 0x0062626200626262,
+0x00A3A3A300A3A3A3, 0x002E2E2E002E2E2E, 0x0008080800080808, 0x00AFAFAF00AFAFAF,
+0x0028282800282828, 0x00B0B0B000B0B0B0, 0x0074747400747474, 0x00C2C2C200C2C2C2,
+0x00BDBDBD00BDBDBD, 0x0036363600363636, 0x0022222200222222, 0x0038383800383838,
+0x0064646400646464, 0x001E1E1E001E1E1E, 0x0039393900393939, 0x002C2C2C002C2C2C,
+0x00A6A6A600A6A6A6, 0x0030303000303030, 0x00E5E5E500E5E5E5, 0x0044444400444444,
+0x00FDFDFD00FDFDFD, 0x0088888800888888, 0x009F9F9F009F9F9F, 0x0065656500656565,
+0x0087878700878787, 0x006B6B6B006B6B6B, 0x00F4F4F400F4F4F4, 0x0023232300232323,
+0x0048484800484848, 0x0010101000101010, 0x00D1D1D100D1D1D1, 0x0051515100515151,
+0x00C0C0C000C0C0C0, 0x00F9F9F900F9F9F9, 0x00D2D2D200D2D2D2, 0x00A0A0A000A0A0A0,
+0x0055555500555555, 0x00A1A1A100A1A1A1, 0x0041414100414141, 0x00FAFAFA00FAFAFA,
+0x0043434300434343, 0x0013131300131313, 0x00C4C4C400C4C4C4, 0x002F2F2F002F2F2F,
+0x00A8A8A800A8A8A8, 0x00B6B6B600B6B6B6, 0x003C3C3C003C3C3C, 0x002B2B2B002B2B2B,
+0x00C1C1C100C1C1C1, 0x00FFFFFF00FFFFFF, 0x00C8C8C800C8C8C8, 0x00A5A5A500A5A5A5,
+0x0020202000202020, 0x0089898900898989, 0x0000000000000000, 0x0090909000909090,
+0x0047474700474747, 0x00EFEFEF00EFEFEF, 0x00EAEAEA00EAEAEA, 0x00B7B7B700B7B7B7,
+0x0015151500151515, 0x0006060600060606, 0x00CDCDCD00CDCDCD, 0x00B5B5B500B5B5B5,
+0x0012121200121212, 0x007E7E7E007E7E7E, 0x00BBBBBB00BBBBBB, 0x0029292900292929,
+0x000F0F0F000F0F0F, 0x00B8B8B800B8B8B8, 0x0007070700070707, 0x0004040400040404,
+0x009B9B9B009B9B9B, 0x0094949400949494, 0x0021212100212121, 0x0066666600666666,
+0x00E6E6E600E6E6E6, 0x00CECECE00CECECE, 0x00EDEDED00EDEDED, 0x00E7E7E700E7E7E7,
+0x003B3B3B003B3B3B, 0x00FEFEFE00FEFEFE, 0x007F7F7F007F7F7F, 0x00C5C5C500C5C5C5,
+0x00A4A4A400A4A4A4, 0x0037373700373737, 0x00B1B1B100B1B1B1, 0x004C4C4C004C4C4C,
+0x0091919100919191, 0x006E6E6E006E6E6E, 0x008D8D8D008D8D8D, 0x0076767600767676,
+0x0003030300030303, 0x002D2D2D002D2D2D, 0x00DEDEDE00DEDEDE, 0x0096969600969696,
+0x0026262600262626, 0x007D7D7D007D7D7D, 0x00C6C6C600C6C6C6, 0x005C5C5C005C5C5C,
+0x00D3D3D300D3D3D3, 0x00F2F2F200F2F2F2, 0x004F4F4F004F4F4F, 0x0019191900191919,
+0x003F3F3F003F3F3F, 0x00DCDCDC00DCDCDC, 0x0079797900797979, 0x001D1D1D001D1D1D,
+0x0052525200525252, 0x00EBEBEB00EBEBEB, 0x00F3F3F300F3F3F3, 0x006D6D6D006D6D6D,
+0x005E5E5E005E5E5E, 0x00FBFBFB00FBFBFB, 0x0069696900696969, 0x00B2B2B200B2B2B2,
+0x00F0F0F000F0F0F0, 0x0031313100313131, 0x000C0C0C000C0C0C, 0x00D4D4D400D4D4D4,
+0x00CFCFCF00CFCFCF, 0x008C8C8C008C8C8C, 0x00E2E2E200E2E2E2, 0x0075757500757575,
+0x00A9A9A900A9A9A9, 0x004A4A4A004A4A4A, 0x0057575700575757, 0x0084848400848484,
+0x0011111100111111, 0x0045454500454545, 0x001B1B1B001B1B1B, 0x00F5F5F500F5F5F5,
+0x00E4E4E400E4E4E4, 0x000E0E0E000E0E0E, 0x0073737300737373, 0x00AAAAAA00AAAAAA,
+0x00F1F1F100F1F1F1, 0x00DDDDDD00DDDDDD, 0x0059595900595959, 0x0014141400141414,
+0x006C6C6C006C6C6C, 0x0092929200929292, 0x0054545400545454, 0x00D0D0D000D0D0D0,
+0x0078787800787878, 0x0070707000707070, 0x00E3E3E300E3E3E3, 0x0049494900494949,
+0x0080808000808080, 0x0050505000505050, 0x00A7A7A700A7A7A7, 0x00F6F6F600F6F6F6,
+0x0077777700777777, 0x0093939300939393, 0x0086868600868686, 0x0083838300838383,
+0x002A2A2A002A2A2A, 0x00C7C7C700C7C7C7, 0x005B5B5B005B5B5B, 0x00E9E9E900E9E9E9,
+0x00EEEEEE00EEEEEE, 0x008F8F8F008F8F8F, 0x0001010100010101, 0x003D3D3D003D3D3D };
+
+const uint64_t Camellia_SBOX6[256] = {
+0x3800383838003838, 0x4100414141004141, 0x1600161616001616, 0x7600767676007676,
+0xD900D9D9D900D9D9, 0x9300939393009393, 0x6000606060006060, 0xF200F2F2F200F2F2,
+0x7200727272007272, 0xC200C2C2C200C2C2, 0xAB00ABABAB00ABAB, 0x9A009A9A9A009A9A,
+0x7500757575007575, 0x0600060606000606, 0x5700575757005757, 0xA000A0A0A000A0A0,
+0x9100919191009191, 0xF700F7F7F700F7F7, 0xB500B5B5B500B5B5, 0xC900C9C9C900C9C9,
+0xA200A2A2A200A2A2, 0x8C008C8C8C008C8C, 0xD200D2D2D200D2D2, 0x9000909090009090,
+0xF600F6F6F600F6F6, 0x0700070707000707, 0xA700A7A7A700A7A7, 0x2700272727002727,
+0x8E008E8E8E008E8E, 0xB200B2B2B200B2B2, 0x4900494949004949, 0xDE00DEDEDE00DEDE,
+0x4300434343004343, 0x5C005C5C5C005C5C, 0xD700D7D7D700D7D7, 0xC700C7C7C700C7C7,
+0x3E003E3E3E003E3E, 0xF500F5F5F500F5F5, 0x8F008F8F8F008F8F, 0x6700676767006767,
+0x1F001F1F1F001F1F, 0x1800181818001818, 0x6E006E6E6E006E6E, 0xAF00AFAFAF00AFAF,
+0x2F002F2F2F002F2F, 0xE200E2E2E200E2E2, 0x8500858585008585, 0x0D000D0D0D000D0D,
+0x5300535353005353, 0xF000F0F0F000F0F0, 0x9C009C9C9C009C9C, 0x6500656565006565,
+0xEA00EAEAEA00EAEA, 0xA300A3A3A300A3A3, 0xAE00AEAEAE00AEAE, 0x9E009E9E9E009E9E,
+0xEC00ECECEC00ECEC, 0x8000808080008080, 0x2D002D2D2D002D2D, 0x6B006B6B6B006B6B,
+0xA800A8A8A800A8A8, 0x2B002B2B2B002B2B, 0x3600363636003636, 0xA600A6A6A600A6A6,
+0xC500C5C5C500C5C5, 0x8600868686008686, 0x4D004D4D4D004D4D, 0x3300333333003333,
+0xFD00FDFDFD00FDFD, 0x6600666666006666, 0x5800585858005858, 0x9600969696009696,
+0x3A003A3A3A003A3A, 0x0900090909000909, 0x9500959595009595, 0x1000101010001010,
+0x7800787878007878, 0xD800D8D8D800D8D8, 0x4200424242004242, 0xCC00CCCCCC00CCCC,
+0xEF00EFEFEF00EFEF, 0x2600262626002626, 0xE500E5E5E500E5E5, 0x6100616161006161,
+0x1A001A1A1A001A1A, 0x3F003F3F3F003F3F, 0x3B003B3B3B003B3B, 0x8200828282008282,
+0xB600B6B6B600B6B6, 0xDB00DBDBDB00DBDB, 0xD400D4D4D400D4D4, 0x9800989898009898,
+0xE800E8E8E800E8E8, 0x8B008B8B8B008B8B, 0x0200020202000202, 0xEB00EBEBEB00EBEB,
+0x0A000A0A0A000A0A, 0x2C002C2C2C002C2C, 0x1D001D1D1D001D1D, 0xB000B0B0B000B0B0,
+0x6F006F6F6F006F6F, 0x8D008D8D8D008D8D, 0x8800888888008888, 0x0E000E0E0E000E0E,
+0x1900191919001919, 0x8700878787008787, 0x4E004E4E4E004E4E, 0x0B000B0B0B000B0B,
+0xA900A9A9A900A9A9, 0x0C000C0C0C000C0C, 0x7900797979007979, 0x1100111111001111,
+0x7F007F7F7F007F7F, 0x2200222222002222, 0xE700E7E7E700E7E7, 0x5900595959005959,
+0xE100E1E1E100E1E1, 0xDA00DADADA00DADA, 0x3D003D3D3D003D3D, 0xC800C8C8C800C8C8,
+0x1200121212001212, 0x0400040404000404, 0x7400747474007474, 0x5400545454005454,
+0x3000303030003030, 0x7E007E7E7E007E7E, 0xB400B4B4B400B4B4, 0x2800282828002828,
+0x5500555555005555, 0x6800686868006868, 0x5000505050005050, 0xBE00BEBEBE00BEBE,
+0xD000D0D0D000D0D0, 0xC400C4C4C400C4C4, 0x3100313131003131, 0xCB00CBCBCB00CBCB,
+0x2A002A2A2A002A2A, 0xAD00ADADAD00ADAD, 0x0F000F0F0F000F0F, 0xCA00CACACA00CACA,
+0x7000707070007070, 0xFF00FFFFFF00FFFF, 0x3200323232003232, 0x6900696969006969,
+0x0800080808000808, 0x6200626262006262, 0x0000000000000000, 0x2400242424002424,
+0xD100D1D1D100D1D1, 0xFB00FBFBFB00FBFB, 0xBA00BABABA00BABA, 0xED00EDEDED00EDED,
+0x4500454545004545, 0x8100818181008181, 0x7300737373007373, 0x6D006D6D6D006D6D,
+0x8400848484008484, 0x9F009F9F9F009F9F, 0xEE00EEEEEE00EEEE, 0x4A004A4A4A004A4A,
+0xC300C3C3C300C3C3, 0x2E002E2E2E002E2E, 0xC100C1C1C100C1C1, 0x0100010101000101,
+0xE600E6E6E600E6E6, 0x2500252525002525, 0x4800484848004848, 0x9900999999009999,
+0xB900B9B9B900B9B9, 0xB300B3B3B300B3B3, 0x7B007B7B7B007B7B, 0xF900F9F9F900F9F9,
+0xCE00CECECE00CECE, 0xBF00BFBFBF00BFBF, 0xDF00DFDFDF00DFDF, 0x7100717171007171,
+0x2900292929002929, 0xCD00CDCDCD00CDCD, 0x6C006C6C6C006C6C, 0x1300131313001313,
+0x6400646464006464, 0x9B009B9B9B009B9B, 0x6300636363006363, 0x9D009D9D9D009D9D,
+0xC000C0C0C000C0C0, 0x4B004B4B4B004B4B, 0xB700B7B7B700B7B7, 0xA500A5A5A500A5A5,
+0x8900898989008989, 0x5F005F5F5F005F5F, 0xB100B1B1B100B1B1, 0x1700171717001717,
+0xF400F4F4F400F4F4, 0xBC00BCBCBC00BCBC, 0xD300D3D3D300D3D3, 0x4600464646004646,
+0xCF00CFCFCF00CFCF, 0x3700373737003737, 0x5E005E5E5E005E5E, 0x4700474747004747,
+0x9400949494009494, 0xFA00FAFAFA00FAFA, 0xFC00FCFCFC00FCFC, 0x5B005B5B5B005B5B,
+0x9700979797009797, 0xFE00FEFEFE00FEFE, 0x5A005A5A5A005A5A, 0xAC00ACACAC00ACAC,
+0x3C003C3C3C003C3C, 0x4C004C4C4C004C4C, 0x0300030303000303, 0x3500353535003535,
+0xF300F3F3F300F3F3, 0x2300232323002323, 0xB800B8B8B800B8B8, 0x5D005D5D5D005D5D,
+0x6A006A6A6A006A6A, 0x9200929292009292, 0xD500D5D5D500D5D5, 0x2100212121002121,
+0x4400444444004444, 0x5100515151005151, 0xC600C6C6C600C6C6, 0x7D007D7D7D007D7D,
+0x3900393939003939, 0x8300838383008383, 0xDC00DCDCDC00DCDC, 0xAA00AAAAAA00AAAA,
+0x7C007C7C7C007C7C, 0x7700777777007777, 0x5600565656005656, 0x0500050505000505,
+0x1B001B1B1B001B1B, 0xA400A4A4A400A4A4, 0x1500151515001515, 0x3400343434003434,
+0x1E001E1E1E001E1E, 0x1C001C1C1C001C1C, 0xF800F8F8F800F8F8, 0x5200525252005252,
+0x2000202020002020, 0x1400141414001414, 0xE900E9E9E900E9E9, 0xBD00BDBDBD00BDBD,
+0xDD00DDDDDD00DDDD, 0xE400E4E4E400E4E4, 0xA100A1A1A100A1A1, 0xE000E0E0E000E0E0,
+0x8A008A8A8A008A8A, 0xF100F1F1F100F1F1, 0xD600D6D6D600D6D6, 0x7A007A7A7A007A7A,
+0xBB00BBBBBB00BBBB, 0xE300E3E3E300E3E3, 0x4000404040004040, 0x4F004F4F4F004F4F };
+
+const uint64_t Camellia_SBOX7[256] = {
+0x7070007070700070, 0x2C2C002C2C2C002C, 0xB3B300B3B3B300B3, 0xC0C000C0C0C000C0,
+0xE4E400E4E4E400E4, 0x5757005757570057, 0xEAEA00EAEAEA00EA, 0xAEAE00AEAEAE00AE,
+0x2323002323230023, 0x6B6B006B6B6B006B, 0x4545004545450045, 0xA5A500A5A5A500A5,
+0xEDED00EDEDED00ED, 0x4F4F004F4F4F004F, 0x1D1D001D1D1D001D, 0x9292009292920092,
+0x8686008686860086, 0xAFAF00AFAFAF00AF, 0x7C7C007C7C7C007C, 0x1F1F001F1F1F001F,
+0x3E3E003E3E3E003E, 0xDCDC00DCDCDC00DC, 0x5E5E005E5E5E005E, 0x0B0B000B0B0B000B,
+0xA6A600A6A6A600A6, 0x3939003939390039, 0xD5D500D5D5D500D5, 0x5D5D005D5D5D005D,
+0xD9D900D9D9D900D9, 0x5A5A005A5A5A005A, 0x5151005151510051, 0x6C6C006C6C6C006C,
+0x8B8B008B8B8B008B, 0x9A9A009A9A9A009A, 0xFBFB00FBFBFB00FB, 0xB0B000B0B0B000B0,
+0x7474007474740074, 0x2B2B002B2B2B002B, 0xF0F000F0F0F000F0, 0x8484008484840084,
+0xDFDF00DFDFDF00DF, 0xCBCB00CBCBCB00CB, 0x3434003434340034, 0x7676007676760076,
+0x6D6D006D6D6D006D, 0xA9A900A9A9A900A9, 0xD1D100D1D1D100D1, 0x0404000404040004,
+0x1414001414140014, 0x3A3A003A3A3A003A, 0xDEDE00DEDEDE00DE, 0x1111001111110011,
+0x3232003232320032, 0x9C9C009C9C9C009C, 0x5353005353530053, 0xF2F200F2F2F200F2,
+0xFEFE00FEFEFE00FE, 0xCFCF00CFCFCF00CF, 0xC3C300C3C3C300C3, 0x7A7A007A7A7A007A,
+0x2424002424240024, 0xE8E800E8E8E800E8, 0x6060006060600060, 0x6969006969690069,
+0xAAAA00AAAAAA00AA, 0xA0A000A0A0A000A0, 0xA1A100A1A1A100A1, 0x6262006262620062,
+0x5454005454540054, 0x1E1E001E1E1E001E, 0xE0E000E0E0E000E0, 0x6464006464640064,
+0x1010001010100010, 0x0000000000000000, 0xA3A300A3A3A300A3, 0x7575007575750075,
+0x8A8A008A8A8A008A, 0xE6E600E6E6E600E6, 0x0909000909090009, 0xDDDD00DDDDDD00DD,
+0x8787008787870087, 0x8383008383830083, 0xCDCD00CDCDCD00CD, 0x9090009090900090,
+0x7373007373730073, 0xF6F600F6F6F600F6, 0x9D9D009D9D9D009D, 0xBFBF00BFBFBF00BF,
+0x5252005252520052, 0xD8D800D8D8D800D8, 0xC8C800C8C8C800C8, 0xC6C600C6C6C600C6,
+0x8181008181810081, 0x6F6F006F6F6F006F, 0x1313001313130013, 0x6363006363630063,
+0xE9E900E9E9E900E9, 0xA7A700A7A7A700A7, 0x9F9F009F9F9F009F, 0xBCBC00BCBCBC00BC,
+0x2929002929290029, 0xF9F900F9F9F900F9, 0x2F2F002F2F2F002F, 0xB4B400B4B4B400B4,
+0x7878007878780078, 0x0606000606060006, 0xE7E700E7E7E700E7, 0x7171007171710071,
+0xD4D400D4D4D400D4, 0xABAB00ABABAB00AB, 0x8888008888880088, 0x8D8D008D8D8D008D,
+0x7272007272720072, 0xB9B900B9B9B900B9, 0xF8F800F8F8F800F8, 0xACAC00ACACAC00AC,
+0x3636003636360036, 0x2A2A002A2A2A002A, 0x3C3C003C3C3C003C, 0xF1F100F1F1F100F1,
+0x4040004040400040, 0xD3D300D3D3D300D3, 0xBBBB00BBBBBB00BB, 0x4343004343430043,
+0x1515001515150015, 0xADAD00ADADAD00AD, 0x7777007777770077, 0x8080008080800080,
+0x8282008282820082, 0xECEC00ECECEC00EC, 0x2727002727270027, 0xE5E500E5E5E500E5,
+0x8585008585850085, 0x3535003535350035, 0x0C0C000C0C0C000C, 0x4141004141410041,
+0xEFEF00EFEFEF00EF, 0x9393009393930093, 0x1919001919190019, 0x2121002121210021,
+0x0E0E000E0E0E000E, 0x4E4E004E4E4E004E, 0x6565006565650065, 0xBDBD00BDBDBD00BD,
+0xB8B800B8B8B800B8, 0x8F8F008F8F8F008F, 0xEBEB00EBEBEB00EB, 0xCECE00CECECE00CE,
+0x3030003030300030, 0x5F5F005F5F5F005F, 0xC5C500C5C5C500C5, 0x1A1A001A1A1A001A,
+0xE1E100E1E1E100E1, 0xCACA00CACACA00CA, 0x4747004747470047, 0x3D3D003D3D3D003D,
+0x0101000101010001, 0xD6D600D6D6D600D6, 0x5656005656560056, 0x4D4D004D4D4D004D,
+0x0D0D000D0D0D000D, 0x6666006666660066, 0xCCCC00CCCCCC00CC, 0x2D2D002D2D2D002D,
+0x1212001212120012, 0x2020002020200020, 0xB1B100B1B1B100B1, 0x9999009999990099,
+0x4C4C004C4C4C004C, 0xC2C200C2C2C200C2, 0x7E7E007E7E7E007E, 0x0505000505050005,
+0xB7B700B7B7B700B7, 0x3131003131310031, 0x1717001717170017, 0xD7D700D7D7D700D7,
+0x5858005858580058, 0x6161006161610061, 0x1B1B001B1B1B001B, 0x1C1C001C1C1C001C,
+0x0F0F000F0F0F000F, 0x1616001616160016, 0x1818001818180018, 0x2222002222220022,
+0x4444004444440044, 0xB2B200B2B2B200B2, 0xB5B500B5B5B500B5, 0x9191009191910091,
+0x0808000808080008, 0xA8A800A8A8A800A8, 0xFCFC00FCFCFC00FC, 0x5050005050500050,
+0xD0D000D0D0D000D0, 0x7D7D007D7D7D007D, 0x8989008989890089, 0x9797009797970097,
+0x5B5B005B5B5B005B, 0x9595009595950095, 0xFFFF00FFFFFF00FF, 0xD2D200D2D2D200D2,
+0xC4C400C4C4C400C4, 0x4848004848480048, 0xF7F700F7F7F700F7, 0xDBDB00DBDBDB00DB,
+0x0303000303030003, 0xDADA00DADADA00DA, 0x3F3F003F3F3F003F, 0x9494009494940094,
+0x5C5C005C5C5C005C, 0x0202000202020002, 0x4A4A004A4A4A004A, 0x3333003333330033,
+0x6767006767670067, 0xF3F300F3F3F300F3, 0x7F7F007F7F7F007F, 0xE2E200E2E2E200E2,
+0x9B9B009B9B9B009B, 0x2626002626260026, 0x3737003737370037, 0x3B3B003B3B3B003B,
+0x9696009696960096, 0x4B4B004B4B4B004B, 0xBEBE00BEBEBE00BE, 0x2E2E002E2E2E002E,
+0x7979007979790079, 0x8C8C008C8C8C008C, 0x6E6E006E6E6E006E, 0x8E8E008E8E8E008E,
+0xF5F500F5F5F500F5, 0xB6B600B6B6B600B6, 0xFDFD00FDFDFD00FD, 0x5959005959590059,
+0x9898009898980098, 0x6A6A006A6A6A006A, 0x4646004646460046, 0xBABA00BABABA00BA,
+0x2525002525250025, 0x4242004242420042, 0xA2A200A2A2A200A2, 0xFAFA00FAFAFA00FA,
+0x0707000707070007, 0x5555005555550055, 0xEEEE00EEEEEE00EE, 0x0A0A000A0A0A000A,
+0x4949004949490049, 0x6868006868680068, 0x3838003838380038, 0xA4A400A4A4A400A4,
+0x2828002828280028, 0x7B7B007B7B7B007B, 0xC9C900C9C9C900C9, 0xC1C100C1C1C100C1,
+0xE3E300E3E3E300E3, 0xF4F400F4F4F400F4, 0xC7C700C7C7C700C7, 0x9E9E009E9E9E009E };
+
+const uint64_t Camellia_SBOX8[256] = {
+0x7070700070707000, 0x8282820082828200, 0x2C2C2C002C2C2C00, 0xECECEC00ECECEC00,
+0xB3B3B300B3B3B300, 0x2727270027272700, 0xC0C0C000C0C0C000, 0xE5E5E500E5E5E500,
+0xE4E4E400E4E4E400, 0x8585850085858500, 0x5757570057575700, 0x3535350035353500,
+0xEAEAEA00EAEAEA00, 0x0C0C0C000C0C0C00, 0xAEAEAE00AEAEAE00, 0x4141410041414100,
+0x2323230023232300, 0xEFEFEF00EFEFEF00, 0x6B6B6B006B6B6B00, 0x9393930093939300,
+0x4545450045454500, 0x1919190019191900, 0xA5A5A500A5A5A500, 0x2121210021212100,
+0xEDEDED00EDEDED00, 0x0E0E0E000E0E0E00, 0x4F4F4F004F4F4F00, 0x4E4E4E004E4E4E00,
+0x1D1D1D001D1D1D00, 0x6565650065656500, 0x9292920092929200, 0xBDBDBD00BDBDBD00,
+0x8686860086868600, 0xB8B8B800B8B8B800, 0xAFAFAF00AFAFAF00, 0x8F8F8F008F8F8F00,
+0x7C7C7C007C7C7C00, 0xEBEBEB00EBEBEB00, 0x1F1F1F001F1F1F00, 0xCECECE00CECECE00,
+0x3E3E3E003E3E3E00, 0x3030300030303000, 0xDCDCDC00DCDCDC00, 0x5F5F5F005F5F5F00,
+0x5E5E5E005E5E5E00, 0xC5C5C500C5C5C500, 0x0B0B0B000B0B0B00, 0x1A1A1A001A1A1A00,
+0xA6A6A600A6A6A600, 0xE1E1E100E1E1E100, 0x3939390039393900, 0xCACACA00CACACA00,
+0xD5D5D500D5D5D500, 0x4747470047474700, 0x5D5D5D005D5D5D00, 0x3D3D3D003D3D3D00,
+0xD9D9D900D9D9D900, 0x0101010001010100, 0x5A5A5A005A5A5A00, 0xD6D6D600D6D6D600,
+0x5151510051515100, 0x5656560056565600, 0x6C6C6C006C6C6C00, 0x4D4D4D004D4D4D00,
+0x8B8B8B008B8B8B00, 0x0D0D0D000D0D0D00, 0x9A9A9A009A9A9A00, 0x6666660066666600,
+0xFBFBFB00FBFBFB00, 0xCCCCCC00CCCCCC00, 0xB0B0B000B0B0B000, 0x2D2D2D002D2D2D00,
+0x7474740074747400, 0x1212120012121200, 0x2B2B2B002B2B2B00, 0x2020200020202000,
+0xF0F0F000F0F0F000, 0xB1B1B100B1B1B100, 0x8484840084848400, 0x9999990099999900,
+0xDFDFDF00DFDFDF00, 0x4C4C4C004C4C4C00, 0xCBCBCB00CBCBCB00, 0xC2C2C200C2C2C200,
+0x3434340034343400, 0x7E7E7E007E7E7E00, 0x7676760076767600, 0x0505050005050500,
+0x6D6D6D006D6D6D00, 0xB7B7B700B7B7B700, 0xA9A9A900A9A9A900, 0x3131310031313100,
+0xD1D1D100D1D1D100, 0x1717170017171700, 0x0404040004040400, 0xD7D7D700D7D7D700,
+0x1414140014141400, 0x5858580058585800, 0x3A3A3A003A3A3A00, 0x6161610061616100,
+0xDEDEDE00DEDEDE00, 0x1B1B1B001B1B1B00, 0x1111110011111100, 0x1C1C1C001C1C1C00,
+0x3232320032323200, 0x0F0F0F000F0F0F00, 0x9C9C9C009C9C9C00, 0x1616160016161600,
+0x5353530053535300, 0x1818180018181800, 0xF2F2F200F2F2F200, 0x2222220022222200,
+0xFEFEFE00FEFEFE00, 0x4444440044444400, 0xCFCFCF00CFCFCF00, 0xB2B2B200B2B2B200,
+0xC3C3C300C3C3C300, 0xB5B5B500B5B5B500, 0x7A7A7A007A7A7A00, 0x9191910091919100,
+0x2424240024242400, 0x0808080008080800, 0xE8E8E800E8E8E800, 0xA8A8A800A8A8A800,
+0x6060600060606000, 0xFCFCFC00FCFCFC00, 0x6969690069696900, 0x5050500050505000,
+0xAAAAAA00AAAAAA00, 0xD0D0D000D0D0D000, 0xA0A0A000A0A0A000, 0x7D7D7D007D7D7D00,
+0xA1A1A100A1A1A100, 0x8989890089898900, 0x6262620062626200, 0x9797970097979700,
+0x5454540054545400, 0x5B5B5B005B5B5B00, 0x1E1E1E001E1E1E00, 0x9595950095959500,
+0xE0E0E000E0E0E000, 0xFFFFFF00FFFFFF00, 0x6464640064646400, 0xD2D2D200D2D2D200,
+0x1010100010101000, 0xC4C4C400C4C4C400, 0x0000000000000000, 0x4848480048484800,
+0xA3A3A300A3A3A300, 0xF7F7F700F7F7F700, 0x7575750075757500, 0xDBDBDB00DBDBDB00,
+0x8A8A8A008A8A8A00, 0x0303030003030300, 0xE6E6E600E6E6E600, 0xDADADA00DADADA00,
+0x0909090009090900, 0x3F3F3F003F3F3F00, 0xDDDDDD00DDDDDD00, 0x9494940094949400,
+0x8787870087878700, 0x5C5C5C005C5C5C00, 0x8383830083838300, 0x0202020002020200,
+0xCDCDCD00CDCDCD00, 0x4A4A4A004A4A4A00, 0x9090900090909000, 0x3333330033333300,
+0x7373730073737300, 0x6767670067676700, 0xF6F6F600F6F6F600, 0xF3F3F300F3F3F300,
+0x9D9D9D009D9D9D00, 0x7F7F7F007F7F7F00, 0xBFBFBF00BFBFBF00, 0xE2E2E200E2E2E200,
+0x5252520052525200, 0x9B9B9B009B9B9B00, 0xD8D8D800D8D8D800, 0x2626260026262600,
+0xC8C8C800C8C8C800, 0x3737370037373700, 0xC6C6C600C6C6C600, 0x3B3B3B003B3B3B00,
+0x8181810081818100, 0x9696960096969600, 0x6F6F6F006F6F6F00, 0x4B4B4B004B4B4B00,
+0x1313130013131300, 0xBEBEBE00BEBEBE00, 0x6363630063636300, 0x2E2E2E002E2E2E00,
+0xE9E9E900E9E9E900, 0x7979790079797900, 0xA7A7A700A7A7A700, 0x8C8C8C008C8C8C00,
+0x9F9F9F009F9F9F00, 0x6E6E6E006E6E6E00, 0xBCBCBC00BCBCBC00, 0x8E8E8E008E8E8E00,
+0x2929290029292900, 0xF5F5F500F5F5F500, 0xF9F9F900F9F9F900, 0xB6B6B600B6B6B600,
+0x2F2F2F002F2F2F00, 0xFDFDFD00FDFDFD00, 0xB4B4B400B4B4B400, 0x5959590059595900,
+0x7878780078787800, 0x9898980098989800, 0x0606060006060600, 0x6A6A6A006A6A6A00,
+0xE7E7E700E7E7E700, 0x4646460046464600, 0x7171710071717100, 0xBABABA00BABABA00,
+0xD4D4D400D4D4D400, 0x2525250025252500, 0xABABAB00ABABAB00, 0x4242420042424200,
+0x8888880088888800, 0xA2A2A200A2A2A200, 0x8D8D8D008D8D8D00, 0xFAFAFA00FAFAFA00,
+0x7272720072727200, 0x0707070007070700, 0xB9B9B900B9B9B900, 0x5555550055555500,
+0xF8F8F800F8F8F800, 0xEEEEEE00EEEEEE00, 0xACACAC00ACACAC00, 0x0A0A0A000A0A0A00,
+0x3636360036363600, 0x4949490049494900, 0x2A2A2A002A2A2A00, 0x6868680068686800,
+0x3C3C3C003C3C3C00, 0x3838380038383800, 0xF1F1F100F1F1F100, 0xA4A4A400A4A4A400,
+0x4040400040404000, 0x2828280028282800, 0xD3D3D300D3D3D300, 0x7B7B7B007B7B7B00,
+0xBBBBBB00BBBBBB00, 0xC9C9C900C9C9C900, 0x4343430043434300, 0xC1C1C100C1C1C100,
+0x1515150015151500, 0xE3E3E300E3E3E300, 0xADADAD00ADADAD00, 0xF4F4F400F4F4F400,
+0x7777770077777700, 0xC7C7C700C7C7C700, 0x8080800080808000, 0x9E9E9E009E9E9E00 };
+
+namespace Camellia_F {
+
+/*
+* We use the slow byte-wise version of F in the first and last rounds
+* to help protect against side channels analyzing cache hits on the
+* larger sbox tables.
+*/
+uint64_t F_SLOW(uint64_t v, uint64_t K)
+   {
+   alignas(64)
+   static const uint8_t SBOX[256] = {
+      0x70, 0x82, 0x2C, 0xEC, 0xB3, 0x27, 0xC0, 0xE5, 0xE4, 0x85, 0x57,
+      0x35, 0xEA, 0x0C, 0xAE, 0x41, 0x23, 0xEF, 0x6B, 0x93, 0x45, 0x19,
+      0xA5, 0x21, 0xED, 0x0E, 0x4F, 0x4E, 0x1D, 0x65, 0x92, 0xBD, 0x86,
+      0xB8, 0xAF, 0x8F, 0x7C, 0xEB, 0x1F, 0xCE, 0x3E, 0x30, 0xDC, 0x5F,
+      0x5E, 0xC5, 0x0B, 0x1A, 0xA6, 0xE1, 0x39, 0xCA, 0xD5, 0x47, 0x5D,
+      0x3D, 0xD9, 0x01, 0x5A, 0xD6, 0x51, 0x56, 0x6C, 0x4D, 0x8B, 0x0D,
+      0x9A, 0x66, 0xFB, 0xCC, 0xB0, 0x2D, 0x74, 0x12, 0x2B, 0x20, 0xF0,
+      0xB1, 0x84, 0x99, 0xDF, 0x4C, 0xCB, 0xC2, 0x34, 0x7E, 0x76, 0x05,
+      0x6D, 0xB7, 0xA9, 0x31, 0xD1, 0x17, 0x04, 0xD7, 0x14, 0x58, 0x3A,
+      0x61, 0xDE, 0x1B, 0x11, 0x1C, 0x32, 0x0F, 0x9C, 0x16, 0x53, 0x18,
+      0xF2, 0x22, 0xFE, 0x44, 0xCF, 0xB2, 0xC3, 0xB5, 0x7A, 0x91, 0x24,
+      0x08, 0xE8, 0xA8, 0x60, 0xFC, 0x69, 0x50, 0xAA, 0xD0, 0xA0, 0x7D,
+      0xA1, 0x89, 0x62, 0x97, 0x54, 0x5B, 0x1E, 0x95, 0xE0, 0xFF, 0x64,
+      0xD2, 0x10, 0xC4, 0x00, 0x48, 0xA3, 0xF7, 0x75, 0xDB, 0x8A, 0x03,
+      0xE6, 0xDA, 0x09, 0x3F, 0xDD, 0x94, 0x87, 0x5C, 0x83, 0x02, 0xCD,
+      0x4A, 0x90, 0x33, 0x73, 0x67, 0xF6, 0xF3, 0x9D, 0x7F, 0xBF, 0xE2,
+      0x52, 0x9B, 0xD8, 0x26, 0xC8, 0x37, 0xC6, 0x3B, 0x81, 0x96, 0x6F,
+      0x4B, 0x13, 0xBE, 0x63, 0x2E, 0xE9, 0x79, 0xA7, 0x8C, 0x9F, 0x6E,
+      0xBC, 0x8E, 0x29, 0xF5, 0xF9, 0xB6, 0x2F, 0xFD, 0xB4, 0x59, 0x78,
+      0x98, 0x06, 0x6A, 0xE7, 0x46, 0x71, 0xBA, 0xD4, 0x25, 0xAB, 0x42,
+      0x88, 0xA2, 0x8D, 0xFA, 0x72, 0x07, 0xB9, 0x55, 0xF8, 0xEE, 0xAC,
+      0x0A, 0x36, 0x49, 0x2A, 0x68, 0x3C, 0x38, 0xF1, 0xA4, 0x40, 0x28,
+      0xD3, 0x7B, 0xBB, 0xC9, 0x43, 0xC1, 0x15, 0xE3, 0xAD, 0xF4, 0x77,
+      0xC7, 0x80, 0x9E };
+
+   const uint64_t x = v ^ K;
+
+   const uint8_t t1 = SBOX[get_byte(0, x)];
+   const uint8_t t2 = rotl<1>(SBOX[get_byte(1, x)]);
+   const uint8_t t3 = rotl<7>(SBOX[get_byte(2, x)]);
+   const uint8_t t4 = SBOX[rotl<1>(get_byte(3, x))];
+   const uint8_t t5 = rotl<1>(SBOX[get_byte(4, x)]);
+   const uint8_t t6 = rotl<7>(SBOX[get_byte(5, x)]);
+   const uint8_t t7 = SBOX[rotl<1>(get_byte(6, x))];
+   const uint8_t t8 = SBOX[get_byte(7, x)];
+
+   const uint8_t y1 = t1 ^ t3 ^ t4 ^ t6 ^ t7 ^ t8;
+   const uint8_t y2 = t1 ^ t2 ^ t4 ^ t5 ^ t7 ^ t8;
+   const uint8_t y3 = t1 ^ t2 ^ t3 ^ t5 ^ t6 ^ t8;
+   const uint8_t y4 = t2 ^ t3 ^ t4 ^ t5 ^ t6 ^ t7;
+   const uint8_t y5 = t1 ^ t2 ^ t6 ^ t7 ^ t8;
+   const uint8_t y6 = t2 ^ t3 ^ t5 ^ t7 ^ t8;
+   const uint8_t y7 = t3 ^ t4 ^ t5 ^ t6 ^ t8;
+   const uint8_t y8 = t1 ^ t4 ^ t5 ^ t6 ^ t7;
+
+   return make_uint64(y1, y2, y3, y4, y5, y6, y7, y8);
+   }
+
+inline uint64_t F(uint64_t v, uint64_t K)
+   {
+   const uint64_t x = v ^ K;
+
+   return Camellia_SBOX1[get_byte(0, x)] ^
+          Camellia_SBOX2[get_byte(1, x)] ^
+          Camellia_SBOX3[get_byte(2, x)] ^
+          Camellia_SBOX4[get_byte(3, x)] ^
+          Camellia_SBOX5[get_byte(4, x)] ^
+          Camellia_SBOX6[get_byte(5, x)] ^
+          Camellia_SBOX7[get_byte(6, x)] ^
+          Camellia_SBOX8[get_byte(7, x)];
+   }
+
+inline uint64_t FL(uint64_t v, uint64_t K)
+   {
+   uint32_t x1 = static_cast<uint32_t>(v >> 32);
+   uint32_t x2 = static_cast<uint32_t>(v & 0xFFFFFFFF);
+
+   const uint32_t k1 = static_cast<uint32_t>(K >> 32);
+   const uint32_t k2 = static_cast<uint32_t>(K & 0xFFFFFFFF);
+
+   x2 ^= rotl<1>(x1 & k1);
+   x1 ^= (x2 | k2);
+
+   return ((static_cast<uint64_t>(x1) << 32) | x2);
+   }
+
+inline uint64_t FLINV(uint64_t v, uint64_t K)
+   {
+   uint32_t x1 = static_cast<uint32_t>(v >> 32);
+   uint32_t x2 = static_cast<uint32_t>(v & 0xFFFFFFFF);
+
+   const uint32_t k1 = static_cast<uint32_t>(K >> 32);
+   const uint32_t k2 = static_cast<uint32_t>(K & 0xFFFFFFFF);
+
+   x1 ^= (x2 | k2);
+   x2 ^= rotl<1>(x1 & k1);
+
+   return ((static_cast<uint64_t>(x1) << 32) | x2);
+   }
+
+/*
+* Camellia Encryption
+*/
+void encrypt(const uint8_t in[], uint8_t out[], size_t blocks,
+             const secure_vector<uint64_t>& SK, const size_t rounds)
+   {
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint64_t D1, D2;
+      load_be(in + 16*i, D1, D2);
+
+      const uint64_t* K = SK.data();
+
+      D1 ^= *K++;
+      D2 ^= *K++;
+
+      D2 ^= F_SLOW(D1, *K++);
+      D1 ^= F_SLOW(D2, *K++);
+
+      for(size_t r = 1; r != rounds - 1; ++r)
+         {
+         if(r % 3 == 0)
+            {
+            D1 = FL   (D1, *K++);
+            D2 = FLINV(D2, *K++);
+            }
+
+         D2 ^= F(D1, *K++);
+         D1 ^= F(D2, *K++);
+         }
+
+      D2 ^= F_SLOW(D1, *K++);
+      D1 ^= F_SLOW(D2, *K++);
+
+      D2 ^= *K++;
+      D1 ^= *K++;
+
+      store_be(out + 16*i, D2, D1);
+      }
+   }
+
+/*
+* Camellia Decryption
+*/
+void decrypt(const uint8_t in[], uint8_t out[], size_t blocks,
+             const secure_vector<uint64_t>& SK, const size_t rounds)
+   {
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint64_t D1, D2;
+      load_be(in + 16*i, D1, D2);
+
+      const uint64_t* K = &SK[SK.size()-1];
+
+      D2 ^= *K--;
+      D1 ^= *K--;
+
+      D2 ^= F_SLOW(D1, *K--);
+      D1 ^= F_SLOW(D2, *K--);
+
+      for(size_t r = 1; r != rounds - 1; ++r)
+         {
+         if(r % 3 == 0)
+            {
+            D1 = FL   (D1, *K--);
+            D2 = FLINV(D2, *K--);
+            }
+
+         D2 ^= F(D1, *K--);
+         D1 ^= F(D2, *K--);
+         }
+
+      D2 ^= F_SLOW(D1, *K--);
+      D1 ^= F_SLOW(D2, *K--);
+
+      D1 ^= *K--;
+      D2 ^= *K;
+
+      store_be(out + 16*i, D2, D1);
+      }
+   }
+
+uint64_t left_rot_hi(uint64_t h, uint64_t l, size_t shift)
+   {
+   return (h << shift) | (l >> (64-shift));
+   }
+
+uint64_t left_rot_lo(uint64_t h, uint64_t l, size_t shift)
+   {
+   return (h >> (64-shift)) | (l << shift);
+   }
+
+/*
+* Camellia Key Schedule
+*/
+void key_schedule(secure_vector<uint64_t>& SK, const uint8_t key[], size_t length)
+   {
+   const uint64_t Sigma1 = 0xA09E667F3BCC908B;
+   const uint64_t Sigma2 = 0xB67AE8584CAA73B2;
+   const uint64_t Sigma3 = 0xC6EF372FE94F82BE;
+   const uint64_t Sigma4 = 0x54FF53A5F1D36F1C;
+   const uint64_t Sigma5 = 0x10E527FADE682D1D;
+   const uint64_t Sigma6 = 0xB05688C2B3E6C1FD;
+
+   const uint64_t KL_H = load_be<uint64_t>(key, 0);
+   const uint64_t KL_L = load_be<uint64_t>(key, 1);
+
+   const uint64_t KR_H = (length >= 24) ? load_be<uint64_t>(key, 2) : 0;
+   const uint64_t KR_L =
+      (length == 32) ? load_be<uint64_t>(key, 3) : ((length == 24) ? ~KR_H : 0);
+
+   uint64_t D1 = KL_H ^ KR_H;
+   uint64_t D2 = KL_L ^ KR_L;
+   D2 ^= F(D1, Sigma1);
+   D1 ^= F(D2, Sigma2);
+   D1 ^= KL_H;
+   D2 ^= KL_L;
+   D2 ^= F(D1, Sigma3);
+   D1 ^= F(D2, Sigma4);
+
+   const uint64_t KA_H = D1;
+   const uint64_t KA_L = D2;
+
+   D1 = KA_H ^ KR_H;
+   D2 = KA_L ^ KR_L;
+   D2 ^= F(D1, Sigma5);
+   D1 ^= F(D2, Sigma6);
+
+   const uint64_t KB_H = D1;
+   const uint64_t KB_L = D2;
+
+   if(length == 16)
+      {
+      SK.resize(26);
+
+      SK[ 0] = KL_H;
+      SK[ 1] = KL_L;
+      SK[ 2] = KA_H;
+      SK[ 3] = KA_L;
+      SK[ 4] = left_rot_hi(KL_H, KL_L, 15);
+      SK[ 5] = left_rot_lo(KL_H, KL_L, 15);
+      SK[ 6] = left_rot_hi(KA_H, KA_L, 15);
+      SK[ 7] = left_rot_lo(KA_H, KA_L, 15);
+      SK[ 8] = left_rot_hi(KA_H, KA_L, 30);
+      SK[ 9] = left_rot_lo(KA_H, KA_L, 30);
+      SK[10] = left_rot_hi(KL_H, KL_L, 45);
+      SK[11] = left_rot_lo(KL_H, KL_L, 45);
+      SK[12] = left_rot_hi(KA_H, KA_L,  45);
+      SK[13] = left_rot_lo(KL_H, KL_L,  60);
+      SK[14] = left_rot_hi(KA_H, KA_L,  60);
+      SK[15] = left_rot_lo(KA_H, KA_L,  60);
+      SK[16] = left_rot_lo(KL_H, KL_L,  77-64);
+      SK[17] = left_rot_hi(KL_H, KL_L,  77-64);
+      SK[18] = left_rot_lo(KL_H, KL_L,  94-64);
+      SK[19] = left_rot_hi(KL_H, KL_L,  94-64);
+      SK[20] = left_rot_lo(KA_H, KA_L,  94-64);
+      SK[21] = left_rot_hi(KA_H, KA_L,  94-64);
+      SK[22] = left_rot_lo(KL_H, KL_L, 111-64);
+      SK[23] = left_rot_hi(KL_H, KL_L, 111-64);
+      SK[24] = left_rot_lo(KA_H, KA_L, 111-64);
+      SK[25] = left_rot_hi(KA_H, KA_L, 111-64);
+      }
+   else
+      {
+      SK.resize(34);
+
+      SK[ 0] = KL_H;
+      SK[ 1] = KL_L;
+      SK[ 2] = KB_H;
+      SK[ 3] = KB_L;
+
+      SK[ 4] = left_rot_hi(KR_H, KR_L, 15);
+      SK[ 5] = left_rot_lo(KR_H, KR_L, 15);
+      SK[ 6] = left_rot_hi(KA_H, KA_L, 15);
+      SK[ 7] = left_rot_lo(KA_H, KA_L, 15);
+
+      SK[ 8] = left_rot_hi(KR_H, KR_L, 30);
+      SK[ 9] = left_rot_lo(KR_H, KR_L, 30);
+      SK[10] = left_rot_hi(KB_H, KB_L, 30);
+      SK[11] = left_rot_lo(KB_H, KB_L, 30);
+
+      SK[12] = left_rot_hi(KL_H, KL_L, 45);
+      SK[13] = left_rot_lo(KL_H, KL_L, 45);
+      SK[14] = left_rot_hi(KA_H, KA_L, 45);
+      SK[15] = left_rot_lo(KA_H, KA_L, 45);
+
+      SK[16] = left_rot_hi(KL_H, KL_L, 60);
+      SK[17] = left_rot_lo(KL_H, KL_L, 60);
+      SK[18] = left_rot_hi(KR_H, KR_L, 60);
+      SK[19] = left_rot_lo(KR_H, KR_L, 60);
+      SK[20] = left_rot_hi(KB_H, KB_L, 60);
+      SK[21] = left_rot_lo(KB_H, KB_L, 60);
+
+      SK[22] = left_rot_lo(KL_H, KL_L,  77-64);
+      SK[23] = left_rot_hi(KL_H, KL_L,  77-64);
+      SK[24] = left_rot_lo(KA_H, KA_L,  77-64);
+      SK[25] = left_rot_hi(KA_H, KA_L,  77-64);
+
+      SK[26] = left_rot_lo(KR_H, KR_L,  94-64);
+      SK[27] = left_rot_hi(KR_H, KR_L,  94-64);
+      SK[28] = left_rot_lo(KA_H, KA_L,  94-64);
+      SK[29] = left_rot_hi(KA_H, KA_L,  94-64);
+      SK[30] = left_rot_lo(KL_H, KL_L, 111-64);
+      SK[31] = left_rot_hi(KL_H, KL_L, 111-64);
+      SK[32] = left_rot_lo(KB_H, KB_L, 111-64);
+      SK[33] = left_rot_hi(KB_H, KB_L, 111-64);
+      }
+   }
+
+}
+
+}
+
+void Camellia_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::encrypt(in, out, blocks, m_SK, 9);
+   }
+
+void Camellia_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::encrypt(in, out, blocks, m_SK, 12);
+   }
+
+void Camellia_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::encrypt(in, out, blocks, m_SK, 12);
+   }
+
+void Camellia_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::decrypt(in, out, blocks, m_SK, 9);
+   }
+
+void Camellia_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::decrypt(in, out, blocks, m_SK, 12);
+   }
+
+void Camellia_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SK.empty() == false);
+   Camellia_F::decrypt(in, out, blocks, m_SK, 12);
+   }
+
+void Camellia_128::key_schedule(const uint8_t key[], size_t length)
+   {
+   Camellia_F::key_schedule(m_SK, key, length);
+   }
+
+void Camellia_192::key_schedule(const uint8_t key[], size_t length)
+   {
+   Camellia_F::key_schedule(m_SK, key, length);
+   }
+
+void Camellia_256::key_schedule(const uint8_t key[], size_t length)
+   {
+   Camellia_F::key_schedule(m_SK, key, length);
+   }
+
+void Camellia_128::clear()
+   {
+   zap(m_SK);
+   }
+
+void Camellia_192::clear()
+   {
+   zap(m_SK);
+   }
+
+void Camellia_256::clear()
+   {
+   zap(m_SK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/camellia/camellia.h b/third_party/botan/src/lib/block/camellia/camellia.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/camellia/camellia.h
@@ -0,0 +1,73 @@
+/*
+* Camellia
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAMELLIA_H_
+#define BOTAN_CAMELLIA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(camellia.h)
+
+namespace Botan {
+
+/**
+* Camellia-128
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_128 final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "Camellia-128"; }
+      BlockCipher* clone() const override { return new Camellia_128; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint64_t> m_SK;
+   };
+
+/**
+* Camellia-192
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_192 final : public Block_Cipher_Fixed_Params<16, 24>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "Camellia-192"; }
+      BlockCipher* clone() const override { return new Camellia_192; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint64_t> m_SK;
+   };
+
+/**
+* Camellia-256
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_256 final : public Block_Cipher_Fixed_Params<16, 32>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "Camellia-256"; }
+      BlockCipher* clone() const override { return new Camellia_256; }
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint64_t> m_SK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/cascade/cascade.cpp b/third_party/botan/src/lib/block/cascade/cascade.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cascade/cascade.cpp
@@ -0,0 +1,93 @@
+/*
+* Block Cipher Cascade
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cascade.h>
+
+namespace Botan {
+
+void Cascade_Cipher::encrypt_n(const uint8_t in[], uint8_t out[],
+                               size_t blocks) const
+   {
+   size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size());
+   size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size());
+
+   m_cipher1->encrypt_n(in, out, c1_blocks);
+   m_cipher2->encrypt_n(out, out, c2_blocks);
+   }
+
+void Cascade_Cipher::decrypt_n(const uint8_t in[], uint8_t out[],
+                               size_t blocks) const
+   {
+   size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size());
+   size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size());
+
+   m_cipher2->decrypt_n(in, out, c2_blocks);
+   m_cipher1->decrypt_n(out, out, c1_blocks);
+   }
+
+void Cascade_Cipher::key_schedule(const uint8_t key[], size_t)
+   {
+   const uint8_t* key2 = key + m_cipher1->maximum_keylength();
+
+   m_cipher1->set_key(key , m_cipher1->maximum_keylength());
+   m_cipher2->set_key(key2, m_cipher2->maximum_keylength());
+   }
+
+void Cascade_Cipher::clear()
+   {
+   m_cipher1->clear();
+   m_cipher2->clear();
+   }
+
+std::string Cascade_Cipher::name() const
+   {
+   return "Cascade(" + m_cipher1->name() + "," + m_cipher2->name() + ")";
+   }
+
+BlockCipher* Cascade_Cipher::clone() const
+   {
+   return new Cascade_Cipher(m_cipher1->clone(),
+                             m_cipher2->clone());
+   }
+
+namespace {
+
+size_t euclids_algorithm(size_t a, size_t b)
+   {
+   while(b != 0)
+      {
+      size_t t = b;
+      b = a % b;
+      a = t;
+      }
+
+   return a;
+   }
+
+size_t block_size_for_cascade(size_t bs, size_t bs2)
+   {
+   if(bs == bs2)
+      return bs;
+
+   const size_t gcd = euclids_algorithm(bs, bs2);
+
+   return (bs * bs2) / gcd;
+   }
+
+}
+
+Cascade_Cipher::Cascade_Cipher(BlockCipher* c1, BlockCipher* c2) :
+   m_cipher1(c1), m_cipher2(c2)
+   {
+   m_block = block_size_for_cascade(c1->block_size(), c2->block_size());
+
+   BOTAN_ASSERT(m_block % c1->block_size() == 0 &&
+                m_block % c2->block_size() == 0,
+                "Combined block size is a multiple of each ciphers block");
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/cascade/cascade.h b/third_party/botan/src/lib/block/cascade/cascade.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cascade/cascade.h
@@ -0,0 +1,57 @@
+/*
+* Block Cipher Cascade
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CASCADE_H_
+#define BOTAN_CASCADE_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cascade.h)
+
+namespace Botan {
+
+/**
+* Block Cipher Cascade
+*/
+class BOTAN_PUBLIC_API(2,0) Cascade_Cipher final : public BlockCipher
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      size_t block_size() const override { return m_block; }
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(m_cipher1->maximum_keylength() +
+                                         m_cipher2->maximum_keylength());
+         }
+
+      void clear() override;
+      std::string name() const override;
+      BlockCipher* clone() const override;
+
+      /**
+      * Create a cascade of two block ciphers
+      * @param cipher1 the first cipher
+      * @param cipher2 the second cipher
+      */
+      Cascade_Cipher(BlockCipher* cipher1, BlockCipher* cipher2);
+
+      Cascade_Cipher(const Cascade_Cipher&) = delete;
+      Cascade_Cipher& operator=(const Cascade_Cipher&) = delete;
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      size_t m_block;
+      std::unique_ptr<BlockCipher> m_cipher1, m_cipher2;
+   };
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/cast128/cast128.cpp b/third_party/botan/src/lib/block/cast128/cast128.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cast128/cast128.cpp
@@ -0,0 +1,471 @@
+/*
+* CAST-128
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cast128.h>
+#include <botan/internal/cast_sboxes.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* CAST-128 Round Type 1
+*/
+inline uint32_t F1(uint32_t R, uint32_t MK, uint8_t RK)
+   {
+   const uint32_t T = rotl_var(MK + R, RK);
+   return (CAST_SBOX1[get_byte(0, T)] ^ CAST_SBOX2[get_byte(1, T)]) -
+           CAST_SBOX3[get_byte(2, T)] + CAST_SBOX4[get_byte(3, T)];
+   }
+
+/*
+* CAST-128 Round Type 2
+*/
+inline uint32_t F2(uint32_t R, uint32_t MK, uint8_t RK)
+   {
+   const uint32_t T = rotl_var(MK ^ R, RK);
+   return (CAST_SBOX1[get_byte(0, T)]  - CAST_SBOX2[get_byte(1, T)] +
+           CAST_SBOX3[get_byte(2, T)]) ^ CAST_SBOX4[get_byte(3, T)];
+   }
+
+/*
+* CAST-128 Round Type 3
+*/
+inline uint32_t F3(uint32_t R, uint32_t MK, uint8_t RK)
+   {
+   const uint32_t T = rotl_var(MK - R, RK);
+   return ((CAST_SBOX1[get_byte(0, T)]  + CAST_SBOX2[get_byte(1, T)]) ^
+            CAST_SBOX3[get_byte(2, T)]) - CAST_SBOX4[get_byte(3, T)];
+   }
+
+}
+
+/*
+* CAST-128 Encryption
+*/
+void CAST_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0, L1, R1;
+      load_be(in, L0, R0, L1, R1);
+
+      L0 ^= F1(R0, m_MK[ 0], m_RK[ 0]);
+      L1 ^= F1(R1, m_MK[ 0], m_RK[ 0]);
+      R0 ^= F2(L0, m_MK[ 1], m_RK[ 1]);
+      R1 ^= F2(L1, m_MK[ 1], m_RK[ 1]);
+      L0 ^= F3(R0, m_MK[ 2], m_RK[ 2]);
+      L1 ^= F3(R1, m_MK[ 2], m_RK[ 2]);
+      R0 ^= F1(L0, m_MK[ 3], m_RK[ 3]);
+      R1 ^= F1(L1, m_MK[ 3], m_RK[ 3]);
+      L0 ^= F2(R0, m_MK[ 4], m_RK[ 4]);
+      L1 ^= F2(R1, m_MK[ 4], m_RK[ 4]);
+      R0 ^= F3(L0, m_MK[ 5], m_RK[ 5]);
+      R1 ^= F3(L1, m_MK[ 5], m_RK[ 5]);
+      L0 ^= F1(R0, m_MK[ 6], m_RK[ 6]);
+      L1 ^= F1(R1, m_MK[ 6], m_RK[ 6]);
+      R0 ^= F2(L0, m_MK[ 7], m_RK[ 7]);
+      R1 ^= F2(L1, m_MK[ 7], m_RK[ 7]);
+      L0 ^= F3(R0, m_MK[ 8], m_RK[ 8]);
+      L1 ^= F3(R1, m_MK[ 8], m_RK[ 8]);
+      R0 ^= F1(L0, m_MK[ 9], m_RK[ 9]);
+      R1 ^= F1(L1, m_MK[ 9], m_RK[ 9]);
+      L0 ^= F2(R0, m_MK[10], m_RK[10]);
+      L1 ^= F2(R1, m_MK[10], m_RK[10]);
+      R0 ^= F3(L0, m_MK[11], m_RK[11]);
+      R1 ^= F3(L1, m_MK[11], m_RK[11]);
+      L0 ^= F1(R0, m_MK[12], m_RK[12]);
+      L1 ^= F1(R1, m_MK[12], m_RK[12]);
+      R0 ^= F2(L0, m_MK[13], m_RK[13]);
+      R1 ^= F2(L1, m_MK[13], m_RK[13]);
+      L0 ^= F3(R0, m_MK[14], m_RK[14]);
+      L1 ^= F3(R1, m_MK[14], m_RK[14]);
+      R0 ^= F1(L0, m_MK[15], m_RK[15]);
+      R1 ^= F1(L1, m_MK[15], m_RK[15]);
+
+      store_be(out, R0, L0, R1, L1);
+
+      blocks -= 2;
+      out += 2 * BLOCK_SIZE;
+      in  += 2 * BLOCK_SIZE;
+      }
+
+   if(blocks)
+      {
+      uint32_t L, R;
+      load_be(in, L, R);
+
+      L ^= F1(R, m_MK[ 0], m_RK[ 0]);
+      R ^= F2(L, m_MK[ 1], m_RK[ 1]);
+      L ^= F3(R, m_MK[ 2], m_RK[ 2]);
+      R ^= F1(L, m_MK[ 3], m_RK[ 3]);
+      L ^= F2(R, m_MK[ 4], m_RK[ 4]);
+      R ^= F3(L, m_MK[ 5], m_RK[ 5]);
+      L ^= F1(R, m_MK[ 6], m_RK[ 6]);
+      R ^= F2(L, m_MK[ 7], m_RK[ 7]);
+      L ^= F3(R, m_MK[ 8], m_RK[ 8]);
+      R ^= F1(L, m_MK[ 9], m_RK[ 9]);
+      L ^= F2(R, m_MK[10], m_RK[10]);
+      R ^= F3(L, m_MK[11], m_RK[11]);
+      L ^= F1(R, m_MK[12], m_RK[12]);
+      R ^= F2(L, m_MK[13], m_RK[13]);
+      L ^= F3(R, m_MK[14], m_RK[14]);
+      R ^= F1(L, m_MK[15], m_RK[15]);
+
+      store_be(out, R, L);
+      }
+   }
+
+/*
+* CAST-128 Decryption
+*/
+void CAST_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0, L1, R1;
+      load_be(in, L0, R0, L1, R1);
+
+      L0 ^= F1(R0, m_MK[15], m_RK[15]);
+      L1 ^= F1(R1, m_MK[15], m_RK[15]);
+      R0 ^= F3(L0, m_MK[14], m_RK[14]);
+      R1 ^= F3(L1, m_MK[14], m_RK[14]);
+      L0 ^= F2(R0, m_MK[13], m_RK[13]);
+      L1 ^= F2(R1, m_MK[13], m_RK[13]);
+      R0 ^= F1(L0, m_MK[12], m_RK[12]);
+      R1 ^= F1(L1, m_MK[12], m_RK[12]);
+      L0 ^= F3(R0, m_MK[11], m_RK[11]);
+      L1 ^= F3(R1, m_MK[11], m_RK[11]);
+      R0 ^= F2(L0, m_MK[10], m_RK[10]);
+      R1 ^= F2(L1, m_MK[10], m_RK[10]);
+      L0 ^= F1(R0, m_MK[ 9], m_RK[ 9]);
+      L1 ^= F1(R1, m_MK[ 9], m_RK[ 9]);
+      R0 ^= F3(L0, m_MK[ 8], m_RK[ 8]);
+      R1 ^= F3(L1, m_MK[ 8], m_RK[ 8]);
+      L0 ^= F2(R0, m_MK[ 7], m_RK[ 7]);
+      L1 ^= F2(R1, m_MK[ 7], m_RK[ 7]);
+      R0 ^= F1(L0, m_MK[ 6], m_RK[ 6]);
+      R1 ^= F1(L1, m_MK[ 6], m_RK[ 6]);
+      L0 ^= F3(R0, m_MK[ 5], m_RK[ 5]);
+      L1 ^= F3(R1, m_MK[ 5], m_RK[ 5]);
+      R0 ^= F2(L0, m_MK[ 4], m_RK[ 4]);
+      R1 ^= F2(L1, m_MK[ 4], m_RK[ 4]);
+      L0 ^= F1(R0, m_MK[ 3], m_RK[ 3]);
+      L1 ^= F1(R1, m_MK[ 3], m_RK[ 3]);
+      R0 ^= F3(L0, m_MK[ 2], m_RK[ 2]);
+      R1 ^= F3(L1, m_MK[ 2], m_RK[ 2]);
+      L0 ^= F2(R0, m_MK[ 1], m_RK[ 1]);
+      L1 ^= F2(R1, m_MK[ 1], m_RK[ 1]);
+      R0 ^= F1(L0, m_MK[ 0], m_RK[ 0]);
+      R1 ^= F1(L1, m_MK[ 0], m_RK[ 0]);
+
+      store_be(out, R0, L0, R1, L1);
+
+      blocks -= 2;
+      out += 2 * BLOCK_SIZE;
+      in  += 2 * BLOCK_SIZE;
+      }
+
+   if(blocks)
+      {
+      uint32_t L, R;
+      load_be(in, L, R);
+
+      L ^= F1(R, m_MK[15], m_RK[15]);
+      R ^= F3(L, m_MK[14], m_RK[14]);
+      L ^= F2(R, m_MK[13], m_RK[13]);
+      R ^= F1(L, m_MK[12], m_RK[12]);
+      L ^= F3(R, m_MK[11], m_RK[11]);
+      R ^= F2(L, m_MK[10], m_RK[10]);
+      L ^= F1(R, m_MK[ 9], m_RK[ 9]);
+      R ^= F3(L, m_MK[ 8], m_RK[ 8]);
+      L ^= F2(R, m_MK[ 7], m_RK[ 7]);
+      R ^= F1(L, m_MK[ 6], m_RK[ 6]);
+      L ^= F3(R, m_MK[ 5], m_RK[ 5]);
+      R ^= F2(L, m_MK[ 4], m_RK[ 4]);
+      L ^= F1(R, m_MK[ 3], m_RK[ 3]);
+      R ^= F3(L, m_MK[ 2], m_RK[ 2]);
+      L ^= F2(R, m_MK[ 1], m_RK[ 1]);
+      R ^= F1(L, m_MK[ 0], m_RK[ 0]);
+
+      store_be(out, R, L);
+      }
+   }
+
+/*
+* CAST-128 Key Schedule
+*/
+void CAST_128::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_MK.resize(48);
+   m_RK.resize(48);
+
+   secure_vector<uint8_t> key16(16);
+   copy_mem(key16.data(), key, length);
+
+   secure_vector<uint32_t> X(4);
+   for(size_t i = 0; i != 4; ++i)
+      X[i] = load_be<uint32_t>(key16.data(), i);
+
+   cast_ks(m_MK, X);
+
+   secure_vector<uint32_t> RK32(48);
+   cast_ks(RK32, X);
+
+   for(size_t i = 0; i != 16; ++i)
+      m_RK[i] = RK32[i] % 32;
+   }
+
+void CAST_128::clear()
+   {
+   zap(m_MK);
+   zap(m_RK);
+   }
+
+/*
+* S-Box Based Key Expansion
+*/
+void CAST_128::cast_ks(secure_vector<uint32_t>& K,
+                       secure_vector<uint32_t>& X)
+   {
+   static const uint32_t S5[256] = {
+      0x7EC90C04, 0x2C6E74B9, 0x9B0E66DF, 0xA6337911, 0xB86A7FFF, 0x1DD358F5,
+      0x44DD9D44, 0x1731167F, 0x08FBF1FA, 0xE7F511CC, 0xD2051B00, 0x735ABA00,
+      0x2AB722D8, 0x386381CB, 0xACF6243A, 0x69BEFD7A, 0xE6A2E77F, 0xF0C720CD,
+      0xC4494816, 0xCCF5C180, 0x38851640, 0x15B0A848, 0xE68B18CB, 0x4CAADEFF,
+      0x5F480A01, 0x0412B2AA, 0x259814FC, 0x41D0EFE2, 0x4E40B48D, 0x248EB6FB,
+      0x8DBA1CFE, 0x41A99B02, 0x1A550A04, 0xBA8F65CB, 0x7251F4E7, 0x95A51725,
+      0xC106ECD7, 0x97A5980A, 0xC539B9AA, 0x4D79FE6A, 0xF2F3F763, 0x68AF8040,
+      0xED0C9E56, 0x11B4958B, 0xE1EB5A88, 0x8709E6B0, 0xD7E07156, 0x4E29FEA7,
+      0x6366E52D, 0x02D1C000, 0xC4AC8E05, 0x9377F571, 0x0C05372A, 0x578535F2,
+      0x2261BE02, 0xD642A0C9, 0xDF13A280, 0x74B55BD2, 0x682199C0, 0xD421E5EC,
+      0x53FB3CE8, 0xC8ADEDB3, 0x28A87FC9, 0x3D959981, 0x5C1FF900, 0xFE38D399,
+      0x0C4EFF0B, 0x062407EA, 0xAA2F4FB1, 0x4FB96976, 0x90C79505, 0xB0A8A774,
+      0xEF55A1FF, 0xE59CA2C2, 0xA6B62D27, 0xE66A4263, 0xDF65001F, 0x0EC50966,
+      0xDFDD55BC, 0x29DE0655, 0x911E739A, 0x17AF8975, 0x32C7911C, 0x89F89468,
+      0x0D01E980, 0x524755F4, 0x03B63CC9, 0x0CC844B2, 0xBCF3F0AA, 0x87AC36E9,
+      0xE53A7426, 0x01B3D82B, 0x1A9E7449, 0x64EE2D7E, 0xCDDBB1DA, 0x01C94910,
+      0xB868BF80, 0x0D26F3FD, 0x9342EDE7, 0x04A5C284, 0x636737B6, 0x50F5B616,
+      0xF24766E3, 0x8ECA36C1, 0x136E05DB, 0xFEF18391, 0xFB887A37, 0xD6E7F7D4,
+      0xC7FB7DC9, 0x3063FCDF, 0xB6F589DE, 0xEC2941DA, 0x26E46695, 0xB7566419,
+      0xF654EFC5, 0xD08D58B7, 0x48925401, 0xC1BACB7F, 0xE5FF550F, 0xB6083049,
+      0x5BB5D0E8, 0x87D72E5A, 0xAB6A6EE1, 0x223A66CE, 0xC62BF3CD, 0x9E0885F9,
+      0x68CB3E47, 0x086C010F, 0xA21DE820, 0xD18B69DE, 0xF3F65777, 0xFA02C3F6,
+      0x407EDAC3, 0xCBB3D550, 0x1793084D, 0xB0D70EBA, 0x0AB378D5, 0xD951FB0C,
+      0xDED7DA56, 0x4124BBE4, 0x94CA0B56, 0x0F5755D1, 0xE0E1E56E, 0x6184B5BE,
+      0x580A249F, 0x94F74BC0, 0xE327888E, 0x9F7B5561, 0xC3DC0280, 0x05687715,
+      0x646C6BD7, 0x44904DB3, 0x66B4F0A3, 0xC0F1648A, 0x697ED5AF, 0x49E92FF6,
+      0x309E374F, 0x2CB6356A, 0x85808573, 0x4991F840, 0x76F0AE02, 0x083BE84D,
+      0x28421C9A, 0x44489406, 0x736E4CB8, 0xC1092910, 0x8BC95FC6, 0x7D869CF4,
+      0x134F616F, 0x2E77118D, 0xB31B2BE1, 0xAA90B472, 0x3CA5D717, 0x7D161BBA,
+      0x9CAD9010, 0xAF462BA2, 0x9FE459D2, 0x45D34559, 0xD9F2DA13, 0xDBC65487,
+      0xF3E4F94E, 0x176D486F, 0x097C13EA, 0x631DA5C7, 0x445F7382, 0x175683F4,
+      0xCDC66A97, 0x70BE0288, 0xB3CDCF72, 0x6E5DD2F3, 0x20936079, 0x459B80A5,
+      0xBE60E2DB, 0xA9C23101, 0xEBA5315C, 0x224E42F2, 0x1C5C1572, 0xF6721B2C,
+      0x1AD2FFF3, 0x8C25404E, 0x324ED72F, 0x4067B7FD, 0x0523138E, 0x5CA3BC78,
+      0xDC0FD66E, 0x75922283, 0x784D6B17, 0x58EBB16E, 0x44094F85, 0x3F481D87,
+      0xFCFEAE7B, 0x77B5FF76, 0x8C2302BF, 0xAAF47556, 0x5F46B02A, 0x2B092801,
+      0x3D38F5F7, 0x0CA81F36, 0x52AF4A8A, 0x66D5E7C0, 0xDF3B0874, 0x95055110,
+      0x1B5AD7A8, 0xF61ED5AD, 0x6CF6E479, 0x20758184, 0xD0CEFA65, 0x88F7BE58,
+      0x4A046826, 0x0FF6F8F3, 0xA09C7F70, 0x5346ABA0, 0x5CE96C28, 0xE176EDA3,
+      0x6BAC307F, 0x376829D2, 0x85360FA9, 0x17E3FE2A, 0x24B79767, 0xF5A96B20,
+      0xD6CD2595, 0x68FF1EBF, 0x7555442C, 0xF19F06BE, 0xF9E0659A, 0xEEB9491D,
+      0x34010718, 0xBB30CAB8, 0xE822FE15, 0x88570983, 0x750E6249, 0xDA627E55,
+      0x5E76FFA8, 0xB1534546, 0x6D47DE08, 0xEFE9E7D4 };
+
+   static const uint32_t S6[256] = {
+      0xF6FA8F9D, 0x2CAC6CE1, 0x4CA34867, 0xE2337F7C, 0x95DB08E7, 0x016843B4,
+      0xECED5CBC, 0x325553AC, 0xBF9F0960, 0xDFA1E2ED, 0x83F0579D, 0x63ED86B9,
+      0x1AB6A6B8, 0xDE5EBE39, 0xF38FF732, 0x8989B138, 0x33F14961, 0xC01937BD,
+      0xF506C6DA, 0xE4625E7E, 0xA308EA99, 0x4E23E33C, 0x79CBD7CC, 0x48A14367,
+      0xA3149619, 0xFEC94BD5, 0xA114174A, 0xEAA01866, 0xA084DB2D, 0x09A8486F,
+      0xA888614A, 0x2900AF98, 0x01665991, 0xE1992863, 0xC8F30C60, 0x2E78EF3C,
+      0xD0D51932, 0xCF0FEC14, 0xF7CA07D2, 0xD0A82072, 0xFD41197E, 0x9305A6B0,
+      0xE86BE3DA, 0x74BED3CD, 0x372DA53C, 0x4C7F4448, 0xDAB5D440, 0x6DBA0EC3,
+      0x083919A7, 0x9FBAEED9, 0x49DBCFB0, 0x4E670C53, 0x5C3D9C01, 0x64BDB941,
+      0x2C0E636A, 0xBA7DD9CD, 0xEA6F7388, 0xE70BC762, 0x35F29ADB, 0x5C4CDD8D,
+      0xF0D48D8C, 0xB88153E2, 0x08A19866, 0x1AE2EAC8, 0x284CAF89, 0xAA928223,
+      0x9334BE53, 0x3B3A21BF, 0x16434BE3, 0x9AEA3906, 0xEFE8C36E, 0xF890CDD9,
+      0x80226DAE, 0xC340A4A3, 0xDF7E9C09, 0xA694A807, 0x5B7C5ECC, 0x221DB3A6,
+      0x9A69A02F, 0x68818A54, 0xCEB2296F, 0x53C0843A, 0xFE893655, 0x25BFE68A,
+      0xB4628ABC, 0xCF222EBF, 0x25AC6F48, 0xA9A99387, 0x53BDDB65, 0xE76FFBE7,
+      0xE967FD78, 0x0BA93563, 0x8E342BC1, 0xE8A11BE9, 0x4980740D, 0xC8087DFC,
+      0x8DE4BF99, 0xA11101A0, 0x7FD37975, 0xDA5A26C0, 0xE81F994F, 0x9528CD89,
+      0xFD339FED, 0xB87834BF, 0x5F04456D, 0x22258698, 0xC9C4C83B, 0x2DC156BE,
+      0x4F628DAA, 0x57F55EC5, 0xE2220ABE, 0xD2916EBF, 0x4EC75B95, 0x24F2C3C0,
+      0x42D15D99, 0xCD0D7FA0, 0x7B6E27FF, 0xA8DC8AF0, 0x7345C106, 0xF41E232F,
+      0x35162386, 0xE6EA8926, 0x3333B094, 0x157EC6F2, 0x372B74AF, 0x692573E4,
+      0xE9A9D848, 0xF3160289, 0x3A62EF1D, 0xA787E238, 0xF3A5F676, 0x74364853,
+      0x20951063, 0x4576698D, 0xB6FAD407, 0x592AF950, 0x36F73523, 0x4CFB6E87,
+      0x7DA4CEC0, 0x6C152DAA, 0xCB0396A8, 0xC50DFE5D, 0xFCD707AB, 0x0921C42F,
+      0x89DFF0BB, 0x5FE2BE78, 0x448F4F33, 0x754613C9, 0x2B05D08D, 0x48B9D585,
+      0xDC049441, 0xC8098F9B, 0x7DEDE786, 0xC39A3373, 0x42410005, 0x6A091751,
+      0x0EF3C8A6, 0x890072D6, 0x28207682, 0xA9A9F7BE, 0xBF32679D, 0xD45B5B75,
+      0xB353FD00, 0xCBB0E358, 0x830F220A, 0x1F8FB214, 0xD372CF08, 0xCC3C4A13,
+      0x8CF63166, 0x061C87BE, 0x88C98F88, 0x6062E397, 0x47CF8E7A, 0xB6C85283,
+      0x3CC2ACFB, 0x3FC06976, 0x4E8F0252, 0x64D8314D, 0xDA3870E3, 0x1E665459,
+      0xC10908F0, 0x513021A5, 0x6C5B68B7, 0x822F8AA0, 0x3007CD3E, 0x74719EEF,
+      0xDC872681, 0x073340D4, 0x7E432FD9, 0x0C5EC241, 0x8809286C, 0xF592D891,
+      0x08A930F6, 0x957EF305, 0xB7FBFFBD, 0xC266E96F, 0x6FE4AC98, 0xB173ECC0,
+      0xBC60B42A, 0x953498DA, 0xFBA1AE12, 0x2D4BD736, 0x0F25FAAB, 0xA4F3FCEB,
+      0xE2969123, 0x257F0C3D, 0x9348AF49, 0x361400BC, 0xE8816F4A, 0x3814F200,
+      0xA3F94043, 0x9C7A54C2, 0xBC704F57, 0xDA41E7F9, 0xC25AD33A, 0x54F4A084,
+      0xB17F5505, 0x59357CBE, 0xEDBD15C8, 0x7F97C5AB, 0xBA5AC7B5, 0xB6F6DEAF,
+      0x3A479C3A, 0x5302DA25, 0x653D7E6A, 0x54268D49, 0x51A477EA, 0x5017D55B,
+      0xD7D25D88, 0x44136C76, 0x0404A8C8, 0xB8E5A121, 0xB81A928A, 0x60ED5869,
+      0x97C55B96, 0xEAEC991B, 0x29935913, 0x01FDB7F1, 0x088E8DFA, 0x9AB6F6F5,
+      0x3B4CBF9F, 0x4A5DE3AB, 0xE6051D35, 0xA0E1D855, 0xD36B4CF1, 0xF544EDEB,
+      0xB0E93524, 0xBEBB8FBD, 0xA2D762CF, 0x49C92F54, 0x38B5F331, 0x7128A454,
+      0x48392905, 0xA65B1DB8, 0x851C97BD, 0xD675CF2F };
+
+   static const uint32_t S7[256] = {
+      0x85E04019, 0x332BF567, 0x662DBFFF, 0xCFC65693, 0x2A8D7F6F, 0xAB9BC912,
+      0xDE6008A1, 0x2028DA1F, 0x0227BCE7, 0x4D642916, 0x18FAC300, 0x50F18B82,
+      0x2CB2CB11, 0xB232E75C, 0x4B3695F2, 0xB28707DE, 0xA05FBCF6, 0xCD4181E9,
+      0xE150210C, 0xE24EF1BD, 0xB168C381, 0xFDE4E789, 0x5C79B0D8, 0x1E8BFD43,
+      0x4D495001, 0x38BE4341, 0x913CEE1D, 0x92A79C3F, 0x089766BE, 0xBAEEADF4,
+      0x1286BECF, 0xB6EACB19, 0x2660C200, 0x7565BDE4, 0x64241F7A, 0x8248DCA9,
+      0xC3B3AD66, 0x28136086, 0x0BD8DFA8, 0x356D1CF2, 0x107789BE, 0xB3B2E9CE,
+      0x0502AA8F, 0x0BC0351E, 0x166BF52A, 0xEB12FF82, 0xE3486911, 0xD34D7516,
+      0x4E7B3AFF, 0x5F43671B, 0x9CF6E037, 0x4981AC83, 0x334266CE, 0x8C9341B7,
+      0xD0D854C0, 0xCB3A6C88, 0x47BC2829, 0x4725BA37, 0xA66AD22B, 0x7AD61F1E,
+      0x0C5CBAFA, 0x4437F107, 0xB6E79962, 0x42D2D816, 0x0A961288, 0xE1A5C06E,
+      0x13749E67, 0x72FC081A, 0xB1D139F7, 0xF9583745, 0xCF19DF58, 0xBEC3F756,
+      0xC06EBA30, 0x07211B24, 0x45C28829, 0xC95E317F, 0xBC8EC511, 0x38BC46E9,
+      0xC6E6FA14, 0xBAE8584A, 0xAD4EBC46, 0x468F508B, 0x7829435F, 0xF124183B,
+      0x821DBA9F, 0xAFF60FF4, 0xEA2C4E6D, 0x16E39264, 0x92544A8B, 0x009B4FC3,
+      0xABA68CED, 0x9AC96F78, 0x06A5B79A, 0xB2856E6E, 0x1AEC3CA9, 0xBE838688,
+      0x0E0804E9, 0x55F1BE56, 0xE7E5363B, 0xB3A1F25D, 0xF7DEBB85, 0x61FE033C,
+      0x16746233, 0x3C034C28, 0xDA6D0C74, 0x79AAC56C, 0x3CE4E1AD, 0x51F0C802,
+      0x98F8F35A, 0x1626A49F, 0xEED82B29, 0x1D382FE3, 0x0C4FB99A, 0xBB325778,
+      0x3EC6D97B, 0x6E77A6A9, 0xCB658B5C, 0xD45230C7, 0x2BD1408B, 0x60C03EB7,
+      0xB9068D78, 0xA33754F4, 0xF430C87D, 0xC8A71302, 0xB96D8C32, 0xEBD4E7BE,
+      0xBE8B9D2D, 0x7979FB06, 0xE7225308, 0x8B75CF77, 0x11EF8DA4, 0xE083C858,
+      0x8D6B786F, 0x5A6317A6, 0xFA5CF7A0, 0x5DDA0033, 0xF28EBFB0, 0xF5B9C310,
+      0xA0EAC280, 0x08B9767A, 0xA3D9D2B0, 0x79D34217, 0x021A718D, 0x9AC6336A,
+      0x2711FD60, 0x438050E3, 0x069908A8, 0x3D7FEDC4, 0x826D2BEF, 0x4EEB8476,
+      0x488DCF25, 0x36C9D566, 0x28E74E41, 0xC2610ACA, 0x3D49A9CF, 0xBAE3B9DF,
+      0xB65F8DE6, 0x92AEAF64, 0x3AC7D5E6, 0x9EA80509, 0xF22B017D, 0xA4173F70,
+      0xDD1E16C3, 0x15E0D7F9, 0x50B1B887, 0x2B9F4FD5, 0x625ABA82, 0x6A017962,
+      0x2EC01B9C, 0x15488AA9, 0xD716E740, 0x40055A2C, 0x93D29A22, 0xE32DBF9A,
+      0x058745B9, 0x3453DC1E, 0xD699296E, 0x496CFF6F, 0x1C9F4986, 0xDFE2ED07,
+      0xB87242D1, 0x19DE7EAE, 0x053E561A, 0x15AD6F8C, 0x66626C1C, 0x7154C24C,
+      0xEA082B2A, 0x93EB2939, 0x17DCB0F0, 0x58D4F2AE, 0x9EA294FB, 0x52CF564C,
+      0x9883FE66, 0x2EC40581, 0x763953C3, 0x01D6692E, 0xD3A0C108, 0xA1E7160E,
+      0xE4F2DFA6, 0x693ED285, 0x74904698, 0x4C2B0EDD, 0x4F757656, 0x5D393378,
+      0xA132234F, 0x3D321C5D, 0xC3F5E194, 0x4B269301, 0xC79F022F, 0x3C997E7E,
+      0x5E4F9504, 0x3FFAFBBD, 0x76F7AD0E, 0x296693F4, 0x3D1FCE6F, 0xC61E45BE,
+      0xD3B5AB34, 0xF72BF9B7, 0x1B0434C0, 0x4E72B567, 0x5592A33D, 0xB5229301,
+      0xCFD2A87F, 0x60AEB767, 0x1814386B, 0x30BCC33D, 0x38A0C07D, 0xFD1606F2,
+      0xC363519B, 0x589DD390, 0x5479F8E6, 0x1CB8D647, 0x97FD61A9, 0xEA7759F4,
+      0x2D57539D, 0x569A58CF, 0xE84E63AD, 0x462E1B78, 0x6580F87E, 0xF3817914,
+      0x91DA55F4, 0x40A230F3, 0xD1988F35, 0xB6E318D2, 0x3FFA50BC, 0x3D40F021,
+      0xC3C0BDAE, 0x4958C24C, 0x518F36B2, 0x84B1D370, 0x0FEDCE83, 0x878DDADA,
+      0xF2A279C7, 0x94E01BE8, 0x90716F4B, 0x954B8AA3 };
+
+   static const uint32_t S8[256] = {
+      0xE216300D, 0xBBDDFFFC, 0xA7EBDABD, 0x35648095, 0x7789F8B7, 0xE6C1121B,
+      0x0E241600, 0x052CE8B5, 0x11A9CFB0, 0xE5952F11, 0xECE7990A, 0x9386D174,
+      0x2A42931C, 0x76E38111, 0xB12DEF3A, 0x37DDDDFC, 0xDE9ADEB1, 0x0A0CC32C,
+      0xBE197029, 0x84A00940, 0xBB243A0F, 0xB4D137CF, 0xB44E79F0, 0x049EEDFD,
+      0x0B15A15D, 0x480D3168, 0x8BBBDE5A, 0x669DED42, 0xC7ECE831, 0x3F8F95E7,
+      0x72DF191B, 0x7580330D, 0x94074251, 0x5C7DCDFA, 0xABBE6D63, 0xAA402164,
+      0xB301D40A, 0x02E7D1CA, 0x53571DAE, 0x7A3182A2, 0x12A8DDEC, 0xFDAA335D,
+      0x176F43E8, 0x71FB46D4, 0x38129022, 0xCE949AD4, 0xB84769AD, 0x965BD862,
+      0x82F3D055, 0x66FB9767, 0x15B80B4E, 0x1D5B47A0, 0x4CFDE06F, 0xC28EC4B8,
+      0x57E8726E, 0x647A78FC, 0x99865D44, 0x608BD593, 0x6C200E03, 0x39DC5FF6,
+      0x5D0B00A3, 0xAE63AFF2, 0x7E8BD632, 0x70108C0C, 0xBBD35049, 0x2998DF04,
+      0x980CF42A, 0x9B6DF491, 0x9E7EDD53, 0x06918548, 0x58CB7E07, 0x3B74EF2E,
+      0x522FFFB1, 0xD24708CC, 0x1C7E27CD, 0xA4EB215B, 0x3CF1D2E2, 0x19B47A38,
+      0x424F7618, 0x35856039, 0x9D17DEE7, 0x27EB35E6, 0xC9AFF67B, 0x36BAF5B8,
+      0x09C467CD, 0xC18910B1, 0xE11DBF7B, 0x06CD1AF8, 0x7170C608, 0x2D5E3354,
+      0xD4DE495A, 0x64C6D006, 0xBCC0C62C, 0x3DD00DB3, 0x708F8F34, 0x77D51B42,
+      0x264F620F, 0x24B8D2BF, 0x15C1B79E, 0x46A52564, 0xF8D7E54E, 0x3E378160,
+      0x7895CDA5, 0x859C15A5, 0xE6459788, 0xC37BC75F, 0xDB07BA0C, 0x0676A3AB,
+      0x7F229B1E, 0x31842E7B, 0x24259FD7, 0xF8BEF472, 0x835FFCB8, 0x6DF4C1F2,
+      0x96F5B195, 0xFD0AF0FC, 0xB0FE134C, 0xE2506D3D, 0x4F9B12EA, 0xF215F225,
+      0xA223736F, 0x9FB4C428, 0x25D04979, 0x34C713F8, 0xC4618187, 0xEA7A6E98,
+      0x7CD16EFC, 0x1436876C, 0xF1544107, 0xBEDEEE14, 0x56E9AF27, 0xA04AA441,
+      0x3CF7C899, 0x92ECBAE6, 0xDD67016D, 0x151682EB, 0xA842EEDF, 0xFDBA60B4,
+      0xF1907B75, 0x20E3030F, 0x24D8C29E, 0xE139673B, 0xEFA63FB8, 0x71873054,
+      0xB6F2CF3B, 0x9F326442, 0xCB15A4CC, 0xB01A4504, 0xF1E47D8D, 0x844A1BE5,
+      0xBAE7DFDC, 0x42CBDA70, 0xCD7DAE0A, 0x57E85B7A, 0xD53F5AF6, 0x20CF4D8C,
+      0xCEA4D428, 0x79D130A4, 0x3486EBFB, 0x33D3CDDC, 0x77853B53, 0x37EFFCB5,
+      0xC5068778, 0xE580B3E6, 0x4E68B8F4, 0xC5C8B37E, 0x0D809EA2, 0x398FEB7C,
+      0x132A4F94, 0x43B7950E, 0x2FEE7D1C, 0x223613BD, 0xDD06CAA2, 0x37DF932B,
+      0xC4248289, 0xACF3EBC3, 0x5715F6B7, 0xEF3478DD, 0xF267616F, 0xC148CBE4,
+      0x9052815E, 0x5E410FAB, 0xB48A2465, 0x2EDA7FA4, 0xE87B40E4, 0xE98EA084,
+      0x5889E9E1, 0xEFD390FC, 0xDD07D35B, 0xDB485694, 0x38D7E5B2, 0x57720101,
+      0x730EDEBC, 0x5B643113, 0x94917E4F, 0x503C2FBA, 0x646F1282, 0x7523D24A,
+      0xE0779695, 0xF9C17A8F, 0x7A5B2121, 0xD187B896, 0x29263A4D, 0xBA510CDF,
+      0x81F47C9F, 0xAD1163ED, 0xEA7B5965, 0x1A00726E, 0x11403092, 0x00DA6D77,
+      0x4A0CDD61, 0xAD1F4603, 0x605BDFB0, 0x9EEDC364, 0x22EBE6A8, 0xCEE7D28A,
+      0xA0E736A0, 0x5564A6B9, 0x10853209, 0xC7EB8F37, 0x2DE705CA, 0x8951570F,
+      0xDF09822B, 0xBD691A6C, 0xAA12E4F2, 0x87451C0F, 0xE0F6A27A, 0x3ADA4819,
+      0x4CF1764F, 0x0D771C2B, 0x67CDB156, 0x350D8384, 0x5938FA0F, 0x42399EF3,
+      0x36997B07, 0x0E84093D, 0x4AA93E61, 0x8360D87B, 0x1FA98B0C, 0x1149382C,
+      0xE97625A5, 0x0614D1B7, 0x0E25244B, 0x0C768347, 0x589E8D82, 0x0D2059D1,
+      0xA466BB1E, 0xF8DA0A82, 0x04F19130, 0xBA6E4EC0, 0x99265164, 0x1EE7230D,
+      0x50B2AD80, 0xEAEE6801, 0x8DB2A283, 0xEA8BF59E };
+
+   class ByteReader final
+      {
+      public:
+         uint8_t operator()(size_t i) const
+            {
+            return static_cast<uint8_t>(m_X[i/4] >> (8*(3 - (i%4))));
+            }
+
+         explicit ByteReader(const uint32_t* x) : m_X(x) {}
+      private:
+         const uint32_t* m_X;
+      };
+
+   secure_vector<uint32_t> Z(4);
+   ByteReader x(X.data()), z(Z.data());
+
+   Z[0]  = X[0] ^ S5[x(13)] ^ S6[x(15)] ^ S7[x(12)] ^ S8[x(14)] ^ S7[x( 8)];
+   Z[1]  = X[2] ^ S5[z( 0)] ^ S6[z( 2)] ^ S7[z( 1)] ^ S8[z( 3)] ^ S8[x(10)];
+   Z[2]  = X[3] ^ S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S5[x( 9)];
+   Z[3]  = X[1] ^ S5[z(10)] ^ S6[z( 9)] ^ S7[z(11)] ^ S8[z( 8)] ^ S6[x(11)];
+   K[ 0] = S5[z( 8)] ^ S6[z( 9)] ^ S7[z( 7)] ^ S8[z( 6)] ^ S5[z( 2)];
+   K[ 1] = S5[z(10)] ^ S6[z(11)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S6[z( 6)];
+   K[ 2] = S5[z(12)] ^ S6[z(13)] ^ S7[z( 3)] ^ S8[z( 2)] ^ S7[z( 9)];
+   K[ 3] = S5[z(14)] ^ S6[z(15)] ^ S7[z( 1)] ^ S8[z( 0)] ^ S8[z(12)];
+   X[0]  = Z[2] ^ S5[z( 5)] ^ S6[z( 7)] ^ S7[z( 4)] ^ S8[z( 6)] ^ S7[z( 0)];
+   X[1]  = Z[0] ^ S5[x( 0)] ^ S6[x( 2)] ^ S7[x( 1)] ^ S8[x( 3)] ^ S8[z( 2)];
+   X[2]  = Z[1] ^ S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S5[z( 1)];
+   X[3]  = Z[3] ^ S5[x(10)] ^ S6[x( 9)] ^ S7[x(11)] ^ S8[x( 8)] ^ S6[z( 3)];
+   K[ 4] = S5[x( 3)] ^ S6[x( 2)] ^ S7[x(12)] ^ S8[x(13)] ^ S5[x( 8)];
+   K[ 5] = S5[x( 1)] ^ S6[x( 0)] ^ S7[x(14)] ^ S8[x(15)] ^ S6[x(13)];
+   K[ 6] = S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 8)] ^ S8[x( 9)] ^ S7[x( 3)];
+   K[ 7] = S5[x( 5)] ^ S6[x( 4)] ^ S7[x(10)] ^ S8[x(11)] ^ S8[x( 7)];
+   Z[0]  = X[0] ^ S5[x(13)] ^ S6[x(15)] ^ S7[x(12)] ^ S8[x(14)] ^ S7[x( 8)];
+   Z[1]  = X[2] ^ S5[z( 0)] ^ S6[z( 2)] ^ S7[z( 1)] ^ S8[z( 3)] ^ S8[x(10)];
+   Z[2]  = X[3] ^ S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S5[x( 9)];
+   Z[3]  = X[1] ^ S5[z(10)] ^ S6[z( 9)] ^ S7[z(11)] ^ S8[z( 8)] ^ S6[x(11)];
+   K[ 8] = S5[z( 3)] ^ S6[z( 2)] ^ S7[z(12)] ^ S8[z(13)] ^ S5[z( 9)];
+   K[ 9] = S5[z( 1)] ^ S6[z( 0)] ^ S7[z(14)] ^ S8[z(15)] ^ S6[z(12)];
+   K[10] = S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 8)] ^ S8[z( 9)] ^ S7[z( 2)];
+   K[11] = S5[z( 5)] ^ S6[z( 4)] ^ S7[z(10)] ^ S8[z(11)] ^ S8[z( 6)];
+   X[0]  = Z[2] ^ S5[z( 5)] ^ S6[z( 7)] ^ S7[z( 4)] ^ S8[z( 6)] ^ S7[z( 0)];
+   X[1]  = Z[0] ^ S5[x( 0)] ^ S6[x( 2)] ^ S7[x( 1)] ^ S8[x( 3)] ^ S8[z( 2)];
+   X[2]  = Z[1] ^ S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S5[z( 1)];
+   X[3]  = Z[3] ^ S5[x(10)] ^ S6[x( 9)] ^ S7[x(11)] ^ S8[x( 8)] ^ S6[z( 3)];
+   K[12] = S5[x( 8)] ^ S6[x( 9)] ^ S7[x( 7)] ^ S8[x( 6)] ^ S5[x( 3)];
+   K[13] = S5[x(10)] ^ S6[x(11)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S6[x( 7)];
+   K[14] = S5[x(12)] ^ S6[x(13)] ^ S7[x( 3)] ^ S8[x( 2)] ^ S7[x( 8)];
+   K[15] = S5[x(14)] ^ S6[x(15)] ^ S7[x( 1)] ^ S8[x( 0)] ^ S8[x(13)];
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/cast128/cast128.h b/third_party/botan/src/lib/block/cast128/cast128.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cast128/cast128.h
@@ -0,0 +1,42 @@
+/*
+* CAST-128
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST128_H_
+#define BOTAN_CAST128_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cast128.h)
+
+namespace Botan {
+
+/**
+* CAST-128
+*/
+class BOTAN_PUBLIC_API(2,0) CAST_128 final : public Block_Cipher_Fixed_Params<8, 11, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "CAST-128"; }
+      BlockCipher* clone() const override { return new CAST_128; }
+
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      static void cast_ks(secure_vector<uint32_t>& ks,
+                          secure_vector<uint32_t>& user_key);
+
+      secure_vector<uint32_t> m_MK;
+      secure_vector<uint8_t> m_RK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/cast128/cast_sboxes.h b/third_party/botan/src/lib/block/cast128/cast_sboxes.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cast128/cast_sboxes.h
@@ -0,0 +1,197 @@
+/*
+* S-Box Tables for CAST-128 and CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST_SBOX_TABLES_H_
+#define BOTAN_CAST_SBOX_TABLES_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+const uint32_t CAST_SBOX1[256] = {
+   0x30FB40D4, 0x9FA0FF0B, 0x6BECCD2F, 0x3F258C7A, 0x1E213F2F, 0x9C004DD3,
+   0x6003E540, 0xCF9FC949, 0xBFD4AF27, 0x88BBBDB5, 0xE2034090, 0x98D09675,
+   0x6E63A0E0, 0x15C361D2, 0xC2E7661D, 0x22D4FF8E, 0x28683B6F, 0xC07FD059,
+   0xFF2379C8, 0x775F50E2, 0x43C340D3, 0xDF2F8656, 0x887CA41A, 0xA2D2BD2D,
+   0xA1C9E0D6, 0x346C4819, 0x61B76D87, 0x22540F2F, 0x2ABE32E1, 0xAA54166B,
+   0x22568E3A, 0xA2D341D0, 0x66DB40C8, 0xA784392F, 0x004DFF2F, 0x2DB9D2DE,
+   0x97943FAC, 0x4A97C1D8, 0x527644B7, 0xB5F437A7, 0xB82CBAEF, 0xD751D159,
+   0x6FF7F0ED, 0x5A097A1F, 0x827B68D0, 0x90ECF52E, 0x22B0C054, 0xBC8E5935,
+   0x4B6D2F7F, 0x50BB64A2, 0xD2664910, 0xBEE5812D, 0xB7332290, 0xE93B159F,
+   0xB48EE411, 0x4BFF345D, 0xFD45C240, 0xAD31973F, 0xC4F6D02E, 0x55FC8165,
+   0xD5B1CAAD, 0xA1AC2DAE, 0xA2D4B76D, 0xC19B0C50, 0x882240F2, 0x0C6E4F38,
+   0xA4E4BFD7, 0x4F5BA272, 0x564C1D2F, 0xC59C5319, 0xB949E354, 0xB04669FE,
+   0xB1B6AB8A, 0xC71358DD, 0x6385C545, 0x110F935D, 0x57538AD5, 0x6A390493,
+   0xE63D37E0, 0x2A54F6B3, 0x3A787D5F, 0x6276A0B5, 0x19A6FCDF, 0x7A42206A,
+   0x29F9D4D5, 0xF61B1891, 0xBB72275E, 0xAA508167, 0x38901091, 0xC6B505EB,
+   0x84C7CB8C, 0x2AD75A0F, 0x874A1427, 0xA2D1936B, 0x2AD286AF, 0xAA56D291,
+   0xD7894360, 0x425C750D, 0x93B39E26, 0x187184C9, 0x6C00B32D, 0x73E2BB14,
+   0xA0BEBC3C, 0x54623779, 0x64459EAB, 0x3F328B82, 0x7718CF82, 0x59A2CEA6,
+   0x04EE002E, 0x89FE78E6, 0x3FAB0950, 0x325FF6C2, 0x81383F05, 0x6963C5C8,
+   0x76CB5AD6, 0xD49974C9, 0xCA180DCF, 0x380782D5, 0xC7FA5CF6, 0x8AC31511,
+   0x35E79E13, 0x47DA91D0, 0xF40F9086, 0xA7E2419E, 0x31366241, 0x051EF495,
+   0xAA573B04, 0x4A805D8D, 0x548300D0, 0x00322A3C, 0xBF64CDDF, 0xBA57A68E,
+   0x75C6372B, 0x50AFD341, 0xA7C13275, 0x915A0BF5, 0x6B54BFAB, 0x2B0B1426,
+   0xAB4CC9D7, 0x449CCD82, 0xF7FBF265, 0xAB85C5F3, 0x1B55DB94, 0xAAD4E324,
+   0xCFA4BD3F, 0x2DEAA3E2, 0x9E204D02, 0xC8BD25AC, 0xEADF55B3, 0xD5BD9E98,
+   0xE31231B2, 0x2AD5AD6C, 0x954329DE, 0xADBE4528, 0xD8710F69, 0xAA51C90F,
+   0xAA786BF6, 0x22513F1E, 0xAA51A79B, 0x2AD344CC, 0x7B5A41F0, 0xD37CFBAD,
+   0x1B069505, 0x41ECE491, 0xB4C332E6, 0x032268D4, 0xC9600ACC, 0xCE387E6D,
+   0xBF6BB16C, 0x6A70FB78, 0x0D03D9C9, 0xD4DF39DE, 0xE01063DA, 0x4736F464,
+   0x5AD328D8, 0xB347CC96, 0x75BB0FC3, 0x98511BFB, 0x4FFBCC35, 0xB58BCF6A,
+   0xE11F0ABC, 0xBFC5FE4A, 0xA70AEC10, 0xAC39570A, 0x3F04442F, 0x6188B153,
+   0xE0397A2E, 0x5727CB79, 0x9CEB418F, 0x1CACD68D, 0x2AD37C96, 0x0175CB9D,
+   0xC69DFF09, 0xC75B65F0, 0xD9DB40D8, 0xEC0E7779, 0x4744EAD4, 0xB11C3274,
+   0xDD24CB9E, 0x7E1C54BD, 0xF01144F9, 0xD2240EB1, 0x9675B3FD, 0xA3AC3755,
+   0xD47C27AF, 0x51C85F4D, 0x56907596, 0xA5BB15E6, 0x580304F0, 0xCA042CF1,
+   0x011A37EA, 0x8DBFAADB, 0x35BA3E4A, 0x3526FFA0, 0xC37B4D09, 0xBC306ED9,
+   0x98A52666, 0x5648F725, 0xFF5E569D, 0x0CED63D0, 0x7C63B2CF, 0x700B45E1,
+   0xD5EA50F1, 0x85A92872, 0xAF1FBDA7, 0xD4234870, 0xA7870BF3, 0x2D3B4D79,
+   0x42E04198, 0x0CD0EDE7, 0x26470DB8, 0xF881814C, 0x474D6AD7, 0x7C0C5E5C,
+   0xD1231959, 0x381B7298, 0xF5D2F4DB, 0xAB838653, 0x6E2F1E23, 0x83719C9E,
+   0xBD91E046, 0x9A56456E, 0xDC39200C, 0x20C8C571, 0x962BDA1C, 0xE1E696FF,
+   0xB141AB08, 0x7CCA89B9, 0x1A69E783, 0x02CC4843, 0xA2F7C579, 0x429EF47D,
+   0x427B169C, 0x5AC9F049, 0xDD8F0F00, 0x5C8165BF };
+
+const uint32_t CAST_SBOX2[256] = {
+   0x1F201094, 0xEF0BA75B, 0x69E3CF7E, 0x393F4380, 0xFE61CF7A, 0xEEC5207A,
+   0x55889C94, 0x72FC0651, 0xADA7EF79, 0x4E1D7235, 0xD55A63CE, 0xDE0436BA,
+   0x99C430EF, 0x5F0C0794, 0x18DCDB7D, 0xA1D6EFF3, 0xA0B52F7B, 0x59E83605,
+   0xEE15B094, 0xE9FFD909, 0xDC440086, 0xEF944459, 0xBA83CCB3, 0xE0C3CDFB,
+   0xD1DA4181, 0x3B092AB1, 0xF997F1C1, 0xA5E6CF7B, 0x01420DDB, 0xE4E7EF5B,
+   0x25A1FF41, 0xE180F806, 0x1FC41080, 0x179BEE7A, 0xD37AC6A9, 0xFE5830A4,
+   0x98DE8B7F, 0x77E83F4E, 0x79929269, 0x24FA9F7B, 0xE113C85B, 0xACC40083,
+   0xD7503525, 0xF7EA615F, 0x62143154, 0x0D554B63, 0x5D681121, 0xC866C359,
+   0x3D63CF73, 0xCEE234C0, 0xD4D87E87, 0x5C672B21, 0x071F6181, 0x39F7627F,
+   0x361E3084, 0xE4EB573B, 0x602F64A4, 0xD63ACD9C, 0x1BBC4635, 0x9E81032D,
+   0x2701F50C, 0x99847AB4, 0xA0E3DF79, 0xBA6CF38C, 0x10843094, 0x2537A95E,
+   0xF46F6FFE, 0xA1FF3B1F, 0x208CFB6A, 0x8F458C74, 0xD9E0A227, 0x4EC73A34,
+   0xFC884F69, 0x3E4DE8DF, 0xEF0E0088, 0x3559648D, 0x8A45388C, 0x1D804366,
+   0x721D9BFD, 0xA58684BB, 0xE8256333, 0x844E8212, 0x128D8098, 0xFED33FB4,
+   0xCE280AE1, 0x27E19BA5, 0xD5A6C252, 0xE49754BD, 0xC5D655DD, 0xEB667064,
+   0x77840B4D, 0xA1B6A801, 0x84DB26A9, 0xE0B56714, 0x21F043B7, 0xE5D05860,
+   0x54F03084, 0x066FF472, 0xA31AA153, 0xDADC4755, 0xB5625DBF, 0x68561BE6,
+   0x83CA6B94, 0x2D6ED23B, 0xECCF01DB, 0xA6D3D0BA, 0xB6803D5C, 0xAF77A709,
+   0x33B4A34C, 0x397BC8D6, 0x5EE22B95, 0x5F0E5304, 0x81ED6F61, 0x20E74364,
+   0xB45E1378, 0xDE18639B, 0x881CA122, 0xB96726D1, 0x8049A7E8, 0x22B7DA7B,
+   0x5E552D25, 0x5272D237, 0x79D2951C, 0xC60D894C, 0x488CB402, 0x1BA4FE5B,
+   0xA4B09F6B, 0x1CA815CF, 0xA20C3005, 0x8871DF63, 0xB9DE2FCB, 0x0CC6C9E9,
+   0x0BEEFF53, 0xE3214517, 0xB4542835, 0x9F63293C, 0xEE41E729, 0x6E1D2D7C,
+   0x50045286, 0x1E6685F3, 0xF33401C6, 0x30A22C95, 0x31A70850, 0x60930F13,
+   0x73F98417, 0xA1269859, 0xEC645C44, 0x52C877A9, 0xCDFF33A6, 0xA02B1741,
+   0x7CBAD9A2, 0x2180036F, 0x50D99C08, 0xCB3F4861, 0xC26BD765, 0x64A3F6AB,
+   0x80342676, 0x25A75E7B, 0xE4E6D1FC, 0x20C710E6, 0xCDF0B680, 0x17844D3B,
+   0x31EEF84D, 0x7E0824E4, 0x2CCB49EB, 0x846A3BAE, 0x8FF77888, 0xEE5D60F6,
+   0x7AF75673, 0x2FDD5CDB, 0xA11631C1, 0x30F66F43, 0xB3FAEC54, 0x157FD7FA,
+   0xEF8579CC, 0xD152DE58, 0xDB2FFD5E, 0x8F32CE19, 0x306AF97A, 0x02F03EF8,
+   0x99319AD5, 0xC242FA0F, 0xA7E3EBB0, 0xC68E4906, 0xB8DA230C, 0x80823028,
+   0xDCDEF3C8, 0xD35FB171, 0x088A1BC8, 0xBEC0C560, 0x61A3C9E8, 0xBCA8F54D,
+   0xC72FEFFA, 0x22822E99, 0x82C570B4, 0xD8D94E89, 0x8B1C34BC, 0x301E16E6,
+   0x273BE979, 0xB0FFEAA6, 0x61D9B8C6, 0x00B24869, 0xB7FFCE3F, 0x08DC283B,
+   0x43DAF65A, 0xF7E19798, 0x7619B72F, 0x8F1C9BA4, 0xDC8637A0, 0x16A7D3B1,
+   0x9FC393B7, 0xA7136EEB, 0xC6BCC63E, 0x1A513742, 0xEF6828BC, 0x520365D6,
+   0x2D6A77AB, 0x3527ED4B, 0x821FD216, 0x095C6E2E, 0xDB92F2FB, 0x5EEA29CB,
+   0x145892F5, 0x91584F7F, 0x5483697B, 0x2667A8CC, 0x85196048, 0x8C4BACEA,
+   0x833860D4, 0x0D23E0F9, 0x6C387E8A, 0x0AE6D249, 0xB284600C, 0xD835731D,
+   0xDCB1C647, 0xAC4C56EA, 0x3EBD81B3, 0x230EABB0, 0x6438BC87, 0xF0B5B1FA,
+   0x8F5EA2B3, 0xFC184642, 0x0A036B7A, 0x4FB089BD, 0x649DA589, 0xA345415E,
+   0x5C038323, 0x3E5D3BB9, 0x43D79572, 0x7E6DD07C, 0x06DFDF1E, 0x6C6CC4EF,
+   0x7160A539, 0x73BFBE70, 0x83877605, 0x4523ECF1 };
+
+const uint32_t CAST_SBOX3[256] = {
+   0x8DEFC240, 0x25FA5D9F, 0xEB903DBF, 0xE810C907, 0x47607FFF, 0x369FE44B,
+   0x8C1FC644, 0xAECECA90, 0xBEB1F9BF, 0xEEFBCAEA, 0xE8CF1950, 0x51DF07AE,
+   0x920E8806, 0xF0AD0548, 0xE13C8D83, 0x927010D5, 0x11107D9F, 0x07647DB9,
+   0xB2E3E4D4, 0x3D4F285E, 0xB9AFA820, 0xFADE82E0, 0xA067268B, 0x8272792E,
+   0x553FB2C0, 0x489AE22B, 0xD4EF9794, 0x125E3FBC, 0x21FFFCEE, 0x825B1BFD,
+   0x9255C5ED, 0x1257A240, 0x4E1A8302, 0xBAE07FFF, 0x528246E7, 0x8E57140E,
+   0x3373F7BF, 0x8C9F8188, 0xA6FC4EE8, 0xC982B5A5, 0xA8C01DB7, 0x579FC264,
+   0x67094F31, 0xF2BD3F5F, 0x40FFF7C1, 0x1FB78DFC, 0x8E6BD2C1, 0x437BE59B,
+   0x99B03DBF, 0xB5DBC64B, 0x638DC0E6, 0x55819D99, 0xA197C81C, 0x4A012D6E,
+   0xC5884A28, 0xCCC36F71, 0xB843C213, 0x6C0743F1, 0x8309893C, 0x0FEDDD5F,
+   0x2F7FE850, 0xD7C07F7E, 0x02507FBF, 0x5AFB9A04, 0xA747D2D0, 0x1651192E,
+   0xAF70BF3E, 0x58C31380, 0x5F98302E, 0x727CC3C4, 0x0A0FB402, 0x0F7FEF82,
+   0x8C96FDAD, 0x5D2C2AAE, 0x8EE99A49, 0x50DA88B8, 0x8427F4A0, 0x1EAC5790,
+   0x796FB449, 0x8252DC15, 0xEFBD7D9B, 0xA672597D, 0xADA840D8, 0x45F54504,
+   0xFA5D7403, 0xE83EC305, 0x4F91751A, 0x925669C2, 0x23EFE941, 0xA903F12E,
+   0x60270DF2, 0x0276E4B6, 0x94FD6574, 0x927985B2, 0x8276DBCB, 0x02778176,
+   0xF8AF918D, 0x4E48F79E, 0x8F616DDF, 0xE29D840E, 0x842F7D83, 0x340CE5C8,
+   0x96BBB682, 0x93B4B148, 0xEF303CAB, 0x984FAF28, 0x779FAF9B, 0x92DC560D,
+   0x224D1E20, 0x8437AA88, 0x7D29DC96, 0x2756D3DC, 0x8B907CEE, 0xB51FD240,
+   0xE7C07CE3, 0xE566B4A1, 0xC3E9615E, 0x3CF8209D, 0x6094D1E3, 0xCD9CA341,
+   0x5C76460E, 0x00EA983B, 0xD4D67881, 0xFD47572C, 0xF76CEDD9, 0xBDA8229C,
+   0x127DADAA, 0x438A074E, 0x1F97C090, 0x081BDB8A, 0x93A07EBE, 0xB938CA15,
+   0x97B03CFF, 0x3DC2C0F8, 0x8D1AB2EC, 0x64380E51, 0x68CC7BFB, 0xD90F2788,
+   0x12490181, 0x5DE5FFD4, 0xDD7EF86A, 0x76A2E214, 0xB9A40368, 0x925D958F,
+   0x4B39FFFA, 0xBA39AEE9, 0xA4FFD30B, 0xFAF7933B, 0x6D498623, 0x193CBCFA,
+   0x27627545, 0x825CF47A, 0x61BD8BA0, 0xD11E42D1, 0xCEAD04F4, 0x127EA392,
+   0x10428DB7, 0x8272A972, 0x9270C4A8, 0x127DE50B, 0x285BA1C8, 0x3C62F44F,
+   0x35C0EAA5, 0xE805D231, 0x428929FB, 0xB4FCDF82, 0x4FB66A53, 0x0E7DC15B,
+   0x1F081FAB, 0x108618AE, 0xFCFD086D, 0xF9FF2889, 0x694BCC11, 0x236A5CAE,
+   0x12DECA4D, 0x2C3F8CC5, 0xD2D02DFE, 0xF8EF5896, 0xE4CF52DA, 0x95155B67,
+   0x494A488C, 0xB9B6A80C, 0x5C8F82BC, 0x89D36B45, 0x3A609437, 0xEC00C9A9,
+   0x44715253, 0x0A874B49, 0xD773BC40, 0x7C34671C, 0x02717EF6, 0x4FEB5536,
+   0xA2D02FFF, 0xD2BF60C4, 0xD43F03C0, 0x50B4EF6D, 0x07478CD1, 0x006E1888,
+   0xA2E53F55, 0xB9E6D4BC, 0xA2048016, 0x97573833, 0xD7207D67, 0xDE0F8F3D,
+   0x72F87B33, 0xABCC4F33, 0x7688C55D, 0x7B00A6B0, 0x947B0001, 0x570075D2,
+   0xF9BB88F8, 0x8942019E, 0x4264A5FF, 0x856302E0, 0x72DBD92B, 0xEE971B69,
+   0x6EA22FDE, 0x5F08AE2B, 0xAF7A616D, 0xE5C98767, 0xCF1FEBD2, 0x61EFC8C2,
+   0xF1AC2571, 0xCC8239C2, 0x67214CB8, 0xB1E583D1, 0xB7DC3E62, 0x7F10BDCE,
+   0xF90A5C38, 0x0FF0443D, 0x606E6DC6, 0x60543A49, 0x5727C148, 0x2BE98A1D,
+   0x8AB41738, 0x20E1BE24, 0xAF96DA0F, 0x68458425, 0x99833BE5, 0x600D457D,
+   0x282F9350, 0x8334B362, 0xD91D1120, 0x2B6D8DA0, 0x642B1E31, 0x9C305A00,
+   0x52BCE688, 0x1B03588A, 0xF7BAEFD5, 0x4142ED9C, 0xA4315C11, 0x83323EC5,
+   0xDFEF4636, 0xA133C501, 0xE9D3531C, 0xEE353783 };
+
+const uint32_t CAST_SBOX4[256] = {
+   0x9DB30420, 0x1FB6E9DE, 0xA7BE7BEF, 0xD273A298, 0x4A4F7BDB, 0x64AD8C57,
+   0x85510443, 0xFA020ED1, 0x7E287AFF, 0xE60FB663, 0x095F35A1, 0x79EBF120,
+   0xFD059D43, 0x6497B7B1, 0xF3641F63, 0x241E4ADF, 0x28147F5F, 0x4FA2B8CD,
+   0xC9430040, 0x0CC32220, 0xFDD30B30, 0xC0A5374F, 0x1D2D00D9, 0x24147B15,
+   0xEE4D111A, 0x0FCA5167, 0x71FF904C, 0x2D195FFE, 0x1A05645F, 0x0C13FEFE,
+   0x081B08CA, 0x05170121, 0x80530100, 0xE83E5EFE, 0xAC9AF4F8, 0x7FE72701,
+   0xD2B8EE5F, 0x06DF4261, 0xBB9E9B8A, 0x7293EA25, 0xCE84FFDF, 0xF5718801,
+   0x3DD64B04, 0xA26F263B, 0x7ED48400, 0x547EEBE6, 0x446D4CA0, 0x6CF3D6F5,
+   0x2649ABDF, 0xAEA0C7F5, 0x36338CC1, 0x503F7E93, 0xD3772061, 0x11B638E1,
+   0x72500E03, 0xF80EB2BB, 0xABE0502E, 0xEC8D77DE, 0x57971E81, 0xE14F6746,
+   0xC9335400, 0x6920318F, 0x081DBB99, 0xFFC304A5, 0x4D351805, 0x7F3D5CE3,
+   0xA6C866C6, 0x5D5BCCA9, 0xDAEC6FEA, 0x9F926F91, 0x9F46222F, 0x3991467D,
+   0xA5BF6D8E, 0x1143C44F, 0x43958302, 0xD0214EEB, 0x022083B8, 0x3FB6180C,
+   0x18F8931E, 0x281658E6, 0x26486E3E, 0x8BD78A70, 0x7477E4C1, 0xB506E07C,
+   0xF32D0A25, 0x79098B02, 0xE4EABB81, 0x28123B23, 0x69DEAD38, 0x1574CA16,
+   0xDF871B62, 0x211C40B7, 0xA51A9EF9, 0x0014377B, 0x041E8AC8, 0x09114003,
+   0xBD59E4D2, 0xE3D156D5, 0x4FE876D5, 0x2F91A340, 0x557BE8DE, 0x00EAE4A7,
+   0x0CE5C2EC, 0x4DB4BBA6, 0xE756BDFF, 0xDD3369AC, 0xEC17B035, 0x06572327,
+   0x99AFC8B0, 0x56C8C391, 0x6B65811C, 0x5E146119, 0x6E85CB75, 0xBE07C002,
+   0xC2325577, 0x893FF4EC, 0x5BBFC92D, 0xD0EC3B25, 0xB7801AB7, 0x8D6D3B24,
+   0x20C763EF, 0xC366A5FC, 0x9C382880, 0x0ACE3205, 0xAAC9548A, 0xECA1D7C7,
+   0x041AFA32, 0x1D16625A, 0x6701902C, 0x9B757A54, 0x31D477F7, 0x9126B031,
+   0x36CC6FDB, 0xC70B8B46, 0xD9E66A48, 0x56E55A79, 0x026A4CEB, 0x52437EFF,
+   0x2F8F76B4, 0x0DF980A5, 0x8674CDE3, 0xEDDA04EB, 0x17A9BE04, 0x2C18F4DF,
+   0xB7747F9D, 0xAB2AF7B4, 0xEFC34D20, 0x2E096B7C, 0x1741A254, 0xE5B6A035,
+   0x213D42F6, 0x2C1C7C26, 0x61C2F50F, 0x6552DAF9, 0xD2C231F8, 0x25130F69,
+   0xD8167FA2, 0x0418F2C8, 0x001A96A6, 0x0D1526AB, 0x63315C21, 0x5E0A72EC,
+   0x49BAFEFD, 0x187908D9, 0x8D0DBD86, 0x311170A7, 0x3E9B640C, 0xCC3E10D7,
+   0xD5CAD3B6, 0x0CAEC388, 0xF73001E1, 0x6C728AFF, 0x71EAE2A1, 0x1F9AF36E,
+   0xCFCBD12F, 0xC1DE8417, 0xAC07BE6B, 0xCB44A1D8, 0x8B9B0F56, 0x013988C3,
+   0xB1C52FCA, 0xB4BE31CD, 0xD8782806, 0x12A3A4E2, 0x6F7DE532, 0x58FD7EB6,
+   0xD01EE900, 0x24ADFFC2, 0xF4990FC5, 0x9711AAC5, 0x001D7B95, 0x82E5E7D2,
+   0x109873F6, 0x00613096, 0xC32D9521, 0xADA121FF, 0x29908415, 0x7FBB977F,
+   0xAF9EB3DB, 0x29C9ED2A, 0x5CE2A465, 0xA730F32C, 0xD0AA3FE8, 0x8A5CC091,
+   0xD49E2CE7, 0x0CE454A9, 0xD60ACD86, 0x015F1919, 0x77079103, 0xDEA03AF6,
+   0x78A8565E, 0xDEE356DF, 0x21F05CBE, 0x8B75E387, 0xB3C50651, 0xB8A5C3EF,
+   0xD8EEB6D2, 0xE523BE77, 0xC2154529, 0x2F69EFDF, 0xAFE67AFB, 0xF470C4B2,
+   0xF3E0EB5B, 0xD6CC9876, 0x39E4460C, 0x1FDA8538, 0x1987832F, 0xCA007367,
+   0xA99144F8, 0x296B299E, 0x492FC295, 0x9266BEAB, 0xB5676E69, 0x9BD3DDDA,
+   0xDF7E052F, 0xDB25701C, 0x1B5E51EE, 0xF65324E6, 0x6AFCE36C, 0x0316CC04,
+   0x8644213E, 0xB7DC59D0, 0x7965291F, 0xCCD6FD43, 0x41823979, 0x932BCDF6,
+   0xB657C34D, 0x4EDFD282, 0x7AE5290C, 0x3CB9536B, 0x851E20FE, 0x9833557E,
+   0x13ECF0B0, 0xD3FFB372, 0x3F85C5C1, 0x0AEF7ED2 };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/cast256/cast256.cpp b/third_party/botan/src/lib/block/cast256/cast256.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cast256/cast256.cpp
@@ -0,0 +1,232 @@
+/*
+* CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cast256.h>
+#include <botan/internal/cast_sboxes.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* CAST-256 Round Type 1
+*/
+void round1(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+   {
+   const uint32_t T = rotl_var(MK + in, RK);
+   out ^= (CAST_SBOX1[get_byte(0, T)] ^ CAST_SBOX2[get_byte(1, T)]) -
+           CAST_SBOX3[get_byte(2, T)] + CAST_SBOX4[get_byte(3, T)];
+   }
+
+/*
+* CAST-256 Round Type 2
+*/
+void round2(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+   {
+   const uint32_t T = rotl_var(MK ^ in, RK);
+   out ^= (CAST_SBOX1[get_byte(0, T)]  - CAST_SBOX2[get_byte(1, T)] +
+           CAST_SBOX3[get_byte(2, T)]) ^ CAST_SBOX4[get_byte(3, T)];
+   }
+
+/*
+* CAST-256 Round Type 3
+*/
+void round3(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+   {
+   const uint32_t T = rotl_var(MK - in, RK);
+   out ^= ((CAST_SBOX1[get_byte(0, T)]  + CAST_SBOX2[get_byte(1, T)]) ^
+            CAST_SBOX3[get_byte(2, T)]) - CAST_SBOX4[get_byte(3, T)];
+   }
+
+}
+
+/*
+* CAST-256 Encryption
+*/
+void CAST_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A = load_be<uint32_t>(in, 0);
+      uint32_t B = load_be<uint32_t>(in, 1);
+      uint32_t C = load_be<uint32_t>(in, 2);
+      uint32_t D = load_be<uint32_t>(in, 3);
+
+      round1(C, D, m_MK[ 0], m_RK[ 0]); round2(B, C, m_MK[ 1], m_RK[ 1]);
+      round3(A, B, m_MK[ 2], m_RK[ 2]); round1(D, A, m_MK[ 3], m_RK[ 3]);
+      round1(C, D, m_MK[ 4], m_RK[ 4]); round2(B, C, m_MK[ 5], m_RK[ 5]);
+      round3(A, B, m_MK[ 6], m_RK[ 6]); round1(D, A, m_MK[ 7], m_RK[ 7]);
+      round1(C, D, m_MK[ 8], m_RK[ 8]); round2(B, C, m_MK[ 9], m_RK[ 9]);
+      round3(A, B, m_MK[10], m_RK[10]); round1(D, A, m_MK[11], m_RK[11]);
+      round1(C, D, m_MK[12], m_RK[12]); round2(B, C, m_MK[13], m_RK[13]);
+      round3(A, B, m_MK[14], m_RK[14]); round1(D, A, m_MK[15], m_RK[15]);
+      round1(C, D, m_MK[16], m_RK[16]); round2(B, C, m_MK[17], m_RK[17]);
+      round3(A, B, m_MK[18], m_RK[18]); round1(D, A, m_MK[19], m_RK[19]);
+      round1(C, D, m_MK[20], m_RK[20]); round2(B, C, m_MK[21], m_RK[21]);
+      round3(A, B, m_MK[22], m_RK[22]); round1(D, A, m_MK[23], m_RK[23]);
+      round1(D, A, m_MK[27], m_RK[27]); round3(A, B, m_MK[26], m_RK[26]);
+      round2(B, C, m_MK[25], m_RK[25]); round1(C, D, m_MK[24], m_RK[24]);
+      round1(D, A, m_MK[31], m_RK[31]); round3(A, B, m_MK[30], m_RK[30]);
+      round2(B, C, m_MK[29], m_RK[29]); round1(C, D, m_MK[28], m_RK[28]);
+      round1(D, A, m_MK[35], m_RK[35]); round3(A, B, m_MK[34], m_RK[34]);
+      round2(B, C, m_MK[33], m_RK[33]); round1(C, D, m_MK[32], m_RK[32]);
+      round1(D, A, m_MK[39], m_RK[39]); round3(A, B, m_MK[38], m_RK[38]);
+      round2(B, C, m_MK[37], m_RK[37]); round1(C, D, m_MK[36], m_RK[36]);
+      round1(D, A, m_MK[43], m_RK[43]); round3(A, B, m_MK[42], m_RK[42]);
+      round2(B, C, m_MK[41], m_RK[41]); round1(C, D, m_MK[40], m_RK[40]);
+      round1(D, A, m_MK[47], m_RK[47]); round3(A, B, m_MK[46], m_RK[46]);
+      round2(B, C, m_MK[45], m_RK[45]); round1(C, D, m_MK[44], m_RK[44]);
+
+      store_be(out, A, B, C, D);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* CAST-256 Decryption
+*/
+void CAST_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A = load_be<uint32_t>(in, 0);
+      uint32_t B = load_be<uint32_t>(in, 1);
+      uint32_t C = load_be<uint32_t>(in, 2);
+      uint32_t D = load_be<uint32_t>(in, 3);
+
+      round1(C, D, m_MK[44], m_RK[44]); round2(B, C, m_MK[45], m_RK[45]);
+      round3(A, B, m_MK[46], m_RK[46]); round1(D, A, m_MK[47], m_RK[47]);
+      round1(C, D, m_MK[40], m_RK[40]); round2(B, C, m_MK[41], m_RK[41]);
+      round3(A, B, m_MK[42], m_RK[42]); round1(D, A, m_MK[43], m_RK[43]);
+      round1(C, D, m_MK[36], m_RK[36]); round2(B, C, m_MK[37], m_RK[37]);
+      round3(A, B, m_MK[38], m_RK[38]); round1(D, A, m_MK[39], m_RK[39]);
+      round1(C, D, m_MK[32], m_RK[32]); round2(B, C, m_MK[33], m_RK[33]);
+      round3(A, B, m_MK[34], m_RK[34]); round1(D, A, m_MK[35], m_RK[35]);
+      round1(C, D, m_MK[28], m_RK[28]); round2(B, C, m_MK[29], m_RK[29]);
+      round3(A, B, m_MK[30], m_RK[30]); round1(D, A, m_MK[31], m_RK[31]);
+      round1(C, D, m_MK[24], m_RK[24]); round2(B, C, m_MK[25], m_RK[25]);
+      round3(A, B, m_MK[26], m_RK[26]); round1(D, A, m_MK[27], m_RK[27]);
+      round1(D, A, m_MK[23], m_RK[23]); round3(A, B, m_MK[22], m_RK[22]);
+      round2(B, C, m_MK[21], m_RK[21]); round1(C, D, m_MK[20], m_RK[20]);
+      round1(D, A, m_MK[19], m_RK[19]); round3(A, B, m_MK[18], m_RK[18]);
+      round2(B, C, m_MK[17], m_RK[17]); round1(C, D, m_MK[16], m_RK[16]);
+      round1(D, A, m_MK[15], m_RK[15]); round3(A, B, m_MK[14], m_RK[14]);
+      round2(B, C, m_MK[13], m_RK[13]); round1(C, D, m_MK[12], m_RK[12]);
+      round1(D, A, m_MK[11], m_RK[11]); round3(A, B, m_MK[10], m_RK[10]);
+      round2(B, C, m_MK[ 9], m_RK[ 9]); round1(C, D, m_MK[ 8], m_RK[ 8]);
+      round1(D, A, m_MK[ 7], m_RK[ 7]); round3(A, B, m_MK[ 6], m_RK[ 6]);
+      round2(B, C, m_MK[ 5], m_RK[ 5]); round1(C, D, m_MK[ 4], m_RK[ 4]);
+      round1(D, A, m_MK[ 3], m_RK[ 3]); round3(A, B, m_MK[ 2], m_RK[ 2]);
+      round2(B, C, m_MK[ 1], m_RK[ 1]); round1(C, D, m_MK[ 0], m_RK[ 0]);
+
+      store_be(out, A, B, C, D);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* CAST-256 Key Schedule
+*/
+void CAST_256::key_schedule(const uint8_t key[], size_t length)
+   {
+   static const uint32_t KEY_MASK[192] = {
+      0x5A827999, 0xC95C653A, 0x383650DB, 0xA7103C7C, 0x15EA281D, 0x84C413BE,
+      0xF39DFF5F, 0x6277EB00, 0xD151D6A1, 0x402BC242, 0xAF05ADE3, 0x1DDF9984,
+      0x8CB98525, 0xFB9370C6, 0x6A6D5C67, 0xD9474808, 0x482133A9, 0xB6FB1F4A,
+      0x25D50AEB, 0x94AEF68C, 0x0388E22D, 0x7262CDCE, 0xE13CB96F, 0x5016A510,
+      0xBEF090B1, 0x2DCA7C52, 0x9CA467F3, 0x0B7E5394, 0x7A583F35, 0xE9322AD6,
+      0x580C1677, 0xC6E60218, 0x35BFEDB9, 0xA499D95A, 0x1373C4FB, 0x824DB09C,
+      0xF1279C3D, 0x600187DE, 0xCEDB737F, 0x3DB55F20, 0xAC8F4AC1, 0x1B693662,
+      0x8A432203, 0xF91D0DA4, 0x67F6F945, 0xD6D0E4E6, 0x45AAD087, 0xB484BC28,
+      0x235EA7C9, 0x9238936A, 0x01127F0B, 0x6FEC6AAC, 0xDEC6564D, 0x4DA041EE,
+      0xBC7A2D8F, 0x2B541930, 0x9A2E04D1, 0x0907F072, 0x77E1DC13, 0xE6BBC7B4,
+      0x5595B355, 0xC46F9EF6, 0x33498A97, 0xA2237638, 0x10FD61D9, 0x7FD74D7A,
+      0xEEB1391B, 0x5D8B24BC, 0xCC65105D, 0x3B3EFBFE, 0xAA18E79F, 0x18F2D340,
+      0x87CCBEE1, 0xF6A6AA82, 0x65809623, 0xD45A81C4, 0x43346D65, 0xB20E5906,
+      0x20E844A7, 0x8FC23048, 0xFE9C1BE9, 0x6D76078A, 0xDC4FF32B, 0x4B29DECC,
+      0xBA03CA6D, 0x28DDB60E, 0x97B7A1AF, 0x06918D50, 0x756B78F1, 0xE4456492,
+      0x531F5033, 0xC1F93BD4, 0x30D32775, 0x9FAD1316, 0x0E86FEB7, 0x7D60EA58,
+      0xEC3AD5F9, 0x5B14C19A, 0xC9EEAD3B, 0x38C898DC, 0xA7A2847D, 0x167C701E,
+      0x85565BBF, 0xF4304760, 0x630A3301, 0xD1E41EA2, 0x40BE0A43, 0xAF97F5E4,
+      0x1E71E185, 0x8D4BCD26, 0xFC25B8C7, 0x6AFFA468, 0xD9D99009, 0x48B37BAA,
+      0xB78D674B, 0x266752EC, 0x95413E8D, 0x041B2A2E, 0x72F515CF, 0xE1CF0170,
+      0x50A8ED11, 0xBF82D8B2, 0x2E5CC453, 0x9D36AFF4, 0x0C109B95, 0x7AEA8736,
+      0xE9C472D7, 0x589E5E78, 0xC7784A19, 0x365235BA, 0xA52C215B, 0x14060CFC,
+      0x82DFF89D, 0xF1B9E43E, 0x6093CFDF, 0xCF6DBB80, 0x3E47A721, 0xAD2192C2,
+      0x1BFB7E63, 0x8AD56A04, 0xF9AF55A5, 0x68894146, 0xD7632CE7, 0x463D1888,
+      0xB5170429, 0x23F0EFCA, 0x92CADB6B, 0x01A4C70C, 0x707EB2AD, 0xDF589E4E,
+      0x4E3289EF, 0xBD0C7590, 0x2BE66131, 0x9AC04CD2, 0x099A3873, 0x78742414,
+      0xE74E0FB5, 0x5627FB56, 0xC501E6F7, 0x33DBD298, 0xA2B5BE39, 0x118FA9DA,
+      0x8069957B, 0xEF43811C, 0x5E1D6CBD, 0xCCF7585E, 0x3BD143FF, 0xAAAB2FA0,
+      0x19851B41, 0x885F06E2, 0xF738F283, 0x6612DE24, 0xD4ECC9C5, 0x43C6B566,
+      0xB2A0A107, 0x217A8CA8, 0x90547849, 0xFF2E63EA, 0x6E084F8B, 0xDCE23B2C,
+      0x4BBC26CD, 0xBA96126E, 0x296FFE0F, 0x9849E9B0, 0x0723D551, 0x75FDC0F2,
+      0xE4D7AC93, 0x53B19834, 0xC28B83D5, 0x31656F76, 0xA03F5B17, 0x0F1946B8 };
+
+   static const uint8_t KEY_ROT[32] = {
+      0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0A, 0x1B, 0x0C,
+      0x1D, 0x0E, 0x1F, 0x10, 0x01, 0x12, 0x03, 0x14, 0x05, 0x16,
+      0x07, 0x18, 0x09, 0x1A, 0x0B, 0x1C, 0x0D, 0x1E, 0x0F, 0x00,
+      0x11, 0x02 };
+
+   m_MK.resize(48);
+   m_RK.resize(48);
+
+   secure_vector<uint32_t> K(8);
+   for(size_t i = 0; i != length; ++i)
+      K[i/4] = (K[i/4] << 8) + key[i];
+
+   uint32_t A = K[0], B = K[1], C = K[2], D = K[3],
+          E = K[4], F = K[5], G = K[6], H = K[7];
+
+   for(size_t i = 0; i != 48; i += 4)
+      {
+      round1(G, H, KEY_MASK[4*i+ 0], KEY_ROT[(4*i+ 0) % 32]);
+      round2(F, G, KEY_MASK[4*i+ 1], KEY_ROT[(4*i+ 1) % 32]);
+      round3(E, F, KEY_MASK[4*i+ 2], KEY_ROT[(4*i+ 2) % 32]);
+      round1(D, E, KEY_MASK[4*i+ 3], KEY_ROT[(4*i+ 3) % 32]);
+      round2(C, D, KEY_MASK[4*i+ 4], KEY_ROT[(4*i+ 4) % 32]);
+      round3(B, C, KEY_MASK[4*i+ 5], KEY_ROT[(4*i+ 5) % 32]);
+      round1(A, B, KEY_MASK[4*i+ 6], KEY_ROT[(4*i+ 6) % 32]);
+      round2(H, A, KEY_MASK[4*i+ 7], KEY_ROT[(4*i+ 7) % 32]);
+      round1(G, H, KEY_MASK[4*i+ 8], KEY_ROT[(4*i+ 8) % 32]);
+      round2(F, G, KEY_MASK[4*i+ 9], KEY_ROT[(4*i+ 9) % 32]);
+      round3(E, F, KEY_MASK[4*i+10], KEY_ROT[(4*i+10) % 32]);
+      round1(D, E, KEY_MASK[4*i+11], KEY_ROT[(4*i+11) % 32]);
+      round2(C, D, KEY_MASK[4*i+12], KEY_ROT[(4*i+12) % 32]);
+      round3(B, C, KEY_MASK[4*i+13], KEY_ROT[(4*i+13) % 32]);
+      round1(A, B, KEY_MASK[4*i+14], KEY_ROT[(4*i+14) % 32]);
+      round2(H, A, KEY_MASK[4*i+15], KEY_ROT[(4*i+15) % 32]);
+
+      m_RK[i  ] = (A % 32);
+      m_RK[i+1] = (C % 32);
+      m_RK[i+2] = (E % 32);
+      m_RK[i+3] = (G % 32);
+      m_MK[i  ] = H;
+      m_MK[i+1] = F;
+      m_MK[i+2] = D;
+      m_MK[i+3] = B;
+      }
+   }
+
+void CAST_256::clear()
+   {
+   zap(m_MK);
+   zap(m_RK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/cast256/cast256.h b/third_party/botan/src/lib/block/cast256/cast256.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/cast256/cast256.h
@@ -0,0 +1,38 @@
+/*
+* CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST256_H_
+#define BOTAN_CAST256_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cast256.h)
+
+namespace Botan {
+
+/**
+* CAST-256
+*/
+class BOTAN_PUBLIC_API(2,0) CAST_256 final : public Block_Cipher_Fixed_Params<16, 4, 32, 4>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "CAST-256"; }
+      BlockCipher* clone() const override { return new CAST_256; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint32_t> m_MK;
+      secure_vector<uint8_t> m_RK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/des/des.cpp b/third_party/botan/src/lib/block/des/des.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/des/des.cpp
@@ -0,0 +1,410 @@
+/*
+* DES
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Based on a public domain implemenation by Phil Karn (who in turn
+* credited Richard Outerbridge and Jim Gillogly)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/des.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* DES Key Schedule
+*/
+void des_key_schedule(uint32_t round_key[32], const uint8_t key[8])
+   {
+   static const uint8_t ROT[16] = { 1, 1, 2, 2, 2, 2, 2, 2,
+                                 1, 2, 2, 2, 2, 2, 2, 1 };
+
+   uint32_t C = ((key[7] & 0x80) << 20) | ((key[6] & 0x80) << 19) |
+                ((key[5] & 0x80) << 18) | ((key[4] & 0x80) << 17) |
+                ((key[3] & 0x80) << 16) | ((key[2] & 0x80) << 15) |
+                ((key[1] & 0x80) << 14) | ((key[0] & 0x80) << 13) |
+                ((key[7] & 0x40) << 13) | ((key[6] & 0x40) << 12) |
+                ((key[5] & 0x40) << 11) | ((key[4] & 0x40) << 10) |
+                ((key[3] & 0x40) <<  9) | ((key[2] & 0x40) <<  8) |
+                ((key[1] & 0x40) <<  7) | ((key[0] & 0x40) <<  6) |
+                ((key[7] & 0x20) <<  6) | ((key[6] & 0x20) <<  5) |
+                ((key[5] & 0x20) <<  4) | ((key[4] & 0x20) <<  3) |
+                ((key[3] & 0x20) <<  2) | ((key[2] & 0x20) <<  1) |
+                ((key[1] & 0x20)      ) | ((key[0] & 0x20) >>  1) |
+                ((key[7] & 0x10) >>  1) | ((key[6] & 0x10) >>  2) |
+                ((key[5] & 0x10) >>  3) | ((key[4] & 0x10) >>  4);
+   uint32_t D = ((key[7] & 0x02) << 26) | ((key[6] & 0x02) << 25) |
+                ((key[5] & 0x02) << 24) | ((key[4] & 0x02) << 23) |
+                ((key[3] & 0x02) << 22) | ((key[2] & 0x02) << 21) |
+                ((key[1] & 0x02) << 20) | ((key[0] & 0x02) << 19) |
+                ((key[7] & 0x04) << 17) | ((key[6] & 0x04) << 16) |
+                ((key[5] & 0x04) << 15) | ((key[4] & 0x04) << 14) |
+                ((key[3] & 0x04) << 13) | ((key[2] & 0x04) << 12) |
+                ((key[1] & 0x04) << 11) | ((key[0] & 0x04) << 10) |
+                ((key[7] & 0x08) <<  8) | ((key[6] & 0x08) <<  7) |
+                ((key[5] & 0x08) <<  6) | ((key[4] & 0x08) <<  5) |
+                ((key[3] & 0x08) <<  4) | ((key[2] & 0x08) <<  3) |
+                ((key[1] & 0x08) <<  2) | ((key[0] & 0x08) <<  1) |
+                ((key[3] & 0x10) >>  1) | ((key[2] & 0x10) >>  2) |
+                ((key[1] & 0x10) >>  3) | ((key[0] & 0x10) >>  4);
+
+   for(size_t i = 0; i != 16; ++i)
+      {
+      C = ((C << ROT[i]) | (C >> (28-ROT[i]))) & 0x0FFFFFFF;
+      D = ((D << ROT[i]) | (D >> (28-ROT[i]))) & 0x0FFFFFFF;
+      round_key[2*i  ] = ((C & 0x00000010) << 22) | ((C & 0x00000800) << 17) |
+                         ((C & 0x00000020) << 16) | ((C & 0x00004004) << 15) |
+                         ((C & 0x00000200) << 11) | ((C & 0x00020000) << 10) |
+                         ((C & 0x01000000) >>  6) | ((C & 0x00100000) >>  4) |
+                         ((C & 0x00010000) <<  3) | ((C & 0x08000000) >>  2) |
+                         ((C & 0x00800000) <<  1) | ((D & 0x00000010) <<  8) |
+                         ((D & 0x00000002) <<  7) | ((D & 0x00000001) <<  2) |
+                         ((D & 0x00000200)      ) | ((D & 0x00008000) >>  2) |
+                         ((D & 0x00000088) >>  3) | ((D & 0x00001000) >>  7) |
+                         ((D & 0x00080000) >>  9) | ((D & 0x02020000) >> 14) |
+                         ((D & 0x00400000) >> 21);
+      round_key[2*i+1] = ((C & 0x00000001) << 28) | ((C & 0x00000082) << 18) |
+                         ((C & 0x00002000) << 14) | ((C & 0x00000100) << 10) |
+                         ((C & 0x00001000) <<  9) | ((C & 0x00040000) <<  6) |
+                         ((C & 0x02400000) <<  4) | ((C & 0x00008000) <<  2) |
+                         ((C & 0x00200000) >>  1) | ((C & 0x04000000) >> 10) |
+                         ((D & 0x00000020) <<  6) | ((D & 0x00000100)      ) |
+                         ((D & 0x00000800) >>  1) | ((D & 0x00000040) >>  3) |
+                         ((D & 0x00010000) >>  4) | ((D & 0x00000400) >>  5) |
+                         ((D & 0x00004000) >> 10) | ((D & 0x04000000) >> 13) |
+                         ((D & 0x00800000) >> 14) | ((D & 0x00100000) >> 18) |
+                         ((D & 0x01000000) >> 24) | ((D & 0x08000000) >> 26);
+      }
+   }
+
+inline uint32_t spbox(uint32_t T0, uint32_t T1)
+   {
+   return DES_SPBOX1[get_byte(0, T0)] ^ DES_SPBOX2[get_byte(0, T1)] ^
+          DES_SPBOX3[get_byte(1, T0)] ^ DES_SPBOX4[get_byte(1, T1)] ^
+          DES_SPBOX5[get_byte(2, T0)] ^ DES_SPBOX6[get_byte(2, T1)] ^
+          DES_SPBOX7[get_byte(3, T0)] ^ DES_SPBOX8[get_byte(3, T1)];
+   }
+
+/*
+* DES Encryption
+*/
+inline void des_encrypt(uint32_t& Lr, uint32_t& Rr,
+                        const uint32_t round_key[32])
+   {
+   uint32_t L = Lr;
+   uint32_t R = Rr;
+   for(size_t i = 0; i != 16; i += 2)
+      {
+      L ^= spbox(rotr<4>(R) ^ round_key[2*i  ], R ^ round_key[2*i+1]);
+      R ^= spbox(rotr<4>(L) ^ round_key[2*i+2], L ^ round_key[2*i+3]);
+      }
+
+   Lr = L;
+   Rr = R;
+   }
+
+inline void des_encrypt_x2(uint32_t& L0r, uint32_t& R0r,
+                           uint32_t& L1r, uint32_t& R1r,
+                           const uint32_t round_key[32])
+   {
+   uint32_t L0 = L0r;
+   uint32_t R0 = R0r;
+   uint32_t L1 = L1r;
+   uint32_t R1 = R1r;
+
+   for(size_t i = 0; i != 16; i += 2)
+      {
+      L0 ^= spbox(rotr<4>(R0) ^ round_key[2*i  ], R0 ^ round_key[2*i+1]);
+      L1 ^= spbox(rotr<4>(R1) ^ round_key[2*i  ], R1 ^ round_key[2*i+1]);
+
+      R0 ^= spbox(rotr<4>(L0) ^ round_key[2*i+2], L0 ^ round_key[2*i+3]);
+      R1 ^= spbox(rotr<4>(L1) ^ round_key[2*i+2], L1 ^ round_key[2*i+3]);
+      }
+
+   L0r = L0;
+   R0r = R0;
+   L1r = L1;
+   R1r = R1;
+   }
+
+/*
+* DES Decryption
+*/
+inline void des_decrypt(uint32_t& Lr, uint32_t& Rr,
+                        const uint32_t round_key[32])
+   {
+   uint32_t L = Lr;
+   uint32_t R = Rr;
+   for(size_t i = 16; i != 0; i -= 2)
+      {
+      L ^= spbox(rotr<4>(R) ^ round_key[2*i - 2], R  ^ round_key[2*i - 1]);
+      R ^= spbox(rotr<4>(L) ^ round_key[2*i - 4], L  ^ round_key[2*i - 3]);
+      }
+   Lr = L;
+   Rr = R;
+   }
+
+inline void des_decrypt_x2(uint32_t& L0r, uint32_t& R0r,
+                           uint32_t& L1r, uint32_t& R1r,
+                           const uint32_t round_key[32])
+   {
+   uint32_t L0 = L0r;
+   uint32_t R0 = R0r;
+   uint32_t L1 = L1r;
+   uint32_t R1 = R1r;
+
+   for(size_t i = 16; i != 0; i -= 2)
+      {
+      L0 ^= spbox(rotr<4>(R0) ^ round_key[2*i - 2], R0  ^ round_key[2*i - 1]);
+      L1 ^= spbox(rotr<4>(R1) ^ round_key[2*i - 2], R1  ^ round_key[2*i - 1]);
+
+      R0 ^= spbox(rotr<4>(L0) ^ round_key[2*i - 4], L0  ^ round_key[2*i - 3]);
+      R1 ^= spbox(rotr<4>(L1) ^ round_key[2*i - 4], L1  ^ round_key[2*i - 3]);
+      }
+
+   L0r = L0;
+   R0r = R0;
+   L1r = L1;
+   R1r = R1;
+   }
+
+inline void des_IP(uint32_t& L, uint32_t& R, const uint8_t block[])
+   {
+   // IP sequence by Wei Dai, taken from public domain Crypto++
+   L = load_be<uint32_t>(block, 0);
+   R = load_be<uint32_t>(block, 1);
+
+   uint32_t T;
+   R = rotl<4>(R);
+   T = (L ^ R) & 0xF0F0F0F0;
+   L ^= T;
+   R = rotr<20>(R ^ T);
+   T = (L ^ R) & 0xFFFF0000;
+   L ^= T;
+   R = rotr<18>(R ^ T);
+   T = (L ^ R) & 0x33333333;
+   L ^= T;
+   R = rotr<6>(R ^ T);
+   T = (L ^ R) & 0x00FF00FF;
+   L ^= T;
+   R = rotl<9>(R ^ T);
+   T = (L ^ R) & 0xAAAAAAAA;
+   L = rotl<1>(L ^ T);
+   R ^= T;
+   }
+
+inline void des_FP(uint32_t L, uint32_t R, uint8_t out[])
+   {
+   // FP sequence by Wei Dai, taken from public domain Crypto++
+   uint32_t T;
+
+   R = rotr<1>(R);
+   T = (L ^ R) & 0xAAAAAAAA;
+   R ^= T;
+   L = rotr<9>(L ^ T);
+   T = (L ^ R) & 0x00FF00FF;
+   R ^= T;
+   L = rotl<6>(L ^ T);
+   T = (L ^ R) & 0x33333333;
+   R ^= T;
+   L = rotl<18>(L ^ T);
+   T = (L ^ R) & 0xFFFF0000;
+   R ^= T;
+   L = rotl<20>(L ^ T);
+   T = (L ^ R) & 0xF0F0F0F0;
+   R ^= T;
+   L = rotr<4>(L ^ T);
+
+   store_be(out, R, L);
+   }
+
+}
+
+/*
+* DES Encryption
+*/
+void DES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0;
+      uint32_t L1, R1;
+
+      des_IP(L0, R0, in);
+      des_IP(L1, R1, in + BLOCK_SIZE);
+
+      des_encrypt_x2(L0, R0, L1, R1, m_round_key.data());
+
+      des_FP(L0, R0, out);
+      des_FP(L1, R1, out + BLOCK_SIZE);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i < blocks; ++i)
+      {
+      uint32_t L, R;
+      des_IP(L, R, in + BLOCK_SIZE*i);
+      des_encrypt(L, R, m_round_key.data());
+      des_FP(L, R, out + BLOCK_SIZE*i);
+      }
+   }
+
+/*
+* DES Decryption
+*/
+void DES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0;
+      uint32_t L1, R1;
+
+      des_IP(L0, R0, in);
+      des_IP(L1, R1, in + BLOCK_SIZE);
+
+      des_decrypt_x2(L0, R0, L1, R1, m_round_key.data());
+
+      des_FP(L0, R0, out);
+      des_FP(L1, R1, out + BLOCK_SIZE);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i < blocks; ++i)
+      {
+      uint32_t L, R;
+      des_IP(L, R, in + BLOCK_SIZE*i);
+      des_decrypt(L, R, m_round_key.data());
+      des_FP(L, R, out + BLOCK_SIZE*i);
+      }
+   }
+
+/*
+* DES Key Schedule
+*/
+void DES::key_schedule(const uint8_t key[], size_t)
+   {
+   m_round_key.resize(32);
+   des_key_schedule(m_round_key.data(), key);
+   }
+
+void DES::clear()
+   {
+   zap(m_round_key);
+   }
+
+/*
+* TripleDES Encryption
+*/
+void TripleDES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0;
+      uint32_t L1, R1;
+
+      des_IP(L0, R0, in);
+      des_IP(L1, R1, in + BLOCK_SIZE);
+
+      des_encrypt_x2(L0, R0, L1, R1, &m_round_key[0]);
+      des_decrypt_x2(R0, L0, R1, L1, &m_round_key[32]);
+      des_encrypt_x2(L0, R0, L1, R1, &m_round_key[64]);
+
+      des_FP(L0, R0, out);
+      des_FP(L1, R1, out + BLOCK_SIZE);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t L, R;
+      des_IP(L, R, in + BLOCK_SIZE*i);
+
+      des_encrypt(L, R, &m_round_key[0]);
+      des_decrypt(R, L, &m_round_key[32]);
+      des_encrypt(L, R, &m_round_key[64]);
+
+      des_FP(L, R, out + BLOCK_SIZE*i);
+      }
+   }
+
+/*
+* TripleDES Decryption
+*/
+void TripleDES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t L0, R0;
+      uint32_t L1, R1;
+
+      des_IP(L0, R0, in);
+      des_IP(L1, R1, in + BLOCK_SIZE);
+
+      des_decrypt_x2(L0, R0, L1, R1, &m_round_key[64]);
+      des_encrypt_x2(R0, L0, R1, L1, &m_round_key[32]);
+      des_decrypt_x2(L0, R0, L1, R1, &m_round_key[0]);
+
+      des_FP(L0, R0, out);
+      des_FP(L1, R1, out + BLOCK_SIZE);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t L, R;
+      des_IP(L, R, in + BLOCK_SIZE*i);
+
+      des_decrypt(L, R, &m_round_key[64]);
+      des_encrypt(R, L, &m_round_key[32]);
+      des_decrypt(L, R, &m_round_key[0]);
+
+      des_FP(L, R, out + BLOCK_SIZE*i);
+      }
+   }
+
+/*
+* TripleDES Key Schedule
+*/
+void TripleDES::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_round_key.resize(3*32);
+   des_key_schedule(&m_round_key[0], key);
+   des_key_schedule(&m_round_key[32], key + 8);
+
+   if(length == 24)
+      des_key_schedule(&m_round_key[64], key + 16);
+   else
+      copy_mem(&m_round_key[64], &m_round_key[0], 32);
+   }
+
+void TripleDES::clear()
+   {
+   zap(m_round_key);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/des/des.h b/third_party/botan/src/lib/block/des/des.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/des/des.h
@@ -0,0 +1,67 @@
+/*
+* DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DES_H_
+#define BOTAN_DES_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(des.h)
+
+namespace Botan {
+
+/**
+* DES
+*/
+class BOTAN_PUBLIC_API(2,0) DES final : public Block_Cipher_Fixed_Params<8, 8>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "DES"; }
+      BlockCipher* clone() const override { return new DES; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint32_t> m_round_key;
+   };
+
+/**
+* Triple DES
+*/
+class BOTAN_PUBLIC_API(2,0) TripleDES final : public Block_Cipher_Fixed_Params<8, 16, 24, 8>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "TripleDES"; }
+      BlockCipher* clone() const override { return new TripleDES; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint32_t> m_round_key;
+   };
+
+/*
+* DES Tables
+*/
+extern const uint32_t DES_SPBOX1[256];
+extern const uint32_t DES_SPBOX2[256];
+extern const uint32_t DES_SPBOX3[256];
+extern const uint32_t DES_SPBOX4[256];
+extern const uint32_t DES_SPBOX5[256];
+extern const uint32_t DES_SPBOX6[256];
+extern const uint32_t DES_SPBOX7[256];
+extern const uint32_t DES_SPBOX8[256];
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/des/des_tab.cpp b/third_party/botan/src/lib/block/des/des_tab.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/des/des_tab.cpp
@@ -0,0 +1,372 @@
+/*
+* Substitution/Permutation Tables for DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/des.h>
+
+namespace Botan {
+
+const uint32_t DES_SPBOX1[256] = {
+   0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404,
+   0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+   0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400,
+   0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+   0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404,
+   0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+   0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000,
+   0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+   0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404,
+   0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+   0x00010004, 0x00010400, 0x00000000, 0x01010004, 0x01010400, 0x00000000,
+   0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+   0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004,
+   0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+   0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004,
+   0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+   0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000,
+   0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+   0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004,
+   0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+   0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400,
+   0x00000000, 0x01010004, 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+   0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400,
+   0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+   0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000,
+   0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+   0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
+   0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+   0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004,
+   0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+   0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400,
+   0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004,
+   0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404,
+   0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+   0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400,
+   0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+   0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404,
+   0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+   0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000,
+   0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+   0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404,
+   0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+   0x00010004, 0x00010400, 0x00000000, 0x01010004 };
+
+const uint32_t DES_SPBOX2[256] = {
+   0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020,
+   0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+   0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020,
+   0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+   0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000,
+   0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+   0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000,
+   0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+   0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020,
+   0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+   0x80000000, 0x80100020, 0x80108020, 0x00108000, 0x80108020, 0x80008000,
+   0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+   0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000,
+   0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+   0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020,
+   0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+   0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000,
+   0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+   0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000,
+   0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+   0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020,
+   0x80108020, 0x00108000, 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+   0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020,
+   0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+   0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000,
+   0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+   0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
+   0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+   0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020,
+   0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+   0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000,
+   0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000,
+   0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020,
+   0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+   0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020,
+   0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+   0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000,
+   0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+   0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000,
+   0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+   0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020,
+   0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+   0x80000000, 0x80100020, 0x80108020, 0x00108000 };
+
+const uint32_t DES_SPBOX3[256] = {
+   0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000,
+   0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+   0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008,
+   0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+   0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208,
+   0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+   0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008,
+   0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+   0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208,
+   0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+   0x00020208, 0x00000008, 0x08020008, 0x00020200, 0x00000208, 0x08020200,
+   0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+   0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008,
+   0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+   0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200,
+   0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+   0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200,
+   0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+   0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000,
+   0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+   0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008,
+   0x08020008, 0x00020200, 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+   0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008,
+   0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+   0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000,
+   0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+   0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
+   0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+   0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200,
+   0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+   0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208,
+   0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200,
+   0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000,
+   0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+   0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008,
+   0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+   0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208,
+   0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+   0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008,
+   0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+   0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208,
+   0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+   0x00020208, 0x00000008, 0x08020008, 0x00020200 };
+
+const uint32_t DES_SPBOX4[256] = {
+   0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081,
+   0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+   0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000,
+   0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+   0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080,
+   0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+   0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080,
+   0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+   0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001,
+   0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+   0x00000080, 0x00800000, 0x00002000, 0x00802080, 0x00802001, 0x00002081,
+   0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+   0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000,
+   0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+   0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001,
+   0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+   0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000,
+   0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+   0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081,
+   0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+   0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000,
+   0x00002000, 0x00802080, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+   0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000,
+   0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+   0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000,
+   0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+   0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
+   0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+   0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081,
+   0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+   0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080,
+   0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080,
+   0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081,
+   0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+   0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000,
+   0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+   0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080,
+   0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+   0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080,
+   0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+   0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001,
+   0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+   0x00000080, 0x00800000, 0x00002000, 0x00802080 };
+
+const uint32_t DES_SPBOX5[256] = {
+   0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100,
+   0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+   0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000,
+   0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+   0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000,
+   0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+   0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000,
+   0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+   0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000,
+   0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+   0x00000000, 0x40080000, 0x02080100, 0x40000100, 0x00000100, 0x02080100,
+   0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+   0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000,
+   0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+   0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100,
+   0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+   0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000,
+   0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+   0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100,
+   0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+   0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000,
+   0x02080100, 0x40000100, 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+   0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000,
+   0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+   0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100,
+   0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+   0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
+   0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+   0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100,
+   0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+   0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100,
+   0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100,
+   0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100,
+   0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+   0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000,
+   0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+   0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000,
+   0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+   0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000,
+   0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+   0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000,
+   0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+   0x00000000, 0x40080000, 0x02080100, 0x40000100 };
+
+const uint32_t DES_SPBOX6[256] = {
+   0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010,
+   0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+   0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010,
+   0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+   0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000,
+   0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+   0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000,
+   0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+   0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000,
+   0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+   0x20404000, 0x20000000, 0x00400010, 0x20004010, 0x20000010, 0x20400000,
+   0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+   0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000,
+   0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+   0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000,
+   0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+   0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000,
+   0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+   0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000,
+   0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+   0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000,
+   0x00400010, 0x20004010, 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+   0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010,
+   0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+   0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010,
+   0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+   0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
+   0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+   0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010,
+   0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+   0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010,
+   0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010,
+   0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010,
+   0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+   0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010,
+   0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+   0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000,
+   0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+   0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000,
+   0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+   0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000,
+   0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+   0x20404000, 0x20000000, 0x00400010, 0x20004010 };
+
+const uint32_t DES_SPBOX7[256] = {
+   0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802,
+   0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+   0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802,
+   0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+   0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002,
+   0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+   0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000,
+   0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+   0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000,
+   0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+   0x04000002, 0x04000800, 0x00000800, 0x00200002, 0x00200000, 0x04200002,
+   0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+   0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000,
+   0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+   0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800,
+   0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+   0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002,
+   0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+   0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002,
+   0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+   0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800,
+   0x00000800, 0x00200002, 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+   0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000,
+   0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+   0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000,
+   0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+   0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
+   0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+   0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802,
+   0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+   0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802,
+   0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002,
+   0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802,
+   0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+   0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802,
+   0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+   0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002,
+   0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+   0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000,
+   0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+   0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000,
+   0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+   0x04000002, 0x04000800, 0x00000800, 0x00200002 };
+
+const uint32_t DES_SPBOX8[256] = {
+   0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040,
+   0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+   0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040,
+   0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+   0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000,
+   0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+   0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040,
+   0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+   0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000,
+   0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+   0x00001040, 0x00040040, 0x10000000, 0x10041000, 0x10001040, 0x00001000,
+   0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+   0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040,
+   0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+   0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000,
+   0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+   0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040,
+   0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+   0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040,
+   0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+   0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040,
+   0x10000000, 0x10041000, 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+   0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000,
+   0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+   0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040,
+   0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+   0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
+   0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+   0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000,
+   0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+   0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000,
+   0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000,
+   0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040,
+   0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+   0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040,
+   0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+   0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000,
+   0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+   0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040,
+   0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+   0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000,
+   0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+   0x00001040, 0x00040040, 0x10000000, 0x10041000 };
+
+}
diff --git a/third_party/botan/src/lib/block/des/desx.cpp b/third_party/botan/src/lib/block/des/desx.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/des/desx.cpp
@@ -0,0 +1,65 @@
+/*
+* DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/desx.h>
+
+namespace Botan {
+
+/*
+* DESX Encryption
+*/
+void DESX::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K1.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      xor_buf(out, in, m_K1.data(), BLOCK_SIZE);
+      m_des.encrypt(out);
+      xor_buf(out, m_K2.data(), BLOCK_SIZE);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* DESX Decryption
+*/
+void DESX::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K1.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      xor_buf(out, in, m_K2.data(), BLOCK_SIZE);
+      m_des.decrypt(out);
+      xor_buf(out, m_K1.data(), BLOCK_SIZE);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* DESX Key Schedule
+*/
+void DESX::key_schedule(const uint8_t key[], size_t)
+   {
+   m_K1.assign(key, key + 8);
+   m_des.set_key(key + 8, 8);
+   m_K2.assign(key + 16, key + 24);
+   }
+
+void DESX::clear()
+   {
+   m_des.clear();
+   zap(m_K1);
+   zap(m_K2);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/des/desx.h b/third_party/botan/src/lib/block/des/desx.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/des/desx.h
@@ -0,0 +1,37 @@
+/*
+* DESX
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DESX_H_
+#define BOTAN_DESX_H_
+
+#include <botan/des.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(desx.h)
+
+namespace Botan {
+
+/**
+* DESX
+*/
+class BOTAN_PUBLIC_API(2,0) DESX final : public Block_Cipher_Fixed_Params<8, 24>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "DESX"; }
+      BlockCipher* clone() const override { return new DESX; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+      secure_vector<uint8_t> m_K1, m_K2;
+      DES m_des;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp b/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp
@@ -0,0 +1,189 @@
+/*
+* GOST 28147-89
+* (C) 1999-2009,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_28147.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+uint8_t GOST_28147_89_Params::sbox_entry(size_t row, size_t col) const
+   {
+   const uint8_t x = m_sboxes[4 * col + (row / 2)];
+   return (row % 2 == 0) ? (x >> 4) : (x & 0x0F);
+   }
+
+uint8_t GOST_28147_89_Params::sbox_pair(size_t row, size_t col) const
+   {
+   const uint8_t x = m_sboxes[4 * (col % 16) + row];
+   const uint8_t y = m_sboxes[4 * (col / 16) + row];
+   return (x >> 4) | (y << 4);
+   }
+
+GOST_28147_89_Params::GOST_28147_89_Params(const std::string& n) : m_name(n)
+   {
+   // Encoded in the packed fromat from RFC 4357
+
+   // GostR3411_94_TestParamSet (OID 1.2.643.2.2.31.0)
+   static const uint8_t GOST_R_3411_TEST_PARAMS[64] = {
+      0x4E, 0x57, 0x64, 0xD1, 0xAB, 0x8D, 0xCB, 0xBF, 0x94, 0x1A, 0x7A,
+      0x4D, 0x2C, 0xD1, 0x10, 0x10, 0xD6, 0xA0, 0x57, 0x35, 0x8D, 0x38,
+      0xF2, 0xF7, 0x0F, 0x49, 0xD1, 0x5A, 0xEA, 0x2F, 0x8D, 0x94, 0x62,
+      0xEE, 0x43, 0x09, 0xB3, 0xF4, 0xA6, 0xA2, 0x18, 0xC6, 0x98, 0xE3,
+      0xC1, 0x7C, 0xE5, 0x7E, 0x70, 0x6B, 0x09, 0x66, 0xF7, 0x02, 0x3C,
+      0x8B, 0x55, 0x95, 0xBF, 0x28, 0x39, 0xB3, 0x2E, 0xCC };
+
+   // GostR3411-94-CryptoProParamSet (OID 1.2.643.2.2.31.1)
+   static const uint8_t GOST_R_3411_CRYPTOPRO_PARAMS[64] = {
+      0xA5, 0x74, 0x77, 0xD1, 0x4F, 0xFA, 0x66, 0xE3, 0x54, 0xC7, 0x42,
+      0x4A, 0x60, 0xEC, 0xB4, 0x19, 0x82, 0x90, 0x9D, 0x75, 0x1D, 0x4F,
+      0xC9, 0x0B, 0x3B, 0x12, 0x2F, 0x54, 0x79, 0x08, 0xA0, 0xAF, 0xD1,
+      0x3E, 0x1A, 0x38, 0xC7, 0xB1, 0x81, 0xC6, 0xE6, 0x56, 0x05, 0x87,
+      0x03, 0x25, 0xEB, 0xFE, 0x9C, 0x6D, 0xF8, 0x6D, 0x2E, 0xAB, 0xDE,
+      0x20, 0xBA, 0x89, 0x3C, 0x92, 0xF8, 0xD3, 0x53, 0xBC };
+
+   if(m_name == "R3411_94_TestParam")
+      m_sboxes = GOST_R_3411_TEST_PARAMS;
+   else if(m_name == "R3411_CryptoPro")
+      m_sboxes = GOST_R_3411_CRYPTOPRO_PARAMS;
+   else
+      throw Invalid_Argument("GOST_28147_89_Params: Unknown " + m_name);
+   }
+
+/*
+* GOST Constructor
+*/
+GOST_28147_89::GOST_28147_89(const GOST_28147_89_Params& param) : m_SBOX(1024)
+   {
+   // Convert the parallel 4x4 sboxes into larger word-based sboxes
+
+   for(size_t i = 0; i != 256; ++i)
+      {
+      m_SBOX[i    ] = rotl<11, uint32_t>(param.sbox_pair(0, i));
+      m_SBOX[i+256] = rotl<19, uint32_t>(param.sbox_pair(1, i));
+      m_SBOX[i+512] = rotl<27, uint32_t>(param.sbox_pair(2, i));
+      m_SBOX[i+768] = rotl< 3, uint32_t>(param.sbox_pair(3, i));
+      }
+   }
+
+std::string GOST_28147_89::name() const
+   {
+   /*
+   'Guess' the right name for the sbox on the basis of the values.
+   This would need to be updated if support for other sbox parameters
+   is added. Preferably, we would just store the string value in the
+   constructor, but can't break binary compat.
+   */
+   std::string sbox_name = "";
+   if(m_SBOX[0] == 0x00072000)
+      sbox_name = "R3411_94_TestParam";
+   else if(m_SBOX[0] == 0x0002D000)
+      sbox_name = "R3411_CryptoPro";
+   else
+      throw Internal_Error("GOST-28147 unrecognized sbox value");
+
+   return "GOST-28147-89(" + sbox_name + ")";
+   }
+
+/*
+* Two rounds of GOST
+*/
+#define GOST_2ROUND(N1, N2, R1, R2)   \
+   do {                               \
+   uint32_t T0 = N1 + m_EK[R1];           \
+   N2 ^= m_SBOX[get_byte(3, T0)] |      \
+         m_SBOX[get_byte(2, T0)+256] |  \
+         m_SBOX[get_byte(1, T0)+512] |  \
+         m_SBOX[get_byte(0, T0)+768];   \
+                                      \
+   uint32_t T1 = N2 + m_EK[R2];           \
+   N1 ^= m_SBOX[get_byte(3, T1)] |      \
+         m_SBOX[get_byte(2, T1)+256] |  \
+         m_SBOX[get_byte(1, T1)+512] |  \
+         m_SBOX[get_byte(0, T1)+768];   \
+   } while(0)
+
+/*
+* GOST Encryption
+*/
+void GOST_28147_89::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t N1 = load_le<uint32_t>(in, 0);
+      uint32_t N2 = load_le<uint32_t>(in, 1);
+
+      for(size_t j = 0; j != 3; ++j)
+         {
+         GOST_2ROUND(N1, N2, 0, 1);
+         GOST_2ROUND(N1, N2, 2, 3);
+         GOST_2ROUND(N1, N2, 4, 5);
+         GOST_2ROUND(N1, N2, 6, 7);
+         }
+
+      GOST_2ROUND(N1, N2, 7, 6);
+      GOST_2ROUND(N1, N2, 5, 4);
+      GOST_2ROUND(N1, N2, 3, 2);
+      GOST_2ROUND(N1, N2, 1, 0);
+
+      store_le(out, N2, N1);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* GOST Decryption
+*/
+void GOST_28147_89::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t N1 = load_le<uint32_t>(in, 0);
+      uint32_t N2 = load_le<uint32_t>(in, 1);
+
+      GOST_2ROUND(N1, N2, 0, 1);
+      GOST_2ROUND(N1, N2, 2, 3);
+      GOST_2ROUND(N1, N2, 4, 5);
+      GOST_2ROUND(N1, N2, 6, 7);
+
+      for(size_t j = 0; j != 3; ++j)
+         {
+         GOST_2ROUND(N1, N2, 7, 6);
+         GOST_2ROUND(N1, N2, 5, 4);
+         GOST_2ROUND(N1, N2, 3, 2);
+         GOST_2ROUND(N1, N2, 1, 0);
+         }
+
+      store_le(out, N2, N1);
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* GOST Key Schedule
+*/
+void GOST_28147_89::key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(8);
+   for(size_t i = 0; i != 8; ++i)
+      m_EK[i] = load_le<uint32_t>(key, i);
+   }
+
+void GOST_28147_89::clear()
+   {
+   zap(m_EK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/gost_28147/gost_28147.h b/third_party/botan/src/lib/block/gost_28147/gost_28147.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/gost_28147/gost_28147.h
@@ -0,0 +1,95 @@
+/*
+* GOST 28147-89
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_28147_89_H_
+#define BOTAN_GOST_28147_89_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gost_28147.h)
+
+namespace Botan {
+
+/**
+* The GOST 28147-89 block cipher uses a set of 4 bit Sboxes, however
+* the standard does not actually define these Sboxes; they are
+* considered a local configuration issue. Several different sets are
+* used.
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_28147_89_Params final
+   {
+   public:
+      /**
+      * @param row the row
+      * @param col the column
+      * @return sbox entry at this row/column
+      */
+      uint8_t sbox_entry(size_t row, size_t col) const;
+
+      /**
+      * @return name of this parameter set
+      */
+      std::string param_name() const { return m_name; }
+
+      /**
+      * Return a representation used for building larger tables
+      * For internal use
+      */
+      uint8_t sbox_pair(size_t row, size_t col) const;
+
+      /**
+      * Default GOST parameters are the ones given in GOST R 34.11 for
+      * testing purposes; these sboxes are also used by Crypto++, and,
+      * at least according to Wikipedia, the Central Bank of Russian
+      * Federation
+      * @param name of the parameter set
+      */
+      explicit GOST_28147_89_Params(const std::string& name = "R3411_94_TestParam");
+   private:
+      const uint8_t* m_sboxes;
+      std::string m_name;
+   };
+
+/**
+* GOST 28147-89
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_28147_89 final : public Block_Cipher_Fixed_Params<8, 32>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+
+      std::string name() const override;
+      BlockCipher* clone() const override { return new GOST_28147_89(m_SBOX); }
+
+      /**
+      * @param params the sbox parameters to use
+      */
+      explicit GOST_28147_89(const GOST_28147_89_Params& params);
+
+      explicit GOST_28147_89(const std::string& param_name) :
+         GOST_28147_89(GOST_28147_89_Params(param_name)) {}
+   private:
+      explicit GOST_28147_89(const std::vector<uint32_t>& other_SBOX) :
+         m_SBOX(other_SBOX), m_EK(8) {}
+
+      void key_schedule(const uint8_t[], size_t) override;
+
+      /*
+      * The sbox is not secret, this is just a larger expansion of it
+      * which we generate at runtime for faster execution
+      */
+      std::vector<uint32_t> m_SBOX;
+
+      secure_vector<uint32_t> m_EK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/idea/idea.cpp b/third_party/botan/src/lib/block/idea/idea.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/idea/idea.cpp
@@ -0,0 +1,240 @@
+/*
+* IDEA
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/idea.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Multiplication modulo 65537
+*/
+inline uint16_t mul(uint16_t x, uint16_t y)
+   {
+   const uint32_t P = static_cast<uint32_t>(x) * y;
+   const auto P_mask = CT::Mask<uint16_t>(CT::Mask<uint32_t>::is_zero(P));
+
+   const uint32_t P_hi = P >> 16;
+   const uint32_t P_lo = P & 0xFFFF;
+
+   const uint16_t carry = (P_lo < P_hi);
+   const uint16_t r_1 = static_cast<uint16_t>((P_lo - P_hi) + carry);
+   const uint16_t r_2 = 1 - x - y;
+
+   return P_mask.select(r_2, r_1);
+   }
+
+/*
+* Find multiplicative inverses modulo 65537
+*
+* 65537 is prime; thus Fermat's little theorem tells us that
+* x^65537 == x modulo 65537, which means
+* x^(65537-2) == x^-1 modulo 65537 since
+* x^(65537-2) * x == 1 mod 65537
+*
+* Do the exponentiation with a basic square and multiply: all bits are
+* of exponent are 1 so we always multiply
+*/
+uint16_t mul_inv(uint16_t x)
+   {
+   uint16_t y = x;
+
+   for(size_t i = 0; i != 15; ++i)
+      {
+      y = mul(y, y); // square
+      y = mul(y, x);
+      }
+
+   return y;
+   }
+
+/**
+* IDEA is involutional, depending only on the key schedule
+*/
+void idea_op(const uint8_t in[], uint8_t out[], size_t blocks, const uint16_t K[52])
+   {
+   const size_t BLOCK_SIZE = 8;
+
+   CT::poison(in, blocks * 8);
+   CT::poison(out, blocks * 8);
+   CT::poison(K, 52);
+
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint16_t X1, X2, X3, X4;
+      load_be(in + BLOCK_SIZE*i, X1, X2, X3, X4);
+
+      for(size_t j = 0; j != 8; ++j)
+         {
+         X1 = mul(X1, K[6*j+0]);
+         X2 += K[6*j+1];
+         X3 += K[6*j+2];
+         X4 = mul(X4, K[6*j+3]);
+
+         const uint16_t T0 = X3;
+         X3 = mul(X3 ^ X1, K[6*j+4]);
+
+         const uint16_t T1 = X2;
+         X2 = mul((X2 ^ X4) + X3, K[6*j+5]);
+         X3 += X2;
+
+         X1 ^= X2;
+         X4 ^= X3;
+         X2 ^= T0;
+         X3 ^= T1;
+         }
+
+      X1  = mul(X1, K[48]);
+      X2 += K[50];
+      X3 += K[49];
+      X4  = mul(X4, K[51]);
+
+      store_be(out + BLOCK_SIZE*i, X1, X3, X2, X4);
+      }
+
+   CT::unpoison(in, blocks * 8);
+   CT::unpoison(out, blocks * 8);
+   CT::unpoison(K, 52);
+   }
+
+}
+
+size_t IDEA::parallelism() const
+   {
+#if defined(BOTAN_HAS_IDEA_SSE2)
+   if(CPUID::has_sse2())
+      {
+      return 8;
+      }
+#endif
+
+   return 1;
+   }
+
+std::string IDEA::provider() const
+   {
+#if defined(BOTAN_HAS_IDEA_SSE2)
+   if(CPUID::has_sse2())
+      {
+      return "sse2";
+      }
+#endif
+
+   return "base";
+   }
+
+/*
+* IDEA Encryption
+*/
+void IDEA::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_IDEA_SSE2)
+   if(CPUID::has_sse2())
+      {
+      while(blocks >= 8)
+         {
+         sse2_idea_op_8(in, out, m_EK.data());
+         in += 8 * BLOCK_SIZE;
+         out += 8 * BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+   idea_op(in, out, blocks, m_EK.data());
+   }
+
+/*
+* IDEA Decryption
+*/
+void IDEA::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_IDEA_SSE2)
+   if(CPUID::has_sse2())
+      {
+      while(blocks >= 8)
+         {
+         sse2_idea_op_8(in, out, m_DK.data());
+         in += 8 * BLOCK_SIZE;
+         out += 8 * BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+   idea_op(in, out, blocks, m_DK.data());
+   }
+
+/*
+* IDEA Key Schedule
+*/
+void IDEA::key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(52);
+   m_DK.resize(52);
+
+   CT::poison(key, 16);
+   CT::poison(m_EK.data(), 52);
+   CT::poison(m_DK.data(), 52);
+
+   secure_vector<uint64_t> K(2);
+
+   K[0] = load_be<uint64_t>(key, 0);
+   K[1] = load_be<uint64_t>(key, 1);
+
+   for(size_t off = 0; off != 48; off += 8)
+      {
+      for(size_t i = 0; i != 8; ++i)
+         m_EK[off+i] = static_cast<uint16_t>(K[i/4] >> (48-16*(i % 4)));
+
+      const uint64_t Kx = (K[0] >> 39);
+      const uint64_t Ky = (K[1] >> 39);
+
+      K[0] = (K[0] << 25) | Ky;
+      K[1] = (K[1] << 25) | Kx;
+      }
+
+   for(size_t i = 0; i != 4; ++i)
+      m_EK[48+i] = static_cast<uint16_t>(K[i/4] >> (48-16*(i % 4)));
+
+   m_DK[0] = mul_inv(m_EK[48]);
+   m_DK[1] = -m_EK[49];
+   m_DK[2] = -m_EK[50];
+   m_DK[3] = mul_inv(m_EK[51]);
+
+   for(size_t i = 0; i != 8*6; i += 6)
+      {
+      m_DK[i+4] = m_EK[46-i];
+      m_DK[i+5] = m_EK[47-i];
+      m_DK[i+6] = mul_inv(m_EK[42-i]);
+      m_DK[i+7] = -m_EK[44-i];
+      m_DK[i+8] = -m_EK[43-i];
+      m_DK[i+9] = mul_inv(m_EK[45-i]);
+      }
+
+   std::swap(m_DK[49], m_DK[50]);
+
+   CT::unpoison(key, 16);
+   CT::unpoison(m_EK.data(), 52);
+   CT::unpoison(m_DK.data(), 52);
+   }
+
+void IDEA::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/idea/idea.h b/third_party/botan/src/lib/block/idea/idea.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/idea/idea.h
@@ -0,0 +1,45 @@
+/*
+* IDEA
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_IDEA_H_
+#define BOTAN_IDEA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(idea.h)
+
+namespace Botan {
+
+/**
+* IDEA
+*/
+class BOTAN_PUBLIC_API(2,0) IDEA final : public Block_Cipher_Fixed_Params<8, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+      std::string name() const override { return "IDEA"; }
+      BlockCipher* clone() const override { return new IDEA; }
+      size_t parallelism() const override;
+
+   private:
+#if defined(BOTAN_HAS_IDEA_SSE2)
+      void sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]) const;
+#endif
+
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint16_t> m_EK, m_DK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp b/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp
@@ -0,0 +1,208 @@
+/*
+* IDEA in SSE2
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/idea.h>
+#include <botan/internal/ct_utils.h>
+#include <emmintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("sse2")
+inline __m128i mul(__m128i X, uint16_t K_16)
+   {
+   const __m128i zeros = _mm_set1_epi16(0);
+   const __m128i ones = _mm_set1_epi16(1);
+
+   const __m128i K = _mm_set1_epi16(K_16);
+
+   const __m128i X_is_zero = _mm_cmpeq_epi16(X, zeros);
+   const __m128i K_is_zero = _mm_cmpeq_epi16(K, zeros);
+
+   const __m128i mul_lo = _mm_mullo_epi16(X, K);
+   const __m128i mul_hi = _mm_mulhi_epu16(X, K);
+
+   __m128i T = _mm_sub_epi16(mul_lo, mul_hi);
+
+   // Unsigned compare; cmp = 1 if mul_lo < mul_hi else 0
+   const __m128i subs = _mm_subs_epu16(mul_hi, mul_lo);
+   const __m128i cmp = _mm_min_epu8(
+     _mm_or_si128(subs, _mm_srli_epi16(subs, 8)), ones);
+
+   T = _mm_add_epi16(T, cmp);
+
+   /* Selection: if X[i] is zero then assign 1-K
+                 if K is zero then assign 1-X[i]
+
+      Could if() off value of K_16 for the second, but this gives a
+      constant time implementation which is a nice bonus.
+   */
+
+   T = _mm_or_si128(
+      _mm_andnot_si128(X_is_zero, T),
+      _mm_and_si128(_mm_sub_epi16(ones, K), X_is_zero));
+
+   T = _mm_or_si128(
+      _mm_andnot_si128(K_is_zero, T),
+      _mm_and_si128(_mm_sub_epi16(ones, X), K_is_zero));
+
+   return T;
+   }
+
+/*
+* 4x8 matrix transpose
+*
+* FIXME: why do I need the extra set of unpack_epi32 here? Inverse in
+* transpose_out doesn't need it. Something with the shuffle? Removing
+* that extra unpack could easily save 3-4 cycles per block, and would
+* also help a lot with register pressure on 32-bit x86
+*/
+BOTAN_FUNC_ISA("sse2")
+void transpose_in(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3)
+   {
+   __m128i T0 = _mm_unpackhi_epi32(B0, B1);
+   __m128i T1 = _mm_unpacklo_epi32(B0, B1);
+   __m128i T2 = _mm_unpackhi_epi32(B2, B3);
+   __m128i T3 = _mm_unpacklo_epi32(B2, B3);
+
+   __m128i T4 = _mm_unpacklo_epi32(T0, T1);
+   __m128i T5 = _mm_unpackhi_epi32(T0, T1);
+   __m128i T6 = _mm_unpacklo_epi32(T2, T3);
+   __m128i T7 = _mm_unpackhi_epi32(T2, T3);
+
+   T0 = _mm_shufflehi_epi16(T4, _MM_SHUFFLE(1, 3, 0, 2));
+   T1 = _mm_shufflehi_epi16(T5, _MM_SHUFFLE(1, 3, 0, 2));
+   T2 = _mm_shufflehi_epi16(T6, _MM_SHUFFLE(1, 3, 0, 2));
+   T3 = _mm_shufflehi_epi16(T7, _MM_SHUFFLE(1, 3, 0, 2));
+
+   T0 = _mm_shufflelo_epi16(T0, _MM_SHUFFLE(1, 3, 0, 2));
+   T1 = _mm_shufflelo_epi16(T1, _MM_SHUFFLE(1, 3, 0, 2));
+   T2 = _mm_shufflelo_epi16(T2, _MM_SHUFFLE(1, 3, 0, 2));
+   T3 = _mm_shufflelo_epi16(T3, _MM_SHUFFLE(1, 3, 0, 2));
+
+   T0 = _mm_shuffle_epi32(T0, _MM_SHUFFLE(3, 1, 2, 0));
+   T1 = _mm_shuffle_epi32(T1, _MM_SHUFFLE(3, 1, 2, 0));
+   T2 = _mm_shuffle_epi32(T2, _MM_SHUFFLE(3, 1, 2, 0));
+   T3 = _mm_shuffle_epi32(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+   B0 = _mm_unpacklo_epi64(T0, T2);
+   B1 = _mm_unpackhi_epi64(T0, T2);
+   B2 = _mm_unpacklo_epi64(T1, T3);
+   B3 = _mm_unpackhi_epi64(T1, T3);
+   }
+
+/*
+* 4x8 matrix transpose (reverse)
+*/
+BOTAN_FUNC_ISA("sse2")
+void transpose_out(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3)
+   {
+   __m128i T0 = _mm_unpacklo_epi64(B0, B1);
+   __m128i T1 = _mm_unpacklo_epi64(B2, B3);
+   __m128i T2 = _mm_unpackhi_epi64(B0, B1);
+   __m128i T3 = _mm_unpackhi_epi64(B2, B3);
+
+   T0 = _mm_shuffle_epi32(T0, _MM_SHUFFLE(3, 1, 2, 0));
+   T1 = _mm_shuffle_epi32(T1, _MM_SHUFFLE(3, 1, 2, 0));
+   T2 = _mm_shuffle_epi32(T2, _MM_SHUFFLE(3, 1, 2, 0));
+   T3 = _mm_shuffle_epi32(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+   T0 = _mm_shufflehi_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0));
+   T1 = _mm_shufflehi_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0));
+   T2 = _mm_shufflehi_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0));
+   T3 = _mm_shufflehi_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+   T0 = _mm_shufflelo_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0));
+   T1 = _mm_shufflelo_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0));
+   T2 = _mm_shufflelo_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0));
+   T3 = _mm_shufflelo_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+   B0 = _mm_unpacklo_epi32(T0, T1);
+   B1 = _mm_unpackhi_epi32(T0, T1);
+   B2 = _mm_unpacklo_epi32(T2, T3);
+   B3 = _mm_unpackhi_epi32(T2, T3);
+   }
+
+}
+
+/*
+* 8 wide IDEA encryption/decryption in SSE2
+*/
+BOTAN_FUNC_ISA("sse2")
+void IDEA::sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]) const
+   {
+   CT::poison(in, 64);
+   CT::poison(out, 64);
+   CT::poison(EK, 52);
+
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+
+   __m128i B0 = _mm_loadu_si128(in_mm + 0);
+   __m128i B1 = _mm_loadu_si128(in_mm + 1);
+   __m128i B2 = _mm_loadu_si128(in_mm + 2);
+   __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+   transpose_in(B0, B1, B2, B3);
+
+   // byte swap
+   B0 = _mm_or_si128(_mm_slli_epi16(B0, 8), _mm_srli_epi16(B0, 8));
+   B1 = _mm_or_si128(_mm_slli_epi16(B1, 8), _mm_srli_epi16(B1, 8));
+   B2 = _mm_or_si128(_mm_slli_epi16(B2, 8), _mm_srli_epi16(B2, 8));
+   B3 = _mm_or_si128(_mm_slli_epi16(B3, 8), _mm_srli_epi16(B3, 8));
+
+   for(size_t i = 0; i != 8; ++i)
+      {
+      B0 = mul(B0, EK[6*i+0]);
+      B1 = _mm_add_epi16(B1, _mm_set1_epi16(EK[6*i+1]));
+      B2 = _mm_add_epi16(B2, _mm_set1_epi16(EK[6*i+2]));
+      B3 = mul(B3, EK[6*i+3]);
+
+      __m128i T0 = B2;
+      B2 = _mm_xor_si128(B2, B0);
+      B2 = mul(B2, EK[6*i+4]);
+
+      __m128i T1 = B1;
+
+      B1 = _mm_xor_si128(B1, B3);
+      B1 = _mm_add_epi16(B1, B2);
+      B1 = mul(B1, EK[6*i+5]);
+
+      B2 = _mm_add_epi16(B2, B1);
+
+      B0 = _mm_xor_si128(B0, B1);
+      B1 = _mm_xor_si128(B1, T0);
+      B3 = _mm_xor_si128(B3, B2);
+      B2 = _mm_xor_si128(B2, T1);
+      }
+
+   B0 = mul(B0, EK[48]);
+   B1 = _mm_add_epi16(B1, _mm_set1_epi16(EK[50]));
+   B2 = _mm_add_epi16(B2, _mm_set1_epi16(EK[49]));
+   B3 = mul(B3, EK[51]);
+
+   // byte swap
+   B0 = _mm_or_si128(_mm_slli_epi16(B0, 8), _mm_srli_epi16(B0, 8));
+   B1 = _mm_or_si128(_mm_slli_epi16(B1, 8), _mm_srli_epi16(B1, 8));
+   B2 = _mm_or_si128(_mm_slli_epi16(B2, 8), _mm_srli_epi16(B2, 8));
+   B3 = _mm_or_si128(_mm_slli_epi16(B3, 8), _mm_srli_epi16(B3, 8));
+
+   transpose_out(B0, B2, B1, B3);
+
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   _mm_storeu_si128(out_mm + 0, B0);
+   _mm_storeu_si128(out_mm + 1, B2);
+   _mm_storeu_si128(out_mm + 2, B1);
+   _mm_storeu_si128(out_mm + 3, B3);
+
+   CT::unpoison(in, 64);
+   CT::unpoison(out, 64);
+   CT::unpoison(EK, 52);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/kasumi/kasumi.cpp b/third_party/botan/src/lib/block/kasumi/kasumi.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/kasumi/kasumi.cpp
@@ -0,0 +1,238 @@
+/*
+* KASUMI
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kasumi.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* KASUMI S-Boxes
+*/
+const uint8_t KASUMI_SBOX_S7[128] = {
+   0x36, 0x32, 0x3E, 0x38, 0x16, 0x22, 0x5E, 0x60, 0x26, 0x06, 0x3F, 0x5D,
+   0x02, 0x12, 0x7B, 0x21, 0x37, 0x71, 0x27, 0x72, 0x15, 0x43, 0x41, 0x0C,
+   0x2F, 0x49, 0x2E, 0x1B, 0x19, 0x6F, 0x7C, 0x51, 0x35, 0x09, 0x79, 0x4F,
+   0x34, 0x3C, 0x3A, 0x30, 0x65, 0x7F, 0x28, 0x78, 0x68, 0x46, 0x47, 0x2B,
+   0x14, 0x7A, 0x48, 0x3D, 0x17, 0x6D, 0x0D, 0x64, 0x4D, 0x01, 0x10, 0x07,
+   0x52, 0x0A, 0x69, 0x62, 0x75, 0x74, 0x4C, 0x0B, 0x59, 0x6A, 0x00, 0x7D,
+   0x76, 0x63, 0x56, 0x45, 0x1E, 0x39, 0x7E, 0x57, 0x70, 0x33, 0x11, 0x05,
+   0x5F, 0x0E, 0x5A, 0x54, 0x5B, 0x08, 0x23, 0x67, 0x20, 0x61, 0x1C, 0x42,
+   0x66, 0x1F, 0x1A, 0x2D, 0x4B, 0x04, 0x55, 0x5C, 0x25, 0x4A, 0x50, 0x31,
+   0x44, 0x1D, 0x73, 0x2C, 0x40, 0x6B, 0x6C, 0x18, 0x6E, 0x53, 0x24, 0x4E,
+   0x2A, 0x13, 0x0F, 0x29, 0x58, 0x77, 0x3B, 0x03 };
+
+const uint16_t KASUMI_SBOX_S9[512] = {
+   0x00A7, 0x00EF, 0x00A1, 0x017B, 0x0187, 0x014E, 0x0009, 0x0152, 0x0026,
+   0x00E2, 0x0030, 0x0166, 0x01C4, 0x0181, 0x005A, 0x018D, 0x00B7, 0x00FD,
+   0x0093, 0x014B, 0x019F, 0x0154, 0x0033, 0x016A, 0x0132, 0x01F4, 0x0106,
+   0x0052, 0x00D8, 0x009F, 0x0164, 0x00B1, 0x00AF, 0x00F1, 0x01E9, 0x0025,
+   0x00CE, 0x0011, 0x0000, 0x014D, 0x002C, 0x00FE, 0x017A, 0x003A, 0x008F,
+   0x00DC, 0x0051, 0x0190, 0x005F, 0x0003, 0x013B, 0x00F5, 0x0036, 0x00EB,
+   0x00DA, 0x0195, 0x01D8, 0x0108, 0x00AC, 0x01EE, 0x0173, 0x0122, 0x018F,
+   0x004C, 0x00A5, 0x00C5, 0x018B, 0x0079, 0x0101, 0x01E0, 0x01A7, 0x00D4,
+   0x00F0, 0x001C, 0x01CE, 0x00B0, 0x0196, 0x01FB, 0x0120, 0x00DF, 0x01F5,
+   0x0197, 0x00F9, 0x0109, 0x0059, 0x00BA, 0x00DD, 0x01AC, 0x00A4, 0x004A,
+   0x01B8, 0x00C4, 0x01CA, 0x01A5, 0x015E, 0x00A3, 0x00E8, 0x009E, 0x0086,
+   0x0162, 0x000D, 0x00FA, 0x01EB, 0x008E, 0x00BF, 0x0045, 0x00C1, 0x01A9,
+   0x0098, 0x00E3, 0x016E, 0x0087, 0x0158, 0x012C, 0x0114, 0x00F2, 0x01B5,
+   0x0140, 0x0071, 0x0116, 0x000B, 0x00F3, 0x0057, 0x013D, 0x0024, 0x005D,
+   0x01F0, 0x001B, 0x01E7, 0x01BE, 0x01E2, 0x0029, 0x0044, 0x009C, 0x01C9,
+   0x0083, 0x0146, 0x0193, 0x0153, 0x0014, 0x0027, 0x0073, 0x01BA, 0x007C,
+   0x01DB, 0x0180, 0x01FC, 0x0035, 0x0070, 0x00AA, 0x01DF, 0x0097, 0x007E,
+   0x00A9, 0x0049, 0x010C, 0x0117, 0x0141, 0x00A8, 0x016C, 0x016B, 0x0124,
+   0x002E, 0x01F3, 0x0189, 0x0147, 0x0144, 0x0018, 0x01C8, 0x010B, 0x009D,
+   0x01CC, 0x01E8, 0x01AA, 0x0135, 0x00E5, 0x01B7, 0x01FA, 0x00D0, 0x010F,
+   0x015D, 0x0191, 0x01B2, 0x00EC, 0x0010, 0x00D1, 0x0167, 0x0034, 0x0038,
+   0x0078, 0x00C7, 0x0115, 0x01D1, 0x01A0, 0x00FC, 0x011F, 0x00F6, 0x0006,
+   0x0053, 0x0131, 0x01A4, 0x0159, 0x0099, 0x01F6, 0x0041, 0x003D, 0x00F4,
+   0x011A, 0x00AD, 0x00DE, 0x01A2, 0x0043, 0x0182, 0x0170, 0x0105, 0x0065,
+   0x01DC, 0x0123, 0x00C3, 0x01AE, 0x0031, 0x004F, 0x00A6, 0x014A, 0x0118,
+   0x017F, 0x0175, 0x0080, 0x017E, 0x0198, 0x009B, 0x01EF, 0x016F, 0x0184,
+   0x0112, 0x006B, 0x01CB, 0x01A1, 0x003E, 0x01C6, 0x0084, 0x00E1, 0x00CB,
+   0x013C, 0x00EA, 0x000E, 0x012D, 0x005B, 0x01F7, 0x011E, 0x01A8, 0x00D3,
+   0x015B, 0x0133, 0x008C, 0x0176, 0x0023, 0x0067, 0x007D, 0x01AB, 0x0013,
+   0x00D6, 0x01C5, 0x0092, 0x01F2, 0x013A, 0x01BC, 0x00E6, 0x0100, 0x0149,
+   0x00C6, 0x011D, 0x0032, 0x0074, 0x004E, 0x019A, 0x000A, 0x00CD, 0x01FE,
+   0x00AB, 0x00E7, 0x002D, 0x008B, 0x01D3, 0x001D, 0x0056, 0x01F9, 0x0020,
+   0x0048, 0x001A, 0x0156, 0x0096, 0x0139, 0x01EA, 0x01AF, 0x00EE, 0x019B,
+   0x0145, 0x0095, 0x01D9, 0x0028, 0x0077, 0x00AE, 0x0163, 0x00B9, 0x00E9,
+   0x0185, 0x0047, 0x01C0, 0x0111, 0x0174, 0x0037, 0x006E, 0x00B2, 0x0142,
+   0x000C, 0x01D5, 0x0188, 0x0171, 0x00BE, 0x0001, 0x006D, 0x0177, 0x0089,
+   0x00B5, 0x0058, 0x004B, 0x0134, 0x0104, 0x01E4, 0x0062, 0x0110, 0x0172,
+   0x0113, 0x019C, 0x006F, 0x0150, 0x013E, 0x0004, 0x01F8, 0x01EC, 0x0103,
+   0x0130, 0x004D, 0x0151, 0x01B3, 0x0015, 0x0165, 0x012F, 0x014C, 0x01E3,
+   0x0012, 0x002F, 0x0055, 0x0019, 0x01F1, 0x01DA, 0x0121, 0x0064, 0x010D,
+   0x0128, 0x01DE, 0x010E, 0x006A, 0x001F, 0x0068, 0x01B1, 0x0054, 0x019E,
+   0x01E6, 0x018A, 0x0060, 0x0063, 0x009A, 0x01FF, 0x0094, 0x019D, 0x0169,
+   0x0199, 0x00FF, 0x00A2, 0x00D7, 0x012E, 0x00C9, 0x010A, 0x015F, 0x0157,
+   0x0090, 0x01B9, 0x016D, 0x006C, 0x012A, 0x00FB, 0x0022, 0x00B6, 0x01FD,
+   0x008A, 0x00D2, 0x014F, 0x0085, 0x0137, 0x0160, 0x0148, 0x008D, 0x018C,
+   0x015A, 0x007B, 0x013F, 0x01C2, 0x0119, 0x01AD, 0x00E4, 0x01BB, 0x01E1,
+   0x005C, 0x0194, 0x01E5, 0x01A6, 0x00F8, 0x0129, 0x0017, 0x00D5, 0x0082,
+   0x01D2, 0x0016, 0x00D9, 0x011B, 0x0046, 0x0126, 0x0168, 0x01A3, 0x007F,
+   0x0138, 0x0179, 0x0007, 0x01D4, 0x00C2, 0x0002, 0x0075, 0x0127, 0x01CF,
+   0x0102, 0x00E0, 0x01BF, 0x00F7, 0x00BB, 0x0050, 0x018E, 0x011C, 0x0161,
+   0x0069, 0x0186, 0x012B, 0x01D7, 0x01D6, 0x00B8, 0x0039, 0x00C8, 0x015C,
+   0x003F, 0x00CC, 0x00BC, 0x0021, 0x01C3, 0x0061, 0x001E, 0x0136, 0x00DB,
+   0x005E, 0x00A0, 0x0081, 0x01ED, 0x0040, 0x00B3, 0x0107, 0x0066, 0x00BD,
+   0x00CF, 0x0072, 0x0192, 0x01B6, 0x01DD, 0x0183, 0x007A, 0x00C0, 0x002A,
+   0x017D, 0x0005, 0x0091, 0x0076, 0x00B4, 0x01C1, 0x0125, 0x0143, 0x0088,
+   0x017C, 0x002B, 0x0042, 0x003C, 0x01C7, 0x0155, 0x01BD, 0x00CA, 0x01B0,
+   0x0008, 0x00ED, 0x000F, 0x0178, 0x01B4, 0x01D0, 0x003B, 0x01CD };
+
+/*
+* KASUMI FI Function
+*/
+uint16_t FI(uint16_t I, uint16_t K)
+   {
+   uint16_t D9 = (I >> 7);
+   uint8_t D7 = (I & 0x7F);
+   D9 = KASUMI_SBOX_S9[D9] ^ D7;
+   D7 = KASUMI_SBOX_S7[D7] ^ (D9 & 0x7F);
+
+   D7 ^= (K >> 9);
+   D9 = KASUMI_SBOX_S9[D9 ^ (K & 0x1FF)] ^ D7;
+   D7 = KASUMI_SBOX_S7[D7] ^ (D9 & 0x7F);
+   return static_cast<uint16_t>(D7 << 9) | D9;
+   }
+
+}
+
+/*
+* KASUMI Encryption
+*/
+void KASUMI::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint16_t B0 = load_be<uint16_t>(in, 0);
+      uint16_t B1 = load_be<uint16_t>(in, 1);
+      uint16_t B2 = load_be<uint16_t>(in, 2);
+      uint16_t B3 = load_be<uint16_t>(in, 3);
+
+      for(size_t j = 0; j != 8; j += 2)
+         {
+         const uint16_t* K = &m_EK[8*j];
+
+         uint16_t R = B1 ^ (rotl<1>(B0) & K[0]);
+         uint16_t L = B0 ^ (rotl<1>(R) | K[1]);
+
+         L = FI(L ^ K[ 2], K[ 3]) ^ R;
+         R = FI(R ^ K[ 4], K[ 5]) ^ L;
+         L = FI(L ^ K[ 6], K[ 7]) ^ R;
+
+         R = B2 ^= R;
+         L = B3 ^= L;
+
+         R = FI(R ^ K[10], K[11]) ^ L;
+         L = FI(L ^ K[12], K[13]) ^ R;
+         R = FI(R ^ K[14], K[15]) ^ L;
+
+         R ^= (rotl<1>(L) & K[8]);
+         L ^= (rotl<1>(R) | K[9]);
+
+         B0 ^= L;
+         B1 ^= R;
+         }
+
+      store_be(out, B0, B1, B2, B3);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* KASUMI Decryption
+*/
+void KASUMI::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint16_t B0 = load_be<uint16_t>(in, 0);
+      uint16_t B1 = load_be<uint16_t>(in, 1);
+      uint16_t B2 = load_be<uint16_t>(in, 2);
+      uint16_t B3 = load_be<uint16_t>(in, 3);
+
+      for(size_t j = 0; j != 8; j += 2)
+         {
+         const uint16_t* K = &m_EK[8*(6-j)];
+
+         uint16_t L = B2, R = B3;
+
+         L = FI(L ^ K[10], K[11]) ^ R;
+         R = FI(R ^ K[12], K[13]) ^ L;
+         L = FI(L ^ K[14], K[15]) ^ R;
+
+         L ^= (rotl<1>(R) & K[8]);
+         R ^= (rotl<1>(L) | K[9]);
+
+         R = B0 ^= R;
+         L = B1 ^= L;
+
+         L ^= (rotl<1>(R) & K[0]);
+         R ^= (rotl<1>(L) | K[1]);
+
+         R = FI(R ^ K[2], K[3]) ^ L;
+         L = FI(L ^ K[4], K[5]) ^ R;
+         R = FI(R ^ K[6], K[7]) ^ L;
+
+         B2 ^= L;
+         B3 ^= R;
+         }
+
+      store_be(out, B0, B1, B2, B3);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* KASUMI Key Schedule
+*/
+void KASUMI::key_schedule(const uint8_t key[], size_t)
+   {
+   static const uint16_t RC[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF,
+                                0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+   secure_vector<uint16_t> K(16);
+   for(size_t i = 0; i != 8; ++i)
+      {
+      K[i] = load_be<uint16_t>(key, i);
+      K[i+8] = K[i] ^ RC[i];
+      }
+
+   m_EK.resize(64);
+
+   for(size_t i = 0; i != 8; ++i)
+      {
+      m_EK[8*i  ] = rotl<2>(K[(i+0) % 8]);
+      m_EK[8*i+1] = rotl<1>(K[(i+2) % 8 + 8]);
+      m_EK[8*i+2] = rotl<5>(K[(i+1) % 8]);
+      m_EK[8*i+3] = K[(i+4) % 8 + 8];
+      m_EK[8*i+4] = rotl<8>(K[(i+5) % 8]);
+      m_EK[8*i+5] = K[(i+3) % 8 + 8];
+      m_EK[8*i+6] = rotl<13>(K[(i+6) % 8]);
+      m_EK[8*i+7] = K[(i+7) % 8 + 8];
+      }
+   }
+
+void KASUMI::clear()
+   {
+   zap(m_EK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/kasumi/kasumi.h b/third_party/botan/src/lib/block/kasumi/kasumi.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/kasumi/kasumi.h
@@ -0,0 +1,37 @@
+/*
+* KASUMI
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KASUMI_H_
+#define BOTAN_KASUMI_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kasumi.h)
+
+namespace Botan {
+
+/**
+* KASUMI, the block cipher used in 3G telephony
+*/
+class BOTAN_PUBLIC_API(2,0) KASUMI final : public Block_Cipher_Fixed_Params<8, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "KASUMI"; }
+      BlockCipher* clone() const override { return new KASUMI; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint16_t> m_EK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/lion/lion.cpp b/third_party/botan/src/lib/block/lion/lion.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/lion/lion.cpp
@@ -0,0 +1,138 @@
+/*
+* Lion
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/lion.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Lion Encryption
+*/
+void Lion::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_key1.empty() == false);
+
+   const size_t LEFT_SIZE = left_size();
+   const size_t RIGHT_SIZE = right_size();
+
+   secure_vector<uint8_t> buffer_vec(LEFT_SIZE);
+   uint8_t* buffer = buffer_vec.data();
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      xor_buf(buffer, in, m_key1.data(), LEFT_SIZE);
+      m_cipher->set_key(buffer, LEFT_SIZE);
+      m_cipher->cipher(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);
+
+      m_hash->update(out + LEFT_SIZE, RIGHT_SIZE);
+      m_hash->final(buffer);
+      xor_buf(out, in, buffer, LEFT_SIZE);
+
+      xor_buf(buffer, out, m_key2.data(), LEFT_SIZE);
+      m_cipher->set_key(buffer, LEFT_SIZE);
+      m_cipher->cipher1(out + LEFT_SIZE, RIGHT_SIZE);
+
+      in += m_block_size;
+      out += m_block_size;
+      }
+   }
+
+/*
+* Lion Decryption
+*/
+void Lion::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_key1.empty() == false);
+
+   const size_t LEFT_SIZE = left_size();
+   const size_t RIGHT_SIZE = right_size();
+
+   secure_vector<uint8_t> buffer_vec(LEFT_SIZE);
+   uint8_t* buffer = buffer_vec.data();
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      xor_buf(buffer, in, m_key2.data(), LEFT_SIZE);
+      m_cipher->set_key(buffer, LEFT_SIZE);
+      m_cipher->cipher(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);
+
+      m_hash->update(out + LEFT_SIZE, RIGHT_SIZE);
+      m_hash->final(buffer);
+      xor_buf(out, in, buffer, LEFT_SIZE);
+
+      xor_buf(buffer, out, m_key1.data(), LEFT_SIZE);
+      m_cipher->set_key(buffer, LEFT_SIZE);
+      m_cipher->cipher1(out + LEFT_SIZE, RIGHT_SIZE);
+
+      in += m_block_size;
+      out += m_block_size;
+      }
+   }
+
+/*
+* Lion Key Schedule
+*/
+void Lion::key_schedule(const uint8_t key[], size_t length)
+   {
+   clear();
+
+   const size_t half = length / 2;
+
+   m_key1.resize(left_size());
+   m_key2.resize(left_size());
+   clear_mem(m_key1.data(), m_key1.size());
+   clear_mem(m_key2.data(), m_key2.size());
+   copy_mem(m_key1.data(), key, half);
+   copy_mem(m_key2.data(), key + half, half);
+   }
+
+/*
+* Return the name of this type
+*/
+std::string Lion::name() const
+   {
+   return "Lion(" + m_hash->name() + "," +
+                    m_cipher->name() + "," +
+                    std::to_string(block_size()) + ")";
+   }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* Lion::clone() const
+   {
+   return new Lion(m_hash->clone(), m_cipher->clone(), block_size());
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Lion::clear()
+   {
+   zap(m_key1);
+   zap(m_key2);
+   m_hash->clear();
+   m_cipher->clear();
+   }
+
+/*
+* Lion Constructor
+*/
+Lion::Lion(HashFunction* hash, StreamCipher* cipher, size_t bs) :
+   m_block_size(std::max<size_t>(2*hash->output_length() + 1, bs)),
+   m_hash(hash),
+   m_cipher(cipher)
+   {
+   if(2*left_size() + 1 > m_block_size)
+      throw Invalid_Argument(name() + ": Chosen block size is too small");
+
+   if(!m_cipher->valid_keylength(left_size()))
+      throw Invalid_Argument(name() + ": This stream/hash combo is invalid");
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/lion/lion.h b/third_party/botan/src/lib/block/lion/lion.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/lion/lion.h
@@ -0,0 +1,66 @@
+/*
+* Lion
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LION_H_
+#define BOTAN_LION_H_
+
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(lion.h)
+
+namespace Botan {
+
+/**
+* Lion is a block cipher construction designed by Ross Anderson and
+* Eli Biham, described in "Two Practical and Provably Secure Block
+* Ciphers: BEAR and LION". It has a variable block size and is
+* designed to encrypt very large blocks (up to a megabyte)
+
+* https://www.cl.cam.ac.uk/~rja14/Papers/bear-lion.pdf
+*/
+class BOTAN_PUBLIC_API(2,0) Lion final : public BlockCipher
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      size_t block_size() const override { return m_block_size; }
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(2, 2*m_hash->output_length(), 2);
+         }
+
+      void clear() override;
+      std::string name() const override;
+      BlockCipher* clone() const override;
+
+      /**
+      * @param hash the hash to use internally
+      * @param cipher the stream cipher to use internally
+      * @param block_size the size of the block to use
+      */
+      Lion(HashFunction* hash,
+           StreamCipher* cipher,
+           size_t block_size);
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      size_t left_size() const { return m_hash->output_length(); }
+      size_t right_size() const { return m_block_size - left_size(); }
+
+      const size_t m_block_size;
+      std::unique_ptr<HashFunction> m_hash;
+      std::unique_ptr<StreamCipher> m_cipher;
+      secure_vector<uint8_t> m_key1, m_key2;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/misty1/misty1.cpp b/third_party/botan/src/lib/block/misty1/misty1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/misty1/misty1.cpp
@@ -0,0 +1,263 @@
+/*
+* MISTY1
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/misty1.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+static const uint8_t MISTY1_SBOX_S7[128] = {
+   0x1B, 0x32, 0x33, 0x5A, 0x3B, 0x10, 0x17, 0x54, 0x5B, 0x1A, 0x72, 0x73,
+   0x6B, 0x2C, 0x66, 0x49, 0x1F, 0x24, 0x13, 0x6C, 0x37, 0x2E, 0x3F, 0x4A,
+   0x5D, 0x0F, 0x40, 0x56, 0x25, 0x51, 0x1C, 0x04, 0x0B, 0x46, 0x20, 0x0D,
+   0x7B, 0x35, 0x44, 0x42, 0x2B, 0x1E, 0x41, 0x14, 0x4B, 0x79, 0x15, 0x6F,
+   0x0E, 0x55, 0x09, 0x36, 0x74, 0x0C, 0x67, 0x53, 0x28, 0x0A, 0x7E, 0x38,
+   0x02, 0x07, 0x60, 0x29, 0x19, 0x12, 0x65, 0x2F, 0x30, 0x39, 0x08, 0x68,
+   0x5F, 0x78, 0x2A, 0x4C, 0x64, 0x45, 0x75, 0x3D, 0x59, 0x48, 0x03, 0x57,
+   0x7C, 0x4F, 0x62, 0x3C, 0x1D, 0x21, 0x5E, 0x27, 0x6A, 0x70, 0x4D, 0x3A,
+   0x01, 0x6D, 0x6E, 0x63, 0x18, 0x77, 0x23, 0x05, 0x26, 0x76, 0x00, 0x31,
+   0x2D, 0x7A, 0x7F, 0x61, 0x50, 0x22, 0x11, 0x06, 0x47, 0x16, 0x52, 0x4E,
+   0x71, 0x3E, 0x69, 0x43, 0x34, 0x5C, 0x58, 0x7D };
+
+static const uint16_t MISTY1_SBOX_S9[512] = {
+   0x01C3, 0x00CB, 0x0153, 0x019F, 0x01E3, 0x00E9, 0x00FB, 0x0035, 0x0181,
+   0x00B9, 0x0117, 0x01EB, 0x0133, 0x0009, 0x002D, 0x00D3, 0x00C7, 0x014A,
+   0x0037, 0x007E, 0x00EB, 0x0164, 0x0193, 0x01D8, 0x00A3, 0x011E, 0x0055,
+   0x002C, 0x001D, 0x01A2, 0x0163, 0x0118, 0x014B, 0x0152, 0x01D2, 0x000F,
+   0x002B, 0x0030, 0x013A, 0x00E5, 0x0111, 0x0138, 0x018E, 0x0063, 0x00E3,
+   0x00C8, 0x01F4, 0x001B, 0x0001, 0x009D, 0x00F8, 0x01A0, 0x016D, 0x01F3,
+   0x001C, 0x0146, 0x007D, 0x00D1, 0x0082, 0x01EA, 0x0183, 0x012D, 0x00F4,
+   0x019E, 0x01D3, 0x00DD, 0x01E2, 0x0128, 0x01E0, 0x00EC, 0x0059, 0x0091,
+   0x0011, 0x012F, 0x0026, 0x00DC, 0x00B0, 0x018C, 0x010F, 0x01F7, 0x00E7,
+   0x016C, 0x00B6, 0x00F9, 0x00D8, 0x0151, 0x0101, 0x014C, 0x0103, 0x00B8,
+   0x0154, 0x012B, 0x01AE, 0x0017, 0x0071, 0x000C, 0x0047, 0x0058, 0x007F,
+   0x01A4, 0x0134, 0x0129, 0x0084, 0x015D, 0x019D, 0x01B2, 0x01A3, 0x0048,
+   0x007C, 0x0051, 0x01CA, 0x0023, 0x013D, 0x01A7, 0x0165, 0x003B, 0x0042,
+   0x00DA, 0x0192, 0x00CE, 0x00C1, 0x006B, 0x009F, 0x01F1, 0x012C, 0x0184,
+   0x00FA, 0x0196, 0x01E1, 0x0169, 0x017D, 0x0031, 0x0180, 0x010A, 0x0094,
+   0x01DA, 0x0186, 0x013E, 0x011C, 0x0060, 0x0175, 0x01CF, 0x0067, 0x0119,
+   0x0065, 0x0068, 0x0099, 0x0150, 0x0008, 0x0007, 0x017C, 0x00B7, 0x0024,
+   0x0019, 0x00DE, 0x0127, 0x00DB, 0x00E4, 0x01A9, 0x0052, 0x0109, 0x0090,
+   0x019C, 0x01C1, 0x0028, 0x01B3, 0x0135, 0x016A, 0x0176, 0x00DF, 0x01E5,
+   0x0188, 0x00C5, 0x016E, 0x01DE, 0x01B1, 0x00C3, 0x01DF, 0x0036, 0x00EE,
+   0x01EE, 0x00F0, 0x0093, 0x0049, 0x009A, 0x01B6, 0x0069, 0x0081, 0x0125,
+   0x000B, 0x005E, 0x00B4, 0x0149, 0x01C7, 0x0174, 0x003E, 0x013B, 0x01B7,
+   0x008E, 0x01C6, 0x00AE, 0x0010, 0x0095, 0x01EF, 0x004E, 0x00F2, 0x01FD,
+   0x0085, 0x00FD, 0x00F6, 0x00A0, 0x016F, 0x0083, 0x008A, 0x0156, 0x009B,
+   0x013C, 0x0107, 0x0167, 0x0098, 0x01D0, 0x01E9, 0x0003, 0x01FE, 0x00BD,
+   0x0122, 0x0089, 0x00D2, 0x018F, 0x0012, 0x0033, 0x006A, 0x0142, 0x00ED,
+   0x0170, 0x011B, 0x00E2, 0x014F, 0x0158, 0x0131, 0x0147, 0x005D, 0x0113,
+   0x01CD, 0x0079, 0x0161, 0x01A5, 0x0179, 0x009E, 0x01B4, 0x00CC, 0x0022,
+   0x0132, 0x001A, 0x00E8, 0x0004, 0x0187, 0x01ED, 0x0197, 0x0039, 0x01BF,
+   0x01D7, 0x0027, 0x018B, 0x00C6, 0x009C, 0x00D0, 0x014E, 0x006C, 0x0034,
+   0x01F2, 0x006E, 0x00CA, 0x0025, 0x00BA, 0x0191, 0x00FE, 0x0013, 0x0106,
+   0x002F, 0x01AD, 0x0172, 0x01DB, 0x00C0, 0x010B, 0x01D6, 0x00F5, 0x01EC,
+   0x010D, 0x0076, 0x0114, 0x01AB, 0x0075, 0x010C, 0x01E4, 0x0159, 0x0054,
+   0x011F, 0x004B, 0x00C4, 0x01BE, 0x00F7, 0x0029, 0x00A4, 0x000E, 0x01F0,
+   0x0077, 0x004D, 0x017A, 0x0086, 0x008B, 0x00B3, 0x0171, 0x00BF, 0x010E,
+   0x0104, 0x0097, 0x015B, 0x0160, 0x0168, 0x00D7, 0x00BB, 0x0066, 0x01CE,
+   0x00FC, 0x0092, 0x01C5, 0x006F, 0x0016, 0x004A, 0x00A1, 0x0139, 0x00AF,
+   0x00F1, 0x0190, 0x000A, 0x01AA, 0x0143, 0x017B, 0x0056, 0x018D, 0x0166,
+   0x00D4, 0x01FB, 0x014D, 0x0194, 0x019A, 0x0087, 0x01F8, 0x0123, 0x00A7,
+   0x01B8, 0x0141, 0x003C, 0x01F9, 0x0140, 0x002A, 0x0155, 0x011A, 0x01A1,
+   0x0198, 0x00D5, 0x0126, 0x01AF, 0x0061, 0x012E, 0x0157, 0x01DC, 0x0072,
+   0x018A, 0x00AA, 0x0096, 0x0115, 0x00EF, 0x0045, 0x007B, 0x008D, 0x0145,
+   0x0053, 0x005F, 0x0178, 0x00B2, 0x002E, 0x0020, 0x01D5, 0x003F, 0x01C9,
+   0x01E7, 0x01AC, 0x0044, 0x0038, 0x0014, 0x00B1, 0x016B, 0x00AB, 0x00B5,
+   0x005A, 0x0182, 0x01C8, 0x01D4, 0x0018, 0x0177, 0x0064, 0x00CF, 0x006D,
+   0x0100, 0x0199, 0x0130, 0x015A, 0x0005, 0x0120, 0x01BB, 0x01BD, 0x00E0,
+   0x004F, 0x00D6, 0x013F, 0x01C4, 0x012A, 0x0015, 0x0006, 0x00FF, 0x019B,
+   0x00A6, 0x0043, 0x0088, 0x0050, 0x015F, 0x01E8, 0x0121, 0x0073, 0x017E,
+   0x00BC, 0x00C2, 0x00C9, 0x0173, 0x0189, 0x01F5, 0x0074, 0x01CC, 0x01E6,
+   0x01A8, 0x0195, 0x001F, 0x0041, 0x000D, 0x01BA, 0x0032, 0x003D, 0x01D1,
+   0x0080, 0x00A8, 0x0057, 0x01B9, 0x0162, 0x0148, 0x00D9, 0x0105, 0x0062,
+   0x007A, 0x0021, 0x01FF, 0x0112, 0x0108, 0x01C0, 0x00A9, 0x011D, 0x01B0,
+   0x01A6, 0x00CD, 0x00F3, 0x005C, 0x0102, 0x005B, 0x01D9, 0x0144, 0x01F6,
+   0x00AD, 0x00A5, 0x003A, 0x01CB, 0x0136, 0x017F, 0x0046, 0x00E1, 0x001E,
+   0x01DD, 0x00E6, 0x0137, 0x01FA, 0x0185, 0x008C, 0x008F, 0x0040, 0x01B5,
+   0x00BE, 0x0078, 0x0000, 0x00AC, 0x0110, 0x015E, 0x0124, 0x0002, 0x01BC,
+   0x00A2, 0x00EA, 0x0070, 0x01FC, 0x0116, 0x015C, 0x004C, 0x01C2 };
+
+/*
+* MISTY1 FI Function
+*/
+uint16_t FI(uint16_t input, uint16_t key7, uint16_t key9)
+   {
+   uint16_t D9 = input >> 7, D7 = input & 0x7F;
+   D9 = MISTY1_SBOX_S9[D9] ^ D7;
+   D7 = (MISTY1_SBOX_S7[D7] ^ key7 ^ D9) & 0x7F;
+   D9 = MISTY1_SBOX_S9[D9 ^ key9] ^ D7;
+   return static_cast<uint16_t>(D7 << 9) | D9;
+   }
+
+}
+
+/*
+* MISTY1 Encryption
+*/
+void MISTY1::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint16_t B0 = load_be<uint16_t>(in, 0);
+      uint16_t B1 = load_be<uint16_t>(in, 1);
+      uint16_t B2 = load_be<uint16_t>(in, 2);
+      uint16_t B3 = load_be<uint16_t>(in, 3);
+
+      for(size_t j = 0; j != 12; j += 3)
+         {
+         const uint16_t* RK = &m_EK[8 * j];
+
+         B1 ^= B0 & RK[0];
+         B0 ^= B1 | RK[1];
+         B3 ^= B2 & RK[2];
+         B2 ^= B3 | RK[3];
+
+         uint16_t T0, T1;
+
+         T0  = FI(B0 ^ RK[ 4], RK[ 5], RK[ 6]) ^ B1;
+         T1  = FI(B1 ^ RK[ 7], RK[ 8], RK[ 9]) ^ T0;
+         T0  = FI(T0 ^ RK[10], RK[11], RK[12]) ^ T1;
+
+         B2 ^= T1 ^ RK[13];
+         B3 ^= T0;
+
+         T0  = FI(B2 ^ RK[14], RK[15], RK[16]) ^ B3;
+         T1  = FI(B3 ^ RK[17], RK[18], RK[19]) ^ T0;
+         T0  = FI(T0 ^ RK[20], RK[21], RK[22]) ^ T1;
+
+         B0 ^= T1 ^ RK[23];
+         B1 ^= T0;
+         }
+
+      B1 ^= B0 & m_EK[96];
+      B0 ^= B1 | m_EK[97];
+      B3 ^= B2 & m_EK[98];
+      B2 ^= B3 | m_EK[99];
+
+      store_be(out, B2, B3, B0, B1);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* MISTY1 Decryption
+*/
+void MISTY1::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint16_t B0 = load_be<uint16_t>(in, 2);
+      uint16_t B1 = load_be<uint16_t>(in, 3);
+      uint16_t B2 = load_be<uint16_t>(in, 0);
+      uint16_t B3 = load_be<uint16_t>(in, 1);
+
+      for(size_t j = 0; j != 12; j += 3)
+         {
+         const uint16_t* RK = &m_DK[8 * j];
+
+         B2 ^= B3 | RK[0];
+         B3 ^= B2 & RK[1];
+         B0 ^= B1 | RK[2];
+         B1 ^= B0 & RK[3];
+
+         uint16_t T0, T1;
+
+         T0  = FI(B2 ^ RK[ 4], RK[ 5], RK[ 6]) ^ B3;
+         T1  = FI(B3 ^ RK[ 7], RK[ 8], RK[ 9]) ^ T0;
+         T0  = FI(T0 ^ RK[10], RK[11], RK[12]) ^ T1;
+
+         B0 ^= T1 ^ RK[13];
+         B1 ^= T0;
+
+         T0  = FI(B0 ^ RK[14], RK[15], RK[16]) ^ B1;
+         T1  = FI(B1 ^ RK[17], RK[18], RK[19]) ^ T0;
+         T0  = FI(T0 ^ RK[20], RK[21], RK[22]) ^ T1;
+
+         B2 ^= T1 ^ RK[23];
+         B3 ^= T0;
+         }
+
+      B2 ^= B3 | m_DK[96];
+      B3 ^= B2 & m_DK[97];
+      B0 ^= B1 | m_DK[98];
+      B1 ^= B0 & m_DK[99];
+
+      store_be(out, B0, B1, B2, B3);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* MISTY1 Key Schedule
+*/
+void MISTY1::key_schedule(const uint8_t key[], size_t length)
+   {
+   secure_vector<uint16_t> KS(32);
+   for(size_t i = 0; i != length / 2; ++i)
+      KS[i] = load_be<uint16_t>(key, i);
+
+   for(size_t i = 0; i != 8; ++i)
+      {
+      KS[i+ 8] = FI(KS[i], KS[(i+1) % 8] >> 9, KS[(i+1) % 8] & 0x1FF);
+      KS[i+16] = KS[i+8] >> 9;
+      KS[i+24] = KS[i+8] & 0x1FF;
+      }
+
+   /*
+   * Precomputed indexes for the orderings of the subkeys (MISTY1 reuses
+   * values)
+   */
+   static const uint8_t EK_ORDER[100] = {
+      0x00, 0x0E, 0x0A, 0x04, 0x00, 0x15, 0x1D, 0x02, 0x11, 0x19, 0x07, 0x13,
+      0x1B, 0x04, 0x01, 0x16, 0x1E, 0x03, 0x12, 0x1A, 0x00, 0x14, 0x1C, 0x05,
+      0x01, 0x0F, 0x0B, 0x05, 0x02, 0x17, 0x1F, 0x04, 0x13, 0x1B, 0x01, 0x15,
+      0x1D, 0x06, 0x03, 0x10, 0x18, 0x05, 0x14, 0x1C, 0x02, 0x16, 0x1E, 0x07,
+      0x02, 0x08, 0x0C, 0x06, 0x04, 0x11, 0x19, 0x06, 0x15, 0x1D, 0x03, 0x17,
+      0x1F, 0x00, 0x05, 0x12, 0x1A, 0x07, 0x16, 0x1E, 0x04, 0x10, 0x18, 0x01,
+      0x03, 0x09, 0x0D, 0x07, 0x06, 0x13, 0x1B, 0x00, 0x17, 0x1F, 0x05, 0x11,
+      0x19, 0x02, 0x07, 0x14, 0x1C, 0x01, 0x10, 0x18, 0x06, 0x12, 0x1A, 0x03,
+      0x04, 0x0A, 0x0E, 0x00 };
+
+   static const uint8_t DK_ORDER[100] = {
+      0x00, 0x0E, 0x0A, 0x04, 0x07, 0x14, 0x1C, 0x01, 0x10, 0x18, 0x06, 0x12,
+      0x1A, 0x03, 0x06, 0x13, 0x1B, 0x00, 0x17, 0x1F, 0x05, 0x11, 0x19, 0x02,
+      0x07, 0x0D, 0x09, 0x03, 0x05, 0x12, 0x1A, 0x07, 0x16, 0x1E, 0x04, 0x10,
+      0x18, 0x01, 0x04, 0x11, 0x19, 0x06, 0x15, 0x1D, 0x03, 0x17, 0x1F, 0x00,
+      0x06, 0x0C, 0x08, 0x02, 0x03, 0x10, 0x18, 0x05, 0x14, 0x1C, 0x02, 0x16,
+      0x1E, 0x07, 0x02, 0x17, 0x1F, 0x04, 0x13, 0x1B, 0x01, 0x15, 0x1D, 0x06,
+      0x05, 0x0B, 0x0F, 0x01, 0x01, 0x16, 0x1E, 0x03, 0x12, 0x1A, 0x00, 0x14,
+      0x1C, 0x05, 0x00, 0x15, 0x1D, 0x02, 0x11, 0x19, 0x07, 0x13, 0x1B, 0x04,
+      0x04, 0x0A, 0x0E, 0x00 };
+
+   m_EK.resize(100);
+   m_DK.resize(100);
+
+   for(size_t i = 0; i != 100; ++i)
+      {
+      m_EK[i] = KS[EK_ORDER[i]];
+      m_DK[i] = KS[DK_ORDER[i]];
+      }
+   }
+
+void MISTY1::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/misty1/misty1.h b/third_party/botan/src/lib/block/misty1/misty1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/misty1/misty1.h
@@ -0,0 +1,37 @@
+/*
+* MISTY1
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MISTY1_H_
+#define BOTAN_MISTY1_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(misty1.h)
+
+namespace Botan {
+
+/**
+* MISTY1 with 8 rounds
+*/
+class BOTAN_PUBLIC_API(2,0) MISTY1 final : public Block_Cipher_Fixed_Params<8, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "MISTY1"; }
+      BlockCipher* clone() const override { return new MISTY1; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint16_t> m_EK, m_DK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/noekeon/noekeon.cpp b/third_party/botan/src/lib/block/noekeon/noekeon.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/noekeon/noekeon.cpp
@@ -0,0 +1,267 @@
+/*
+* Noekeon
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/noekeon.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Noekeon's Theta Operation
+*/
+inline void theta(uint32_t& A0, uint32_t& A1,
+                  uint32_t& A2, uint32_t& A3,
+                  const uint32_t EK[4])
+   {
+   uint32_t T = A0 ^ A2;
+   T ^= rotl<8>(T) ^ rotr<8>(T);
+   A1 ^= T;
+   A3 ^= T;
+
+   A0 ^= EK[0];
+   A1 ^= EK[1];
+   A2 ^= EK[2];
+   A3 ^= EK[3];
+
+   T = A1 ^ A3;
+   T ^= rotl<8>(T) ^ rotr<8>(T);
+   A0 ^= T;
+   A2 ^= T;
+   }
+
+/*
+* Theta With Null Key
+*/
+inline void theta(uint32_t& A0, uint32_t& A1,
+                  uint32_t& A2, uint32_t& A3)
+   {
+   uint32_t T = A0 ^ A2;
+   T ^= rotl<8>(T) ^ rotr<8>(T);
+   A1 ^= T;
+   A3 ^= T;
+
+   T = A1 ^ A3;
+   T ^= rotl<8>(T) ^ rotr<8>(T);
+   A0 ^= T;
+   A2 ^= T;
+   }
+
+/*
+* Noekeon's Gamma S-Box Layer
+*/
+inline void gamma(uint32_t& A0, uint32_t& A1, uint32_t& A2, uint32_t& A3)
+   {
+   A1 ^= ~A3 & ~A2;
+   A0 ^= A2 & A1;
+
+   uint32_t T = A3;
+   A3 = A0;
+   A0 = T;
+
+   A2 ^= A0 ^ A1 ^ A3;
+
+   A1 ^= ~A3 & ~A2;
+   A0 ^= A2 & A1;
+   }
+
+}
+
+size_t Noekeon::parallelism() const
+   {
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      return 4;
+      }
+#endif
+
+   return 1;
+   }
+
+std::string Noekeon::provider() const
+   {
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      return "simd";
+      }
+#endif
+
+   return "base";
+   }
+
+/*
+* Noekeon Round Constants
+*/
+const uint8_t Noekeon::RC[] = {
+   0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
+   0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
+   0xD4 };
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_encrypt_4(in, out);
+         in += 4 * BLOCK_SIZE;
+         out += 4 * BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A0 = load_be<uint32_t>(in, 0);
+      uint32_t A1 = load_be<uint32_t>(in, 1);
+      uint32_t A2 = load_be<uint32_t>(in, 2);
+      uint32_t A3 = load_be<uint32_t>(in, 3);
+
+      for(size_t j = 0; j != 16; ++j)
+         {
+         A0 ^= RC[j];
+         theta(A0, A1, A2, A3, m_EK.data());
+
+         A1 = rotl<1>(A1);
+         A2 = rotl<5>(A2);
+         A3 = rotl<2>(A3);
+
+         gamma(A0, A1, A2, A3);
+
+         A1 = rotr<1>(A1);
+         A2 = rotr<5>(A2);
+         A3 = rotr<2>(A3);
+         }
+
+      A0 ^= RC[16];
+      theta(A0, A1, A2, A3, m_EK.data());
+
+      store_be(out, A0, A1, A2, A3);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_decrypt_4(in, out);
+         in += 4 * BLOCK_SIZE;
+         out += 4 * BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A0 = load_be<uint32_t>(in, 0);
+      uint32_t A1 = load_be<uint32_t>(in, 1);
+      uint32_t A2 = load_be<uint32_t>(in, 2);
+      uint32_t A3 = load_be<uint32_t>(in, 3);
+
+      for(size_t j = 16; j != 0; --j)
+         {
+         theta(A0, A1, A2, A3, m_DK.data());
+         A0 ^= RC[j];
+
+         A1 = rotl<1>(A1);
+         A2 = rotl<5>(A2);
+         A3 = rotl<2>(A3);
+
+         gamma(A0, A1, A2, A3);
+
+         A1 = rotr<1>(A1);
+         A2 = rotr<5>(A2);
+         A3 = rotr<2>(A3);
+         }
+
+      theta(A0, A1, A2, A3, m_DK.data());
+      A0 ^= RC[0];
+
+      store_be(out, A0, A1, A2, A3);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* Noekeon Key Schedule
+*/
+void Noekeon::key_schedule(const uint8_t key[], size_t)
+   {
+   uint32_t A0 = load_be<uint32_t>(key, 0);
+   uint32_t A1 = load_be<uint32_t>(key, 1);
+   uint32_t A2 = load_be<uint32_t>(key, 2);
+   uint32_t A3 = load_be<uint32_t>(key, 3);
+
+   for(size_t i = 0; i != 16; ++i)
+      {
+      A0 ^= RC[i];
+      theta(A0, A1, A2, A3);
+
+      A1 = rotl<1>(A1);
+      A2 = rotl<5>(A2);
+      A3 = rotl<2>(A3);
+
+      gamma(A0, A1, A2, A3);
+
+      A1 = rotr<1>(A1);
+      A2 = rotr<5>(A2);
+      A3 = rotr<2>(A3);
+      }
+
+   A0 ^= RC[16];
+
+   m_DK.resize(4);
+   m_DK[0] = A0;
+   m_DK[1] = A1;
+   m_DK[2] = A2;
+   m_DK[3] = A3;
+
+   theta(A0, A1, A2, A3);
+
+   m_EK.resize(4);
+   m_EK[0] = A0;
+   m_EK[1] = A1;
+   m_EK[2] = A2;
+   m_EK[3] = A3;
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Noekeon::clear()
+   {
+   zap(m_EK);
+   zap(m_DK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/noekeon/noekeon.h b/third_party/botan/src/lib/block/noekeon/noekeon.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/noekeon/noekeon.h
@@ -0,0 +1,49 @@
+/*
+* Noekeon
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NOEKEON_H_
+#define BOTAN_NOEKEON_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(noekeon.h)
+
+namespace Botan {
+
+/**
+* Noekeon
+*/
+class BOTAN_PUBLIC_API(2,0) Noekeon final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      std::string provider() const override;
+      void clear() override;
+      std::string name() const override { return "Noekeon"; }
+      BlockCipher* clone() const override { return new Noekeon; }
+      size_t parallelism() const override;
+
+   private:
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+      void simd_encrypt_4(const uint8_t in[], uint8_t out[]) const;
+      void simd_decrypt_4(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+      /**
+      * The Noekeon round constants
+      */
+      static const uint8_t RC[17];
+
+      void key_schedule(const uint8_t[], size_t) override;
+      secure_vector<uint32_t> m_EK, m_DK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp b/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp
@@ -0,0 +1,143 @@
+/*
+* Noekeon in SIMD
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/noekeon.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+/*
+* Noekeon's Theta Operation
+*/
+#define NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3)  \
+   do {                                                 \
+      SIMD_4x32 T = A0 ^ A2;                            \
+      T ^= T.rotl<8>() ^ T.rotr<8>();                   \
+      A1 ^= T;                                          \
+      A3 ^= T;                                          \
+                                                        \
+      A0 ^= K0;                                         \
+      A1 ^= K1;                                         \
+      A2 ^= K2;                                         \
+      A3 ^= K3;                                         \
+                                                        \
+      T = A1 ^ A3;                                      \
+      T ^= T.rotl<8>() ^ T.rotr<8>();                   \
+      A0 ^= T;                                          \
+      A2 ^= T;                                          \
+      } while(0)
+
+/*
+* Noekeon's Gamma S-Box Layer
+*/
+#define NOK_SIMD_GAMMA(A0, A1, A2, A3)                                  \
+   do                                                                   \
+      {                                                                 \
+      A1 ^= A3.andc(~A2);                                               \
+      A0 ^= A2 & A1;                                                    \
+                                                                        \
+      SIMD_4x32 T = A3;                                                 \
+      A3 = A0;                                                          \
+      A0 = T;                                                           \
+                                                                        \
+      A2 ^= A0 ^ A1 ^ A3;                                               \
+                                                                        \
+      A1 ^= A3.andc(~A2);                                               \
+      A0 ^= A2 & A1;                                                    \
+      } while(0)
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const
+   {
+   const SIMD_4x32 K0 = SIMD_4x32::splat(m_EK[0]);
+   const SIMD_4x32 K1 = SIMD_4x32::splat(m_EK[1]);
+   const SIMD_4x32 K2 = SIMD_4x32::splat(m_EK[2]);
+   const SIMD_4x32 K3 = SIMD_4x32::splat(m_EK[3]);
+
+   SIMD_4x32 A0 = SIMD_4x32::load_be(in     );
+   SIMD_4x32 A1 = SIMD_4x32::load_be(in + 16);
+   SIMD_4x32 A2 = SIMD_4x32::load_be(in + 32);
+   SIMD_4x32 A3 = SIMD_4x32::load_be(in + 48);
+
+   SIMD_4x32::transpose(A0, A1, A2, A3);
+
+   for(size_t i = 0; i != 16; ++i)
+      {
+      A0 ^= SIMD_4x32::splat(RC[i]);
+
+      NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+      A1 = A1.rotl<1>();
+      A2 = A2.rotl<5>();
+      A3 = A3.rotl<2>();
+
+      NOK_SIMD_GAMMA(A0, A1, A2, A3);
+
+      A1 = A1.rotr<1>();
+      A2 = A2.rotr<5>();
+      A3 = A3.rotr<2>();
+      }
+
+   A0 ^= SIMD_4x32::splat(RC[16]);
+   NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+   SIMD_4x32::transpose(A0, A1, A2, A3);
+
+   A0.store_be(out);
+   A1.store_be(out + 16);
+   A2.store_be(out + 32);
+   A3.store_be(out + 48);
+   }
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const
+   {
+   const SIMD_4x32 K0 = SIMD_4x32::splat(m_DK[0]);
+   const SIMD_4x32 K1 = SIMD_4x32::splat(m_DK[1]);
+   const SIMD_4x32 K2 = SIMD_4x32::splat(m_DK[2]);
+   const SIMD_4x32 K3 = SIMD_4x32::splat(m_DK[3]);
+
+   SIMD_4x32 A0 = SIMD_4x32::load_be(in     );
+   SIMD_4x32 A1 = SIMD_4x32::load_be(in + 16);
+   SIMD_4x32 A2 = SIMD_4x32::load_be(in + 32);
+   SIMD_4x32 A3 = SIMD_4x32::load_be(in + 48);
+
+   SIMD_4x32::transpose(A0, A1, A2, A3);
+
+   for(size_t i = 0; i != 16; ++i)
+      {
+      NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+      A0 ^= SIMD_4x32::splat(RC[16-i]);
+
+      A1 = A1.rotl<1>();
+      A2 = A2.rotl<5>();
+      A3 = A3.rotl<2>();
+
+      NOK_SIMD_GAMMA(A0, A1, A2, A3);
+
+      A1 = A1.rotr<1>();
+      A2 = A2.rotr<5>();
+      A3 = A3.rotr<2>();
+      }
+
+   NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+   A0 ^= SIMD_4x32::splat(RC[0]);
+
+   SIMD_4x32::transpose(A0, A1, A2, A3);
+
+   A0.store_be(out);
+   A1.store_be(out + 16);
+   A2.store_be(out + 32);
+   A3.store_be(out + 48);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/seed/seed.cpp b/third_party/botan/src/lib/block/seed/seed.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/seed/seed.cpp
@@ -0,0 +1,328 @@
+/*
+* SEED
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/seed.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+const uint32_t SEED_S0[256] = {
+   0x2989A1A8, 0x05858184, 0x16C6D2D4, 0x13C3D3D0, 0x14445054, 0x1D0D111C,
+   0x2C8CA0AC, 0x25052124, 0x1D4D515C, 0x03434340, 0x18081018, 0x1E0E121C,
+   0x11415150, 0x3CCCF0FC, 0x0ACAC2C8, 0x23436360, 0x28082028, 0x04444044,
+   0x20002020, 0x1D8D919C, 0x20C0E0E0, 0x22C2E2E0, 0x08C8C0C8, 0x17071314,
+   0x2585A1A4, 0x0F8F838C, 0x03030300, 0x3B4B7378, 0x3B8BB3B8, 0x13031310,
+   0x12C2D2D0, 0x2ECEE2EC, 0x30407070, 0x0C8C808C, 0x3F0F333C, 0x2888A0A8,
+   0x32023230, 0x1DCDD1DC, 0x36C6F2F4, 0x34447074, 0x2CCCE0EC, 0x15859194,
+   0x0B0B0308, 0x17475354, 0x1C4C505C, 0x1B4B5358, 0x3D8DB1BC, 0x01010100,
+   0x24042024, 0x1C0C101C, 0x33437370, 0x18889098, 0x10001010, 0x0CCCC0CC,
+   0x32C2F2F0, 0x19C9D1D8, 0x2C0C202C, 0x27C7E3E4, 0x32427270, 0x03838380,
+   0x1B8B9398, 0x11C1D1D0, 0x06868284, 0x09C9C1C8, 0x20406060, 0x10405050,
+   0x2383A3A0, 0x2BCBE3E8, 0x0D0D010C, 0x3686B2B4, 0x1E8E929C, 0x0F4F434C,
+   0x3787B3B4, 0x1A4A5258, 0x06C6C2C4, 0x38487078, 0x2686A2A4, 0x12021210,
+   0x2F8FA3AC, 0x15C5D1D4, 0x21416160, 0x03C3C3C0, 0x3484B0B4, 0x01414140,
+   0x12425250, 0x3D4D717C, 0x0D8D818C, 0x08080008, 0x1F0F131C, 0x19899198,
+   0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37C7F3F4, 0x21C1E1E0,
+   0x3DCDF1FC, 0x36467274, 0x2F0F232C, 0x27072324, 0x3080B0B0, 0x0B8B8388,
+   0x0E0E020C, 0x2B8BA3A8, 0x2282A2A0, 0x2E4E626C, 0x13839390, 0x0D4D414C,
+   0x29496168, 0x3C4C707C, 0x09090108, 0x0A0A0208, 0x3F8FB3BC, 0x2FCFE3EC,
+   0x33C3F3F0, 0x05C5C1C4, 0x07878384, 0x14041014, 0x3ECEF2FC, 0x24446064,
+   0x1ECED2DC, 0x2E0E222C, 0x0B4B4348, 0x1A0A1218, 0x06060204, 0x21012120,
+   0x2B4B6368, 0x26466264, 0x02020200, 0x35C5F1F4, 0x12829290, 0x0A8A8288,
+   0x0C0C000C, 0x3383B3B0, 0x3E4E727C, 0x10C0D0D0, 0x3A4A7278, 0x07474344,
+   0x16869294, 0x25C5E1E4, 0x26062224, 0x00808080, 0x2D8DA1AC, 0x1FCFD3DC,
+   0x2181A1A0, 0x30003030, 0x37073334, 0x2E8EA2AC, 0x36063234, 0x15051114,
+   0x22022220, 0x38083038, 0x34C4F0F4, 0x2787A3A4, 0x05454144, 0x0C4C404C,
+   0x01818180, 0x29C9E1E8, 0x04848084, 0x17879394, 0x35053134, 0x0BCBC3C8,
+   0x0ECEC2CC, 0x3C0C303C, 0x31417170, 0x11011110, 0x07C7C3C4, 0x09898188,
+   0x35457174, 0x3BCBF3F8, 0x1ACAD2D8, 0x38C8F0F8, 0x14849094, 0x19495158,
+   0x02828280, 0x04C4C0C4, 0x3FCFF3FC, 0x09494148, 0x39093138, 0x27476364,
+   0x00C0C0C0, 0x0FCFC3CC, 0x17C7D3D4, 0x3888B0B8, 0x0F0F030C, 0x0E8E828C,
+   0x02424240, 0x23032320, 0x11819190, 0x2C4C606C, 0x1BCBD3D8, 0x2484A0A4,
+   0x34043034, 0x31C1F1F0, 0x08484048, 0x02C2C2C0, 0x2F4F636C, 0x3D0D313C,
+   0x2D0D212C, 0x00404040, 0x3E8EB2BC, 0x3E0E323C, 0x3C8CB0BC, 0x01C1C1C0,
+   0x2A8AA2A8, 0x3A8AB2B8, 0x0E4E424C, 0x15455154, 0x3B0B3338, 0x1CCCD0DC,
+   0x28486068, 0x3F4F737C, 0x1C8C909C, 0x18C8D0D8, 0x0A4A4248, 0x16465254,
+   0x37477374, 0x2080A0A0, 0x2DCDE1EC, 0x06464244, 0x3585B1B4, 0x2B0B2328,
+   0x25456164, 0x3ACAF2F8, 0x23C3E3E0, 0x3989B1B8, 0x3181B1B0, 0x1F8F939C,
+   0x1E4E525C, 0x39C9F1F8, 0x26C6E2E4, 0x3282B2B0, 0x31013130, 0x2ACAE2E8,
+   0x2D4D616C, 0x1F4F535C, 0x24C4E0E4, 0x30C0F0F0, 0x0DCDC1CC, 0x08888088,
+   0x16061214, 0x3A0A3238, 0x18485058, 0x14C4D0D4, 0x22426260, 0x29092128,
+   0x07070304, 0x33033330, 0x28C8E0E8, 0x1B0B1318, 0x05050104, 0x39497178,
+   0x10809090, 0x2A4A6268, 0x2A0A2228, 0x1A8A9298 };
+
+const uint32_t SEED_S1[256] = {
+   0x38380830, 0xE828C8E0, 0x2C2D0D21, 0xA42686A2, 0xCC0FCFC3, 0xDC1ECED2,
+   0xB03383B3, 0xB83888B0, 0xAC2F8FA3, 0x60204060, 0x54154551, 0xC407C7C3,
+   0x44044440, 0x6C2F4F63, 0x682B4B63, 0x581B4B53, 0xC003C3C3, 0x60224262,
+   0x30330333, 0xB43585B1, 0x28290921, 0xA02080A0, 0xE022C2E2, 0xA42787A3,
+   0xD013C3D3, 0x90118191, 0x10110111, 0x04060602, 0x1C1C0C10, 0xBC3C8CB0,
+   0x34360632, 0x480B4B43, 0xEC2FCFE3, 0x88088880, 0x6C2C4C60, 0xA82888A0,
+   0x14170713, 0xC404C4C0, 0x14160612, 0xF434C4F0, 0xC002C2C2, 0x44054541,
+   0xE021C1E1, 0xD416C6D2, 0x3C3F0F33, 0x3C3D0D31, 0x8C0E8E82, 0x98188890,
+   0x28280820, 0x4C0E4E42, 0xF436C6F2, 0x3C3E0E32, 0xA42585A1, 0xF839C9F1,
+   0x0C0D0D01, 0xDC1FCFD3, 0xD818C8D0, 0x282B0B23, 0x64264662, 0x783A4A72,
+   0x24270723, 0x2C2F0F23, 0xF031C1F1, 0x70324272, 0x40024242, 0xD414C4D0,
+   0x40014141, 0xC000C0C0, 0x70334373, 0x64274763, 0xAC2C8CA0, 0x880B8B83,
+   0xF437C7F3, 0xAC2D8DA1, 0x80008080, 0x1C1F0F13, 0xC80ACAC2, 0x2C2C0C20,
+   0xA82A8AA2, 0x34340430, 0xD012C2D2, 0x080B0B03, 0xEC2ECEE2, 0xE829C9E1,
+   0x5C1D4D51, 0x94148490, 0x18180810, 0xF838C8F0, 0x54174753, 0xAC2E8EA2,
+   0x08080800, 0xC405C5C1, 0x10130313, 0xCC0DCDC1, 0x84068682, 0xB83989B1,
+   0xFC3FCFF3, 0x7C3D4D71, 0xC001C1C1, 0x30310131, 0xF435C5F1, 0x880A8A82,
+   0x682A4A62, 0xB03181B1, 0xD011C1D1, 0x20200020, 0xD417C7D3, 0x00020202,
+   0x20220222, 0x04040400, 0x68284860, 0x70314171, 0x04070703, 0xD81BCBD3,
+   0x9C1D8D91, 0x98198991, 0x60214161, 0xBC3E8EB2, 0xE426C6E2, 0x58194951,
+   0xDC1DCDD1, 0x50114151, 0x90108090, 0xDC1CCCD0, 0x981A8A92, 0xA02383A3,
+   0xA82B8BA3, 0xD010C0D0, 0x80018181, 0x0C0F0F03, 0x44074743, 0x181A0A12,
+   0xE023C3E3, 0xEC2CCCE0, 0x8C0D8D81, 0xBC3F8FB3, 0x94168692, 0x783B4B73,
+   0x5C1C4C50, 0xA02282A2, 0xA02181A1, 0x60234363, 0x20230323, 0x4C0D4D41,
+   0xC808C8C0, 0x9C1E8E92, 0x9C1C8C90, 0x383A0A32, 0x0C0C0C00, 0x2C2E0E22,
+   0xB83A8AB2, 0x6C2E4E62, 0x9C1F8F93, 0x581A4A52, 0xF032C2F2, 0x90128292,
+   0xF033C3F3, 0x48094941, 0x78384870, 0xCC0CCCC0, 0x14150511, 0xF83BCBF3,
+   0x70304070, 0x74354571, 0x7C3F4F73, 0x34350531, 0x10100010, 0x00030303,
+   0x64244460, 0x6C2D4D61, 0xC406C6C2, 0x74344470, 0xD415C5D1, 0xB43484B0,
+   0xE82ACAE2, 0x08090901, 0x74364672, 0x18190911, 0xFC3ECEF2, 0x40004040,
+   0x10120212, 0xE020C0E0, 0xBC3D8DB1, 0x04050501, 0xF83ACAF2, 0x00010101,
+   0xF030C0F0, 0x282A0A22, 0x5C1E4E52, 0xA82989A1, 0x54164652, 0x40034343,
+   0x84058581, 0x14140410, 0x88098981, 0x981B8B93, 0xB03080B0, 0xE425C5E1,
+   0x48084840, 0x78394971, 0x94178793, 0xFC3CCCF0, 0x1C1E0E12, 0x80028282,
+   0x20210121, 0x8C0C8C80, 0x181B0B13, 0x5C1F4F53, 0x74374773, 0x54144450,
+   0xB03282B2, 0x1C1D0D11, 0x24250521, 0x4C0F4F43, 0x00000000, 0x44064642,
+   0xEC2DCDE1, 0x58184850, 0x50124252, 0xE82BCBE3, 0x7C3E4E72, 0xD81ACAD2,
+   0xC809C9C1, 0xFC3DCDF1, 0x30300030, 0x94158591, 0x64254561, 0x3C3C0C30,
+   0xB43686B2, 0xE424C4E0, 0xB83B8BB3, 0x7C3C4C70, 0x0C0E0E02, 0x50104050,
+   0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+   0x34370733, 0xE427C7E3, 0x24240420, 0xA42484A0, 0xC80BCBC3, 0x50134353,
+   0x080A0A02, 0x84078783, 0xD819C9D1, 0x4C0C4C40, 0x80038383, 0x8C0F8F83,
+   0xCC0ECEC2, 0x383B0B33, 0x480A4A42, 0xB43787B3 };
+
+const uint32_t SEED_S2[256] = {
+   0xA1A82989, 0x81840585, 0xD2D416C6, 0xD3D013C3, 0x50541444, 0x111C1D0D,
+   0xA0AC2C8C, 0x21242505, 0x515C1D4D, 0x43400343, 0x10181808, 0x121C1E0E,
+   0x51501141, 0xF0FC3CCC, 0xC2C80ACA, 0x63602343, 0x20282808, 0x40440444,
+   0x20202000, 0x919C1D8D, 0xE0E020C0, 0xE2E022C2, 0xC0C808C8, 0x13141707,
+   0xA1A42585, 0x838C0F8F, 0x03000303, 0x73783B4B, 0xB3B83B8B, 0x13101303,
+   0xD2D012C2, 0xE2EC2ECE, 0x70703040, 0x808C0C8C, 0x333C3F0F, 0xA0A82888,
+   0x32303202, 0xD1DC1DCD, 0xF2F436C6, 0x70743444, 0xE0EC2CCC, 0x91941585,
+   0x03080B0B, 0x53541747, 0x505C1C4C, 0x53581B4B, 0xB1BC3D8D, 0x01000101,
+   0x20242404, 0x101C1C0C, 0x73703343, 0x90981888, 0x10101000, 0xC0CC0CCC,
+   0xF2F032C2, 0xD1D819C9, 0x202C2C0C, 0xE3E427C7, 0x72703242, 0x83800383,
+   0x93981B8B, 0xD1D011C1, 0x82840686, 0xC1C809C9, 0x60602040, 0x50501040,
+   0xA3A02383, 0xE3E82BCB, 0x010C0D0D, 0xB2B43686, 0x929C1E8E, 0x434C0F4F,
+   0xB3B43787, 0x52581A4A, 0xC2C406C6, 0x70783848, 0xA2A42686, 0x12101202,
+   0xA3AC2F8F, 0xD1D415C5, 0x61602141, 0xC3C003C3, 0xB0B43484, 0x41400141,
+   0x52501242, 0x717C3D4D, 0x818C0D8D, 0x00080808, 0x131C1F0F, 0x91981989,
+   0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xF3F437C7, 0xE1E021C1,
+   0xF1FC3DCD, 0x72743646, 0x232C2F0F, 0x23242707, 0xB0B03080, 0x83880B8B,
+   0x020C0E0E, 0xA3A82B8B, 0xA2A02282, 0x626C2E4E, 0x93901383, 0x414C0D4D,
+   0x61682949, 0x707C3C4C, 0x01080909, 0x02080A0A, 0xB3BC3F8F, 0xE3EC2FCF,
+   0xF3F033C3, 0xC1C405C5, 0x83840787, 0x10141404, 0xF2FC3ECE, 0x60642444,
+   0xD2DC1ECE, 0x222C2E0E, 0x43480B4B, 0x12181A0A, 0x02040606, 0x21202101,
+   0x63682B4B, 0x62642646, 0x02000202, 0xF1F435C5, 0x92901282, 0x82880A8A,
+   0x000C0C0C, 0xB3B03383, 0x727C3E4E, 0xD0D010C0, 0x72783A4A, 0x43440747,
+   0x92941686, 0xE1E425C5, 0x22242606, 0x80800080, 0xA1AC2D8D, 0xD3DC1FCF,
+   0xA1A02181, 0x30303000, 0x33343707, 0xA2AC2E8E, 0x32343606, 0x11141505,
+   0x22202202, 0x30383808, 0xF0F434C4, 0xA3A42787, 0x41440545, 0x404C0C4C,
+   0x81800181, 0xE1E829C9, 0x80840484, 0x93941787, 0x31343505, 0xC3C80BCB,
+   0xC2CC0ECE, 0x303C3C0C, 0x71703141, 0x11101101, 0xC3C407C7, 0x81880989,
+   0x71743545, 0xF3F83BCB, 0xD2D81ACA, 0xF0F838C8, 0x90941484, 0x51581949,
+   0x82800282, 0xC0C404C4, 0xF3FC3FCF, 0x41480949, 0x31383909, 0x63642747,
+   0xC0C000C0, 0xC3CC0FCF, 0xD3D417C7, 0xB0B83888, 0x030C0F0F, 0x828C0E8E,
+   0x42400242, 0x23202303, 0x91901181, 0x606C2C4C, 0xD3D81BCB, 0xA0A42484,
+   0x30343404, 0xF1F031C1, 0x40480848, 0xC2C002C2, 0x636C2F4F, 0x313C3D0D,
+   0x212C2D0D, 0x40400040, 0xB2BC3E8E, 0x323C3E0E, 0xB0BC3C8C, 0xC1C001C1,
+   0xA2A82A8A, 0xB2B83A8A, 0x424C0E4E, 0x51541545, 0x33383B0B, 0xD0DC1CCC,
+   0x60682848, 0x737C3F4F, 0x909C1C8C, 0xD0D818C8, 0x42480A4A, 0x52541646,
+   0x73743747, 0xA0A02080, 0xE1EC2DCD, 0x42440646, 0xB1B43585, 0x23282B0B,
+   0x61642545, 0xF2F83ACA, 0xE3E023C3, 0xB1B83989, 0xB1B03181, 0x939C1F8F,
+   0x525C1E4E, 0xF1F839C9, 0xE2E426C6, 0xB2B03282, 0x31303101, 0xE2E82ACA,
+   0x616C2D4D, 0x535C1F4F, 0xE0E424C4, 0xF0F030C0, 0xC1CC0DCD, 0x80880888,
+   0x12141606, 0x32383A0A, 0x50581848, 0xD0D414C4, 0x62602242, 0x21282909,
+   0x03040707, 0x33303303, 0xE0E828C8, 0x13181B0B, 0x01040505, 0x71783949,
+   0x90901080, 0x62682A4A, 0x22282A0A, 0x92981A8A };
+
+const uint32_t SEED_S3[256] = {
+   0x08303838, 0xC8E0E828, 0x0D212C2D, 0x86A2A426, 0xCFC3CC0F, 0xCED2DC1E,
+   0x83B3B033, 0x88B0B838, 0x8FA3AC2F, 0x40606020, 0x45515415, 0xC7C3C407,
+   0x44404404, 0x4F636C2F, 0x4B63682B, 0x4B53581B, 0xC3C3C003, 0x42626022,
+   0x03333033, 0x85B1B435, 0x09212829, 0x80A0A020, 0xC2E2E022, 0x87A3A427,
+   0xC3D3D013, 0x81919011, 0x01111011, 0x06020406, 0x0C101C1C, 0x8CB0BC3C,
+   0x06323436, 0x4B43480B, 0xCFE3EC2F, 0x88808808, 0x4C606C2C, 0x88A0A828,
+   0x07131417, 0xC4C0C404, 0x06121416, 0xC4F0F434, 0xC2C2C002, 0x45414405,
+   0xC1E1E021, 0xC6D2D416, 0x0F333C3F, 0x0D313C3D, 0x8E828C0E, 0x88909818,
+   0x08202828, 0x4E424C0E, 0xC6F2F436, 0x0E323C3E, 0x85A1A425, 0xC9F1F839,
+   0x0D010C0D, 0xCFD3DC1F, 0xC8D0D818, 0x0B23282B, 0x46626426, 0x4A72783A,
+   0x07232427, 0x0F232C2F, 0xC1F1F031, 0x42727032, 0x42424002, 0xC4D0D414,
+   0x41414001, 0xC0C0C000, 0x43737033, 0x47636427, 0x8CA0AC2C, 0x8B83880B,
+   0xC7F3F437, 0x8DA1AC2D, 0x80808000, 0x0F131C1F, 0xCAC2C80A, 0x0C202C2C,
+   0x8AA2A82A, 0x04303434, 0xC2D2D012, 0x0B03080B, 0xCEE2EC2E, 0xC9E1E829,
+   0x4D515C1D, 0x84909414, 0x08101818, 0xC8F0F838, 0x47535417, 0x8EA2AC2E,
+   0x08000808, 0xC5C1C405, 0x03131013, 0xCDC1CC0D, 0x86828406, 0x89B1B839,
+   0xCFF3FC3F, 0x4D717C3D, 0xC1C1C001, 0x01313031, 0xC5F1F435, 0x8A82880A,
+   0x4A62682A, 0x81B1B031, 0xC1D1D011, 0x00202020, 0xC7D3D417, 0x02020002,
+   0x02222022, 0x04000404, 0x48606828, 0x41717031, 0x07030407, 0xCBD3D81B,
+   0x8D919C1D, 0x89919819, 0x41616021, 0x8EB2BC3E, 0xC6E2E426, 0x49515819,
+   0xCDD1DC1D, 0x41515011, 0x80909010, 0xCCD0DC1C, 0x8A92981A, 0x83A3A023,
+   0x8BA3A82B, 0xC0D0D010, 0x81818001, 0x0F030C0F, 0x47434407, 0x0A12181A,
+   0xC3E3E023, 0xCCE0EC2C, 0x8D818C0D, 0x8FB3BC3F, 0x86929416, 0x4B73783B,
+   0x4C505C1C, 0x82A2A022, 0x81A1A021, 0x43636023, 0x03232023, 0x4D414C0D,
+   0xC8C0C808, 0x8E929C1E, 0x8C909C1C, 0x0A32383A, 0x0C000C0C, 0x0E222C2E,
+   0x8AB2B83A, 0x4E626C2E, 0x8F939C1F, 0x4A52581A, 0xC2F2F032, 0x82929012,
+   0xC3F3F033, 0x49414809, 0x48707838, 0xCCC0CC0C, 0x05111415, 0xCBF3F83B,
+   0x40707030, 0x45717435, 0x4F737C3F, 0x05313435, 0x00101010, 0x03030003,
+   0x44606424, 0x4D616C2D, 0xC6C2C406, 0x44707434, 0xC5D1D415, 0x84B0B434,
+   0xCAE2E82A, 0x09010809, 0x46727436, 0x09111819, 0xCEF2FC3E, 0x40404000,
+   0x02121012, 0xC0E0E020, 0x8DB1BC3D, 0x05010405, 0xCAF2F83A, 0x01010001,
+   0xC0F0F030, 0x0A22282A, 0x4E525C1E, 0x89A1A829, 0x46525416, 0x43434003,
+   0x85818405, 0x04101414, 0x89818809, 0x8B93981B, 0x80B0B030, 0xC5E1E425,
+   0x48404808, 0x49717839, 0x87939417, 0xCCF0FC3C, 0x0E121C1E, 0x82828002,
+   0x01212021, 0x8C808C0C, 0x0B13181B, 0x4F535C1F, 0x47737437, 0x44505414,
+   0x82B2B032, 0x0D111C1D, 0x05212425, 0x4F434C0F, 0x00000000, 0x46424406,
+   0xCDE1EC2D, 0x48505818, 0x42525012, 0xCBE3E82B, 0x4E727C3E, 0xCAD2D81A,
+   0xC9C1C809, 0xCDF1FC3D, 0x00303030, 0x85919415, 0x45616425, 0x0C303C3C,
+   0x86B2B436, 0xC4E0E424, 0x8BB3B83B, 0x4C707C3C, 0x0E020C0E, 0x40505010,
+   0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+   0x07333437, 0xC7E3E427, 0x04202424, 0x84A0A424, 0xCBC3C80B, 0x43535013,
+   0x0A02080A, 0x87838407, 0xC9D1D819, 0x4C404C0C, 0x83838003, 0x8F838C0F,
+   0xCEC2CC0E, 0x0B33383B, 0x4A42480A, 0x87B3B437 };
+
+/*
+* SEED G Function
+*/
+inline uint32_t SEED_G(uint32_t X)
+   {
+   return (SEED_S0[get_byte(3, X)] ^ SEED_S1[get_byte(2, X)] ^
+           SEED_S2[get_byte(1, X)] ^ SEED_S3[get_byte(0, X)]);
+   }
+
+}
+
+/*
+* SEED Encryption
+*/
+void SEED::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      for(size_t j = 0; j != 16; j += 2)
+         {
+         uint32_t T0, T1;
+
+         T0 = B2 ^ m_K[2*j];
+         T1 = SEED_G(B2 ^ B3 ^ m_K[2*j+1]);
+         T0 = SEED_G(T1 + T0);
+         T1 = SEED_G(T1 + T0);
+         B1 ^= T1;
+         B0 ^= T0 + T1;
+
+         T0 = B0 ^ m_K[2*j+2];
+         T1 = SEED_G(B0 ^ B1 ^ m_K[2*j+3]);
+         T0 = SEED_G(T1 + T0);
+         T1 = SEED_G(T1 + T0);
+         B3 ^= T1;
+         B2 ^= T0 + T1;
+         }
+
+      store_be(out, B2, B3, B0, B1);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* SEED Decryption
+*/
+void SEED::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K.empty() == false);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      for(size_t j = 0; j != 16; j += 2)
+         {
+         uint32_t T0, T1;
+
+         T0 = B2 ^ m_K[30-2*j];
+         T1 = SEED_G(B2 ^ B3 ^ m_K[31-2*j]);
+         T0 = SEED_G(T1 + T0);
+         T1 = SEED_G(T1 + T0);
+         B1 ^= T1;
+         B0 ^= T0 + T1;
+
+         T0 = B0 ^ m_K[28-2*j];
+         T1 = SEED_G(B0 ^ B1 ^ m_K[29-2*j]);
+         T0 = SEED_G(T1 + T0);
+         T1 = SEED_G(T1 + T0);
+         B3 ^= T1;
+         B2 ^= T0 + T1;
+         }
+
+      store_be(out, B2, B3, B0, B1);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* SEED Key Schedule
+*/
+void SEED::key_schedule(const uint8_t key[], size_t)
+   {
+   const uint32_t RC[16] = {
+      0x9E3779B9, 0x3C6EF373, 0x78DDE6E6, 0xF1BBCDCC,
+      0xE3779B99, 0xC6EF3733, 0x8DDE6E67, 0x1BBCDCCF,
+      0x3779B99E, 0x6EF3733C, 0xDDE6E678, 0xBBCDCCF1,
+      0x779B99E3, 0xEF3733C6, 0xDE6E678D, 0xBCDCCF1B
+   };
+
+   secure_vector<uint32_t> WK(4);
+
+   for(size_t i = 0; i != 4; ++i)
+      WK[i] = load_be<uint32_t>(key, i);
+
+   m_K.resize(32);
+
+   for(size_t i = 0; i != 16; i += 2)
+      {
+      m_K[2*i  ] = SEED_G(WK[0] + WK[2] - RC[i]);
+      m_K[2*i+1] = SEED_G(WK[1] - WK[3] + RC[i]) ^ m_K[2*i];
+
+      uint32_t T = (WK[0] & 0xFF) << 24;
+      WK[0] = (WK[0] >> 8) | (get_byte(3, WK[1]) << 24);
+      WK[1] = (WK[1] >> 8) | T;
+
+      m_K[2*i+2] = SEED_G(WK[0] + WK[2] - RC[i+1]);
+      m_K[2*i+3] = SEED_G(WK[1] - WK[3] + RC[i+1]) ^ m_K[2*i+2];
+
+      T = get_byte(0, WK[3]);
+      WK[3] = (WK[3] << 8) | get_byte(0, WK[2]);
+      WK[2] = (WK[2] << 8) | T;
+      }
+   }
+
+void SEED::clear()
+   {
+   zap(m_K);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/seed/seed.h b/third_party/botan/src/lib/block/seed/seed.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/seed/seed.h
@@ -0,0 +1,37 @@
+/*
+* SEED
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SEED_H_
+#define BOTAN_SEED_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(seed.h)
+
+namespace Botan {
+
+/**
+* SEED, a Korean block cipher
+*/
+class BOTAN_PUBLIC_API(2,0) SEED final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "SEED"; }
+      BlockCipher* clone() const override { return new SEED; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint32_t> m_K;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/serpent/serpent.cpp b/third_party/botan/src/lib/block/serpent/serpent.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/serpent/serpent.cpp
@@ -0,0 +1,299 @@
+/*
+* Serpent
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/internal/serpent_sbox.h>
+
+#if defined(BOTAN_HAS_SERPENT_SIMD) || defined(BOTAN_HAS_SERPENT_AVX2)
+  #include <botan/cpuid.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+/*
+* Serpent's Linear Transform
+*/
+inline void transform(uint32_t& B0, uint32_t& B1, uint32_t& B2, uint32_t& B3)
+   {
+   B0  = rotl<13>(B0);   B2  = rotl<3>(B2);
+   B1 ^= B0 ^ B2;        B3 ^= B2 ^ (B0 << 3);
+   B1  = rotl<1>(B1);    B3  = rotl<7>(B3);
+   B0 ^= B1 ^ B3;        B2 ^= B3 ^ (B1 << 7);
+   B0  = rotl<5>(B0);    B2  = rotl<22>(B2);
+   }
+
+/*
+* Serpent's Inverse Linear Transform
+*/
+inline void i_transform(uint32_t& B0, uint32_t& B1, uint32_t& B2, uint32_t& B3)
+   {
+   B2  = rotr<22>(B2);   B0  = rotr<5>(B0);
+   B2 ^= B3 ^ (B1 << 7); B0 ^= B1 ^ B3;
+   B3  = rotr<7>(B3);    B1  = rotr<1>(B1);
+   B3 ^= B2 ^ (B0 << 3); B1 ^= B0 ^ B2;
+   B2  = rotr<3>(B2);    B0  = rotr<13>(B0);
+   }
+
+}
+
+/*
+* XOR a key block with a data block
+*/
+#define key_xor(round, B0, B1, B2, B3) \
+   B0 ^= m_round_key[4*round  ]; \
+   B1 ^= m_round_key[4*round+1]; \
+   B2 ^= m_round_key[4*round+2]; \
+   B3 ^= m_round_key[4*round+3];
+
+/*
+* Serpent Encryption
+*/
+void Serpent::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+   if(CPUID::has_avx2())
+      {
+      while(blocks >= 8)
+         {
+         avx2_encrypt_8(in, out);
+         in += 8 * BLOCK_SIZE;
+         out += 8 * BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_encrypt_4(in, out);
+         in += 4 * BLOCK_SIZE;
+         out += 4 * BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint32_t B0, B1, B2, B3;
+      load_le(in + 16*i, B0, B1, B2, B3);
+
+      key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+      key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+      store_le(out + 16*i, B0, B1, B2, B3);
+      }
+   }
+
+/*
+* Serpent Decryption
+*/
+void Serpent::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_round_key.empty() == false);
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+   if(CPUID::has_avx2())
+      {
+      while(blocks >= 8)
+         {
+         avx2_decrypt_8(in, out);
+         in += 8 * BLOCK_SIZE;
+         out += 8 * BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_decrypt_4(in, out);
+         in += 4 * BLOCK_SIZE;
+         out += 4 * BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint32_t B0, B1, B2, B3;
+      load_le(in + 16*i, B0, B1, B2, B3);
+
+      key_xor(32,B0,B1,B2,B3);  SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+      i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+      store_le(out + 16*i, B0, B1, B2, B3);
+      }
+   }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+/*
+* Serpent Key Schedule
+*/
+void Serpent::key_schedule(const uint8_t key[], size_t length)
+   {
+   const uint32_t PHI = 0x9E3779B9;
+
+   secure_vector<uint32_t> W(140);
+   for(size_t i = 0; i != length / 4; ++i)
+      W[i] = load_le<uint32_t>(key, i);
+
+   W[length / 4] |= uint32_t(1) << ((length%4)*8);
+
+   for(size_t i = 8; i != 140; ++i)
+      {
+      uint32_t wi = W[i-8] ^ W[i-5] ^ W[i-3] ^ W[i-1] ^ PHI ^ uint32_t(i-8);
+      W[i] = rotl<11>(wi);
+      }
+
+   SBoxE0(W[ 20],W[ 21],W[ 22],W[ 23]);
+   SBoxE0(W[ 52],W[ 53],W[ 54],W[ 55]);
+   SBoxE0(W[ 84],W[ 85],W[ 86],W[ 87]);
+   SBoxE0(W[116],W[117],W[118],W[119]);
+
+   SBoxE1(W[ 16],W[ 17],W[ 18],W[ 19]);
+   SBoxE1(W[ 48],W[ 49],W[ 50],W[ 51]);
+   SBoxE1(W[ 80],W[ 81],W[ 82],W[ 83]);
+   SBoxE1(W[112],W[113],W[114],W[115]);
+
+   SBoxE2(W[ 12],W[ 13],W[ 14],W[ 15]);
+   SBoxE2(W[ 44],W[ 45],W[ 46],W[ 47]);
+   SBoxE2(W[ 76],W[ 77],W[ 78],W[ 79]);
+   SBoxE2(W[108],W[109],W[110],W[111]);
+
+   SBoxE3(W[  8],W[  9],W[ 10],W[ 11]);
+   SBoxE3(W[ 40],W[ 41],W[ 42],W[ 43]);
+   SBoxE3(W[ 72],W[ 73],W[ 74],W[ 75]);
+   SBoxE3(W[104],W[105],W[106],W[107]);
+   SBoxE3(W[136],W[137],W[138],W[139]);
+
+   SBoxE4(W[ 36],W[ 37],W[ 38],W[ 39]);
+   SBoxE4(W[ 68],W[ 69],W[ 70],W[ 71]);
+   SBoxE4(W[100],W[101],W[102],W[103]);
+   SBoxE4(W[132],W[133],W[134],W[135]);
+
+   SBoxE5(W[ 32],W[ 33],W[ 34],W[ 35]);
+   SBoxE5(W[ 64],W[ 65],W[ 66],W[ 67]);
+   SBoxE5(W[ 96],W[ 97],W[ 98],W[ 99]);
+   SBoxE5(W[128],W[129],W[130],W[131]);
+
+   SBoxE6(W[ 28],W[ 29],W[ 30],W[ 31]);
+   SBoxE6(W[ 60],W[ 61],W[ 62],W[ 63]);
+   SBoxE6(W[ 92],W[ 93],W[ 94],W[ 95]);
+   SBoxE6(W[124],W[125],W[126],W[127]);
+
+   SBoxE7(W[ 24],W[ 25],W[ 26],W[ 27]);
+   SBoxE7(W[ 56],W[ 57],W[ 58],W[ 59]);
+   SBoxE7(W[ 88],W[ 89],W[ 90],W[ 91]);
+   SBoxE7(W[120],W[121],W[122],W[123]);
+
+   m_round_key.assign(W.begin() + 8, W.end());
+   }
+
+void Serpent::clear()
+   {
+   zap(m_round_key);
+   }
+
+std::string Serpent::provider() const
+   {
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return "avx2";
+      }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      return "simd";
+      }
+#endif
+
+   return "base";
+   }
+
+#undef key_xor
+
+}
diff --git a/third_party/botan/src/lib/block/serpent/serpent.h b/third_party/botan/src/lib/block/serpent/serpent.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/serpent/serpent.h
@@ -0,0 +1,53 @@
+/*
+* Serpent
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SERPENT_H_
+#define BOTAN_SERPENT_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(serpent.h)
+
+namespace Botan {
+
+/**
+* Serpent is the most conservative of the AES finalists
+* https://www.cl.cam.ac.uk/~rja14/serpent.html
+*/
+class BOTAN_PUBLIC_API(2,0) Serpent final : public Block_Cipher_Fixed_Params<16, 16, 32, 8>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string provider() const override;
+      std::string name() const override { return "Serpent"; }
+      BlockCipher* clone() const override { return new Serpent; }
+
+      size_t parallelism() const override { return 4; }
+
+   private:
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+      void simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const;
+      void simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const;
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+      void avx2_encrypt_8(const uint8_t in[64], uint8_t out[64]) const;
+      void avx2_decrypt_8(const uint8_t in[64], uint8_t out[64]) const;
+#endif
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint32_t> m_round_key;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp b/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp
@@ -0,0 +1,169 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/internal/serpent_sbox.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+
+#define key_xor(round, B0, B1, B2, B3)                             \
+   do {                                                            \
+      B0 ^= SIMD_8x32::splat(m_round_key[4*round  ]);              \
+      B1 ^= SIMD_8x32::splat(m_round_key[4*round+1]);              \
+      B2 ^= SIMD_8x32::splat(m_round_key[4*round+2]);              \
+      B3 ^= SIMD_8x32::splat(m_round_key[4*round+3]);              \
+   } while(0)
+
+/*
+* Serpent's linear transformations
+*/
+#define transform(B0, B1, B2, B3)                                  \
+   do {                                                            \
+      B0 = B0.rotl<13>();                                          \
+      B2 = B2.rotl<3>();                                           \
+      B1 ^= B0 ^ B2;                                               \
+      B3 ^= B2 ^ B0.shl<3>();                                      \
+      B1 = B1.rotl<1>();                                           \
+      B3 = B3.rotl<7>();                                           \
+      B0 ^= B1 ^ B3;                                               \
+      B2 ^= B3 ^ B1.shl<7>();                                      \
+      B0 = B0.rotl<5>();                                           \
+      B2 = B2.rotl<22>();                                          \
+   } while(0)
+
+#define i_transform(B0, B1, B2, B3)                                \
+   do {                                                            \
+      B2 = B2.rotr<22>();                                          \
+      B0 = B0.rotr<5>();                                           \
+      B2 ^= B3 ^ B1.shl<7>();                                      \
+      B0 ^= B1 ^ B3;                                               \
+      B3 = B3.rotr<7>();                                           \
+      B1 = B1.rotr<1>();                                           \
+      B3 ^= B2 ^ B0.shl<3>();                                      \
+      B1 ^= B0 ^ B2;                                               \
+      B2 = B2.rotr<3>();                                           \
+      B0 = B0.rotr<13>();                                          \
+   } while(0)
+
+BOTAN_FUNC_ISA("avx2")
+void Serpent::avx2_encrypt_8(const uint8_t in[128], uint8_t out[128]) const
+   {
+   SIMD_8x32::reset_registers();
+
+   SIMD_8x32 B0 = SIMD_8x32::load_le(in);
+   SIMD_8x32 B1 = SIMD_8x32::load_le(in + 32);
+   SIMD_8x32 B2 = SIMD_8x32::load_le(in + 64);
+   SIMD_8x32 B3 = SIMD_8x32::load_le(in + 96);
+
+   SIMD_8x32::transpose(B0, B1, B2, B3);
+
+   key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+   SIMD_8x32::transpose(B0, B1, B2, B3);
+   B0.store_le(out);
+   B1.store_le(out + 32);
+   B2.store_le(out + 64);
+   B3.store_le(out + 96);
+
+   SIMD_8x32::zero_registers();
+   }
+
+BOTAN_FUNC_ISA("avx2")
+void Serpent::avx2_decrypt_8(const uint8_t in[128], uint8_t out[128]) const
+   {
+   SIMD_8x32::reset_registers();
+
+   SIMD_8x32 B0 = SIMD_8x32::load_le(in);
+   SIMD_8x32 B1 = SIMD_8x32::load_le(in + 32);
+   SIMD_8x32 B2 = SIMD_8x32::load_le(in + 64);
+   SIMD_8x32 B3 = SIMD_8x32::load_le(in + 96);
+
+   SIMD_8x32::transpose(B0, B1, B2, B3);
+
+   key_xor(32,B0,B1,B2,B3);  SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+   SIMD_8x32::transpose(B0, B1, B2, B3);
+
+   B0.store_le(out);
+   B1.store_le(out + 32);
+   B2.store_le(out + 64);
+   B3.store_le(out + 96);
+
+   SIMD_8x32::zero_registers();
+   }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+}
diff --git a/third_party/botan/src/lib/block/serpent/serpent_sbox.h b/third_party/botan/src/lib/block/serpent/serpent_sbox.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/serpent/serpent_sbox.h
@@ -0,0 +1,446 @@
+/*
+* Serpent SBox Expressions
+* (C) 1999-2007,2013 Jack Lloyd
+*
+* The sbox expressions used here were discovered by Dag Arne Osvik and
+* are described in his paper "Speeding Up Serpent".
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SERPENT_SBOX_H_
+#define BOTAN_SERPENT_SBOX_H_
+
+#include <botan/build.h>
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE0(T& a, T& b, T& c, T& d)
+   {
+   d ^= a;
+   T t0 = b;
+   b &= d;
+   t0 ^= c;
+   b ^= a;
+   a |= d;
+   a ^= t0;
+   t0 ^= d;
+   d ^= c;
+   c |= b;
+   c ^= t0;
+   t0 = ~t0;
+   t0 |= b;
+   b ^= d;
+   b ^= t0;
+   d |= a;
+   b ^= d;
+   t0 ^= d;
+   d = a;
+   a = b;
+   b = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE1(T& a, T& b, T& c, T& d)
+   {
+   a = ~a;
+   c = ~c;
+   T t0 = a;
+   a &= b;
+   c ^= a;
+   a |= d;
+   d ^= c;
+   b ^= a;
+   a ^= t0;
+   t0 |= b;
+   b ^= d;
+   c |= a;
+   c &= t0;
+   a ^= b;
+   b &= c;
+   b ^= a;
+   a &= c;
+   t0 ^= a;
+   a = c;
+   c = d;
+   d = b;
+   b = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE2(T& a, T& b, T& c, T& d)
+   {
+   T t0 = a;
+   a &= c;
+   a ^= d;
+   c ^= b;
+   c ^= a;
+   d |= t0;
+   d ^= b;
+   t0 ^= c;
+   b = d;
+   d |= t0;
+   d ^= a;
+   a &= b;
+   t0 ^= a;
+   b ^= d;
+   b ^= t0;
+   a = c;
+   c = b;
+   b = d;
+   d = ~t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE3(T& a, T& b, T& c, T& d)
+   {
+   T t0 = a;
+   a |= d;
+   d ^= b;
+   b &= t0;
+   t0 ^= c;
+   c ^= d;
+   d &= a;
+   t0 |= b;
+   d ^= t0;
+   a ^= b;
+   t0 &= a;
+   b ^= d;
+   t0 ^= c;
+   b |= a;
+   b ^= c;
+   a ^= d;
+   c = b;
+   b |= d;
+   a ^= b;
+   b = c;
+   c = d;
+   d = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE4(T& a, T& b, T& c, T& d)
+   {
+   b ^= d;
+   d = ~d;
+   c ^= d;
+   d ^= a;
+   T t0 = b;
+   b &= d;
+   b ^= c;
+   t0 ^= d;
+   a ^= t0;
+   c &= t0;
+   c ^= a;
+   a &= b;
+   d ^= a;
+   t0 |= b;
+   t0 ^= a;
+   a |= d;
+   a ^= c;
+   c &= d;
+   a = ~a;
+   t0 ^= c;
+   c = a;
+   a = b;
+   b = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE5(T& a, T& b, T& c, T& d)
+   {
+   a ^= b;
+   b ^= d;
+   d = ~d;
+   T t0 = b;
+   b &= a;
+   c ^= d;
+   b ^= c;
+   c |= t0;
+   t0 ^= d;
+   d &= b;
+   d ^= a;
+   t0 ^= b;
+   t0 ^= c;
+   c ^= a;
+   a &= d;
+   c = ~c;
+   a ^= t0;
+   t0 |= d;
+   t0 ^= c;
+   c = a;
+   a = b;
+   b = d;
+   d = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE6(T& a, T& b, T& c, T& d)
+   {
+   c = ~c;
+   T t0 = d;
+   d &= a;
+   a ^= t0;
+   d ^= c;
+   c |= t0;
+   b ^= d;
+   c ^= a;
+   a |= b;
+   c ^= b;
+   t0 ^= a;
+   a |= d;
+   a ^= c;
+   t0 ^= d;
+   t0 ^= a;
+   d = ~d;
+   c &= t0;
+   d ^= c;
+   c = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE7(T& a, T& b, T& c, T& d)
+   {
+   T t0 = b;
+   b |= c;
+   b ^= d;
+   t0 ^= c;
+   c ^= b;
+   d |= t0;
+   d &= a;
+   t0 ^= c;
+   d ^= b;
+   b |= t0;
+   b ^= a;
+   a |= t0;
+   a ^= c;
+   b ^= t0;
+   c ^= b;
+   b &= a;
+   b ^= t0;
+   c = ~c;
+   c |= a;
+   t0 ^= c;
+   c = b;
+   b = d;
+   d = a;
+   a = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD0(T& a, T& b, T& c, T& d)
+   {
+   c = ~c;
+   T t0 = b;
+   b |= a;
+   t0 = ~t0;
+   b ^= c;
+   c |= t0;
+   b ^= d;
+   a ^= t0;
+   c ^= a;
+   a &= d;
+   t0 ^= a;
+   a |= b;
+   a ^= c;
+   d ^= t0;
+   c ^= b;
+   d ^= a;
+   d ^= b;
+   c &= d;
+   t0 ^= c;
+   c = b;
+   b = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD1(T& a, T& b, T& c, T& d)
+   {
+   T t0 = b;
+   b ^= d;
+   d &= b;
+   t0 ^= c;
+   d ^= a;
+   a |= b;
+   c ^= d;
+   a ^= t0;
+   a |= c;
+   b ^= d;
+   a ^= b;
+   b |= d;
+   b ^= a;
+   t0 = ~t0;
+   t0 ^= b;
+   b |= a;
+   b ^= a;
+   b |= t0;
+   d ^= b;
+   b = a;
+   a = t0;
+   t0 = c;
+   c = d;
+   d = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD2(T& a, T& b, T& c, T& d)
+   {
+   c ^= d;
+   d ^= a;
+   T t0 = d;
+   d &= c;
+   d ^= b;
+   b |= c;
+   b ^= t0;
+   t0 &= d;
+   c ^= d;
+   t0 &= a;
+   t0 ^= c;
+   c &= b;
+   c |= a;
+   d = ~d;
+   c ^= d;
+   a ^= d;
+   a &= b;
+   d ^= t0;
+   d ^= a;
+   a = b;
+   b = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD3(T& a, T& b, T& c, T& d)
+   {
+   T t0 = c;
+   c ^= b;
+   a ^= c;
+   t0 &= c;
+   t0 ^= a;
+   a &= b;
+   b ^= d;
+   d |= t0;
+   c ^= d;
+   a ^= d;
+   b ^= t0;
+   d &= c;
+   d ^= b;
+   b ^= a;
+   b |= c;
+   a ^= d;
+   b ^= t0;
+   a ^= b;
+   t0 = a;
+   a = c;
+   c = d;
+   d = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD4(T& a, T& b, T& c, T& d)
+   {
+   T t0 = c;
+   c &= d;
+   c ^= b;
+   b |= d;
+   b &= a;
+   t0 ^= c;
+   t0 ^= b;
+   b &= c;
+   a = ~a;
+   d ^= t0;
+   b ^= d;
+   d &= a;
+   d ^= c;
+   a ^= b;
+   c &= a;
+   d ^= a;
+   c ^= t0;
+   c |= d;
+   d ^= a;
+   c ^= b;
+   b = d;
+   d = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD5(T& a, T& b, T& c, T& d)
+   {
+   b = ~b;
+   T t0 = d;
+   c ^= b;
+   d |= a;
+   d ^= c;
+   c |= b;
+   c &= a;
+   t0 ^= d;
+   c ^= t0;
+   t0 |= a;
+   t0 ^= b;
+   b &= c;
+   b ^= d;
+   t0 ^= c;
+   d &= t0;
+   t0 ^= b;
+   d ^= t0;
+   t0 = ~t0;
+   d ^= a;
+   a = b;
+   b = t0;
+   t0 = d;
+   d = c;
+   c = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD6(T& a, T& b, T& c, T& d)
+   {
+   a ^= c;
+   T t0 = c;
+   c &= a;
+   t0 ^= d;
+   c = ~c;
+   d ^= b;
+   c ^= d;
+   t0 |= a;
+   a ^= c;
+   d ^= t0;
+   t0 ^= b;
+   b &= d;
+   b ^= a;
+   a ^= d;
+   a |= c;
+   d ^= b;
+   t0 ^= a;
+   a = b;
+   b = c;
+   c = t0;
+   }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD7(T& a, T& b, T& c, T& d)
+   {
+   T t0 = c;
+   c ^= a;
+   a &= d;
+   t0 |= d;
+   c = ~c;
+   d ^= b;
+   b |= a;
+   a ^= c;
+   c &= t0;
+   d &= t0;
+   b ^= c;
+   c ^= a;
+   a |= c;
+   t0 ^= b;
+   a ^= d;
+   d ^= t0;
+   t0 |= a;
+   d ^= c;
+   t0 ^= c;
+   c = b;
+   b = a;
+   a = d;
+   d = t0;
+   }
+
+#endif
diff --git a/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp b/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp
@@ -0,0 +1,169 @@
+/*
+* Serpent (SIMD)
+* (C) 2009,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/internal/serpent_sbox.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+#define key_xor(round, B0, B1, B2, B3)                             \
+   do {                                                            \
+      B0 ^= SIMD_4x32::splat(m_round_key[4*round  ]);              \
+      B1 ^= SIMD_4x32::splat(m_round_key[4*round+1]);              \
+      B2 ^= SIMD_4x32::splat(m_round_key[4*round+2]);              \
+      B3 ^= SIMD_4x32::splat(m_round_key[4*round+3]);              \
+   } while(0)
+
+/*
+* Serpent's linear transformations
+*/
+#define transform(B0, B1, B2, B3)                                  \
+   do {                                                            \
+      B0 = B0.rotl<13>();                                          \
+      B2 = B2.rotl<3>();                                           \
+      B1 ^= B0 ^ B2;                                               \
+      B3 ^= B2 ^ B0.shl<3>();                                      \
+      B1 = B1.rotl<1>();                                           \
+      B3 = B3.rotl<7>();                                           \
+      B0 ^= B1 ^ B3;                                               \
+      B2 ^= B3 ^ B1.shl<7>();                                      \
+      B0 = B0.rotl<5>();                                           \
+      B2 = B2.rotl<22>();                                          \
+   } while(0)
+
+#define i_transform(B0, B1, B2, B3)                                \
+   do {                                                            \
+      B2 = B2.rotr<22>();                                          \
+      B0 = B0.rotr<5>();                                           \
+      B2 ^= B3 ^ B1.shl<7>();                                      \
+      B0 ^= B1 ^ B3;                                               \
+      B3 = B3.rotr<7>();                                           \
+      B1 = B1.rotr<1>();                                           \
+      B3 ^= B2 ^ B0.shl<3>();                                      \
+      B1 ^= B0 ^ B2;                                               \
+      B2 = B2.rotr<3>();                                           \
+      B0 = B0.rotr<13>();                                          \
+   } while(0)
+
+/*
+* SIMD Serpent Encryption of 4 blocks in parallel
+*/
+void Serpent::simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const
+   {
+   SIMD_4x32 B0 = SIMD_4x32::load_le(in);
+   SIMD_4x32 B1 = SIMD_4x32::load_le(in + 16);
+   SIMD_4x32 B2 = SIMD_4x32::load_le(in + 32);
+   SIMD_4x32 B3 = SIMD_4x32::load_le(in + 48);
+
+   SIMD_4x32::transpose(B0, B1, B2, B3);
+
+   key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+   key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+   key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+   key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+   key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+   SIMD_4x32::transpose(B0, B1, B2, B3);
+
+   B0.store_le(out);
+   B1.store_le(out + 16);
+   B2.store_le(out + 32);
+   B3.store_le(out + 48);
+   }
+
+/*
+* SIMD Serpent Decryption of 4 blocks in parallel
+*/
+void Serpent::simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const
+   {
+   SIMD_4x32 B0 = SIMD_4x32::load_le(in);
+   SIMD_4x32 B1 = SIMD_4x32::load_le(in + 16);
+   SIMD_4x32 B2 = SIMD_4x32::load_le(in + 32);
+   SIMD_4x32 B3 = SIMD_4x32::load_le(in + 48);
+
+   SIMD_4x32::transpose(B0, B1, B2, B3);
+
+   key_xor(32,B0,B1,B2,B3);  SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+
+   i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+   i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+   SIMD_4x32::transpose(B0, B1, B2, B3);
+
+   B0.store_le(out);
+   B1.store_le(out + 16);
+   B2.store_le(out + 32);
+   B3.store_le(out + 48);
+   }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+}
diff --git a/third_party/botan/src/lib/block/shacal2/shacal2.cpp b/third_party/botan/src/lib/block/shacal2/shacal2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/shacal2/shacal2.cpp
@@ -0,0 +1,280 @@
+/*
+* SHACAL-2
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+inline void SHACAL2_Fwd(uint32_t A, uint32_t B, uint32_t C, uint32_t& D,
+                        uint32_t E, uint32_t F, uint32_t G, uint32_t& H,
+                        uint32_t RK)
+   {
+   const uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A);
+   const uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E);
+
+   H += E_rho + ((E & F) ^ (~E & G)) + RK;
+   D += H;
+   H += A_rho + ((A & B) | ((A | B) & C));
+   }
+
+inline void SHACAL2_Rev(uint32_t A, uint32_t B, uint32_t C, uint32_t& D,
+                        uint32_t E, uint32_t F, uint32_t G, uint32_t& H,
+                        uint32_t RK)
+   {
+   const uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A);
+   const uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E);
+
+   H -= A_rho + ((A & B) | ((A | B) & C));
+   D -= H;
+   H -= E_rho + ((E & F) ^ (~E & G)) + RK;
+   }
+
+}
+
+/*
+* SHACAL2 Encryption
+*/
+void SHACAL2::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SHACAL2_X86)
+   if(CPUID::has_intel_sha())
+      {
+      return x86_encrypt_blocks(in, out, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+   if(CPUID::has_avx2())
+      {
+      while(blocks >= 8)
+         {
+         avx2_encrypt_8(in, out);
+         in += 8*BLOCK_SIZE;
+         out += 8*BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_encrypt_4(in, out);
+         in += 4*BLOCK_SIZE;
+         out += 4*BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A = load_be<uint32_t>(in, 0);
+      uint32_t B = load_be<uint32_t>(in, 1);
+      uint32_t C = load_be<uint32_t>(in, 2);
+      uint32_t D = load_be<uint32_t>(in, 3);
+      uint32_t E = load_be<uint32_t>(in, 4);
+      uint32_t F = load_be<uint32_t>(in, 5);
+      uint32_t G = load_be<uint32_t>(in, 6);
+      uint32_t H = load_be<uint32_t>(in, 7);
+
+      for(size_t r = 0; r != 64; r += 8)
+         {
+         SHACAL2_Fwd(A, B, C, D, E, F, G, H, m_RK[r+0]);
+         SHACAL2_Fwd(H, A, B, C, D, E, F, G, m_RK[r+1]);
+         SHACAL2_Fwd(G, H, A, B, C, D, E, F, m_RK[r+2]);
+         SHACAL2_Fwd(F, G, H, A, B, C, D, E, m_RK[r+3]);
+         SHACAL2_Fwd(E, F, G, H, A, B, C, D, m_RK[r+4]);
+         SHACAL2_Fwd(D, E, F, G, H, A, B, C, m_RK[r+5]);
+         SHACAL2_Fwd(C, D, E, F, G, H, A, B, m_RK[r+6]);
+         SHACAL2_Fwd(B, C, D, E, F, G, H, A, m_RK[r+7]);
+         }
+
+      store_be(out, A, B, C, D, E, F, G, H);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* SHACAL2 Encryption
+*/
+void SHACAL2::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+   if(CPUID::has_avx2())
+      {
+      while(blocks >= 8)
+         {
+         avx2_decrypt_8(in, out);
+         in += 8*BLOCK_SIZE;
+         out += 8*BLOCK_SIZE;
+         blocks -= 8;
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      while(blocks >= 4)
+         {
+         simd_decrypt_4(in, out);
+         in += 4*BLOCK_SIZE;
+         out += 4*BLOCK_SIZE;
+         blocks -= 4;
+         }
+      }
+#endif
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t A = load_be<uint32_t>(in, 0);
+      uint32_t B = load_be<uint32_t>(in, 1);
+      uint32_t C = load_be<uint32_t>(in, 2);
+      uint32_t D = load_be<uint32_t>(in, 3);
+      uint32_t E = load_be<uint32_t>(in, 4);
+      uint32_t F = load_be<uint32_t>(in, 5);
+      uint32_t G = load_be<uint32_t>(in, 6);
+      uint32_t H = load_be<uint32_t>(in, 7);
+
+      for(size_t r = 0; r != 64; r += 8)
+         {
+         SHACAL2_Rev(B, C, D, E, F, G, H, A, m_RK[63-r]);
+         SHACAL2_Rev(C, D, E, F, G, H, A, B, m_RK[62-r]);
+         SHACAL2_Rev(D, E, F, G, H, A, B, C, m_RK[61-r]);
+         SHACAL2_Rev(E, F, G, H, A, B, C, D, m_RK[60-r]);
+         SHACAL2_Rev(F, G, H, A, B, C, D, E, m_RK[59-r]);
+         SHACAL2_Rev(G, H, A, B, C, D, E, F, m_RK[58-r]);
+         SHACAL2_Rev(H, A, B, C, D, E, F, G, m_RK[57-r]);
+         SHACAL2_Rev(A, B, C, D, E, F, G, H, m_RK[56-r]);
+         }
+
+      store_be(out, A, B, C, D, E, F, G, H);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* SHACAL2 Key Schedule
+*/
+void SHACAL2::key_schedule(const uint8_t key[], size_t len)
+   {
+   const uint32_t RC[64] = {
+      0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+      0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+      0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+      0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+      0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+      0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+      0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+      0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+      0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+      0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+      0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+      0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+      0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+      0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+      0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+      0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+   };
+
+   if(m_RK.empty())
+      m_RK.resize(64);
+   else
+      clear_mem(m_RK.data(), m_RK.size());
+
+   load_be(m_RK.data(), key, len/4);
+
+   for(size_t i = 16; i != 64; ++i)
+      {
+      const uint32_t sigma0_15 = rotr< 7>(m_RK[i-15]) ^ rotr<18>(m_RK[i-15]) ^ (m_RK[i-15] >> 3);
+      const uint32_t sigma1_2  = rotr<17>(m_RK[i- 2]) ^ rotr<19>(m_RK[i- 2]) ^ (m_RK[i- 2] >> 10);
+      m_RK[i] = m_RK[i-16] + sigma0_15 + m_RK[i-7] + sigma1_2;
+      }
+
+   for(size_t i = 0; i != 64; ++i)
+      {
+      m_RK[i] += RC[i];
+      }
+   }
+
+size_t SHACAL2::parallelism() const
+   {
+#if defined(BOTAN_HAS_SHACAL2_X86)
+   if(CPUID::has_intel_sha())
+      {
+      return 4;
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return 8;
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      return 4;
+      }
+#endif
+
+   return 1;
+   }
+
+std::string SHACAL2::provider() const
+   {
+#if defined(BOTAN_HAS_SHACAL2_X86)
+   if(CPUID::has_intel_sha())
+      {
+      return "intel_sha";
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return "avx2";
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+   if(CPUID::has_simd_32())
+      {
+      return "simd";
+      }
+#endif
+
+   return "base";
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHACAL2::clear()
+   {
+   zap(m_RK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/shacal2/shacal2.h b/third_party/botan/src/lib/block/shacal2/shacal2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/shacal2/shacal2.h
@@ -0,0 +1,54 @@
+/*
+* SHACAL-2
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHACAL2_H_
+#define BOTAN_SHACAL2_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shacal2.h)
+
+namespace Botan {
+
+/**
+* SHACAL2
+*/
+class BOTAN_PUBLIC_API(2,3) SHACAL2 final : public Block_Cipher_Fixed_Params<32, 16, 64, 4>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      std::string provider() const override;
+      void clear() override;
+      std::string name() const override { return "SHACAL2"; }
+      BlockCipher* clone() const override { return new SHACAL2; }
+      size_t parallelism() const override;
+
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+      void simd_encrypt_4(const uint8_t in[], uint8_t out[]) const;
+      void simd_decrypt_4(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+      void avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const;
+      void avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_X86)
+      void x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      secure_vector<uint32_t> m_RK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp b/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp
@@ -0,0 +1,122 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+namespace {
+
+void BOTAN_FORCE_INLINE BOTAN_FUNC_ISA("avx2")
+   SHACAL2_Fwd(const SIMD_8x32& A, const SIMD_8x32& B, const SIMD_8x32& C, SIMD_8x32& D,
+               const SIMD_8x32& E, const SIMD_8x32& F, const SIMD_8x32& G, SIMD_8x32& H,
+               uint32_t RK)
+   {
+   H += E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_8x32::splat(RK);
+   D += H;
+   H += A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+   }
+
+void BOTAN_FORCE_INLINE BOTAN_FUNC_ISA("avx2")
+   SHACAL2_Rev(const SIMD_8x32& A, const SIMD_8x32& B, const SIMD_8x32& C, SIMD_8x32& D,
+               const SIMD_8x32& E, const SIMD_8x32& F, const SIMD_8x32& G, SIMD_8x32& H,
+               uint32_t RK)
+   {
+   H -= A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+   D -= H;
+   H -= E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_8x32::splat(RK);
+   }
+
+}
+
+void BOTAN_FUNC_ISA("avx2") SHACAL2::avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const
+   {
+   SIMD_8x32::reset_registers();
+
+   SIMD_8x32 A = SIMD_8x32::load_be(in);
+   SIMD_8x32 B = SIMD_8x32::load_be(in+32);
+   SIMD_8x32 C = SIMD_8x32::load_be(in+64);
+   SIMD_8x32 D = SIMD_8x32::load_be(in+96);
+
+   SIMD_8x32 E = SIMD_8x32::load_be(in+128);
+   SIMD_8x32 F = SIMD_8x32::load_be(in+160);
+   SIMD_8x32 G = SIMD_8x32::load_be(in+192);
+   SIMD_8x32 H = SIMD_8x32::load_be(in+224);
+
+   SIMD_8x32::transpose(A, B, C, D, E, F, G, H);
+
+   for(size_t r = 0; r != 64; r += 8)
+      {
+      SHACAL2_Fwd(A, B, C, D, E, F, G, H, m_RK[r+0]);
+      SHACAL2_Fwd(H, A, B, C, D, E, F, G, m_RK[r+1]);
+      SHACAL2_Fwd(G, H, A, B, C, D, E, F, m_RK[r+2]);
+      SHACAL2_Fwd(F, G, H, A, B, C, D, E, m_RK[r+3]);
+      SHACAL2_Fwd(E, F, G, H, A, B, C, D, m_RK[r+4]);
+      SHACAL2_Fwd(D, E, F, G, H, A, B, C, m_RK[r+5]);
+      SHACAL2_Fwd(C, D, E, F, G, H, A, B, m_RK[r+6]);
+      SHACAL2_Fwd(B, C, D, E, F, G, H, A, m_RK[r+7]);
+      }
+
+   SIMD_8x32::transpose(A, B, C, D, E, F, G, H);
+
+   A.store_be(out);
+   B.store_be(out+32);
+   C.store_be(out+64);
+   D.store_be(out+96);
+
+   E.store_be(out+128);
+   F.store_be(out+160);
+   G.store_be(out+192);
+   H.store_be(out+224);
+
+   SIMD_8x32::zero_registers();
+   }
+
+BOTAN_FUNC_ISA("avx2") void SHACAL2::avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const
+   {
+   SIMD_8x32::reset_registers();
+
+   SIMD_8x32 A = SIMD_8x32::load_be(in);
+   SIMD_8x32 B = SIMD_8x32::load_be(in+32);
+   SIMD_8x32 C = SIMD_8x32::load_be(in+64);
+   SIMD_8x32 D = SIMD_8x32::load_be(in+96);
+
+   SIMD_8x32 E = SIMD_8x32::load_be(in+128);
+   SIMD_8x32 F = SIMD_8x32::load_be(in+160);
+   SIMD_8x32 G = SIMD_8x32::load_be(in+192);
+   SIMD_8x32 H = SIMD_8x32::load_be(in+224);
+
+   SIMD_8x32::transpose(A, B, C, D, E, F, G, H);
+
+   for(size_t r = 0; r != 64; r += 8)
+      {
+      SHACAL2_Rev(B, C, D, E, F, G, H, A, m_RK[63-r]);
+      SHACAL2_Rev(C, D, E, F, G, H, A, B, m_RK[62-r]);
+      SHACAL2_Rev(D, E, F, G, H, A, B, C, m_RK[61-r]);
+      SHACAL2_Rev(E, F, G, H, A, B, C, D, m_RK[60-r]);
+      SHACAL2_Rev(F, G, H, A, B, C, D, E, m_RK[59-r]);
+      SHACAL2_Rev(G, H, A, B, C, D, E, F, m_RK[58-r]);
+      SHACAL2_Rev(H, A, B, C, D, E, F, G, m_RK[57-r]);
+      SHACAL2_Rev(A, B, C, D, E, F, G, H, m_RK[56-r]);
+      }
+
+   SIMD_8x32::transpose(A, B, C, D, E, F, G, H);
+
+   A.store_be(out);
+   B.store_be(out+32);
+   C.store_be(out+64);
+   D.store_be(out+96);
+
+   E.store_be(out+128);
+   F.store_be(out+160);
+   G.store_be(out+192);
+   H.store_be(out+224);
+
+   SIMD_8x32::zero_registers();
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp b/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp
@@ -0,0 +1,119 @@
+/*
+* SHACAL-2 using SIMD
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+namespace {
+
+inline
+void SHACAL2_Fwd(const SIMD_4x32& A, const SIMD_4x32& B, const SIMD_4x32& C, SIMD_4x32& D,
+                 const SIMD_4x32& E, const SIMD_4x32& F, const SIMD_4x32& G, SIMD_4x32& H,
+                 uint32_t RK)
+   {
+   H += E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_4x32::splat(RK);
+   D += H;
+   H += A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+   }
+
+inline
+void SHACAL2_Rev(const SIMD_4x32& A, const SIMD_4x32& B, const SIMD_4x32& C, SIMD_4x32& D,
+                 const SIMD_4x32& E, const SIMD_4x32& F, const SIMD_4x32& G, SIMD_4x32& H,
+                 uint32_t RK)
+   {
+   H -= A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+   D -= H;
+   H -= E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_4x32::splat(RK);
+   }
+
+}
+
+void SHACAL2::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const
+   {
+   SIMD_4x32 A = SIMD_4x32::load_be(in);
+   SIMD_4x32 E = SIMD_4x32::load_be(in+16);
+   SIMD_4x32 B = SIMD_4x32::load_be(in+32);
+   SIMD_4x32 F = SIMD_4x32::load_be(in+48);
+
+   SIMD_4x32 C = SIMD_4x32::load_be(in+64);
+   SIMD_4x32 G = SIMD_4x32::load_be(in+80);
+   SIMD_4x32 D = SIMD_4x32::load_be(in+96);
+   SIMD_4x32 H = SIMD_4x32::load_be(in+112);
+
+   SIMD_4x32::transpose(A, B, C, D);
+   SIMD_4x32::transpose(E, F, G, H);
+
+   for(size_t r = 0; r != 64; r += 8)
+      {
+      SHACAL2_Fwd(A, B, C, D, E, F, G, H, m_RK[r+0]);
+      SHACAL2_Fwd(H, A, B, C, D, E, F, G, m_RK[r+1]);
+      SHACAL2_Fwd(G, H, A, B, C, D, E, F, m_RK[r+2]);
+      SHACAL2_Fwd(F, G, H, A, B, C, D, E, m_RK[r+3]);
+      SHACAL2_Fwd(E, F, G, H, A, B, C, D, m_RK[r+4]);
+      SHACAL2_Fwd(D, E, F, G, H, A, B, C, m_RK[r+5]);
+      SHACAL2_Fwd(C, D, E, F, G, H, A, B, m_RK[r+6]);
+      SHACAL2_Fwd(B, C, D, E, F, G, H, A, m_RK[r+7]);
+      }
+
+   SIMD_4x32::transpose(A, B, C, D);
+   SIMD_4x32::transpose(E, F, G, H);
+
+   A.store_be(out);
+   E.store_be(out+16);
+   B.store_be(out+32);
+   F.store_be(out+48);
+
+   C.store_be(out+64);
+   G.store_be(out+80);
+   D.store_be(out+96);
+   H.store_be(out+112);
+   }
+
+void SHACAL2::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const
+   {
+   SIMD_4x32 A = SIMD_4x32::load_be(in);
+   SIMD_4x32 E = SIMD_4x32::load_be(in+16);
+   SIMD_4x32 B = SIMD_4x32::load_be(in+32);
+   SIMD_4x32 F = SIMD_4x32::load_be(in+48);
+
+   SIMD_4x32 C = SIMD_4x32::load_be(in+64);
+   SIMD_4x32 G = SIMD_4x32::load_be(in+80);
+   SIMD_4x32 D = SIMD_4x32::load_be(in+96);
+   SIMD_4x32 H = SIMD_4x32::load_be(in+112);
+
+   SIMD_4x32::transpose(A, B, C, D);
+   SIMD_4x32::transpose(E, F, G, H);
+
+   for(size_t r = 0; r != 64; r += 8)
+      {
+      SHACAL2_Rev(B, C, D, E, F, G, H, A, m_RK[63-r]);
+      SHACAL2_Rev(C, D, E, F, G, H, A, B, m_RK[62-r]);
+      SHACAL2_Rev(D, E, F, G, H, A, B, C, m_RK[61-r]);
+      SHACAL2_Rev(E, F, G, H, A, B, C, D, m_RK[60-r]);
+      SHACAL2_Rev(F, G, H, A, B, C, D, E, m_RK[59-r]);
+      SHACAL2_Rev(G, H, A, B, C, D, E, F, m_RK[58-r]);
+      SHACAL2_Rev(H, A, B, C, D, E, F, G, m_RK[57-r]);
+      SHACAL2_Rev(A, B, C, D, E, F, G, H, m_RK[56-r]);
+      }
+
+   SIMD_4x32::transpose(A, B, C, D);
+   SIMD_4x32::transpose(E, F, G, H);
+
+   A.store_be(out);
+   E.store_be(out+16);
+   B.store_be(out+32);
+   F.store_be(out+48);
+
+   C.store_be(out+64);
+   G.store_be(out+80);
+   D.store_be(out+96);
+   H.store_be(out+112);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp b/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp
@@ -0,0 +1,118 @@
+/*
+* SHACAL-2 using x86 SHA extensions
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+/*
+Only encryption is supported since the inverse round function would
+require a different instruction
+*/
+
+BOTAN_FUNC_ISA("sha,ssse3")
+void SHACAL2::x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   const __m128i MASK1 = _mm_set_epi8(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7);
+   const __m128i MASK2 = _mm_set_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+
+   const __m128i* RK_mm = reinterpret_cast<const __m128i*>(m_RK.data());
+   const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+   __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+   while(blocks >= 2)
+      {
+      __m128i B0_0 = _mm_loadu_si128(in_mm);
+      __m128i B0_1 = _mm_loadu_si128(in_mm+1);
+      __m128i B1_0 = _mm_loadu_si128(in_mm+2);
+      __m128i B1_1 = _mm_loadu_si128(in_mm+3);
+
+      __m128i TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0_0, B0_1), MASK2);
+      B0_1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0_0, B0_1), MASK2);
+      B0_0 = TMP;
+
+      TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B1_0, B1_1), MASK2);
+      B1_1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B1_0, B1_1), MASK2);
+      B1_0 = TMP;
+
+      for(size_t i = 0; i != 8; ++i)
+         {
+         const __m128i RK0 = _mm_loadu_si128(RK_mm + 2*i);
+         const __m128i RK2 = _mm_loadu_si128(RK_mm + 2*i+1);
+         const __m128i RK1 = _mm_srli_si128(RK0, 8);
+         const __m128i RK3 = _mm_srli_si128(RK2, 8);
+
+         B0_1 = _mm_sha256rnds2_epu32(B0_1, B0_0, RK0);
+         B1_1 = _mm_sha256rnds2_epu32(B1_1, B1_0, RK0);
+
+         B0_0 = _mm_sha256rnds2_epu32(B0_0, B0_1, RK1);
+         B1_0 = _mm_sha256rnds2_epu32(B1_0, B1_1, RK1);
+
+         B0_1 = _mm_sha256rnds2_epu32(B0_1, B0_0, RK2);
+         B1_1 = _mm_sha256rnds2_epu32(B1_1, B1_0, RK2);
+
+         B0_0 = _mm_sha256rnds2_epu32(B0_0, B0_1, RK3);
+         B1_0 = _mm_sha256rnds2_epu32(B1_0, B1_1, RK3);
+         }
+
+      TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0_0, B0_1), MASK1);
+      B0_1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0_0, B0_1), MASK1);
+      B0_0 = TMP;
+
+      TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B1_0, B1_1), MASK1);
+      B1_1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B1_0, B1_1), MASK1);
+      B1_0 = TMP;
+
+      // Save state
+      _mm_storeu_si128(out_mm + 0, B0_0);
+      _mm_storeu_si128(out_mm + 1, B0_1);
+      _mm_storeu_si128(out_mm + 2, B1_0);
+      _mm_storeu_si128(out_mm + 3, B1_1);
+
+      blocks -= 2;
+      in_mm += 4;
+      out_mm += 4;
+      }
+
+   while(blocks)
+      {
+      __m128i B0 = _mm_loadu_si128(in_mm);
+      __m128i B1 = _mm_loadu_si128(in_mm+1);
+
+      __m128i TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0, B1), MASK2);
+      B1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0, B1), MASK2);
+      B0 = TMP;
+
+      for(size_t i = 0; i != 8; ++i)
+         {
+         const __m128i RK0 = _mm_loadu_si128(RK_mm + 2*i);
+         const __m128i RK2 = _mm_loadu_si128(RK_mm + 2*i+1);
+         const __m128i RK1 = _mm_srli_si128(RK0, 8);
+         const __m128i RK3 = _mm_srli_si128(RK2, 8);
+
+         B1 = _mm_sha256rnds2_epu32(B1, B0, RK0);
+         B0 = _mm_sha256rnds2_epu32(B0, B1, RK1);
+         B1 = _mm_sha256rnds2_epu32(B1, B0, RK2);
+         B0 = _mm_sha256rnds2_epu32(B0, B1, RK3);
+         }
+
+      TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0, B1), MASK1);
+      B1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0, B1), MASK1);
+      B0 = TMP;
+
+      // Save state
+      _mm_storeu_si128(out_mm, B0);
+      _mm_storeu_si128(out_mm + 1, B1);
+
+      blocks--;
+      in_mm += 2;
+      out_mm += 2;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/sm4/sm4.cpp b/third_party/botan/src/lib/block/sm4/sm4.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/sm4/sm4.cpp
@@ -0,0 +1,341 @@
+/*
+* SM4
+* (C) 2017 Ribose Inc
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm4.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64)
+const uint8_t SM4_SBOX[256] = {
+0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
+0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
+0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
+0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
+0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
+0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
+0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
+0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
+0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
+0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
+0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
+0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
+0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
+0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
+0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
+0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
+};
+
+/*
+* SM4_SBOX_T[j] == L(SM4_SBOX[j]).
+*/
+alignas(64)
+const uint32_t SM4_SBOX_T[256] = {
+   0x8ED55B5B, 0xD0924242, 0x4DEAA7A7, 0x06FDFBFB, 0xFCCF3333, 0x65E28787,
+   0xC93DF4F4, 0x6BB5DEDE, 0x4E165858, 0x6EB4DADA, 0x44145050, 0xCAC10B0B,
+   0x8828A0A0, 0x17F8EFEF, 0x9C2CB0B0, 0x11051414, 0x872BACAC, 0xFB669D9D,
+   0xF2986A6A, 0xAE77D9D9, 0x822AA8A8, 0x46BCFAFA, 0x14041010, 0xCFC00F0F,
+   0x02A8AAAA, 0x54451111, 0x5F134C4C, 0xBE269898, 0x6D482525, 0x9E841A1A,
+   0x1E061818, 0xFD9B6666, 0xEC9E7272, 0x4A430909, 0x10514141, 0x24F7D3D3,
+   0xD5934646, 0x53ECBFBF, 0xF89A6262, 0x927BE9E9, 0xFF33CCCC, 0x04555151,
+   0x270B2C2C, 0x4F420D0D, 0x59EEB7B7, 0xF3CC3F3F, 0x1CAEB2B2, 0xEA638989,
+   0x74E79393, 0x7FB1CECE, 0x6C1C7070, 0x0DABA6A6, 0xEDCA2727, 0x28082020,
+   0x48EBA3A3, 0xC1975656, 0x80820202, 0xA3DC7F7F, 0xC4965252, 0x12F9EBEB,
+   0xA174D5D5, 0xB38D3E3E, 0xC33FFCFC, 0x3EA49A9A, 0x5B461D1D, 0x1B071C1C,
+   0x3BA59E9E, 0x0CFFF3F3, 0x3FF0CFCF, 0xBF72CDCD, 0x4B175C5C, 0x52B8EAEA,
+   0x8F810E0E, 0x3D586565, 0xCC3CF0F0, 0x7D196464, 0x7EE59B9B, 0x91871616,
+   0x734E3D3D, 0x08AAA2A2, 0xC869A1A1, 0xC76AADAD, 0x85830606, 0x7AB0CACA,
+   0xB570C5C5, 0xF4659191, 0xB2D96B6B, 0xA7892E2E, 0x18FBE3E3, 0x47E8AFAF,
+   0x330F3C3C, 0x674A2D2D, 0xB071C1C1, 0x0E575959, 0xE99F7676, 0xE135D4D4,
+   0x661E7878, 0xB4249090, 0x360E3838, 0x265F7979, 0xEF628D8D, 0x38596161,
+   0x95D24747, 0x2AA08A8A, 0xB1259494, 0xAA228888, 0x8C7DF1F1, 0xD73BECEC,
+   0x05010404, 0xA5218484, 0x9879E1E1, 0x9B851E1E, 0x84D75353, 0x00000000,
+   0x5E471919, 0x0B565D5D, 0xE39D7E7E, 0x9FD04F4F, 0xBB279C9C, 0x1A534949,
+   0x7C4D3131, 0xEE36D8D8, 0x0A020808, 0x7BE49F9F, 0x20A28282, 0xD4C71313,
+   0xE8CB2323, 0xE69C7A7A, 0x42E9ABAB, 0x43BDFEFE, 0xA2882A2A, 0x9AD14B4B,
+   0x40410101, 0xDBC41F1F, 0xD838E0E0, 0x61B7D6D6, 0x2FA18E8E, 0x2BF4DFDF,
+   0x3AF1CBCB, 0xF6CD3B3B, 0x1DFAE7E7, 0xE5608585, 0x41155454, 0x25A38686,
+   0x60E38383, 0x16ACBABA, 0x295C7575, 0x34A69292, 0xF7996E6E, 0xE434D0D0,
+   0x721A6868, 0x01545555, 0x19AFB6B6, 0xDF914E4E, 0xFA32C8C8, 0xF030C0C0,
+   0x21F6D7D7, 0xBC8E3232, 0x75B3C6C6, 0x6FE08F8F, 0x691D7474, 0x2EF5DBDB,
+   0x6AE18B8B, 0x962EB8B8, 0x8A800A0A, 0xFE679999, 0xE2C92B2B, 0xE0618181,
+   0xC0C30303, 0x8D29A4A4, 0xAF238C8C, 0x07A9AEAE, 0x390D3434, 0x1F524D4D,
+   0x764F3939, 0xD36EBDBD, 0x81D65757, 0xB7D86F6F, 0xEB37DCDC, 0x51441515,
+   0xA6DD7B7B, 0x09FEF7F7, 0xB68C3A3A, 0x932FBCBC, 0x0F030C0C, 0x03FCFFFF,
+   0xC26BA9A9, 0xBA73C9C9, 0xD96CB5B5, 0xDC6DB1B1, 0x375A6D6D, 0x15504545,
+   0xB98F3636, 0x771B6C6C, 0x13ADBEBE, 0xDA904A4A, 0x57B9EEEE, 0xA9DE7777,
+   0x4CBEF2F2, 0x837EFDFD, 0x55114444, 0xBDDA6767, 0x2C5D7171, 0x45400505,
+   0x631F7C7C, 0x50104040, 0x325B6969, 0xB8DB6363, 0x220A2828, 0xC5C20707,
+   0xF531C4C4, 0xA88A2222, 0x31A79696, 0xF9CE3737, 0x977AEDED, 0x49BFF6F6,
+   0x992DB4B4, 0xA475D1D1, 0x90D34343, 0x5A124848, 0x58BAE2E2, 0x71E69797,
+   0x64B6D2D2, 0x70B2C2C2, 0xAD8B2626, 0xCD68A5A5, 0xCB955E5E, 0x624B2929,
+   0x3C0C3030, 0xCE945A5A, 0xAB76DDDD, 0x867FF9F9, 0xF1649595, 0x5DBBE6E6,
+   0x35F2C7C7, 0x2D092424, 0xD1C61717, 0xD66FB9B9, 0xDEC51B1B, 0x94861212,
+   0x78186060, 0x30F3C3C3, 0x897CF5F5, 0x5CEFB3B3, 0xD23AE8E8, 0xACDF7373,
+   0x794C3535, 0xA0208080, 0x9D78E5E5, 0x56EDBBBB, 0x235E7D7D, 0xC63EF8F8,
+   0x8BD45F5F, 0xE7C82F2F, 0xDD39E4E4, 0x68492121 };
+
+inline uint32_t SM4_T_slow(uint32_t b)
+   {
+   const uint32_t t = make_uint32(SM4_SBOX[get_byte(0,b)],
+                                  SM4_SBOX[get_byte(1,b)],
+                                  SM4_SBOX[get_byte(2,b)],
+                                  SM4_SBOX[get_byte(3,b)]);
+
+   // L linear transform
+   return t ^ rotl<2>(t) ^ rotl<10>(t) ^ rotl<18>(t) ^ rotl<24>(t);
+   }
+
+inline uint32_t SM4_T(uint32_t b)
+   {
+   return          SM4_SBOX_T[get_byte(0,b)]  ^
+          rotr< 8>(SM4_SBOX_T[get_byte(1,b)]) ^
+          rotr<16>(SM4_SBOX_T[get_byte(2,b)]) ^
+          rotr<24>(SM4_SBOX_T[get_byte(3,b)]);
+   }
+
+// Variant of T for key schedule
+inline uint32_t SM4_Tp(uint32_t b)
+   {
+   const uint32_t t = make_uint32(SM4_SBOX[get_byte(0,b)],
+                                  SM4_SBOX[get_byte(1,b)],
+                                  SM4_SBOX[get_byte(2,b)],
+                                  SM4_SBOX[get_byte(3,b)]);
+
+   // L' linear transform
+   return t ^ rotl<13>(t) ^ rotl<23>(t);
+   }
+
+#define SM4_E_RNDS(B, R, F) do {                           \
+   B##0 ^= F(B##1 ^ B##2 ^ B##3 ^ m_RK[4*R+0]);            \
+   B##1 ^= F(B##2 ^ B##3 ^ B##0 ^ m_RK[4*R+1]);            \
+   B##2 ^= F(B##3 ^ B##0 ^ B##1 ^ m_RK[4*R+2]);            \
+   B##3 ^= F(B##0 ^ B##1 ^ B##2 ^ m_RK[4*R+3]);            \
+   } while(0)
+
+#define SM4_D_RNDS(B, R, F) do {                           \
+   B##0 ^= F(B##1 ^ B##2 ^ B##3 ^ m_RK[4*R+3]);            \
+   B##1 ^= F(B##2 ^ B##3 ^ B##0 ^ m_RK[4*R+2]);            \
+   B##2 ^= F(B##3 ^ B##0 ^ B##1 ^ m_RK[4*R+1]);            \
+   B##3 ^= F(B##0 ^ B##1 ^ B##2 ^ m_RK[4*R+0]);            \
+   } while(0)
+
+}
+
+/*
+* SM4 Encryption
+*/
+void SM4::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+   if(CPUID::has_arm_sm4())
+      return sm4_armv8_encrypt(in, out, blocks);
+#endif
+
+   while(blocks >= 2)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      uint32_t C0 = load_be<uint32_t>(in, 4);
+      uint32_t C1 = load_be<uint32_t>(in, 5);
+      uint32_t C2 = load_be<uint32_t>(in, 6);
+      uint32_t C3 = load_be<uint32_t>(in, 7);
+
+      SM4_E_RNDS(B, 0, SM4_T_slow);
+      SM4_E_RNDS(C, 0, SM4_T_slow);
+      SM4_E_RNDS(B, 1, SM4_T);
+      SM4_E_RNDS(C, 1, SM4_T);
+      SM4_E_RNDS(B, 2, SM4_T);
+      SM4_E_RNDS(C, 2, SM4_T);
+      SM4_E_RNDS(B, 3, SM4_T);
+      SM4_E_RNDS(C, 3, SM4_T);
+      SM4_E_RNDS(B, 4, SM4_T);
+      SM4_E_RNDS(C, 4, SM4_T);
+      SM4_E_RNDS(B, 5, SM4_T);
+      SM4_E_RNDS(C, 5, SM4_T);
+      SM4_E_RNDS(B, 6, SM4_T);
+      SM4_E_RNDS(C, 6, SM4_T);
+      SM4_E_RNDS(B, 7, SM4_T_slow);
+      SM4_E_RNDS(C, 7, SM4_T_slow);
+
+      store_be(out, B3, B2, B1, B0, C3, C2, C1, C0);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      SM4_E_RNDS(B, 0, SM4_T_slow);
+      SM4_E_RNDS(B, 1, SM4_T);
+      SM4_E_RNDS(B, 2, SM4_T);
+      SM4_E_RNDS(B, 3, SM4_T);
+      SM4_E_RNDS(B, 4, SM4_T);
+      SM4_E_RNDS(B, 5, SM4_T);
+      SM4_E_RNDS(B, 6, SM4_T);
+      SM4_E_RNDS(B, 7, SM4_T_slow);
+
+      store_be(out, B3, B2, B1, B0);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+/*
+* SM4 Decryption
+*/
+void SM4::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+   if(CPUID::has_arm_sm4())
+      return sm4_armv8_decrypt(in, out, blocks);
+#endif
+
+   while(blocks >= 2)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      uint32_t C0 = load_be<uint32_t>(in, 4);
+      uint32_t C1 = load_be<uint32_t>(in, 5);
+      uint32_t C2 = load_be<uint32_t>(in, 6);
+      uint32_t C3 = load_be<uint32_t>(in, 7);
+
+      SM4_D_RNDS(B, 7, SM4_T_slow);
+      SM4_D_RNDS(C, 7, SM4_T_slow);
+      SM4_D_RNDS(B, 6, SM4_T);
+      SM4_D_RNDS(C, 6, SM4_T);
+      SM4_D_RNDS(B, 5, SM4_T);
+      SM4_D_RNDS(C, 5, SM4_T);
+      SM4_D_RNDS(B, 4, SM4_T);
+      SM4_D_RNDS(C, 4, SM4_T);
+      SM4_D_RNDS(B, 3, SM4_T);
+      SM4_D_RNDS(C, 3, SM4_T);
+      SM4_D_RNDS(B, 2, SM4_T);
+      SM4_D_RNDS(C, 2, SM4_T);
+      SM4_D_RNDS(B, 1, SM4_T);
+      SM4_D_RNDS(C, 1, SM4_T);
+      SM4_D_RNDS(B, 0, SM4_T_slow);
+      SM4_D_RNDS(C, 0, SM4_T_slow);
+
+      store_be(out, B3, B2, B1, B0, C3, C2, C1, C0);
+
+      in += 2*BLOCK_SIZE;
+      out += 2*BLOCK_SIZE;
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t B0 = load_be<uint32_t>(in, 0);
+      uint32_t B1 = load_be<uint32_t>(in, 1);
+      uint32_t B2 = load_be<uint32_t>(in, 2);
+      uint32_t B3 = load_be<uint32_t>(in, 3);
+
+      SM4_D_RNDS(B, 7, SM4_T_slow);
+      SM4_D_RNDS(B, 6, SM4_T);
+      SM4_D_RNDS(B, 5, SM4_T);
+      SM4_D_RNDS(B, 4, SM4_T);
+      SM4_D_RNDS(B, 3, SM4_T);
+      SM4_D_RNDS(B, 2, SM4_T);
+      SM4_D_RNDS(B, 1, SM4_T);
+      SM4_D_RNDS(B, 0, SM4_T_slow);
+
+      store_be(out, B3, B2, B1, B0);
+
+      in += BLOCK_SIZE;
+      out += BLOCK_SIZE;
+      }
+   }
+
+#undef SM4_E_RNDS
+#undef SM4_D_RNDS
+
+/*
+* SM4 Key Schedule
+*/
+void SM4::key_schedule(const uint8_t key[], size_t)
+   {
+   // System parameter or family key
+   const uint32_t FK[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
+
+   const uint32_t CK[32] = {
+      0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
+      0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
+      0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
+      0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
+      0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
+      0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
+      0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
+      0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
+   };
+
+   secure_vector<uint32_t> K(4);
+   K[0] = load_be<uint32_t>(key, 0) ^ FK[0];
+   K[1] = load_be<uint32_t>(key, 1) ^ FK[1];
+   K[2] = load_be<uint32_t>(key, 2) ^ FK[2];
+   K[3] = load_be<uint32_t>(key, 3) ^ FK[3];
+
+   m_RK.resize(32);
+   for(size_t i = 0; i != 32; ++i)
+      {
+      K[i % 4] ^= SM4_Tp(K[(i+1)%4] ^ K[(i+2)%4] ^ K[(i+3)%4] ^ CK[i]);
+      m_RK[i] = K[i % 4];
+      }
+   }
+
+void SM4::clear()
+   {
+   zap(m_RK);
+   }
+
+size_t SM4::parallelism() const
+   {
+#if defined(BOTAN_HAS_SM4_ARMV8)
+   if(CPUID::has_arm_sm4())
+      {
+      return 4;
+      }
+#endif
+
+   return 1;
+   }
+
+std::string SM4::provider() const
+   {
+#if defined(BOTAN_HAS_SM4_ARMV8)
+   if(CPUID::has_arm_sm4())
+      {
+      return "armv8";
+      }
+#endif
+
+   return "base";
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/sm4/sm4.h b/third_party/botan/src/lib/block/sm4/sm4.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/sm4/sm4.h
@@ -0,0 +1,45 @@
+/*
+* SM4
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM4_H_
+#define BOTAN_SM4_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sm4.h)
+
+namespace Botan {
+
+/**
+* SM4
+*/
+class BOTAN_PUBLIC_API(2,2) SM4 final : public Block_Cipher_Fixed_Params<16, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "SM4"; }
+      BlockCipher* clone() const override { return new SM4; }
+
+      std::string provider() const override;
+      size_t parallelism() const override;
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+      void sm4_armv8_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void sm4_armv8_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      secure_vector<uint32_t> m_RK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp b/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp
@@ -0,0 +1,174 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm4.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+namespace {
+
+static const uint8_t qswap_tbl[16] = {
+   12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3
+};
+
+static const uint8_t bswap_tbl[16] = {
+   15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+};
+
+inline uint32x4_t qswap_32(uint32x4_t B)
+   {
+   return vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(B), vld1q_u8(qswap_tbl)));
+   }
+
+inline uint32x4_t bswap_32(uint32x4_t B)
+   {
+   return vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(B)));
+   }
+
+/*
+ Swap both the quad-words and bytes within each word
+ equivalent to return bswap_32(qswap_32(B))
+*/
+inline uint32x4_t bqswap_32(uint32x4_t B)
+   {
+   return vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(B), vld1q_u8(bswap_tbl)));
+   }
+
+#define SM4_E(B0, B1, B2, B3, K) do { \
+   B0 = vsm4eq_u32(B0, K);            \
+   B1 = vsm4eq_u32(B1, K);            \
+   B2 = vsm4eq_u32(B2, K);            \
+   B3 = vsm4eq_u32(B3, K);            \
+   } while(0)
+
+}
+
+void BOTAN_FUNC_ISA("arch=armv8.2-a+sm4")
+SM4::sm4_armv8_encrypt(const uint8_t input8[], uint8_t output8[], size_t blocks) const
+   {
+   const uint32x4_t K0 = vld1q_u32(&m_RK[ 0]);
+   const uint32x4_t K1 = vld1q_u32(&m_RK[ 4]);
+   const uint32x4_t K2 = vld1q_u32(&m_RK[ 8]);
+   const uint32x4_t K3 = vld1q_u32(&m_RK[12]);
+   const uint32x4_t K4 = vld1q_u32(&m_RK[16]);
+   const uint32x4_t K5 = vld1q_u32(&m_RK[20]);
+   const uint32x4_t K6 = vld1q_u32(&m_RK[24]);
+   const uint32x4_t K7 = vld1q_u32(&m_RK[28]);
+
+   const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+   uint32_t* output32 = reinterpret_cast<uint32_t*>(reinterpret_cast<void*>(output8));
+
+   while(blocks >= 4)
+      {
+      uint32x4_t B0 = bswap_32(vld1q_u32(input32));
+      uint32x4_t B1 = bswap_32(vld1q_u32(input32+4));
+      uint32x4_t B2 = bswap_32(vld1q_u32(input32+8));
+      uint32x4_t B3 = bswap_32(vld1q_u32(input32+12));
+
+      SM4_E(B0, B1, B2, B3, K0);
+      SM4_E(B0, B1, B2, B3, K1);
+      SM4_E(B0, B1, B2, B3, K2);
+      SM4_E(B0, B1, B2, B3, K3);
+      SM4_E(B0, B1, B2, B3, K4);
+      SM4_E(B0, B1, B2, B3, K5);
+      SM4_E(B0, B1, B2, B3, K6);
+      SM4_E(B0, B1, B2, B3, K7);
+
+      vst1q_u32(output32   , bqswap_32(B0));
+      vst1q_u32(output32+ 4, bqswap_32(B1));
+      vst1q_u32(output32+ 8, bqswap_32(B2));
+      vst1q_u32(output32+12, bqswap_32(B3));
+
+      input32 += 4*4;
+      output32 += 4*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32x4_t B = bswap_32(vld1q_u32(input32));
+
+      B = vsm4eq_u32(B, K0);
+      B = vsm4eq_u32(B, K1);
+      B = vsm4eq_u32(B, K2);
+      B = vsm4eq_u32(B, K3);
+      B = vsm4eq_u32(B, K4);
+      B = vsm4eq_u32(B, K5);
+      B = vsm4eq_u32(B, K6);
+      B = vsm4eq_u32(B, K7);
+
+      vst1q_u32(output32, bqswap_32(B));
+
+      input32 += 4;
+      output32 += 4;
+      }
+   }
+
+void BOTAN_FUNC_ISA("arch=armv8.2-a+sm4")
+SM4::sm4_armv8_decrypt(const uint8_t input8[], uint8_t output8[], size_t blocks) const
+   {
+   const uint32x4_t K0 = qswap_32(vld1q_u32(&m_RK[ 0]));
+   const uint32x4_t K1 = qswap_32(vld1q_u32(&m_RK[ 4]));
+   const uint32x4_t K2 = qswap_32(vld1q_u32(&m_RK[ 8]));
+   const uint32x4_t K3 = qswap_32(vld1q_u32(&m_RK[12]));
+   const uint32x4_t K4 = qswap_32(vld1q_u32(&m_RK[16]));
+   const uint32x4_t K5 = qswap_32(vld1q_u32(&m_RK[20]));
+   const uint32x4_t K6 = qswap_32(vld1q_u32(&m_RK[24]));
+   const uint32x4_t K7 = qswap_32(vld1q_u32(&m_RK[28]));
+
+   const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+   uint32_t* output32 = reinterpret_cast<uint32_t*>(reinterpret_cast<void*>(output8));
+
+   while(blocks >= 4)
+      {
+      uint32x4_t B0 = bswap_32(vld1q_u32(input32));
+      uint32x4_t B1 = bswap_32(vld1q_u32(input32+4));
+      uint32x4_t B2 = bswap_32(vld1q_u32(input32+8));
+      uint32x4_t B3 = bswap_32(vld1q_u32(input32+12));
+
+      SM4_E(B0, B1, B2, B3, K7);
+      SM4_E(B0, B1, B2, B3, K6);
+      SM4_E(B0, B1, B2, B3, K5);
+      SM4_E(B0, B1, B2, B3, K4);
+      SM4_E(B0, B1, B2, B3, K3);
+      SM4_E(B0, B1, B2, B3, K2);
+      SM4_E(B0, B1, B2, B3, K1);
+      SM4_E(B0, B1, B2, B3, K0);
+
+      vst1q_u32(output32   , bqswap_32(B0));
+      vst1q_u32(output32+ 4, bqswap_32(B1));
+      vst1q_u32(output32+ 8, bqswap_32(B2));
+      vst1q_u32(output32+12, bqswap_32(B3));
+
+      input32 += 4*4;
+      output32 += 4*4;
+      blocks -= 4;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32x4_t B = bswap_32(vld1q_u32(input32));
+
+      B = vsm4eq_u32(B, K7);
+      B = vsm4eq_u32(B, K6);
+      B = vsm4eq_u32(B, K5);
+      B = vsm4eq_u32(B, K4);
+      B = vsm4eq_u32(B, K3);
+      B = vsm4eq_u32(B, K2);
+      B = vsm4eq_u32(B, K1);
+      B = vsm4eq_u32(B, K0);
+
+      vst1q_u32(output32, bqswap_32(B));
+
+      input32 += 4;
+      output32 += 4;
+      }
+   }
+
+#undef SM4_E
+
+}
diff --git a/third_party/botan/src/lib/block/threefish_512/threefish.h b/third_party/botan/src/lib/block/threefish_512/threefish.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/threefish_512/threefish.h
@@ -0,0 +1,17 @@
+/*
+* Threefish
+* (C) 2013,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREEFISH_H_
+#define BOTAN_THREEFISH_H_
+
+// This header is deprecated and will be removed in a future major release
+
+#include <botan/threefish_512.h>
+
+BOTAN_DEPRECATED_HEADER(threefish.h)
+
+#endif
diff --git a/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp b/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp
@@ -0,0 +1,273 @@
+/*
+* Threefish-512
+* (C) 2013,2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/threefish_512.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+#define THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7,ROT1,ROT2,ROT3,ROT4) \
+   do {                                                              \
+      X0 += X4;                                                      \
+      X1 += X5;                                                      \
+      X2 += X6;                                                      \
+      X3 += X7;                                                      \
+      X4 = rotl<ROT1>(X4);                                           \
+      X5 = rotl<ROT2>(X5);                                           \
+      X6 = rotl<ROT3>(X6);                                           \
+      X7 = rotl<ROT4>(X7);                                           \
+      X4 ^= X0;                                                      \
+      X5 ^= X1;                                                      \
+      X6 ^= X2;                                                      \
+      X7 ^= X3;                                                      \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY(r)              \
+   do {                                      \
+      X0 += m_K[(r  ) % 9];                  \
+      X1 += m_K[(r+1) % 9];                  \
+      X2 += m_K[(r+2) % 9];                  \
+      X3 += m_K[(r+3) % 9];                  \
+      X4 += m_K[(r+4) % 9];                  \
+      X5 += m_K[(r+5) % 9] + m_T[(r  ) % 3]; \
+      X6 += m_K[(r+6) % 9] + m_T[(r+1) % 3]; \
+      X7 += m_K[(r+7) % 9] + (r);            \
+   } while(0)
+
+#define THREEFISH_ENC_8_ROUNDS(R1,R2)                         \
+   do {                                                       \
+      THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 46,36,19,37); \
+      THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 33,27,14,42); \
+      THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 17,49,36,39); \
+      THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 44, 9,54,56); \
+      THREEFISH_INJECT_KEY(R1);                               \
+                                                              \
+      THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 39,30,34,24); \
+      THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 13,50,10,17); \
+      THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 25,29,39,43); \
+      THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3,  8,35,56,22); \
+      THREEFISH_INJECT_KEY(R2);                               \
+   } while(0)
+
+void Threefish_512::skein_feedfwd(const secure_vector<uint64_t>& M,
+                                  const secure_vector<uint64_t>& T)
+   {
+   BOTAN_ASSERT(m_K.size() == 9, "Key was set");
+   BOTAN_ASSERT(M.size() == 8, "Single block");
+
+   m_T[0] = T[0];
+   m_T[1] = T[1];
+   m_T[2] = T[0] ^ T[1];
+
+   uint64_t X0 = M[0];
+   uint64_t X1 = M[1];
+   uint64_t X2 = M[2];
+   uint64_t X3 = M[3];
+   uint64_t X4 = M[4];
+   uint64_t X5 = M[5];
+   uint64_t X6 = M[6];
+   uint64_t X7 = M[7];
+
+   THREEFISH_INJECT_KEY(0);
+
+   THREEFISH_ENC_8_ROUNDS(1,2);
+   THREEFISH_ENC_8_ROUNDS(3,4);
+   THREEFISH_ENC_8_ROUNDS(5,6);
+   THREEFISH_ENC_8_ROUNDS(7,8);
+   THREEFISH_ENC_8_ROUNDS(9,10);
+   THREEFISH_ENC_8_ROUNDS(11,12);
+   THREEFISH_ENC_8_ROUNDS(13,14);
+   THREEFISH_ENC_8_ROUNDS(15,16);
+   THREEFISH_ENC_8_ROUNDS(17,18);
+
+   m_K[0] = M[0] ^ X0;
+   m_K[1] = M[1] ^ X1;
+   m_K[2] = M[2] ^ X2;
+   m_K[3] = M[3] ^ X3;
+   m_K[4] = M[4] ^ X4;
+   m_K[5] = M[5] ^ X5;
+   m_K[6] = M[6] ^ X6;
+   m_K[7] = M[7] ^ X7;
+
+   m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^
+            m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22;
+   }
+
+size_t Threefish_512::parallelism() const
+   {
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return 2;
+      }
+#endif
+
+   return 1;
+   }
+
+std::string Threefish_512::provider() const
+   {
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return "avx2";
+      }
+#endif
+
+   return "base";
+   }
+
+void Threefish_512::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K.empty() == false);
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return avx2_encrypt_n(in, out, blocks);
+      }
+#endif
+
+   BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint64_t X0, X1, X2, X3, X4, X5, X6, X7;
+      load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+
+      THREEFISH_INJECT_KEY(0);
+
+      THREEFISH_ENC_8_ROUNDS(1,2);
+      THREEFISH_ENC_8_ROUNDS(3,4);
+      THREEFISH_ENC_8_ROUNDS(5,6);
+      THREEFISH_ENC_8_ROUNDS(7,8);
+      THREEFISH_ENC_8_ROUNDS(9,10);
+      THREEFISH_ENC_8_ROUNDS(11,12);
+      THREEFISH_ENC_8_ROUNDS(13,14);
+      THREEFISH_ENC_8_ROUNDS(15,16);
+      THREEFISH_ENC_8_ROUNDS(17,18);
+
+      store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+      }
+   }
+
+#undef THREEFISH_ENC_8_ROUNDS
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_ROUND
+
+void Threefish_512::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_K.empty() == false);
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return avx2_decrypt_n(in, out, blocks);
+      }
+#endif
+
+#define THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7,ROT1,ROT2,ROT3,ROT4) \
+   do {                                                              \
+      X4 ^= X0;                                                      \
+      X5 ^= X1;                                                      \
+      X6 ^= X2;                                                      \
+      X7 ^= X3;                                                      \
+      X4 = rotr<ROT1>(X4);                                           \
+      X5 = rotr<ROT2>(X5);                                           \
+      X6 = rotr<ROT3>(X6);                                           \
+      X7 = rotr<ROT4>(X7);                                           \
+      X0 -= X4;                                                      \
+      X1 -= X5;                                                      \
+      X2 -= X6;                                                      \
+      X3 -= X7;                                                      \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY(r)              \
+   do {                                      \
+      X0 -= m_K[(r  ) % 9];                  \
+      X1 -= m_K[(r+1) % 9];                  \
+      X2 -= m_K[(r+2) % 9];                  \
+      X3 -= m_K[(r+3) % 9];                  \
+      X4 -= m_K[(r+4) % 9];                  \
+      X5 -= m_K[(r+5) % 9] + m_T[(r  ) % 3]; \
+      X6 -= m_K[(r+6) % 9] + m_T[(r+1) % 3]; \
+      X7 -= m_K[(r+7) % 9] + (r);            \
+   } while(0)
+
+#define THREEFISH_DEC_8_ROUNDS(R1,R2)                         \
+   do {                                                       \
+      THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3,  8,35,56,22); \
+      THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 25,29,39,43); \
+      THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 13,50,10,17); \
+      THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 39,30,34,24); \
+      THREEFISH_INJECT_KEY(R1);                               \
+                                                              \
+      THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 44, 9,54,56); \
+      THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 17,49,36,39); \
+      THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 33,27,14,42); \
+      THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 46,36,19,37); \
+      THREEFISH_INJECT_KEY(R2);                               \
+   } while(0)
+
+   BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+      {
+      uint64_t X0, X1, X2, X3, X4, X5, X6, X7;
+      load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+
+      THREEFISH_INJECT_KEY(18);
+
+      THREEFISH_DEC_8_ROUNDS(17,16);
+      THREEFISH_DEC_8_ROUNDS(15,14);
+      THREEFISH_DEC_8_ROUNDS(13,12);
+      THREEFISH_DEC_8_ROUNDS(11,10);
+      THREEFISH_DEC_8_ROUNDS(9,8);
+      THREEFISH_DEC_8_ROUNDS(7,6);
+      THREEFISH_DEC_8_ROUNDS(5,4);
+      THREEFISH_DEC_8_ROUNDS(3,2);
+      THREEFISH_DEC_8_ROUNDS(1,0);
+
+      store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+      }
+
+#undef THREEFISH_DEC_8_ROUNDS
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_ROUND
+   }
+
+void Threefish_512::set_tweak(const uint8_t tweak[], size_t len)
+   {
+   BOTAN_ARG_CHECK(len == 16, "Threefish-512 requires 128 bit tweak");
+
+   m_T.resize(3);
+   m_T[0] = load_le<uint64_t>(tweak, 0);
+   m_T[1] = load_le<uint64_t>(tweak, 1);
+   m_T[2] = m_T[0] ^ m_T[1];
+   }
+
+void Threefish_512::key_schedule(const uint8_t key[], size_t)
+   {
+   // todo: define key schedule for smaller keys
+   m_K.resize(9);
+
+   for(size_t i = 0; i != 8; ++i)
+      m_K[i] = load_le<uint64_t>(key, i);
+
+   m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^
+            m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22;
+
+   // Reset tweak to all zeros on key reset
+   m_T.resize(3);
+   zeroise(m_T);
+   }
+
+void Threefish_512::clear()
+   {
+   zap(m_K);
+   zap(m_T);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/threefish_512/threefish_512.h b/third_party/botan/src/lib/block/threefish_512/threefish_512.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/threefish_512/threefish_512.h
@@ -0,0 +1,57 @@
+/*
+* Threefish-512
+* (C) 2013,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREEFISH_512_H_
+#define BOTAN_THREEFISH_512_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(threefish_512.h)
+
+namespace Botan {
+
+/**
+* Threefish-512
+*/
+class BOTAN_PUBLIC_API(2,0) Threefish_512 final :
+   public Block_Cipher_Fixed_Params<64, 64, 0, 1, Tweakable_Block_Cipher>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void set_tweak(const uint8_t tweak[], size_t len) override;
+
+      void clear() override;
+      std::string provider() const override;
+      std::string name() const override { return "Threefish-512"; }
+      BlockCipher* clone() const override { return new Threefish_512; }
+      size_t parallelism() const override;
+
+   private:
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+      void avx2_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+      void avx2_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      // Interface for Skein
+      friend class Skein_512;
+
+      void skein_feedfwd(const secure_vector<uint64_t>& M,
+                         const secure_vector<uint64_t>& T);
+
+      // Private data
+      secure_vector<uint64_t> m_T;
+      secure_vector<uint64_t> m_K;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp b/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp
@@ -0,0 +1,444 @@
+/*
+* Threefish-512 using AVX2
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/threefish_512.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("avx2")
+inline void interleave_epi64(__m256i& X0, __m256i& X1)
+   {
+   // interleave X0 and X1 qwords
+   // (X0,X1,X2,X3),(X4,X5,X6,X7) -> (X0,X2,X4,X6),(X1,X3,X5,X7)
+
+   const __m256i T0 = _mm256_unpacklo_epi64(X0, X1);
+   const __m256i T1 = _mm256_unpackhi_epi64(X0, X1);
+
+   X0 = _mm256_permute4x64_epi64(T0, _MM_SHUFFLE(3,1,2,0));
+   X1 = _mm256_permute4x64_epi64(T1, _MM_SHUFFLE(3,1,2,0));
+   }
+
+BOTAN_FUNC_ISA("avx2")
+inline void deinterleave_epi64(__m256i& X0, __m256i& X1)
+   {
+   const __m256i T0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(3,1,2,0));
+   const __m256i T1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(3,1,2,0));
+
+   X0 = _mm256_unpacklo_epi64(T0, T1);
+   X1 = _mm256_unpackhi_epi64(T0, T1);
+   }
+
+BOTAN_FUNC_ISA("avx2")
+inline void rotate_keys(__m256i& R0, __m256i& R1, __m256i R2)
+   {
+   /*
+   Behold. The key schedule progresses like so. The values
+   loop back to the originals after the rounds are complete
+   so we don't need to reload for starting the next block.
+
+                 R0        R1        R2
+     K1,K2,K3 (7,5,3,1),(8,6,4,2),(0,7,5,3)
+     K3,K4,K5 (0,7,5,3),(1,8,6,4),(2,0,7,5)
+     K5,K6,K7 (2,0,7,5),(3,1,8,6),(4,2,0,7)
+
+     K7,K8,K0 (4,2,0,7),(5,3,1,8),(6,4,2,0)
+     K0,K1,K2 (6,4,2,0),(7,5,3,1),(8,6,4,2)
+     K2,K3,K4 (8,6,4,2),(0,7,5,3),(1,8,6,4)
+
+     K4,K5,K6 (1,8,6,4),(2,0,7,5),(3,1,8,6)
+     K6,K7,K8 (3,1,8,6),(4,2,0,7),(5,3,1,8)
+     K8,K0,K1 (5,3,1,8),(6,4,2,0),(7,5,3,1)
+
+   To compute the values for the next round:
+     X0 is X2 from the last round
+     X1 becomes (X0[4],X1[1:3])
+     X2 becomes (X1[4],X2[1:3])
+
+   Uses 3 permutes and 2 blends, is there a faster way?
+   */
+   __m256i T0 = _mm256_permute4x64_epi64(R0, _MM_SHUFFLE(0,0,0,0));
+   __m256i T1 = _mm256_permute4x64_epi64(R1, _MM_SHUFFLE(0,3,2,1));
+   __m256i T2 = _mm256_permute4x64_epi64(R2, _MM_SHUFFLE(0,3,2,1));
+
+   R0 = _mm256_blend_epi32(T1, T0, 0xC0);
+   R1 = _mm256_blend_epi32(T2, T1, 0xC0);
+   }
+
+
+}
+
+BOTAN_FUNC_ISA("avx2")
+void Threefish_512::avx2_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   _mm256_zeroupper();
+
+   const uint64_t* K = m_K.data();
+   const uint64_t* T_64 = m_T.data();
+
+   const __m256i ROTATE_1 = _mm256_set_epi64x(37,19,36,46);
+   const __m256i ROTATE_2 = _mm256_set_epi64x(42,14,27,33);
+   const __m256i ROTATE_3 = _mm256_set_epi64x(39,36,49,17);
+   const __m256i ROTATE_4 = _mm256_set_epi64x(56,54, 9,44);
+   const __m256i ROTATE_5 = _mm256_set_epi64x(24,34,30,39);
+   const __m256i ROTATE_6 = _mm256_set_epi64x(17,10,50,13);
+   const __m256i ROTATE_7 = _mm256_set_epi64x(43,39,29,25);
+   const __m256i ROTATE_8 = _mm256_set_epi64x(22,56,35, 8);
+
+#define THREEFISH_ROUND(X0, X1, SHL)                                                \
+   do {                                                                             \
+      const __m256i SHR = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHL);            \
+      X0 = _mm256_add_epi64(X0, X1);                                                \
+      X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+      X1 = _mm256_xor_si256(X1, X0);                                                \
+      X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(0, 3, 2, 1));                   \
+      X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0));                   \
+   } while(0)
+
+#define THREEFISH_ROUND_2(X0, X1, X2, X3, SHL)                           \
+   do {                                                                             \
+      const __m256i SHR = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHL);            \
+      X0 = _mm256_add_epi64(X0, X1);                                                \
+      X2 = _mm256_add_epi64(X2, X3);                                                \
+      X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+      X3 = _mm256_or_si256(_mm256_sllv_epi64(X3, SHL), _mm256_srlv_epi64(X3, SHR)); \
+      X1 = _mm256_xor_si256(X1, X0);                                                \
+      X3 = _mm256_xor_si256(X3, X2);                                                \
+      X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(0, 3, 2, 1));                   \
+      X2 = _mm256_permute4x64_epi64(X2, _MM_SHUFFLE(0, 3, 2, 1));                   \
+      X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0));                   \
+      X3 = _mm256_permute4x64_epi64(X3, _MM_SHUFFLE(1, 2, 3, 0));                   \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0I, T1I)                        \
+   do {                                                                          \
+      const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+      const __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+      X0 = _mm256_add_epi64(X0, K0);                                             \
+      X1 = _mm256_add_epi64(X1, K1);                                             \
+      X1 = _mm256_add_epi64(X1, _mm256_set_epi64x(R,0,0,0));                     \
+      X0 = _mm256_add_epi64(X0, T0);                                             \
+      X1 = _mm256_add_epi64(X1, T1);                                             \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0I, T1I)              \
+   do {                                                                          \
+      const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+      __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+      X0 = _mm256_add_epi64(X0, K0);                                             \
+      X2 = _mm256_add_epi64(X2, K0);                                             \
+      X1 = _mm256_add_epi64(X1, K1);                                             \
+      X3 = _mm256_add_epi64(X3, K1);                                             \
+      T1 = _mm256_add_epi64(T1, _mm256_set_epi64x(R,0,0,0));                     \
+      X0 = _mm256_add_epi64(X0, T0);                                             \
+      X2 = _mm256_add_epi64(X2, T0);                                             \
+      X1 = _mm256_add_epi64(X1, T1);                                             \
+      X3 = _mm256_add_epi64(X3, T1);                                             \
+   } while(0)
+
+#define THREEFISH_ENC_8_ROUNDS(X0, X1, R, K0, K1, K2, T0, T1, T2)        \
+   do {                                                        \
+      rotate_keys(K1, K2, K0);                                 \
+      THREEFISH_ROUND(X0, X1, ROTATE_1);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_2);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_3);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_4);                       \
+      THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0, T1);         \
+                                                               \
+      THREEFISH_ROUND(X0, X1, ROTATE_5);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_6);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_7);                       \
+      THREEFISH_ROUND(X0, X1, ROTATE_8);                       \
+      THREEFISH_INJECT_KEY(X0, X1, R+1, K1, K2, T2, T0);       \
+   } while(0)
+
+#define THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, R, K0, K1, K2, T0, T1, T2) \
+   do {                                                                  \
+      rotate_keys(K1, K2, K0);                                 \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_1);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_2);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_3);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_4);                       \
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0, T1);         \
+                                                                         \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_5);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_6);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_7);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_8);                       \
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R+1, K1, K2, T2, T0);       \
+   } while(0)
+
+   __m256i K0 = _mm256_set_epi64x(K[5], K[3], K[1], K[8]);
+   __m256i K1 = _mm256_set_epi64x(K[6], K[4], K[2], K[0]);
+   __m256i K2 = _mm256_set_epi64x(K[7], K[5], K[3], K[1]);
+
+   const __m256i* in_mm = reinterpret_cast<const __m256i*>(in);
+   __m256i* out_mm = reinterpret_cast<__m256i*>(out);
+
+   while(blocks >= 2)
+      {
+      __m256i X0 = _mm256_loadu_si256(in_mm++);
+      __m256i X1 = _mm256_loadu_si256(in_mm++);
+      __m256i X2 = _mm256_loadu_si256(in_mm++);
+      __m256i X3 = _mm256_loadu_si256(in_mm++);
+
+      const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+      interleave_epi64(X0, X1);
+      interleave_epi64(X2, X3);
+
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, 0, K1, K2, 2, 3);
+
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3,  1, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3,  3, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3,  5, K0,K1,K2, 3, 1, 2);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3,  7, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3,  9, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 11, K0,K1,K2, 3, 1, 2);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 13, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 15, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 17, K0,K1,K2, 3, 1, 2);
+
+      deinterleave_epi64(X0, X1);
+      deinterleave_epi64(X2, X3);
+
+      _mm256_storeu_si256(out_mm++, X0);
+      _mm256_storeu_si256(out_mm++, X1);
+      _mm256_storeu_si256(out_mm++, X2);
+      _mm256_storeu_si256(out_mm++, X3);
+
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m256i X0 = _mm256_loadu_si256(in_mm++);
+      __m256i X1 = _mm256_loadu_si256(in_mm++);
+
+      const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+      interleave_epi64(X0, X1);
+
+      THREEFISH_INJECT_KEY(X0, X1, 0, K1, K2, 2, 3);
+
+      THREEFISH_ENC_8_ROUNDS(X0, X1,  1, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_8_ROUNDS(X0, X1,  3, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_8_ROUNDS(X0, X1,  5, K0,K1,K2, 3, 1, 2);
+      THREEFISH_ENC_8_ROUNDS(X0, X1,  7, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_8_ROUNDS(X0, X1,  9, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_8_ROUNDS(X0, X1, 11, K0,K1,K2, 3, 1, 2);
+      THREEFISH_ENC_8_ROUNDS(X0, X1, 13, K2,K0,K1, 1, 2, 3);
+      THREEFISH_ENC_8_ROUNDS(X0, X1, 15, K1,K2,K0, 2, 3, 1);
+      THREEFISH_ENC_8_ROUNDS(X0, X1, 17, K0,K1,K2, 3, 1, 2);
+
+      deinterleave_epi64(X0, X1);
+
+      _mm256_storeu_si256(out_mm++, X0);
+      _mm256_storeu_si256(out_mm++, X1);
+      }
+
+   _mm256_zeroall();
+
+#undef THREEFISH_ENC_8_ROUNDS
+#undef THREEFISH_ROUND
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_DEC_2_8_ROUNDS
+#undef THREEFISH_ROUND_2
+#undef THREEFISH_INJECT_KEY_2
+   }
+
+BOTAN_FUNC_ISA("avx2")
+void Threefish_512::avx2_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   _mm256_zeroupper();
+
+   const uint64_t* K = m_K.data();
+   const uint64_t* T_64 = m_T.data();
+
+   const __m256i ROTATE_1 = _mm256_set_epi64x(37,19,36,46);
+   const __m256i ROTATE_2 = _mm256_set_epi64x(42,14,27,33);
+   const __m256i ROTATE_3 = _mm256_set_epi64x(39,36,49,17);
+   const __m256i ROTATE_4 = _mm256_set_epi64x(56,54, 9,44);
+   const __m256i ROTATE_5 = _mm256_set_epi64x(24,34,30,39);
+   const __m256i ROTATE_6 = _mm256_set_epi64x(17,10,50,13);
+   const __m256i ROTATE_7 = _mm256_set_epi64x(43,39,29,25);
+   const __m256i ROTATE_8 = _mm256_set_epi64x(22,56,35, 8);
+
+#define THREEFISH_ROUND(X0, X1, SHR)                                                \
+   do {                                                                             \
+      const __m256i SHL = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHR);            \
+      X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(2, 1, 0, 3));                   \
+      X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0));                   \
+      X1 = _mm256_xor_si256(X1, X0);                                                \
+      X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+      X0 = _mm256_sub_epi64(X0, X1);                                                \
+   } while(0)
+
+#define THREEFISH_ROUND_2(X0, X1, X2, X3, SHR)                                                \
+   do {                                                                             \
+      const __m256i SHL = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHR);            \
+      X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(2, 1, 0, 3));                   \
+      X2 = _mm256_permute4x64_epi64(X2, _MM_SHUFFLE(2, 1, 0, 3));                   \
+      X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0));                   \
+      X3 = _mm256_permute4x64_epi64(X3, _MM_SHUFFLE(1, 2, 3, 0));                   \
+      X1 = _mm256_xor_si256(X1, X0);                                                \
+      X3 = _mm256_xor_si256(X3, X2);                                                \
+      X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+      X3 = _mm256_or_si256(_mm256_sllv_epi64(X3, SHL), _mm256_srlv_epi64(X3, SHR)); \
+      X0 = _mm256_sub_epi64(X0, X1);                                                \
+      X2 = _mm256_sub_epi64(X2, X3);                                                \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0I, T1I)                \
+   do {                                                                          \
+      const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+      const __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+      X0 = _mm256_sub_epi64(X0, K0);                                             \
+      X1 = _mm256_sub_epi64(X1, K1);                                             \
+      X1 = _mm256_sub_epi64(X1, _mm256_set_epi64x(R, 0, 0, 0));                  \
+      X0 = _mm256_sub_epi64(X0, T0);                                             \
+      X1 = _mm256_sub_epi64(X1, T1);                                             \
+   } while(0)
+
+#define THREEFISH_DEC_8_ROUNDS(X0, X1, R, K1, K2, K3, T0, T1, T2)   \
+   do {                                                      \
+      THREEFISH_INJECT_KEY(X0, X1, R+1, K2, K3, T2, T0);     \
+      THREEFISH_ROUND(X0, X1, ROTATE_8);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_7);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_6);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_5);                     \
+                                                             \
+      THREEFISH_INJECT_KEY(X0, X1, R, K1, K2, T0, T1);       \
+      THREEFISH_ROUND(X0, X1, ROTATE_4);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_3);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_2);                     \
+      THREEFISH_ROUND(X0, X1, ROTATE_1);                     \
+   } while(0)
+
+#define THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0I, T1I)              \
+   do {                                                                          \
+      const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+      __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+      X0 = _mm256_sub_epi64(X0, K0);                                             \
+      X2 = _mm256_sub_epi64(X2, K0);                                             \
+      X1 = _mm256_sub_epi64(X1, K1);                                             \
+      X3 = _mm256_sub_epi64(X3, K1);                                             \
+      T1 = _mm256_add_epi64(T1, _mm256_set_epi64x(R,0,0,0));                     \
+      X0 = _mm256_sub_epi64(X0, T0);                                             \
+      X2 = _mm256_sub_epi64(X2, T0);                                             \
+      X1 = _mm256_sub_epi64(X1, T1);                                             \
+      X3 = _mm256_sub_epi64(X3, T1);                                             \
+   } while(0)
+
+#define THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, R, K1, K2, K3, T0, T1, T2) \
+   do {                                                                  \
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R+1, K2, K3, T2, T0);         \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_8);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_7);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_6);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_5);                       \
+                                                                         \
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K1, K2, T0, T1);       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_4);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_3);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_2);                       \
+      THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_1);                       \
+   } while(0)
+
+   /*
+   v1.0 key schedule: 9 ymm registers (only need 2 or 3)
+   (0,1,2,3),(4,5,6,7) [8]
+   then mutating with vpermq
+   */
+   const __m256i K0 = _mm256_set_epi64x(K[6], K[4], K[2], K[0]);
+   const __m256i K1 = _mm256_set_epi64x(K[7], K[5], K[3], K[1]);
+   const __m256i K2 = _mm256_set_epi64x(K[8], K[6], K[4], K[2]);
+   const __m256i K3 = _mm256_set_epi64x(K[0], K[7], K[5], K[3]);
+   const __m256i K4 = _mm256_set_epi64x(K[1], K[8], K[6], K[4]);
+   const __m256i K5 = _mm256_set_epi64x(K[2], K[0], K[7], K[5]);
+   const __m256i K6 = _mm256_set_epi64x(K[3], K[1], K[8], K[6]);
+   const __m256i K7 = _mm256_set_epi64x(K[4], K[2], K[0], K[7]);
+   const __m256i K8 = _mm256_set_epi64x(K[5], K[3], K[1], K[8]);
+
+   const __m256i* in_mm = reinterpret_cast<const __m256i*>(in);
+   __m256i* out_mm = reinterpret_cast<__m256i*>(out);
+
+   while(blocks >= 2)
+      {
+      __m256i X0 = _mm256_loadu_si256(in_mm++);
+      __m256i X1 = _mm256_loadu_si256(in_mm++);
+      __m256i X2 = _mm256_loadu_si256(in_mm++);
+      __m256i X3 = _mm256_loadu_si256(in_mm++);
+
+      const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+      interleave_epi64(X0, X1);
+      interleave_epi64(X2, X3);
+
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 17, K8,K0,K1, 3, 1, 2);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 15, K6,K7,K8, 2, 3, 1);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 13, K4,K5,K6, 1, 2, 3);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 11, K2,K3,K4, 3, 1, 2);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 9, K0,K1,K2, 2, 3, 1);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 7, K7,K8,K0, 1, 2, 3);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 5, K5,K6,K7, 3, 1, 2);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 3, K3,K4,K5, 2, 3, 1);
+      THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 1, K1,K2,K3, 1, 2, 3);
+
+      THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, 0, K0, K1, 2, 3);
+
+      deinterleave_epi64(X0, X1);
+      deinterleave_epi64(X2, X3);
+
+      _mm256_storeu_si256(out_mm++, X0);
+      _mm256_storeu_si256(out_mm++, X1);
+      _mm256_storeu_si256(out_mm++, X2);
+      _mm256_storeu_si256(out_mm++, X3);
+
+      blocks -= 2;
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      __m256i X0 = _mm256_loadu_si256(in_mm++);
+      __m256i X1 = _mm256_loadu_si256(in_mm++);
+
+      const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+      interleave_epi64(X0, X1);
+
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 17, K8,K0,K1, 3, 1, 2);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 15, K6,K7,K8, 2, 3, 1);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 13, K4,K5,K6, 1, 2, 3);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 11, K2,K3,K4, 3, 1, 2);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 9, K0,K1,K2, 2, 3, 1);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 7, K7,K8,K0, 1, 2, 3);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 5, K5,K6,K7, 3, 1, 2);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 3, K3,K4,K5, 2, 3, 1);
+      THREEFISH_DEC_8_ROUNDS(X0, X1, 1, K1,K2,K3, 1, 2, 3);
+
+      THREEFISH_INJECT_KEY(X0, X1, 0, K0, K1, 2, 3);
+
+      deinterleave_epi64(X0, X1);
+
+      _mm256_storeu_si256(out_mm++, X0);
+      _mm256_storeu_si256(out_mm++, X1);
+      }
+
+#undef THREEFISH_DEC_8_ROUNDS
+#undef THREEFISH_ROUND
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_DEC_2_8_ROUNDS
+#undef THREEFISH_ROUND_2
+#undef THREEFISH_INJECT_KEY_2
+
+   _mm256_zeroall();
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/twofish/twofish.cpp b/third_party/botan/src/lib/block/twofish/twofish.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/twofish/twofish.cpp
@@ -0,0 +1,326 @@
+/*
+* Twofish
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* The key schedule implemenation is based on a public domain
+* implementation by Matthew Skala
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/twofish.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+inline void TF_E(uint32_t A, uint32_t B, uint32_t& C, uint32_t& D,
+                 uint32_t RK1, uint32_t RK2,
+                 const secure_vector<uint32_t>& SB)
+   {
+   uint32_t X = SB[    get_byte(3, A)] ^ SB[256+get_byte(2, A)] ^
+                SB[512+get_byte(1, A)] ^ SB[768+get_byte(0, A)];
+   uint32_t Y = SB[    get_byte(0, B)] ^ SB[256+get_byte(3, B)] ^
+                SB[512+get_byte(2, B)] ^ SB[768+get_byte(1, B)];
+
+   X += Y;
+   Y += X;
+
+   X += RK1;
+   Y += RK2;
+
+   C = rotr<1>(C ^ X);
+   D = rotl<1>(D) ^ Y;
+   }
+
+inline void TF_D(uint32_t A, uint32_t B, uint32_t& C, uint32_t& D,
+                 uint32_t RK1, uint32_t RK2,
+                 const secure_vector<uint32_t>& SB)
+   {
+   uint32_t X = SB[    get_byte(3, A)] ^ SB[256+get_byte(2, A)] ^
+                SB[512+get_byte(1, A)] ^ SB[768+get_byte(0, A)];
+   uint32_t Y = SB[    get_byte(0, B)] ^ SB[256+get_byte(3, B)] ^
+                SB[512+get_byte(2, B)] ^ SB[768+get_byte(1, B)];
+
+   X += Y;
+   Y += X;
+
+   X += RK1;
+   Y += RK2;
+
+   C = rotl<1>(C) ^ X;
+   D = rotr<1>(D ^ Y);
+   }
+
+}
+
+/*
+* Twofish Encryption
+*/
+void Twofish::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SB.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t A0, B0, C0, D0;
+      uint32_t A1, B1, C1, D1;
+      load_le(in, A0, B0, C0, D0, A1, B1, C1, D1);
+
+      A0 ^= m_RK[0];
+      A1 ^= m_RK[0];
+      B0 ^= m_RK[1];
+      B1 ^= m_RK[1];
+      C0 ^= m_RK[2];
+      C1 ^= m_RK[2];
+      D0 ^= m_RK[3];
+      D1 ^= m_RK[3];
+
+      for(size_t k = 8; k != 40; k += 4)
+         {
+         TF_E(A0, B0, C0, D0, m_RK[k+0], m_RK[k+1], m_SB);
+         TF_E(A1, B1, C1, D1, m_RK[k+0], m_RK[k+1], m_SB);
+
+         TF_E(C0, D0, A0, B0, m_RK[k+2], m_RK[k+3], m_SB);
+         TF_E(C1, D1, A1, B1, m_RK[k+2], m_RK[k+3], m_SB);
+         }
+
+      C0 ^= m_RK[4];
+      C1 ^= m_RK[4];
+      D0 ^= m_RK[5];
+      D1 ^= m_RK[5];
+      A0 ^= m_RK[6];
+      A1 ^= m_RK[6];
+      B0 ^= m_RK[7];
+      B1 ^= m_RK[7];
+
+      store_le(out, C0, D0, A0, B0, C1, D1, A1, B1);
+
+      blocks -= 2;
+      out += 2*BLOCK_SIZE;
+      in  += 2*BLOCK_SIZE;
+      }
+
+   if(blocks)
+      {
+      uint32_t A, B, C, D;
+      load_le(in, A, B, C, D);
+
+      A ^= m_RK[0];
+      B ^= m_RK[1];
+      C ^= m_RK[2];
+      D ^= m_RK[3];
+
+      for(size_t k = 8; k != 40; k += 4)
+         {
+         TF_E(A, B, C, D, m_RK[k  ], m_RK[k+1], m_SB);
+         TF_E(C, D, A, B, m_RK[k+2], m_RK[k+3], m_SB);
+         }
+
+      C ^= m_RK[4];
+      D ^= m_RK[5];
+      A ^= m_RK[6];
+      B ^= m_RK[7];
+
+      store_le(out, C, D, A, B);
+      }
+   }
+
+/*
+* Twofish Decryption
+*/
+void Twofish::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_SB.empty() == false);
+
+   while(blocks >= 2)
+      {
+      uint32_t A0, B0, C0, D0;
+      uint32_t A1, B1, C1, D1;
+      load_le(in, A0, B0, C0, D0, A1, B1, C1, D1);
+
+      A0 ^= m_RK[4];
+      A1 ^= m_RK[4];
+      B0 ^= m_RK[5];
+      B1 ^= m_RK[5];
+      C0 ^= m_RK[6];
+      C1 ^= m_RK[6];
+      D0 ^= m_RK[7];
+      D1 ^= m_RK[7];
+
+      for(size_t k = 40; k != 8; k -= 4)
+         {
+         TF_D(A0, B0, C0, D0, m_RK[k-2], m_RK[k-1], m_SB);
+         TF_D(A1, B1, C1, D1, m_RK[k-2], m_RK[k-1], m_SB);
+
+         TF_D(C0, D0, A0, B0, m_RK[k-4], m_RK[k-3], m_SB);
+         TF_D(C1, D1, A1, B1, m_RK[k-4], m_RK[k-3], m_SB);
+         }
+
+      C0 ^= m_RK[0];
+      C1 ^= m_RK[0];
+      D0 ^= m_RK[1];
+      D1 ^= m_RK[1];
+      A0 ^= m_RK[2];
+      A1 ^= m_RK[2];
+      B0 ^= m_RK[3];
+      B1 ^= m_RK[3];
+
+      store_le(out, C0, D0, A0, B0, C1, D1, A1, B1);
+
+      blocks -= 2;
+      out += 2*BLOCK_SIZE;
+      in  += 2*BLOCK_SIZE;
+      }
+
+   if(blocks)
+      {
+      uint32_t A, B, C, D;
+      load_le(in, A, B, C, D);
+
+      A ^= m_RK[4];
+      B ^= m_RK[5];
+      C ^= m_RK[6];
+      D ^= m_RK[7];
+
+      for(size_t k = 40; k != 8; k -= 4)
+         {
+         TF_D(A, B, C, D, m_RK[k-2], m_RK[k-1], m_SB);
+         TF_D(C, D, A, B, m_RK[k-4], m_RK[k-3], m_SB);
+         }
+
+      C ^= m_RK[0];
+      D ^= m_RK[1];
+      A ^= m_RK[2];
+      B ^= m_RK[3];
+
+      store_le(out, C, D, A, B);
+      }
+   }
+
+/*
+* Twofish Key Schedule
+*/
+void Twofish::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_SB.resize(1024);
+   m_RK.resize(40);
+
+   secure_vector<uint8_t> S(16);
+
+   for(size_t i = 0; i != length; ++i)
+      {
+      /*
+      * Do one column of the RS matrix multiplcation
+      */
+      if(key[i])
+         {
+         uint8_t X = POLY_TO_EXP[key[i] - 1];
+
+         uint8_t RS1 = RS[(4*i  ) % 32];
+         uint8_t RS2 = RS[(4*i+1) % 32];
+         uint8_t RS3 = RS[(4*i+2) % 32];
+         uint8_t RS4 = RS[(4*i+3) % 32];
+
+         S[4*(i/8)  ] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS1 - 1]) % 255];
+         S[4*(i/8)+1] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS2 - 1]) % 255];
+         S[4*(i/8)+2] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS3 - 1]) % 255];
+         S[4*(i/8)+3] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS4 - 1]) % 255];
+         }
+      }
+
+   if(length == 16)
+      {
+      for(size_t i = 0; i != 256; ++i)
+         {
+         m_SB[    i] = MDS0[Q0[Q0[i]^S[ 0]]^S[ 4]];
+         m_SB[256+i] = MDS1[Q0[Q1[i]^S[ 1]]^S[ 5]];
+         m_SB[512+i] = MDS2[Q1[Q0[i]^S[ 2]]^S[ 6]];
+         m_SB[768+i] = MDS3[Q1[Q1[i]^S[ 3]]^S[ 7]];
+         }
+
+      for(size_t i = 0; i < 40; i += 2)
+         {
+         uint32_t X = MDS0[Q0[Q0[i  ]^key[ 8]]^key[ 0]] ^
+                      MDS1[Q0[Q1[i  ]^key[ 9]]^key[ 1]] ^
+                      MDS2[Q1[Q0[i  ]^key[10]]^key[ 2]] ^
+                      MDS3[Q1[Q1[i  ]^key[11]]^key[ 3]];
+         uint32_t Y = MDS0[Q0[Q0[i+1]^key[12]]^key[ 4]] ^
+                      MDS1[Q0[Q1[i+1]^key[13]]^key[ 5]] ^
+                      MDS2[Q1[Q0[i+1]^key[14]]^key[ 6]] ^
+                      MDS3[Q1[Q1[i+1]^key[15]]^key[ 7]];
+         Y = rotl<8>(Y);
+         X += Y; Y += X;
+
+         m_RK[i] = X;
+         m_RK[i+1] = rotl<9>(Y);
+         }
+      }
+   else if(length == 24)
+      {
+      for(size_t i = 0; i != 256; ++i)
+         {
+         m_SB[    i] = MDS0[Q0[Q0[Q1[i]^S[ 0]]^S[ 4]]^S[ 8]];
+         m_SB[256+i] = MDS1[Q0[Q1[Q1[i]^S[ 1]]^S[ 5]]^S[ 9]];
+         m_SB[512+i] = MDS2[Q1[Q0[Q0[i]^S[ 2]]^S[ 6]]^S[10]];
+         m_SB[768+i] = MDS3[Q1[Q1[Q0[i]^S[ 3]]^S[ 7]]^S[11]];
+         }
+
+      for(size_t i = 0; i < 40; i += 2)
+         {
+         uint32_t X = MDS0[Q0[Q0[Q1[i  ]^key[16]]^key[ 8]]^key[ 0]] ^
+                      MDS1[Q0[Q1[Q1[i  ]^key[17]]^key[ 9]]^key[ 1]] ^
+                      MDS2[Q1[Q0[Q0[i  ]^key[18]]^key[10]]^key[ 2]] ^
+                      MDS3[Q1[Q1[Q0[i  ]^key[19]]^key[11]]^key[ 3]];
+         uint32_t Y = MDS0[Q0[Q0[Q1[i+1]^key[20]]^key[12]]^key[ 4]] ^
+                      MDS1[Q0[Q1[Q1[i+1]^key[21]]^key[13]]^key[ 5]] ^
+                      MDS2[Q1[Q0[Q0[i+1]^key[22]]^key[14]]^key[ 6]] ^
+                      MDS3[Q1[Q1[Q0[i+1]^key[23]]^key[15]]^key[ 7]];
+         Y = rotl<8>(Y);
+         X += Y; Y += X;
+
+         m_RK[i] = X;
+         m_RK[i+1] = rotl<9>(Y);
+         }
+      }
+   else if(length == 32)
+      {
+      for(size_t i = 0; i != 256; ++i)
+         {
+         m_SB[    i] = MDS0[Q0[Q0[Q1[Q1[i]^S[ 0]]^S[ 4]]^S[ 8]]^S[12]];
+         m_SB[256+i] = MDS1[Q0[Q1[Q1[Q0[i]^S[ 1]]^S[ 5]]^S[ 9]]^S[13]];
+         m_SB[512+i] = MDS2[Q1[Q0[Q0[Q0[i]^S[ 2]]^S[ 6]]^S[10]]^S[14]];
+         m_SB[768+i] = MDS3[Q1[Q1[Q0[Q1[i]^S[ 3]]^S[ 7]]^S[11]]^S[15]];
+         }
+
+      for(size_t i = 0; i < 40; i += 2)
+         {
+         uint32_t X = MDS0[Q0[Q0[Q1[Q1[i  ]^key[24]]^key[16]]^key[ 8]]^key[ 0]] ^
+                      MDS1[Q0[Q1[Q1[Q0[i  ]^key[25]]^key[17]]^key[ 9]]^key[ 1]] ^
+                      MDS2[Q1[Q0[Q0[Q0[i  ]^key[26]]^key[18]]^key[10]]^key[ 2]] ^
+                      MDS3[Q1[Q1[Q0[Q1[i  ]^key[27]]^key[19]]^key[11]]^key[ 3]];
+         uint32_t Y = MDS0[Q0[Q0[Q1[Q1[i+1]^key[28]]^key[20]]^key[12]]^key[ 4]] ^
+                      MDS1[Q0[Q1[Q1[Q0[i+1]^key[29]]^key[21]]^key[13]]^key[ 5]] ^
+                      MDS2[Q1[Q0[Q0[Q0[i+1]^key[30]]^key[22]]^key[14]]^key[ 6]] ^
+                      MDS3[Q1[Q1[Q0[Q1[i+1]^key[31]]^key[23]]^key[15]]^key[ 7]];
+         Y = rotl<8>(Y);
+         X += Y; Y += X;
+
+         m_RK[i] = X;
+         m_RK[i+1] = rotl<9>(Y);
+         }
+      }
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Twofish::clear()
+   {
+   zap(m_SB);
+   zap(m_RK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/twofish/twofish.h b/third_party/botan/src/lib/block/twofish/twofish.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/twofish/twofish.h
@@ -0,0 +1,47 @@
+/*
+* Twofish
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TWOFISH_H_
+#define BOTAN_TWOFISH_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(twofish.h)
+
+namespace Botan {
+
+/**
+* Twofish, an AES finalist
+*/
+class BOTAN_PUBLIC_API(2,0) Twofish final : public Block_Cipher_Fixed_Params<16, 16, 32, 8>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "Twofish"; }
+      BlockCipher* clone() const override { return new Twofish; }
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+
+      static const uint32_t MDS0[256];
+      static const uint32_t MDS1[256];
+      static const uint32_t MDS2[256];
+      static const uint32_t MDS3[256];
+      static const uint8_t Q0[256];
+      static const uint8_t Q1[256];
+      static const uint8_t RS[32];
+      static const uint8_t EXP_TO_POLY[255];
+      static const uint8_t POLY_TO_EXP[255];
+
+      secure_vector<uint32_t> m_SB, m_RK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/block/twofish/twofish_tab.cpp b/third_party/botan/src/lib/block/twofish/twofish_tab.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/twofish/twofish_tab.cpp
@@ -0,0 +1,293 @@
+/*
+* S-Box and MDS Tables for Twofish
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/twofish.h>
+
+namespace Botan {
+
+const uint8_t Twofish::Q0[256] = {
+   0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78,
+   0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
+   0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30,
+   0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
+   0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE,
+   0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
+   0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45,
+   0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
+   0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF,
+   0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
+   0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED,
+   0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
+   0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B,
+   0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
+   0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F,
+   0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
+   0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17,
+   0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
+   0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68,
+   0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
+   0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42,
+   0x4A, 0x5E, 0xC1, 0xE0 };
+
+const uint8_t Twofish::Q1[256] = {
+   0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B,
+   0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
+   0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B,
+   0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
+   0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54,
+   0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
+   0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7,
+   0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
+   0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF,
+   0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
+   0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D,
+   0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
+   0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21,
+   0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
+   0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E,
+   0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
+   0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44,
+   0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
+   0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B,
+   0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
+   0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56,
+   0x55, 0x09, 0xBE, 0x91 };
+
+const uint8_t Twofish::RS[32] = {
+   0x01, 0xA4, 0x02, 0xA4, 0xA4, 0x56, 0xA1, 0x55, 0x55, 0x82, 0xFC, 0x87,
+   0x87, 0xF3, 0xC1, 0x5A, 0x5A, 0x1E, 0x47, 0x58, 0x58, 0xC6, 0xAE, 0xDB,
+   0xDB, 0x68, 0x3D, 0x9E, 0x9E, 0xE5, 0x19, 0x03 };
+
+const uint8_t Twofish::EXP_TO_POLY[255] = {
+   0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x4D, 0x9A, 0x79, 0xF2,
+   0xA9, 0x1F, 0x3E, 0x7C, 0xF8, 0xBD, 0x37, 0x6E, 0xDC, 0xF5, 0xA7, 0x03,
+   0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xCD, 0xD7, 0xE3, 0x8B, 0x5B, 0xB6,
+   0x21, 0x42, 0x84, 0x45, 0x8A, 0x59, 0xB2, 0x29, 0x52, 0xA4, 0x05, 0x0A,
+   0x14, 0x28, 0x50, 0xA0, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xED, 0x97, 0x63,
+   0xC6, 0xC1, 0xCF, 0xD3, 0xEB, 0x9B, 0x7B, 0xF6, 0xA1, 0x0F, 0x1E, 0x3C,
+   0x78, 0xF0, 0xAD, 0x17, 0x2E, 0x5C, 0xB8, 0x3D, 0x7A, 0xF4, 0xA5, 0x07,
+   0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x8D, 0x57, 0xAE, 0x11, 0x22, 0x44, 0x88,
+   0x5D, 0xBA, 0x39, 0x72, 0xE4, 0x85, 0x47, 0x8E, 0x51, 0xA2, 0x09, 0x12,
+   0x24, 0x48, 0x90, 0x6D, 0xDA, 0xF9, 0xBF, 0x33, 0x66, 0xCC, 0xD5, 0xE7,
+   0x83, 0x4B, 0x96, 0x61, 0xC2, 0xC9, 0xDF, 0xF3, 0xAB, 0x1B, 0x36, 0x6C,
+   0xD8, 0xFD, 0xB7, 0x23, 0x46, 0x8C, 0x55, 0xAA, 0x19, 0x32, 0x64, 0xC8,
+   0xDD, 0xF7, 0xA3, 0x0B, 0x16, 0x2C, 0x58, 0xB0, 0x2D, 0x5A, 0xB4, 0x25,
+   0x4A, 0x94, 0x65, 0xCA, 0xD9, 0xFF, 0xB3, 0x2B, 0x56, 0xAC, 0x15, 0x2A,
+   0x54, 0xA8, 0x1D, 0x3A, 0x74, 0xE8, 0x9D, 0x77, 0xEE, 0x91, 0x6F, 0xDE,
+   0xF1, 0xAF, 0x13, 0x26, 0x4C, 0x98, 0x7D, 0xFA, 0xB9, 0x3F, 0x7E, 0xFC,
+   0xB5, 0x27, 0x4E, 0x9C, 0x75, 0xEA, 0x99, 0x7F, 0xFE, 0xB1, 0x2F, 0x5E,
+   0xBC, 0x35, 0x6A, 0xD4, 0xE5, 0x87, 0x43, 0x86, 0x41, 0x82, 0x49, 0x92,
+   0x69, 0xD2, 0xE9, 0x9F, 0x73, 0xE6, 0x81, 0x4F, 0x9E, 0x71, 0xE2, 0x89,
+   0x5F, 0xBE, 0x31, 0x62, 0xC4, 0xC5, 0xC7, 0xC3, 0xCB, 0xDB, 0xFB, 0xBB,
+   0x3B, 0x76, 0xEC, 0x95, 0x67, 0xCE, 0xD1, 0xEF, 0x93, 0x6B, 0xD6, 0xE1,
+   0x8F, 0x53, 0xA6 };
+
+const uint8_t Twofish::POLY_TO_EXP[255] = {
+   0x00, 0x01, 0x17, 0x02, 0x2E, 0x18, 0x53, 0x03, 0x6A, 0x2F, 0x93, 0x19,
+   0x34, 0x54, 0x45, 0x04, 0x5C, 0x6B, 0xB6, 0x30, 0xA6, 0x94, 0x4B, 0x1A,
+   0x8C, 0x35, 0x81, 0x55, 0xAA, 0x46, 0x0D, 0x05, 0x24, 0x5D, 0x87, 0x6C,
+   0x9B, 0xB7, 0xC1, 0x31, 0x2B, 0xA7, 0xA3, 0x95, 0x98, 0x4C, 0xCA, 0x1B,
+   0xE6, 0x8D, 0x73, 0x36, 0xCD, 0x82, 0x12, 0x56, 0x62, 0xAB, 0xF0, 0x47,
+   0x4F, 0x0E, 0xBD, 0x06, 0xD4, 0x25, 0xD2, 0x5E, 0x27, 0x88, 0x66, 0x6D,
+   0xD6, 0x9C, 0x79, 0xB8, 0x08, 0xC2, 0xDF, 0x32, 0x68, 0x2C, 0xFD, 0xA8,
+   0x8A, 0xA4, 0x5A, 0x96, 0x29, 0x99, 0x22, 0x4D, 0x60, 0xCB, 0xE4, 0x1C,
+   0x7B, 0xE7, 0x3B, 0x8E, 0x9E, 0x74, 0xF4, 0x37, 0xD8, 0xCE, 0xF9, 0x83,
+   0x6F, 0x13, 0xB2, 0x57, 0xE1, 0x63, 0xDC, 0xAC, 0xC4, 0xF1, 0xAF, 0x48,
+   0x0A, 0x50, 0x42, 0x0F, 0xBA, 0xBE, 0xC7, 0x07, 0xDE, 0xD5, 0x78, 0x26,
+   0x65, 0xD3, 0xD1, 0x5F, 0xE3, 0x28, 0x21, 0x89, 0x59, 0x67, 0xFC, 0x6E,
+   0xB1, 0xD7, 0xF8, 0x9D, 0xF3, 0x7A, 0x3A, 0xB9, 0xC6, 0x09, 0x41, 0xC3,
+   0xAE, 0xE0, 0xDB, 0x33, 0x44, 0x69, 0x92, 0x2D, 0x52, 0xFE, 0x16, 0xA9,
+   0x0C, 0x8B, 0x80, 0xA5, 0x4A, 0x5B, 0xB5, 0x97, 0xC9, 0x2A, 0xA2, 0x9A,
+   0xC0, 0x23, 0x86, 0x4E, 0xBC, 0x61, 0xEF, 0xCC, 0x11, 0xE5, 0x72, 0x1D,
+   0x3D, 0x7C, 0xEB, 0xE8, 0xE9, 0x3C, 0xEA, 0x8F, 0x7D, 0x9F, 0xEC, 0x75,
+   0x1E, 0xF5, 0x3E, 0x38, 0xF6, 0xD9, 0x3F, 0xCF, 0x76, 0xFA, 0x1F, 0x84,
+   0xA0, 0x70, 0xED, 0x14, 0x90, 0xB3, 0x7E, 0x58, 0xFB, 0xE2, 0x20, 0x64,
+   0xD0, 0xDD, 0x77, 0xAD, 0xDA, 0xC5, 0x40, 0xF2, 0x39, 0xB0, 0xF7, 0x49,
+   0xB4, 0x0B, 0x7F, 0x51, 0x15, 0x43, 0x91, 0x10, 0x71, 0xBB, 0xEE, 0xBF,
+   0x85, 0xC8, 0xA1 };
+
+const uint32_t Twofish::MDS0[256] = {
+   0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B,
+   0xE2E22BFB, 0x9E9EFAC8, 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B,
+   0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, 0x3C3C57D6, 0x93938A32,
+   0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1,
+   0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA,
+   0xB0B0B306, 0x7575DE3F, 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B,
+   0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, 0xAEAE2C6D, 0x7F7FABC1,
+   0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
+   0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490,
+   0x3131272C, 0x808065A3, 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154,
+   0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, 0x2A2A3638, 0xC4C49CB0,
+   0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796,
+   0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228,
+   0x6767C027, 0xE9E9AF8C, 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7,
+   0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, 0x29294CCA, 0xF0F035E3,
+   0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
+   0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477,
+   0xC8C81DC3, 0x9999FFCC, 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF,
+   0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, 0xB5B53D79, 0x09090F0C,
+   0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9,
+   0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA,
+   0xEDEDD07A, 0x4343FC17, 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D,
+   0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, 0x5656E70B, 0xE3E3DA72,
+   0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E,
+   0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76,
+   0x8181942A, 0x91910149, 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321,
+   0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, 0x7878AEC5, 0xC5C56D39,
+   0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
+   0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D,
+   0x55559DF9, 0x7E7E5A48, 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E,
+   0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, 0x0606F48D, 0x404086E5,
+   0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
+   0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7,
+   0x2D2D333C, 0x3030D6A5, 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544,
+   0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, 0xD9D97929, 0x8686912E,
+   0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
+   0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A,
+   0xC1C112CF, 0x8585EBDC, 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B,
+   0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, 0xABABA212, 0x6F6F3EA2,
+   0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
+   0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504,
+   0x04047FF6, 0x272746C2, 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756,
+   0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 };
+
+const uint32_t Twofish::MDS1[256] = {
+   0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252,
+   0xA3658080, 0x76DFE4E4, 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A,
+   0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, 0x0D54E6E6, 0xC6432020,
+   0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141,
+   0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444,
+   0x94B1FBFB, 0x485A7E7E, 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424,
+   0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, 0x1945FDFD, 0x5BA33A3A,
+   0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757,
+   0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383,
+   0x9B53AAAA, 0x7C635D5D, 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A,
+   0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, 0xC0F09090, 0x8CAFE9E9,
+   0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656,
+   0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1,
+   0xB499C3C3, 0xF1975B5B, 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898,
+   0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, 0xCCFF9999, 0x95EA1414,
+   0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
+   0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1,
+   0xBF7E9595, 0xBA207D7D, 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989,
+   0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, 0x81FB0F0F, 0x793DB5B5,
+   0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282,
+   0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E,
+   0x86135050, 0xE730F7F7, 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E,
+   0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, 0x410B9F9F, 0x7B8B0202,
+   0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC,
+   0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565,
+   0xB1C72B2B, 0xAB6F8E8E, 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A,
+   0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, 0x91EF1313, 0x85FE0808,
+   0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
+   0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A,
+   0x6929A9A9, 0x647D4F4F, 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969,
+   0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, 0xAC87D1D1, 0x7F8E0505,
+   0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
+   0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D,
+   0x4C5F7979, 0x02B6B7B7, 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343,
+   0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, 0x57AC3333, 0xC718CFCF,
+   0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
+   0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F,
+   0x99E51D1D, 0x34392323, 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646,
+   0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, 0xC8FA9E9E, 0xA882D6D6,
+   0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF,
+   0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A,
+   0x0FE25151, 0x00000000, 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7,
+   0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 };
+
+const uint32_t Twofish::MDS2[256] = {
+   0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B,
+   0xE2FBE22B, 0x9EC89EFA, 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F,
+   0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, 0x3CD63C57, 0x9332938A,
+   0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783,
+   0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70,
+   0xB006B0B3, 0x753F75DE, 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3,
+   0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, 0xAE6DAE2C, 0x7FC17FAB,
+   0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
+   0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4,
+   0x312C3127, 0x80A38065, 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41,
+   0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, 0x2A382A36, 0xC4B0C49C,
+   0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07,
+   0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622,
+   0x672767C0, 0xE98CE9AF, 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18,
+   0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, 0x29CA294C, 0xF0E3F035,
+   0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
+   0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84,
+   0xC8C3C81D, 0x99CC99FF, 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E,
+   0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, 0xB579B53D, 0x090C090F,
+   0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD,
+   0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558,
+   0xED7AEDD0, 0x431743FC, 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40,
+   0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, 0x560B56E7, 0xE372E3DA,
+   0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85,
+   0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF,
+   0x812A8194, 0x91499101, 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773,
+   0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, 0x78C578AE, 0xC539C56D,
+   0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
+   0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C,
+   0x55F9559D, 0x7E487E5A, 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19,
+   0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, 0x068D06F4, 0x40E54086,
+   0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D,
+   0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74,
+   0x2D3C2D33, 0x30A530D6, 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755,
+   0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, 0xD929D979, 0x862E8691,
+   0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D,
+   0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4,
+   0xC1CFC112, 0x85DC85EB, 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53,
+   0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, 0xAB12ABA2, 0x6FA26F3E,
+   0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9,
+   0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705,
+   0x04F6047F, 0x27C22746, 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7,
+   0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF };
+
+const uint32_t Twofish::MDS3[256] = {
+   0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98,
+   0x6580A365, 0xDFE476DF, 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866,
+   0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, 0x54E60D54, 0x4320C643,
+   0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77,
+   0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9,
+   0xB1FB94B1, 0x5A7E485A, 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C,
+   0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, 0x45FD1945, 0xA33A5BA3,
+   0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216,
+   0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F,
+   0x53AA9B53, 0x635D7C63, 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25,
+   0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, 0xF090C0F0, 0xAFE98CAF,
+   0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7,
+   0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4,
+   0x99C3B499, 0x975BF197, 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E,
+   0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, 0xFF99CCFF, 0xEA1495EA,
+   0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
+   0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12,
+   0x7E95BF7E, 0x207DBA20, 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A,
+   0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, 0xFB0F81FB, 0x3DB5793D,
+   0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
+   0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A,
+   0x13508613, 0x30F7E730, 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C,
+   0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, 0x0B9F410B, 0x8B027B8B,
+   0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4,
+   0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B,
+   0xC72BB1C7, 0x6F8EAB6F, 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3,
+   0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, 0xEF1391EF, 0xFE0885FE,
+   0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB,
+   0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85,
+   0x29A96929, 0x7D4F647D, 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA,
+   0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, 0x87D1AC87, 0x8E057F8E,
+   0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
+   0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33,
+   0x5F794C5F, 0xB6B702B6, 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC,
+   0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, 0xAC3357AC, 0x18CFC718,
+   0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
+   0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8,
+   0xE51D99E5, 0x39233439, 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872,
+   0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, 0xFA9EC8FA, 0x82D6A882,
+   0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D,
+   0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10,
+   0xE2510FE2, 0x00000000, 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6,
+   0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 };
+
+}
diff --git a/third_party/botan/src/lib/block/xtea/xtea.cpp b/third_party/botan/src/lib/block/xtea/xtea.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/xtea/xtea.cpp
@@ -0,0 +1,134 @@
+/*
+* XTEA
+* (C) 1999-2009,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/xtea.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/*
+* XTEA Encryption
+*/
+void XTEA::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   const uint32_t* EK = &m_EK[0];
+
+   const size_t blocks4 = blocks / 4;
+   const size_t blocks_left = blocks % 4;
+
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks4; i++)
+      {
+      uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+      load_be(in + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+
+      for(size_t r = 0; r != 32; ++r)
+         {
+         L0 += (((R0 << 4) ^ (R0 >> 5)) + R0) ^ EK[2*r];
+         L1 += (((R1 << 4) ^ (R1 >> 5)) + R1) ^ EK[2*r];
+         L2 += (((R2 << 4) ^ (R2 >> 5)) + R2) ^ EK[2*r];
+         L3 += (((R3 << 4) ^ (R3 >> 5)) + R3) ^ EK[2*r];
+
+         R0 += (((L0 << 4) ^ (L0 >> 5)) + L0) ^ EK[2*r+1];
+         R1 += (((L1 << 4) ^ (L1 >> 5)) + L1) ^ EK[2*r+1];
+         R2 += (((L2 << 4) ^ (L2 >> 5)) + L2) ^ EK[2*r+1];
+         R3 += (((L3 << 4) ^ (L3 >> 5)) + L3) ^ EK[2*r+1];
+         }
+
+      store_be(out + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+      }
+
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks_left; ++i)
+      {
+      uint32_t L, R;
+      load_be(in + BLOCK_SIZE*(4*blocks4+i), L, R);
+
+      for(size_t r = 0; r != 32; ++r)
+         {
+         L += (((R << 4) ^ (R >> 5)) + R) ^ EK[2*r];
+         R += (((L << 4) ^ (L >> 5)) + L) ^ EK[2*r+1];
+         }
+
+      store_be(out + BLOCK_SIZE*(4*blocks4+i), L, R);
+      }
+   }
+
+/*
+* XTEA Decryption
+*/
+void XTEA::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+   {
+   verify_key_set(m_EK.empty() == false);
+
+   const uint32_t* EK = &m_EK[0];
+
+   const size_t blocks4 = blocks / 4;
+   const size_t blocks_left = blocks % 4;
+
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks4; i++)
+      {
+      uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+      load_be(in + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+
+      for(size_t r = 0; r != 32; ++r)
+         {
+         R0 -= (((L0 << 4) ^ (L0 >> 5)) + L0) ^ EK[63 - 2*r];
+         R1 -= (((L1 << 4) ^ (L1 >> 5)) + L1) ^ EK[63 - 2*r];
+         R2 -= (((L2 << 4) ^ (L2 >> 5)) + L2) ^ EK[63 - 2*r];
+         R3 -= (((L3 << 4) ^ (L3 >> 5)) + L3) ^ EK[63 - 2*r];
+
+         L0 -= (((R0 << 4) ^ (R0 >> 5)) + R0) ^ EK[62 - 2*r];
+         L1 -= (((R1 << 4) ^ (R1 >> 5)) + R1) ^ EK[62 - 2*r];
+         L2 -= (((R2 << 4) ^ (R2 >> 5)) + R2) ^ EK[62 - 2*r];
+         L3 -= (((R3 << 4) ^ (R3 >> 5)) + R3) ^ EK[62 - 2*r];
+         }
+
+      store_be(out + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+      }
+
+   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks_left; ++i)
+      {
+      uint32_t L, R;
+      load_be(in + BLOCK_SIZE*(4*blocks4+i), L, R);
+
+      for(size_t r = 0; r != 32; ++r)
+         {
+         R -= (((L << 4) ^ (L >> 5)) + L) ^ m_EK[63 - 2*r];
+         L -= (((R << 4) ^ (R >> 5)) + R) ^ m_EK[62 - 2*r];
+         }
+
+      store_be(out + BLOCK_SIZE*(4*blocks4+i), L, R);
+      }
+   }
+
+/*
+* XTEA Key Schedule
+*/
+void XTEA::key_schedule(const uint8_t key[], size_t)
+   {
+   m_EK.resize(64);
+
+   secure_vector<uint32_t> UK(4);
+   for(size_t i = 0; i != 4; ++i)
+      UK[i] = load_be<uint32_t>(key, i);
+
+   uint32_t D = 0;
+   for(size_t i = 0; i != 64; i += 2)
+      {
+      m_EK[i  ] = D + UK[D % 4];
+      D += 0x9E3779B9;
+      m_EK[i+1] = D + UK[(D >> 11) % 4];
+      }
+   }
+
+void XTEA::clear()
+   {
+   zap(m_EK);
+   }
+
+}
diff --git a/third_party/botan/src/lib/block/xtea/xtea.h b/third_party/botan/src/lib/block/xtea/xtea.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/block/xtea/xtea.h
@@ -0,0 +1,37 @@
+/*
+* XTEA
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_XTEA_H_
+#define BOTAN_XTEA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xtea.h)
+
+namespace Botan {
+
+/**
+* XTEA
+*/
+class BOTAN_PUBLIC_API(2,0) XTEA final : public Block_Cipher_Fixed_Params<8, 16>
+   {
+   public:
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+      void clear() override;
+      std::string name() const override { return "XTEA"; }
+      BlockCipher* clone() const override { return new XTEA; }
+
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+      secure_vector<uint32_t> m_EK;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/codec/base32/base32.cpp b/third_party/botan/src/lib/codec/base32/base32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base32/base32.cpp
@@ -0,0 +1,224 @@
+/*
+* Base32 Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base32.h>
+#include <botan/internal/codec_base.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+namespace {
+
+class Base32 final
+   {
+   public:
+      static inline std::string name() noexcept
+         {
+         return "base32";
+         }
+
+      static inline size_t encoding_bytes_in() noexcept
+         {
+         return m_encoding_bytes_in;
+         }
+      static inline size_t encoding_bytes_out() noexcept
+         {
+         return m_encoding_bytes_out;
+         }
+
+      static inline size_t decoding_bytes_in() noexcept
+         {
+         return m_encoding_bytes_out;
+         }
+      static inline size_t decoding_bytes_out() noexcept
+         {
+         return m_encoding_bytes_in;
+         }
+
+      static inline size_t bits_consumed() noexcept
+         {
+         return m_encoding_bits;
+         }
+      static inline size_t remaining_bits_before_padding() noexcept
+         {
+         return m_remaining_bits_before_padding;
+         }
+
+      static inline size_t encode_max_output(size_t input_length)
+         {
+         return (round_up(input_length, m_encoding_bytes_in) / m_encoding_bytes_in) * m_encoding_bytes_out;
+         }
+      static inline size_t decode_max_output(size_t input_length)
+         {
+         return (round_up(input_length, m_encoding_bytes_out) * m_encoding_bytes_in) / m_encoding_bytes_out;
+         }
+
+      static void encode(char out[8], const uint8_t in[5]) noexcept
+         {
+         out[0] = Base32::m_bin_to_base32[(in[0] & 0xF8) >> 3];
+         out[1] = Base32::m_bin_to_base32[((in[0] & 0x07) << 2) | (in[1] >> 6)];
+         out[2] = Base32::m_bin_to_base32[((in[1] & 0x3E) >> 1)];
+         out[3] = Base32::m_bin_to_base32[((in[1] & 0x01) << 4) | (in[2] >> 4)];
+         out[4] = Base32::m_bin_to_base32[((in[2] & 0x0F) << 1) | (in[3] >> 7)];
+         out[5] = Base32::m_bin_to_base32[((in[3] & 0x7C) >> 2)];
+         out[6] = Base32::m_bin_to_base32[((in[3] & 0x03) << 3) | (in[4] >> 5)];
+         out[7] = Base32::m_bin_to_base32[in[4] & 0x1F];
+         }
+
+      static inline uint8_t lookup_binary_value(char input) noexcept
+         {
+         return Base32::m_base32_to_bin[static_cast<uint8_t>(input)];
+         }
+
+      static inline bool check_bad_char(uint8_t bin, char input, bool ignore_ws)
+         {
+         if(bin <= 0x1F)
+            {
+            return true;
+            }
+         else if(!(bin == 0x81 || (bin == 0x80 && ignore_ws)))
+            {
+            std::string bad_char(1, input);
+            if(bad_char == "\t")
+               { bad_char = "\\t"; }
+            else if(bad_char == "\n")
+               { bad_char = "\\n"; }
+            else if(bad_char == "\r")
+               { bad_char = "\\r"; }
+
+            throw Invalid_Argument(
+               std::string("base32_decode: invalid base32 character '") +
+               bad_char + "'");
+            }
+         return false;
+         }
+
+      static void decode(uint8_t* out_ptr, const uint8_t decode_buf[8])
+         {
+         out_ptr[0] = (decode_buf[0] << 3) | (decode_buf[1] >> 2);
+         out_ptr[1] = (decode_buf[1] << 6) | (decode_buf[2] << 1) | (decode_buf[3] >> 4);
+         out_ptr[2] = (decode_buf[3] << 4) | (decode_buf[4] >> 1);
+         out_ptr[3] = (decode_buf[4] << 7) | (decode_buf[5] << 2) | (decode_buf[6] >> 3);
+         out_ptr[4] = (decode_buf[6] << 5) | decode_buf[7];
+         }
+
+      static inline size_t bytes_to_remove(size_t final_truncate)
+         {
+         return final_truncate ? (final_truncate / 2) + 1 : 0;
+         }
+
+   private:
+      static const size_t m_encoding_bits = 5;
+      static const size_t m_remaining_bits_before_padding = 6;
+
+
+      static const size_t m_encoding_bytes_in = 5;
+      static const size_t m_encoding_bytes_out = 8;
+
+
+      static const uint8_t m_bin_to_base32[32];
+      static const uint8_t m_base32_to_bin[256];
+   };
+
+const uint8_t Base32::m_bin_to_base32[32] =
+   {
+   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+   '2', '3', '4', '5', '6', '7'
+   };
+
+/*
+* base32 Decoder Lookup Table
+* Warning: assumes ASCII encodings
+*/
+const uint8_t Base32::m_base32_to_bin[256] =
+   {
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
+   0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04,
+   0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+   0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+   0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+   };
+
+}
+
+size_t base32_encode(char out[],
+                     const uint8_t in[],
+                     size_t input_length,
+                     size_t& input_consumed,
+                     bool final_inputs)
+   {
+   return base_encode(Base32(), out, in, input_length, input_consumed, final_inputs);
+   }
+
+std::string base32_encode(const uint8_t input[],
+                          size_t input_length)
+   {
+   return base_encode_to_string(Base32(), input, input_length);
+   }
+
+size_t base32_decode(uint8_t out[],
+                     const char in[],
+                     size_t input_length,
+                     size_t& input_consumed,
+                     bool final_inputs,
+                     bool ignore_ws)
+   {
+   return base_decode(Base32(), out, in, input_length, input_consumed, final_inputs, ignore_ws);
+   }
+
+size_t base32_decode(uint8_t output[],
+                     const char input[],
+                     size_t input_length,
+                     bool ignore_ws)
+   {
+   return base_decode_full(Base32(), output, input, input_length, ignore_ws);
+   }
+
+size_t base32_decode(uint8_t output[],
+                     const std::string& input,
+                     bool ignore_ws)
+   {
+   return base32_decode(output, input.data(), input.length(), ignore_ws);
+   }
+
+secure_vector<uint8_t> base32_decode(const char input[],
+                                     size_t input_length,
+                                     bool ignore_ws)
+   {
+   return base_decode_to_vec<secure_vector<uint8_t>>(Base32(), input, input_length, ignore_ws);
+   }
+
+secure_vector<uint8_t> base32_decode(const std::string& input,
+                                     bool ignore_ws)
+   {
+   return base32_decode(input.data(), input.size(), ignore_ws);
+   }
+
+}
diff --git a/third_party/botan/src/lib/codec/base32/base32.h b/third_party/botan/src/lib/codec/base32/base32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base32/base32.h
@@ -0,0 +1,127 @@
+/*
+* Base32 Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE32_CODEC_H_
+#define BOTAN_BASE32_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base32 encoding
+* @param output an array of at least base32_encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_encode(char output[],
+      const uint8_t input[],
+      size_t input_length,
+      size_t& input_consumed,
+      bool final_inputs);
+
+/**
+* Perform base32 encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @return base32 representation of input
+*/
+std::string BOTAN_PUBLIC_API(2, 7) base32_encode(const uint8_t input[],
+      size_t input_length);
+
+/**
+* Perform base32 encoding
+* @param input some input
+* @return base32 representation of input
+*/
+template <typename Alloc>
+std::string base32_encode(const std::vector<uint8_t, Alloc>& input)
+   {
+   return base32_encode(input.data(), input.size());
+   }
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+      const char input[],
+      size_t input_length,
+      size_t& input_consumed,
+      bool final_inputs,
+      bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+      const char input[],
+      size_t input_length,
+      bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+      const std::string& input,
+      bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param input some base32 input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded base32 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const char input[],
+      size_t input_length,
+      bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param input some base32 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded base32 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const std::string& input,
+      bool ignore_ws = true);
+
+} // namespace Botan
+
+#endif
diff --git a/third_party/botan/src/lib/codec/base58/base58.cpp b/third_party/botan/src/lib/codec/base58/base58.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base58/base58.cpp
@@ -0,0 +1,178 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base58.h>
+#include <botan/exceptn.h>
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/loadstor.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+uint32_t sha256_d_checksum(const uint8_t input[], size_t input_length)
+   {
+   std::unique_ptr<HashFunction> sha256 = HashFunction::create_or_throw("SHA-256");
+
+   std::vector<uint8_t> checksum(32);
+
+   sha256->update(input, input_length);
+   sha256->final(checksum);
+
+   sha256->update(checksum);
+   sha256->final(checksum);
+
+   return load_be<uint32_t>(checksum.data(), 0);
+   }
+
+class Character_Table
+   {
+   public:
+      // This must be a literal constant
+      Character_Table(const char* alphabet) :
+         m_alphabet(alphabet)
+         {
+         const size_t alpha_len = std::strlen(alphabet);
+
+         // 128 or up would flow into 0x80 invalid bit
+         if(alpha_len == 0 || alpha_len >= 128)
+            throw Invalid_Argument("Bad Character_Table string");
+
+         m_alphabet_len = static_cast<uint8_t>(alpha_len);
+
+         set_mem(m_tab, 256, 0x80);
+
+         for(size_t i = 0; m_alphabet[i]; ++i)
+            {
+            const uint8_t b = static_cast<uint8_t>(m_alphabet[i]);
+            BOTAN_ASSERT(m_tab[b] == 0x80, "No duplicate chars");
+            m_tab[b] = static_cast<uint8_t>(i);
+            }
+         }
+
+      uint8_t radix() const { return m_alphabet_len; }
+
+      char operator[](size_t i) const
+         {
+         BOTAN_ASSERT(i < m_alphabet_len, "Character in range");
+         return m_alphabet[i];
+         }
+
+      uint8_t code_for(char c) const
+         {
+         return m_tab[static_cast<uint8_t>(c)];
+         }
+
+   private:
+      const char* m_alphabet;
+      uint8_t m_alphabet_len;
+      uint8_t m_tab[256];
+   };
+
+static const Character_Table& BASE58_ALPHA()
+   {
+   static const Character_Table base58_alpha("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
+   return base58_alpha;
+   }
+
+std::string base58_encode(BigInt v, size_t leading_zeros)
+   {
+   const auto base58 = BASE58_ALPHA();
+
+   std::string result;
+   BigInt q;
+   uint8_t r;
+
+   while(v.is_nonzero())
+      {
+      ct_divide_u8(v, base58.radix(), q, r);
+      result.push_back(base58[r]);
+      v.swap(q);
+      }
+
+   for(size_t i = 0; i != leading_zeros; ++i)
+      result.push_back(base58[0]);
+
+   return std::string(result.rbegin(), result.rend());
+   }
+
+template<typename T, typename Z>
+size_t count_leading_zeros(const T input[], size_t input_length, Z zero)
+   {
+   size_t leading_zeros = 0;
+
+   while(leading_zeros < input_length && input[leading_zeros] == zero)
+      leading_zeros += 1;
+
+   return leading_zeros;
+   }
+
+}
+
+std::string base58_encode(const uint8_t input[], size_t input_length)
+   {
+   BigInt v(input, input_length);
+   return base58_encode(v, count_leading_zeros(input, input_length, 0));
+   }
+
+std::string base58_check_encode(const uint8_t input[], size_t input_length)
+   {
+   BigInt v(input, input_length);
+   v <<= 32;
+   v += sha256_d_checksum(input, input_length);
+   return base58_encode(v, count_leading_zeros(input, input_length, 0));
+   }
+
+std::vector<uint8_t> base58_decode(const char input[], size_t input_length)
+   {
+   const auto base58 = BASE58_ALPHA();
+
+   const size_t leading_zeros = count_leading_zeros(input, input_length, base58[0]);
+
+   BigInt v;
+
+   for(size_t i = leading_zeros; i != input_length; ++i)
+      {
+      const char c = input[i];
+
+      if(c == ' ' || c == '\n')
+         continue;
+
+      const size_t idx = base58.code_for(c);
+
+      if(idx == 0x80)
+         throw Decoding_Error("Invalid base58");
+
+      v *= base58.radix();
+      v += idx;
+      }
+
+   std::vector<uint8_t> output(v.bytes() + leading_zeros);
+   v.binary_encode(output.data() + leading_zeros);
+   return output;
+   }
+
+std::vector<uint8_t> base58_check_decode(const char input[], size_t input_length)
+   {
+   std::vector<uint8_t> dec = base58_decode(input, input_length);
+
+   if(dec.size() < 4)
+      throw Decoding_Error("Invalid base58 too short for checksum");
+
+   const uint32_t computed_checksum = sha256_d_checksum(dec.data(), dec.size() - 4);
+   const uint32_t checksum = load_be<uint32_t>(&dec[dec.size()-4], 0);
+
+   if(checksum != computed_checksum)
+      throw Decoding_Error("Invalid base58 checksum");
+
+   dec.resize(dec.size() - 4);
+
+   return dec;
+   }
+
+}
diff --git a/third_party/botan/src/lib/codec/base58/base58.h b/third_party/botan/src/lib/codec/base58/base58.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base58/base58.h
@@ -0,0 +1,76 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE58_CODEC_H_
+#define BOTAN_BASE58_CODEC_H_
+
+#include <botan/secmem.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base58 encoding
+*
+* This is raw base58 encoding, without the checksum
+*/
+std::string
+BOTAN_PUBLIC_API(2,9) base58_encode(const uint8_t input[],
+                                    size_t input_length);
+
+/**
+* Perform base58 encoding with checksum
+*/
+std::string
+BOTAN_PUBLIC_API(2,9) base58_check_encode(const uint8_t input[],
+                                          size_t input_length);
+
+
+/**
+* Perform base58 decoding
+*
+* This is raw base58 encoding, without the checksum
+*/
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,9) base58_decode(const char input[],
+                                    size_t input_length);
+
+/**
+* Perform base58 decoding with checksum
+*/
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,9) base58_check_decode(const char input[],
+                                          size_t input_length);
+
+
+// Some convenience wrappers:
+
+template<typename Alloc>
+inline std::string base58_encode(const std::vector<uint8_t, Alloc>& vec)
+   {
+   return base58_encode(vec.data(), vec.size());
+   }
+
+template<typename Alloc>
+inline std::string base58_check_encode(const std::vector<uint8_t, Alloc>& vec)
+   {
+   return base58_check_encode(vec.data(), vec.size());
+   }
+
+inline std::vector<uint8_t> base58_decode(const std::string& s)
+   {
+   return base58_decode(s.data(), s.size());
+   }
+
+inline std::vector<uint8_t> base58_check_decode(const std::string& s)
+   {
+   return base58_check_decode(s.data(), s.size());
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/codec/base64/base64.cpp b/third_party/botan/src/lib/codec/base64/base64.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base64/base64.cpp
@@ -0,0 +1,229 @@
+/*
+* Base64 Encoding and Decoding
+* (C) 2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base64.h>
+#include <botan/internal/codec_base.h>
+#include <botan/exceptn.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+namespace {
+
+class Base64 final
+   {
+   public:
+      static inline std::string name() noexcept
+         {
+         return "base64";
+         }
+
+      static inline size_t encoding_bytes_in() noexcept
+         {
+         return m_encoding_bytes_in;
+         }
+      static inline size_t encoding_bytes_out() noexcept
+         {
+         return m_encoding_bytes_out;
+         }
+
+      static inline size_t decoding_bytes_in() noexcept
+         {
+         return m_encoding_bytes_out;
+         }
+      static inline size_t decoding_bytes_out() noexcept
+         {
+         return m_encoding_bytes_in;
+         }
+
+      static inline size_t bits_consumed() noexcept
+         {
+         return m_encoding_bits;
+         }
+      static inline size_t remaining_bits_before_padding() noexcept
+         {
+         return m_remaining_bits_before_padding;
+         }
+
+      static inline size_t encode_max_output(size_t input_length)
+         {
+         return (round_up(input_length, m_encoding_bytes_in) / m_encoding_bytes_in) * m_encoding_bytes_out;
+         }
+      static inline size_t decode_max_output(size_t input_length)
+         {
+         return (round_up(input_length, m_encoding_bytes_out) * m_encoding_bytes_in) / m_encoding_bytes_out;
+         }
+
+      static void encode(char out[8], const uint8_t in[5]) noexcept
+         {
+         out[0] = Base64::m_bin_to_base64[(in[0] & 0xFC) >> 2];
+         out[1] = Base64::m_bin_to_base64[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+         out[2] = Base64::m_bin_to_base64[((in[1] & 0x0F) << 2) | (in[2] >> 6)];
+         out[3] = Base64::m_bin_to_base64[in[2] & 0x3F];
+         }
+
+      static inline uint8_t lookup_binary_value(char input) noexcept
+         {
+         return Base64::m_base64_to_bin[static_cast<uint8_t>(input)];
+         }
+
+      static inline bool check_bad_char(uint8_t bin, char input, bool ignore_ws)
+         {
+         if(bin <= 0x3F)
+            {
+            return true;
+            }
+         else if(!(bin == 0x81 || (bin == 0x80 && ignore_ws)))
+            {
+            std::string bad_char(1, input);
+            if(bad_char == "\t")
+               { bad_char = "\\t"; }
+            else if(bad_char == "\n")
+               { bad_char = "\\n"; }
+            else if(bad_char == "\r")
+               { bad_char = "\\r"; }
+
+            throw Invalid_Argument(
+               std::string("base64_decode: invalid base64 character '") +
+               bad_char + "'");
+            }
+         return false;
+         }
+
+      static void decode(uint8_t* out_ptr, const uint8_t decode_buf[4])
+         {
+         out_ptr[0] = (decode_buf[0] << 2) | (decode_buf[1] >> 4);
+         out_ptr[1] = (decode_buf[1] << 4) | (decode_buf[2] >> 2);
+         out_ptr[2] = (decode_buf[2] << 6) | decode_buf[3];
+         }
+
+      static inline size_t bytes_to_remove(size_t final_truncate)
+         {
+         return final_truncate;
+         }
+
+   private:
+      static const size_t m_encoding_bits = 6;
+      static const size_t m_remaining_bits_before_padding = 8;
+
+
+      static const size_t m_encoding_bytes_in = 3;
+      static const size_t m_encoding_bytes_out = 4;
+
+
+      static const uint8_t m_bin_to_base64[64];
+      static const uint8_t m_base64_to_bin[256];
+   };
+
+const uint8_t Base64::m_bin_to_base64[64] =
+   {
+   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+   };
+
+/*
+* base64 Decoder Lookup Table
+* Warning: assumes ASCII encodings
+*/
+const uint8_t Base64::m_base64_to_bin[256] =
+   {
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
+   0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, 0x34, 0x35,
+   0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF,
+   0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04,
+   0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+   0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+   0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x1B, 0x1C,
+   0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+   0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+   0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+   };
+}
+
+size_t base64_encode(char out[],
+                     const uint8_t in[],
+                     size_t input_length,
+                     size_t& input_consumed,
+                     bool final_inputs)
+   {
+   return base_encode(Base64(), out, in, input_length, input_consumed, final_inputs);
+   }
+
+std::string base64_encode(const uint8_t input[],
+                          size_t input_length)
+   {
+   return base_encode_to_string(Base64(), input, input_length);
+   }
+
+size_t base64_decode(uint8_t out[],
+                     const char in[],
+                     size_t input_length,
+                     size_t& input_consumed,
+                     bool final_inputs,
+                     bool ignore_ws)
+   {
+   return base_decode(Base64(), out, in, input_length, input_consumed, final_inputs, ignore_ws);
+   }
+
+size_t base64_decode(uint8_t output[],
+                     const char input[],
+                     size_t input_length,
+                     bool ignore_ws)
+   {
+   return base_decode_full(Base64(), output, input, input_length, ignore_ws);
+   }
+
+size_t base64_decode(uint8_t output[],
+                     const std::string& input,
+                     bool ignore_ws)
+   {
+   return base64_decode(output, input.data(), input.length(), ignore_ws);
+   }
+
+secure_vector<uint8_t> base64_decode(const char input[],
+                                     size_t input_length,
+                                     bool ignore_ws)
+   {
+   return base_decode_to_vec<secure_vector<uint8_t>>(Base64(), input, input_length, ignore_ws);
+   }
+
+secure_vector<uint8_t> base64_decode(const std::string& input,
+                                     bool ignore_ws)
+   {
+   return base64_decode(input.data(), input.size(), ignore_ws);
+   }
+
+size_t base64_encode_max_output(size_t input_length)
+   {
+   return Base64::encode_max_output(input_length);
+   }
+
+size_t base64_decode_max_output(size_t input_length)
+   {
+   return Base64::decode_max_output(input_length);
+   }
+
+}
diff --git a/third_party/botan/src/lib/codec/base64/base64.h b/third_party/botan/src/lib/codec/base64/base64.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/base64/base64.h
@@ -0,0 +1,141 @@
+/*
+* Base64 Encoding and Decoding
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE64_CODEC_H_
+#define BOTAN_BASE64_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base64 encoding
+* @param output an array of at least base64_encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_encode(char output[],
+                               const uint8_t input[],
+                               size_t input_length,
+                               size_t& input_consumed,
+                               bool final_inputs);
+
+/**
+* Perform base64 encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @return base64adecimal representation of input
+*/
+std::string BOTAN_PUBLIC_API(2,0) base64_encode(const uint8_t input[],
+                                    size_t input_length);
+
+/**
+* Perform base64 encoding
+* @param input some input
+* @return base64adecimal representation of input
+*/
+template<typename Alloc>
+std::string base64_encode(const std::vector<uint8_t, Alloc>& input)
+   {
+   return base64_encode(input.data(), input.size());
+   }
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+                               const char input[],
+                               size_t input_length,
+                               size_t& input_consumed,
+                               bool final_inputs,
+                               bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+                               const char input[],
+                               size_t input_length,
+                               bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+                               const std::string& input,
+                               bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param input some base64 input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded base64 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) base64_decode(const char input[],
+                                           size_t input_length,
+                                           bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param input some base64 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded base64 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) base64_decode(const std::string& input,
+                                           bool ignore_ws = true);
+
+/**
+* Calculate the size of output buffer for base64_encode
+* @param input_length the length of input in bytes
+* @return the size of output buffer in bytes
+*/
+size_t BOTAN_PUBLIC_API(2,1) base64_encode_max_output(size_t input_length);
+
+/**
+* Calculate the size of output buffer for base64_decode
+* @param input_length the length of input in bytes
+* @return the size of output buffer in bytes
+*/
+size_t BOTAN_PUBLIC_API(2,1) base64_decode_max_output(size_t input_length);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/codec/hex/hex.cpp b/third_party/botan/src/lib/codec/hex/hex.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/hex/hex.cpp
@@ -0,0 +1,207 @@
+/*
+* Hex Encoding and Decoding
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hex.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+void hex_encode(char output[],
+                const uint8_t input[],
+                size_t input_length,
+                bool uppercase)
+   {
+   static const uint8_t BIN_TO_HEX_UPPER[16] = {
+      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+      'A', 'B', 'C', 'D', 'E', 'F' };
+
+   static const uint8_t BIN_TO_HEX_LOWER[16] = {
+      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+      'a', 'b', 'c', 'd', 'e', 'f' };
+
+   const uint8_t* tbl = uppercase ? BIN_TO_HEX_UPPER : BIN_TO_HEX_LOWER;
+
+   for(size_t i = 0; i != input_length; ++i)
+      {
+      uint8_t x = input[i];
+      output[2*i  ] = tbl[(x >> 4) & 0x0F];
+      output[2*i+1] = tbl[(x     ) & 0x0F];
+      }
+   }
+
+std::string hex_encode(const uint8_t input[],
+                       size_t input_length,
+                       bool uppercase)
+   {
+   std::string output(2 * input_length, 0);
+
+   if(input_length)
+      hex_encode(&output.front(), input, input_length, uppercase);
+
+   return output;
+   }
+
+size_t hex_decode(uint8_t output[],
+                  const char input[],
+                  size_t input_length,
+                  size_t& input_consumed,
+                  bool ignore_ws)
+   {
+   /*
+   * Mapping of hex characters to either their binary equivalent
+   * or to an error code.
+   *  If valid hex (0-9 A-F a-f), the value.
+   *  If whitespace, then 0x80
+   *  Otherwise 0xFF
+   * Warning: this table assumes ASCII character encodings
+   */
+
+   static const uint8_t HEX_TO_BIN[256] = {
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
+      0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
+      0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+      0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C,
+      0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+   uint8_t* out_ptr = output;
+   bool top_nibble = true;
+
+   clear_mem(output, input_length / 2);
+
+   for(size_t i = 0; i != input_length; ++i)
+      {
+      const uint8_t bin = HEX_TO_BIN[static_cast<uint8_t>(input[i])];
+
+      if(bin >= 0x10)
+         {
+         if(bin == 0x80 && ignore_ws)
+            continue;
+
+         std::string bad_char(1, input[i]);
+         if(bad_char == "\t")
+           bad_char = "\\t";
+         else if(bad_char == "\n")
+           bad_char = "\\n";
+
+         throw Invalid_Argument(
+           std::string("hex_decode: invalid hex character '") +
+           bad_char + "'");
+         }
+
+      if(top_nibble)
+         *out_ptr |= bin << 4;
+      else
+         *out_ptr |= bin;
+
+      top_nibble = !top_nibble;
+      if(top_nibble)
+         ++out_ptr;
+      }
+
+   input_consumed = input_length;
+   size_t written = (out_ptr - output);
+
+   /*
+   * We only got half of a uint8_t at the end; zap the half-written
+   * output and mark it as unread
+   */
+   if(!top_nibble)
+      {
+      *out_ptr = 0;
+      input_consumed -= 1;
+      }
+
+   return written;
+   }
+
+size_t hex_decode(uint8_t output[],
+                  const char input[],
+                  size_t input_length,
+                  bool ignore_ws)
+   {
+   size_t consumed = 0;
+   size_t written = hex_decode(output, input, input_length,
+                               consumed, ignore_ws);
+
+   if(consumed != input_length)
+      throw Invalid_Argument("hex_decode: input did not have full bytes");
+
+   return written;
+   }
+
+size_t hex_decode(uint8_t output[],
+                  const std::string& input,
+                  bool ignore_ws)
+   {
+   return hex_decode(output, input.data(), input.length(), ignore_ws);
+   }
+
+secure_vector<uint8_t> hex_decode_locked(const char input[],
+                                      size_t input_length,
+                                      bool ignore_ws)
+   {
+   secure_vector<uint8_t> bin(1 + input_length / 2);
+
+   size_t written = hex_decode(bin.data(),
+                               input,
+                               input_length,
+                               ignore_ws);
+
+   bin.resize(written);
+   return bin;
+   }
+
+secure_vector<uint8_t> hex_decode_locked(const std::string& input,
+                                      bool ignore_ws)
+   {
+   return hex_decode_locked(input.data(), input.size(), ignore_ws);
+   }
+
+std::vector<uint8_t> hex_decode(const char input[],
+                             size_t input_length,
+                             bool ignore_ws)
+   {
+   std::vector<uint8_t> bin(1 + input_length / 2);
+
+   size_t written = hex_decode(bin.data(),
+                               input,
+                               input_length,
+                               ignore_ws);
+
+   bin.resize(written);
+   return bin;
+   }
+
+std::vector<uint8_t> hex_decode(const std::string& input,
+                             bool ignore_ws)
+   {
+   return hex_decode(input.data(), input.size(), ignore_ws);
+   }
+
+}
diff --git a/third_party/botan/src/lib/codec/hex/hex.h b/third_party/botan/src/lib/codec/hex/hex.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/codec/hex/hex.h
@@ -0,0 +1,148 @@
+/*
+* Hex Encoding and Decoding
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HEX_CODEC_H_
+#define BOTAN_HEX_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform hex encoding
+* @param output an array of at least input_length*2 bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param uppercase should output be upper or lower case?
+*/
+void BOTAN_PUBLIC_API(2,0) hex_encode(char output[],
+                          const uint8_t input[],
+                          size_t input_length,
+                          bool uppercase = true);
+
+/**
+* Perform hex encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @param uppercase should output be upper or lower case?
+* @return hexadecimal representation of input
+*/
+std::string BOTAN_PUBLIC_API(2,0) hex_encode(const uint8_t input[],
+                                 size_t input_length,
+                                 bool uppercase = true);
+
+/**
+* Perform hex encoding
+* @param input some input
+* @param uppercase should output be upper or lower case?
+* @return hexadecimal representation of input
+*/
+template<typename Alloc>
+std::string hex_encode(const std::vector<uint8_t, Alloc>& input,
+                       bool uppercase = true)
+   {
+   return hex_encode(input.data(), input.size(), uppercase);
+   }
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+                            const char input[],
+                            size_t input_length,
+                            size_t& input_consumed,
+                            bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+                            const char input[],
+                            size_t input_length,
+                            bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+                            const std::string& input,
+                            bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded hex output
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode(const char input[],
+           size_t input_length,
+           bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded hex output
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode(const std::string& input,
+           bool ignore_ws = true);
+
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded hex output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode_locked(const char input[],
+                  size_t input_length,
+                  bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return decoded hex output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode_locked(const std::string& input,
+                  bool ignore_ws = true);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compat/sodium/sodium.h b/third_party/botan/src/lib/compat/sodium/sodium.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium.h
@@ -0,0 +1,1453 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SODIUM_COMPAT_H_
+#define BOTAN_SODIUM_COMPAT_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* The Sodium namespace contains a partial implementation of the
+* libsodium API.
+*/
+namespace Sodium {
+
+// sodium/randombytes.h
+enum Sodium_Constants : size_t {
+   SODIUM_SIZE_MAX = 0xFFFFFFFF,
+
+   crypto_aead_chacha20poly1305_ABYTES = 16,
+   crypto_aead_chacha20poly1305_KEYBYTES = 32,
+   crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_chacha20poly1305_NPUBBYTES = 8,
+   crypto_aead_chacha20poly1305_NSECBYTES = 0,
+
+   crypto_aead_chacha20poly1305_ietf_ABYTES = 16,
+   crypto_aead_chacha20poly1305_ietf_KEYBYTES = 32,
+   crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_chacha20poly1305_ietf_NPUBBYTES = 12,
+   crypto_aead_chacha20poly1305_ietf_NSECBYTES = 0,
+
+   crypto_aead_xchacha20poly1305_ietf_ABYTES = 16,
+   crypto_aead_xchacha20poly1305_ietf_KEYBYTES = 32,
+   crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = 24,
+   crypto_aead_xchacha20poly1305_ietf_NSECBYTES = 0,
+
+   crypto_auth_hmacsha256_BYTES = 32,
+   crypto_auth_hmacsha256_KEYBYTES = 32,
+   crypto_auth_hmacsha512256_BYTES = 32,
+   crypto_auth_hmacsha512256_KEYBYTES = 32,
+   crypto_auth_hmacsha512_BYTES = 64,
+   crypto_auth_hmacsha512_KEYBYTES = 32,
+
+   crypto_auth_BYTES = crypto_auth_hmacsha512256_BYTES,
+   crypto_auth_KEYBYTES = crypto_auth_hmacsha512256_KEYBYTES,
+
+   crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16,
+   crypto_box_curve25519xsalsa20poly1305_MACBYTES = 16,
+   crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_box_curve25519xsalsa20poly1305_NONCEBYTES = 24,
+   crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_SEEDBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES + crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+
+   crypto_box_BEFORENMBYTES = crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES,
+   crypto_box_BOXZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES,
+   crypto_box_MACBYTES = crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+   crypto_box_MESSAGEBYTES_MAX = crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX,
+   crypto_box_NONCEBYTES = crypto_box_curve25519xsalsa20poly1305_NONCEBYTES,
+   crypto_box_PUBLICKEYBYTES = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES,
+   crypto_box_SECRETKEYBYTES = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES,
+   crypto_box_SEEDBYTES = crypto_box_curve25519xsalsa20poly1305_SEEDBYTES,
+   crypto_box_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_ZEROBYTES,
+
+   crypto_core_hchacha20_CONSTBYTES = 16,
+   crypto_core_hchacha20_INPUTBYTES = 16,
+   crypto_core_hchacha20_KEYBYTES = 32,
+   crypto_core_hchacha20_OUTPUTBYTES = 32,
+
+   crypto_core_hsalsa20_CONSTBYTES = 16,
+   crypto_core_hsalsa20_INPUTBYTES = 16,
+   crypto_core_hsalsa20_KEYBYTES = 32,
+   crypto_core_hsalsa20_OUTPUTBYTES = 32,
+
+   crypto_hash_sha256_BYTES = 32,
+   crypto_hash_sha512_BYTES = 64,
+   crypto_hash_BYTES = crypto_hash_sha512_BYTES,
+
+   crypto_onetimeauth_poly1305_BYTES = 16,
+   crypto_onetimeauth_poly1305_KEYBYTES = 32,
+   crypto_onetimeauth_BYTES = crypto_onetimeauth_poly1305_BYTES,
+   crypto_onetimeauth_KEYBYTES = crypto_onetimeauth_poly1305_KEYBYTES,
+
+   crypto_scalarmult_curve25519_BYTES = 32,
+   crypto_scalarmult_curve25519_SCALARBYTES = 32,
+   crypto_scalarmult_BYTES = crypto_scalarmult_curve25519_BYTES,
+   crypto_scalarmult_SCALARBYTES = crypto_scalarmult_curve25519_SCALARBYTES,
+
+   crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES = 16,
+   crypto_secretbox_xsalsa20poly1305_KEYBYTES = 32,
+   crypto_secretbox_xsalsa20poly1305_MACBYTES = 16,
+   crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_secretbox_xsalsa20poly1305_NONCEBYTES = 24,
+   crypto_secretbox_xsalsa20poly1305_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES + crypto_secretbox_xsalsa20poly1305_MACBYTES,
+
+   crypto_secretbox_BOXZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES,
+   crypto_secretbox_KEYBYTES = crypto_secretbox_xsalsa20poly1305_KEYBYTES,
+   crypto_secretbox_MACBYTES = crypto_secretbox_xsalsa20poly1305_MACBYTES,
+   crypto_secretbox_MESSAGEBYTES_MAX = crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX,
+   crypto_secretbox_NONCEBYTES = crypto_secretbox_xsalsa20poly1305_NONCEBYTES,
+   crypto_secretbox_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_ZEROBYTES,
+
+   crypto_shorthash_siphash24_BYTES = 8,
+   crypto_shorthash_siphash24_KEYBYTES = 16,
+   crypto_shorthash_BYTES = crypto_shorthash_siphash24_BYTES,
+   crypto_shorthash_KEYBYTES = crypto_shorthash_siphash24_KEYBYTES,
+
+   crypto_sign_ed25519_BYTES = 64,
+   crypto_sign_ed25519_MESSAGEBYTES_MAX = (SODIUM_SIZE_MAX - crypto_sign_ed25519_BYTES),
+   crypto_sign_ed25519_PUBLICKEYBYTES = 32,
+   crypto_sign_ed25519_SECRETKEYBYTES = (32 + 32),
+   crypto_sign_ed25519_SEEDBYTES = 32,
+   crypto_sign_BYTES = crypto_sign_ed25519_BYTES,
+   crypto_sign_MESSAGEBYTES_MAX = crypto_sign_ed25519_MESSAGEBYTES_MAX,
+   crypto_sign_PUBLICKEYBYTES = crypto_sign_ed25519_PUBLICKEYBYTES,
+   crypto_sign_SECRETKEYBYTES = crypto_sign_ed25519_SECRETKEYBYTES,
+   crypto_sign_SEEDBYTES = crypto_sign_ed25519_SEEDBYTES,
+
+   crypto_stream_chacha20_KEYBYTES = 32,
+   crypto_stream_chacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_chacha20_NONCEBYTES = 8,
+   crypto_stream_chacha20_ietf_KEYBYTES = 32,
+   crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_chacha20_ietf_NONCEBYTES = 12,
+   crypto_stream_salsa20_KEYBYTES = 32,
+   crypto_stream_salsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_salsa20_NONCEBYTES = 8,
+   crypto_stream_xchacha20_KEYBYTES = 32,
+   crypto_stream_xchacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_xchacha20_NONCEBYTES = 24,
+   crypto_stream_xsalsa20_KEYBYTES = 32,
+   crypto_stream_xsalsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_xsalsa20_NONCEBYTES = 24,
+   crypto_stream_KEYBYTES = crypto_stream_xsalsa20_KEYBYTES,
+   crypto_stream_MESSAGEBYTES_MAX = crypto_stream_xsalsa20_MESSAGEBYTES_MAX,
+   crypto_stream_NONCEBYTES = crypto_stream_xsalsa20_NONCEBYTES,
+
+   crypto_verify_16_BYTES = 16,
+   crypto_verify_32_BYTES = 32,
+   crypto_verify_64_BYTES = 64,
+
+   randombytes_SEEDBYTES = 32,
+};
+
+inline const char* sodium_version_string() { return "Botan Sodium Compat"; }
+
+inline int sodium_library_version_major() { return 0; }
+
+inline int sodium_library_version_minor() { return 0; }
+
+inline int sodium_library_minimal() { return 0; }
+
+inline int sodium_init() { return 0; }
+
+// sodium/crypto_verify_{16,32,64}.h
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_16(const uint8_t x[16], const uint8_t y[16]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_32(const uint8_t x[32], const uint8_t y[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_64(const uint8_t x[64], const uint8_t y[64]);
+
+// sodium/utils.h
+BOTAN_PUBLIC_API(2,11)
+void sodium_memzero(void* ptr, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_memcmp(const void* x, const void* y, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_compare(const uint8_t x[], const uint8_t y[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_is_zero(const uint8_t nonce[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_increment(uint8_t n[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_add(uint8_t a[], const uint8_t b[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_malloc(size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_allocarray(size_t count, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_free(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_noaccess(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_readwrite(void* ptr);
+
+// sodium/randombytes.h
+
+inline size_t randombytes_seedbytes() { return randombytes_SEEDBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf(void* buf, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf_deterministic(void* buf, size_t size,
+                                   const uint8_t seed[randombytes_SEEDBYTES]);
+
+BOTAN_PUBLIC_API(2,11)
+uint32_t randombytes_uniform(uint32_t upper_bound);
+
+inline uint32_t randombytes_random()
+   {
+   uint32_t x = 0;
+   randombytes_buf(&x, 4);
+   return x;
+   }
+
+inline void randombytes_stir() {}
+
+inline int randombytes_close() { return 0; }
+
+inline const char* randombytes_implementation_name()
+   {
+   return "botan";
+   }
+
+inline void randombytes(uint8_t buf[], size_t buf_len)
+   {
+   return randombytes_buf(buf, buf_len);
+   }
+
+// sodium/crypto_secretbox_xsalsa20poly1305.h
+
+inline size_t crypto_secretbox_xsalsa20poly1305_keybytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_KEYBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_noncebytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_NONCEBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_macbytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_MACBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_messagebytes_max()
+   {
+   return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+                                      const uint8_t ptext[],
+                                      size_t ptext_len,
+                                      const uint8_t nonce[],
+                                      const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+                                           const uint8_t ctext[],
+                                           size_t ctext_len,
+                                           const uint8_t nonce[],
+                                           const uint8_t key[]);
+
+inline void crypto_secretbox_xsalsa20poly1305_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_boxzerobytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_zerobytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_ZEROBYTES;
+   }
+
+// sodium/crypto_secretbox.h
+
+inline size_t crypto_secretbox_keybytes() { return crypto_secretbox_KEYBYTES; }
+
+inline size_t crypto_secretbox_noncebytes() { return crypto_secretbox_NONCEBYTES; }
+
+inline size_t crypto_secretbox_macbytes() { return crypto_secretbox_MACBYTES; }
+
+inline size_t crypto_secretbox_messagebytes_max() { return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_secretbox_primitive() { return "xsalsa20poly1305"; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+                              const uint8_t ptext[],
+                              size_t ptext_len,
+                              const uint8_t nonce[],
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_open_detached(uint8_t ptext[],
+                                   const uint8_t ctext[],
+                                   const uint8_t mac[],
+                                   size_t ctext_len,
+                                   const uint8_t nonce[],
+                                   const uint8_t key[]);
+
+inline int crypto_secretbox_easy(uint8_t ctext[], const uint8_t ptext[],
+                                 size_t ptext_len, const uint8_t nonce[],
+                                 const uint8_t key[])
+   {
+   return crypto_secretbox_detached(ctext + crypto_secretbox_MACBYTES, ctext,
+                                    ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_secretbox_open_easy(uint8_t out[], const uint8_t ctext[], size_t ctext_len,
+                                      const uint8_t nonce[], const uint8_t key[])
+   {
+   if(ctext_len < crypto_secretbox_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_secretbox_open_detached(out, ctext + crypto_secretbox_MACBYTES,
+                                         ctext, ctext_len - crypto_secretbox_MACBYTES,
+                                         nonce, key);
+   }
+
+inline void crypto_secretbox_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_secretbox_zerobytes()
+   {
+   return crypto_secretbox_ZEROBYTES;
+   }
+
+inline size_t crypto_secretbox_boxzerobytes()
+   {
+   return crypto_secretbox_BOXZEROBYTES;
+   }
+
+inline int crypto_secretbox(uint8_t ctext[], const uint8_t ptext[],
+                            size_t ptext_len, const uint8_t nonce[],
+                            const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_secretbox_open(uint8_t ptext[], const uint8_t ctext[],
+                                 size_t ctext_len, const uint8_t nonce[],
+                                 const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+   }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_chacha20poly1305_ietf_keybytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_nsecbytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_NSECBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_npubbytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_abytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_ABYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_messagebytes_max()
+   {
+   return crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                              unsigned long long* ctext_len,
+                                              const uint8_t ptext[],
+                                              size_t ptext_len,
+                                              const uint8_t ad[],
+                                              size_t ad_len,
+                                              const uint8_t unused_secret_nonce[],
+                                              const uint8_t nonce[],
+                                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                              unsigned long long* ptext_len,
+                                              uint8_t unused_secret_nonce[],
+                                              const uint8_t ctext[],
+                                              size_t ctext_len,
+                                              const uint8_t ad[],
+                                              size_t ad_len,
+                                              const uint8_t nonce[],
+                                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                       uint8_t mac[],
+                                                       unsigned long long* mac_len,
+                                                       const uint8_t ptext[],
+                                                       size_t ptext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t unused_secret_nonce[],
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t m[],
+                                                       uint8_t unused_secret_nonce[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t mac[],
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
+   }
+
+inline size_t crypto_aead_chacha20poly1305_keybytes()
+   {
+   return crypto_aead_chacha20poly1305_KEYBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_nsecbytes()
+   {
+   return crypto_aead_chacha20poly1305_NSECBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_npubbytes()
+   {
+   return crypto_aead_chacha20poly1305_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_abytes()
+   {
+   return crypto_aead_chacha20poly1305_ABYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_messagebytes_max()
+   {
+   return crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                         unsigned long long* ctext_len,
+                                         const uint8_t ptext[],
+                                         size_t ptext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t unused_secret_nonce[],
+                                         const uint8_t nonce[],
+                                         const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt(uint8_t m[],
+                                         unsigned long long* ptext_len,
+                                         uint8_t unused_secret_nonce[],
+                                         const uint8_t ctext[],
+                                         size_t ctext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                  uint8_t mac[],
+                                                  unsigned long long* mac_len,
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t unused_secret_nonce[],
+                                                  const uint8_t nonce[],
+                                                  const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt_detached(uint8_t m[],
+                                                  uint8_t unused_secret_nonce[],
+                                                  const uint8_t ctext[],
+                                                  size_t ctext_len,
+                                                  const uint8_t mac[],
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_keygen(uint8_t k[32])
+   {
+   randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_keybytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_nsecbytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_NSECBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_npubbytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_abytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_ABYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                               unsigned long long* ctext_len,
+                                               const uint8_t ptext[],
+                                               size_t ptext_len,
+                                               const uint8_t ad[],
+                                               size_t ad_len,
+                                               const uint8_t unused_secret_nonce[],
+                                               const uint8_t nonce[],
+                                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                               unsigned long long* ptext_len,
+                                               uint8_t unused_secret_nonce[],
+                                               const uint8_t ctext[],
+                                               size_t ctext_len,
+                                               const uint8_t ad[],
+                                               size_t ad_len,
+                                               const uint8_t nonce[],
+                                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                        uint8_t mac[],
+                                                        unsigned long long* mac_len,
+                                                        const uint8_t ptext[],
+                                                        size_t ptext_len,
+                                                        const uint8_t ad[],
+                                                        size_t ad_len,
+                                                        const uint8_t unused_secret_nonce[],
+                                                        const uint8_t nonce[],
+                                                        const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                        uint8_t unused_secret_nonce[],
+                                                        const uint8_t ctext[],
+                                                        size_t ctext_len,
+                                                        const uint8_t mac[],
+                                                        const uint8_t ad[],
+                                                        size_t ad_len,
+                                                        const uint8_t nonce[],
+                                                        const uint8_t key[]);
+
+inline void crypto_aead_xchacha20poly1305_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_box_curve25519xsalsa20poly1305.h
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_seedbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_SEEDBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_publickeybytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_secretkeybytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_beforenmbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_noncebytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_NONCEBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_macbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_MACBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_messagebytes_max()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_boxzerobytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_zerobytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+                                                       uint8_t sk[32],
+                                                       const uint8_t seed[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+                                                  uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+                                                   const uint8_t pk[32],
+                                                   const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+                                          const uint8_t ptext[],
+                                          size_t ptext_len,
+                                          const uint8_t nonce[],
+                                          const uint8_t pk[32],
+                                          const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+                                               const uint8_t ctext[],
+                                               size_t ctext_len,
+                                               const uint8_t nonce[],
+                                               const uint8_t pk[32],
+                                               const uint8_t sk[32]);
+
+inline int crypto_box_curve25519xsalsa20poly1305_afternm(uint8_t ctext[],
+                                                         const uint8_t ptext[],
+                                                         size_t ptext_len,
+                                                         const uint8_t nonce[],
+                                                         const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_curve25519xsalsa20poly1305_open_afternm(uint8_t ptext[],
+                                                              const uint8_t ctext[],
+                                                              size_t ctext_len,
+                                                              const uint8_t nonce[],
+                                                              const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+   }
+
+// sodium/crypto_box.h
+
+inline size_t crypto_box_seedbytes()
+   {
+   return crypto_box_SEEDBYTES;
+   }
+
+inline size_t crypto_box_publickeybytes()
+   {
+   return crypto_box_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_box_secretkeybytes()
+   {
+   return crypto_box_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_box_noncebytes()
+   {
+   return crypto_box_NONCEBYTES;
+   }
+
+inline size_t crypto_box_macbytes()
+   {
+   return crypto_box_MACBYTES;
+   }
+
+inline size_t crypto_box_messagebytes_max()
+   {
+   return crypto_box_MESSAGEBYTES_MAX;
+   }
+
+inline size_t crypto_box_beforenmbytes()
+   {
+   return crypto_box_BEFORENMBYTES;
+   }
+
+inline const char* crypto_box_primitive() { return "curve25519xsalsa20poly1305"; }
+
+inline int crypto_box_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+                                   const uint8_t seed[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_seed_keypair(pk, sk, seed);
+   }
+
+inline int crypto_box_keypair(uint8_t pk[32], uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_keypair(pk, sk);
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+                        const uint8_t ptext[], size_t ptext_len,
+                        const uint8_t nonce[], const uint8_t pk[32],
+                        const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+                             const uint8_t mac[],
+                             size_t ctext_len,
+                             const uint8_t nonce[],
+                             const uint8_t pk[32],
+                             const uint8_t sk[32]);
+
+inline int crypto_box_easy(uint8_t ctext[], const uint8_t ptext[],
+                           size_t ptext_len, const uint8_t nonce[],
+                           const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_detached(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, pk, sk);
+   }
+
+inline int crypto_box_open_easy(uint8_t ptext[], const uint8_t ctext[],
+                                size_t ctext_len, const uint8_t nonce[],
+                                const uint8_t pk[32], const uint8_t sk[32])
+   {
+   if(ctext_len < crypto_box_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_box_open_detached(ptext, ctext + crypto_box_MACBYTES,
+                                   ctext, ctext_len - crypto_box_MACBYTES,
+                                   nonce, pk, sk);
+   }
+
+inline int crypto_box_beforenm(uint8_t key[], const uint8_t pk[32],
+                               const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_beforenm(key, pk, sk);
+   }
+
+inline int crypto_box_afternm(uint8_t ctext[], const uint8_t ptext[],
+                              size_t ptext_len, const uint8_t nonce[],
+                              const uint8_t key[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_open_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                   size_t ctext_len, const uint8_t nonce[],
+                                   const uint8_t key[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, key);
+   }
+
+inline int crypto_box_open_detached_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                            const uint8_t mac[],
+                                            size_t ctext_len, const uint8_t nonce[],
+                                            const uint8_t key[])
+   {
+   return crypto_secretbox_open_detached(ptext, ctext, mac, ctext_len, nonce, key);
+   }
+
+inline int crypto_box_open_easy_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                        size_t ctext_len, const uint8_t nonce[],
+                                        const uint8_t key[])
+   {
+   if(ctext_len < crypto_box_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_box_open_detached_afternm(ptext, ctext + crypto_box_MACBYTES,
+                                           ctext, ctext_len - crypto_box_MACBYTES,
+                                           nonce, key);
+   }
+
+inline int crypto_box_detached_afternm(uint8_t ctext[], uint8_t mac[],
+                                       const uint8_t ptext[], size_t ptext_len,
+                                       const uint8_t nonce[], const uint8_t key[])
+   {
+   return crypto_secretbox_detached(ctext, mac, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_easy_afternm(uint8_t ctext[], const uint8_t ptext[],
+                                   size_t ptext_len, const uint8_t nonce[],
+                                   const uint8_t key[])
+   {
+   return crypto_box_detached_afternm(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline size_t crypto_box_zerobytes() { return crypto_box_ZEROBYTES; }
+
+inline size_t crypto_box_boxzerobytes() { return crypto_box_BOXZEROBYTES; }
+
+inline int crypto_box(uint8_t ctext[], const uint8_t ptext[],
+                      size_t ptext_len, const uint8_t nonce[],
+                      const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305(ctext, ptext, ptext_len, nonce, pk, sk);
+   }
+
+inline int crypto_box_open(uint8_t ptext[], const uint8_t ctext[],
+                           size_t ctext_len, const uint8_t nonce[],
+                           const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, pk, sk);
+   }
+
+// sodium/crypto_hash_sha512.h
+
+inline size_t crypto_hash_sha512_bytes() { return crypto_hash_sha512_BYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha512.h
+
+inline size_t crypto_auth_hmacsha512_bytes() { return crypto_auth_hmacsha512_BYTES; }
+
+inline size_t crypto_auth_hmacsha512_keybytes() { return crypto_auth_hmacsha512_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512(uint8_t out[],
+                           const uint8_t in[],
+                           size_t in_len,
+                           const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512_verify(const uint8_t h[],
+                                  const uint8_t in[],
+                                  size_t in_len,
+                                  const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_auth_hmacsha512256.h
+
+inline size_t crypto_auth_hmacsha512256_bytes()
+   {
+   return crypto_auth_hmacsha512256_BYTES;
+   }
+
+inline size_t crypto_auth_hmacsha512256_keybytes()
+   {
+   return crypto_auth_hmacsha512256_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256(uint8_t out[],
+                              const uint8_t in[],
+                              size_t in_len,
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256_verify(const uint8_t h[],
+                                     const uint8_t in[],
+                                     size_t in_len,
+                                     const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512256_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_auth.h
+
+inline size_t crypto_auth_bytes() { return crypto_auth_BYTES; }
+
+inline size_t crypto_auth_keybytes() { return crypto_auth_KEYBYTES; }
+
+inline const char* crypto_auth_primitive() { return "hmacsha512256"; }
+
+inline int crypto_auth(uint8_t out[], const uint8_t in[],
+                       size_t in_len, const uint8_t key[])
+   {
+   return crypto_auth_hmacsha512256(out, in, in_len, key);
+   }
+
+inline int crypto_auth_verify(const uint8_t mac[], const uint8_t in[],
+                              size_t in_len, const uint8_t key[])
+   {
+   return crypto_auth_hmacsha512256_verify(mac, in, in_len, key);
+   }
+
+inline void crypto_auth_keygen(uint8_t k[])
+   {
+   return randombytes_buf(k, crypto_auth_KEYBYTES);
+   }
+
+// sodium/crypto_hash_sha256.h
+
+inline size_t crypto_hash_sha256_bytes()
+   {
+   return crypto_hash_sha256_BYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha256.h
+
+inline size_t crypto_auth_hmacsha256_bytes()
+   {
+   return crypto_auth_hmacsha256_BYTES;
+   }
+
+inline size_t crypto_auth_hmacsha256_keybytes()
+   {
+   return crypto_auth_hmacsha256_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256(uint8_t out[],
+                           const uint8_t in[],
+                           size_t in_len,
+                           const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256_verify(const uint8_t h[],
+                                  const uint8_t in[],
+                                  size_t in_len,
+                                  const uint8_t key[]);
+
+inline void crypto_auth_hmacsha256_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream_xsalsa20.h
+
+inline size_t crypto_stream_xsalsa20_keybytes()
+   {
+   return crypto_stream_xsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_xsalsa20_noncebytes()
+   {
+   return crypto_stream_xsalsa20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_xsalsa20_messagebytes_max()
+   {
+   return crypto_stream_xsalsa20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20(uint8_t out[], size_t ctext_len,
+                           const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t ptext[],
+                               size_t ptext_len, const uint8_t nonce[],
+                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                  size_t ptext_len,
+                                  const uint8_t nonce[], uint64_t ic,
+                                  const uint8_t key[]);
+
+inline void crypto_stream_xsalsa20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_core_hsalsa20.h
+
+inline size_t crypto_core_hsalsa20_outputbytes()
+   {
+   return crypto_core_hsalsa20_OUTPUTBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_inputbytes()
+   {
+   return crypto_core_hsalsa20_INPUTBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_keybytes()
+   {
+   return crypto_core_hsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_constbytes()
+   {
+   return crypto_core_hsalsa20_CONSTBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+                         const uint8_t key[], const uint8_t c[]);
+
+// sodium/crypto_hash.h
+
+inline size_t crypto_hash_bytes()
+   {
+   return crypto_hash_BYTES;
+   }
+
+inline int crypto_hash(uint8_t out[], const uint8_t in[], size_t in_len)
+   {
+   return crypto_hash_sha512(out, in, in_len);
+   }
+
+inline const char* crypto_hash_primitive() { return "sha512"; }
+
+// sodium/crypto_onetimeauth_poly1305.h
+
+inline size_t crypto_onetimeauth_poly1305_bytes()
+   {
+   return crypto_onetimeauth_poly1305_BYTES;
+   }
+
+inline size_t crypto_onetimeauth_poly1305_keybytes()
+   {
+   return crypto_onetimeauth_poly1305_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305(uint8_t out[],
+                                const uint8_t in[],
+                                size_t in_len,
+                                const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305_verify(const uint8_t h[],
+                                       const uint8_t in[],
+                                       size_t in_len,
+                                       const uint8_t key[]);
+
+inline void crypto_onetimeauth_poly1305_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_onetimeauth.h
+
+inline size_t crypto_onetimeauth_bytes() { return crypto_onetimeauth_BYTES; }
+
+inline size_t crypto_onetimeauth_keybytes() { return crypto_onetimeauth_KEYBYTES; }
+
+inline const char* crypto_onetimeauth_primitive() { return "poly1305"; }
+
+inline int crypto_onetimeauth(uint8_t out[], const uint8_t in[],
+                              size_t in_len, const uint8_t key[])
+   {
+   return crypto_onetimeauth_poly1305(out, in, in_len, key);
+   }
+
+inline int crypto_onetimeauth_verify(const uint8_t h[], const uint8_t in[],
+                                     size_t in_len, const uint8_t key[])
+   {
+   return crypto_onetimeauth_poly1305_verify(h, in, in_len, key);
+   }
+
+inline void crypto_onetimeauth_keygen(uint8_t k[32])
+   {
+   return crypto_onetimeauth_poly1305_keygen(k);
+   }
+
+// sodium/crypto_scalarmult_curve25519.h
+
+inline size_t crypto_scalarmult_curve25519_bytes()
+   {
+   return crypto_scalarmult_curve25519_BYTES;
+   }
+
+inline size_t crypto_scalarmult_curve25519_scalarbytes()
+   {
+   return crypto_scalarmult_curve25519_SCALARBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t basepoint[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32]);
+
+// sodium/crypto_scalarmult.h
+
+inline size_t crypto_scalarmult_bytes() { return crypto_scalarmult_curve25519_bytes(); }
+
+inline size_t crypto_scalarmult_scalarbytes() { return crypto_scalarmult_curve25519_scalarbytes(); }
+
+inline const char* crypto_scalarmult_primitive() { return "curve25519"; }
+
+inline int crypto_scalarmult_base(uint8_t out[], const uint8_t scalar[])
+   {
+   return crypto_scalarmult_curve25519_base(out, scalar);
+   }
+
+inline int crypto_scalarmult(uint8_t out[], const uint8_t scalar[], const uint8_t base[])
+   {
+   return crypto_scalarmult_curve25519(out, scalar, base);
+   }
+
+// sodium/crypto_stream_chacha20.h
+
+inline size_t crypto_stream_chacha20_keybytes()
+   {
+   return crypto_stream_chacha20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_noncebytes()
+   {
+   return crypto_stream_chacha20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_messagebytes_max()
+   {
+   return crypto_stream_chacha20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20(uint8_t out[], size_t ctext_len,
+                           const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor(uint8_t out[], const uint8_t ptext[],
+                               size_t ptext_len, const uint8_t nonce[],
+                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                  size_t ptext_len,
+                                  const uint8_t nonce[], uint64_t ic,
+                                  const uint8_t key[]);
+
+inline void crypto_stream_chacha20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_stream_chacha20_ietf_keybytes()
+   {
+   return crypto_stream_chacha20_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_ietf_noncebytes()
+   {
+   return crypto_stream_chacha20_ietf_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_ietf_messagebytes_max()
+   {
+   return crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf(uint8_t out[], size_t ctext_len,
+                                const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor(uint8_t out[], const uint8_t ptext[],
+                                    size_t ptext_len, const uint8_t nonce[],
+                                    const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                       size_t ptext_len,
+                                       const uint8_t nonce[], uint32_t ic,
+                                       const uint8_t key[]);
+
+inline void crypto_stream_chacha20_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream_xchacha20.h
+
+inline size_t crypto_stream_xchacha20_keybytes()
+   {
+   return crypto_stream_xchacha20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_xchacha20_noncebytes()
+   {
+   return crypto_stream_xchacha20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_xchacha20_messagebytes_max()
+   {
+   return crypto_stream_xchacha20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20(uint8_t out[], size_t ctext_len,
+                            const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t ptext[],
+                                size_t ptext_len, const uint8_t nonce[],
+                                const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                   size_t ptext_len,
+                                   const uint8_t nonce[], uint64_t ic,
+                                   const uint8_t key[]);
+
+inline void crypto_stream_xchacha20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, crypto_stream_xchacha20_KEYBYTES);
+   }
+
+// sodium/crypto_stream_salsa20.h
+
+inline size_t crypto_stream_salsa20_keybytes()
+   {
+   return crypto_stream_xsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_salsa20_noncebytes()
+   {
+   return crypto_stream_salsa20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_salsa20_messagebytes_max()
+   {
+   return crypto_stream_salsa20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20(uint8_t out[], size_t ctext_len,
+                          const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor(uint8_t out[], const uint8_t ptext[],
+                              size_t ptext_len, const uint8_t nonce[],
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                 size_t ptext_len,
+                                 const uint8_t nonce[], uint64_t ic,
+                                 const uint8_t key[]);
+
+inline void crypto_stream_salsa20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream.h
+
+inline size_t crypto_stream_keybytes() { return crypto_stream_xsalsa20_keybytes(); }
+
+inline size_t crypto_stream_noncebytes() { return crypto_stream_xsalsa20_noncebytes(); }
+
+inline size_t crypto_stream_messagebytes_max() { return crypto_stream_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_stream_primitive() { return "xsalsa20"; }
+
+inline int crypto_stream(uint8_t out[], size_t out_len,
+                         const uint8_t nonce[24], const uint8_t key[32])
+   {
+   return crypto_stream_xsalsa20(out, out_len, nonce, key);
+   }
+
+inline int crypto_stream_xor(uint8_t out[], const uint8_t in[], size_t in_len,
+                             const uint8_t nonce[24], const uint8_t key[32])
+   {
+   return crypto_stream_xsalsa20_xor(out, in, in_len, nonce, key);
+   }
+
+inline void crypto_stream_keygen(uint8_t key[32])
+   {
+   return randombytes_buf(key, 32);
+   }
+
+// sodium/crypto_shorthash_siphash24.h
+
+inline size_t crypto_shorthash_siphash24_bytes() { return crypto_shorthash_siphash24_BYTES; }
+
+inline size_t crypto_shorthash_siphash24_keybytes() { return crypto_shorthash_siphash24_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[], size_t in_len, const uint8_t key[16]);
+
+// sodium/crypto_shorthash.h
+
+inline size_t crypto_shorthash_bytes() { return crypto_shorthash_siphash24_bytes(); }
+
+inline size_t crypto_shorthash_keybytes() { return crypto_shorthash_siphash24_keybytes(); }
+
+inline const char* crypto_shorthash_primitive() { return "siphash24"; }
+
+inline int crypto_shorthash(uint8_t out[], const uint8_t in[],
+                            size_t in_len, const uint8_t k[16])
+   {
+   return crypto_shorthash_siphash24(out, in, in_len, k);
+   }
+
+inline void crypto_shorthash_keygen(uint8_t k[16])
+   {
+   randombytes_buf(k, crypto_shorthash_siphash24_KEYBYTES);
+   }
+
+// sodium/crypto_sign_ed25519.h
+
+inline size_t crypto_sign_ed25519_bytes()
+   {
+   return crypto_sign_ed25519_BYTES;
+   }
+
+inline size_t crypto_sign_ed25519_seedbytes()
+   {
+   return crypto_sign_ed25519_SEEDBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_publickeybytes()
+   {
+   return crypto_sign_ed25519_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_secretkeybytes()
+   {
+   return crypto_sign_ed25519_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_messagebytes_max()
+   {
+   return crypto_sign_ed25519_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_detached(uint8_t sig[],
+                                 unsigned long long* sig_len,
+                                 const uint8_t msg[],
+                                 size_t msg_len,
+                                 const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_verify_detached(const uint8_t sig[],
+                                        const uint8_t msg[],
+                                        size_t msg_len,
+                                        const uint8_t pk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_seed_keypair(uint8_t pk[], uint8_t sk[],
+                                     const uint8_t seed[]);
+
+// sodium/crypto_sign.h
+
+inline size_t crypto_sign_bytes()
+   {
+   return crypto_sign_BYTES;
+   }
+
+inline size_t crypto_sign_seedbytes()
+   {
+   return crypto_sign_SEEDBYTES;
+   }
+
+inline size_t crypto_sign_publickeybytes()
+   {
+   return crypto_sign_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_sign_secretkeybytes()
+   {
+   return crypto_sign_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_sign_messagebytes_max()
+   {
+   return crypto_sign_MESSAGEBYTES_MAX;
+   }
+
+inline const char* crypto_sign_primitive()
+   {
+   return "ed25519";
+   }
+
+inline int crypto_sign_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+                                    const uint8_t seed[])
+   {
+   return crypto_sign_ed25519_seed_keypair(pk, sk, seed);
+   }
+
+inline int crypto_sign_keypair(uint8_t pk[32], uint8_t sk[32])
+   {
+   return crypto_sign_ed25519_keypair(pk, sk);
+   }
+
+inline int crypto_sign_detached(uint8_t sig[], unsigned long long* sig_len,
+                                const uint8_t msg[], size_t msg_len,
+                                const uint8_t sk[32])
+   {
+   return crypto_sign_ed25519_detached(sig, sig_len, msg, msg_len, sk);
+   }
+
+inline int crypto_sign_verify_detached(const uint8_t sig[],
+                                       const uint8_t in[],
+                                       size_t in_len,
+                                       const uint8_t pk[32])
+   {
+   return crypto_sign_ed25519_verify_detached(sig, in, in_len, pk);
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp b/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp
@@ -0,0 +1,60 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/ed25519.h>
+#include <botan/curve25519.h>
+
+namespace Botan {
+
+int Sodium::crypto_scalarmult_curve25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
+   {
+   curve25519_donna(out, scalar, point);
+   return 0;
+   }
+
+int Sodium::crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32])
+   {
+   curve25519_basepoint(out, scalar);
+   return 0;
+   }
+int Sodium::crypto_sign_ed25519_detached(uint8_t sig[],
+                                         unsigned long long* sig_len,
+                                         const uint8_t msg[],
+                                         size_t msg_len,
+                                         const uint8_t sk[32])
+   {
+   ed25519_sign(sig, msg, msg_len, sk, nullptr, 0);
+
+   if(sig_len)
+      *sig_len = 64;
+   return 0;
+   }
+
+int Sodium::crypto_sign_ed25519_verify_detached(const uint8_t sig[],
+                                                const uint8_t msg[],
+                                                size_t msg_len,
+                                                const uint8_t pk[32])
+   {
+   const bool ok = ed25519_verify(msg, msg_len, sig, pk, nullptr, 0);
+   return ok ? 0 : -1;
+   }
+
+int Sodium::crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64])
+   {
+   secure_vector<uint8_t> seed(32);
+   randombytes_buf(seed.data(), seed.size());
+   return crypto_sign_ed25519_seed_keypair(pk, sk, seed.data());
+   }
+
+int Sodium::crypto_sign_ed25519_seed_keypair(uint8_t pk[], uint8_t sk[],
+                                             const uint8_t seed[])
+   {
+   ed25519_gen_keypair(pk, sk, seed);
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp b/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp
@@ -0,0 +1,359 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/aead.h>
+
+namespace Botan {
+
+namespace {
+
+int sodium_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                         unsigned long long* ctext_len,
+                                         const uint8_t ptext[],
+                                         size_t ptext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         size_t nonce_len,
+                                         const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ptext_len + 16);
+   buf.assign(ptext, ptext + ptext_len);
+
+   chacha20poly1305->finish(buf);
+
+   copy_mem(ctext, buf.data(), buf.size());
+   if(ctext_len)
+      *ctext_len = buf.size();
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+                                         unsigned long long* ptext_len,
+                                         const uint8_t ctext[],
+                                         size_t ctext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         size_t nonce_len,
+                                         const uint8_t key[])
+   {
+   if(ctext_len < 16)
+      return -1;
+
+   *ptext_len = 0;
+
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.assign(ctext, ctext + ctext_len);
+
+   try
+      {
+      chacha20poly1305->finish(buf);
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      return -1;
+      }
+
+   *ptext_len = ctext_len - 16;
+
+   copy_mem(ptext, buf.data(), buf.size());
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                  uint8_t mac[],
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  size_t nonce_len,
+                                                  const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ptext_len + 16);
+   buf.assign(ptext, ptext + ptext_len);
+
+   chacha20poly1305->finish(buf);
+
+   copy_mem(ctext, buf.data(), ptext_len);
+   copy_mem(mac, buf.data() + ptext_len, 16);
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+                                                  const uint8_t ctext[],
+                                                  size_t ctext_len,
+                                                  const uint8_t mac[],
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  size_t nonce_len,
+                                                  const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ctext_len + 16);
+   buf.assign(ctext, ctext + ctext_len);
+   buf.insert(buf.end(), mac, mac + 16);
+
+   try
+      {
+      chacha20poly1305->finish(buf);
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      return -1;
+      }
+
+   copy_mem(ptext, buf.data(), buf.size());
+   return 0;
+   }
+
+}
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                                      unsigned long long* ctext_len,
+                                                      const uint8_t ptext[],
+                                                      size_t ptext_len,
+                                                      const uint8_t ad[],
+                                                      size_t ad_len,
+                                                      const uint8_t unused_secret_nonce[],
+                                                      const uint8_t nonce[],
+                                                      const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                                      unsigned long long* ptext_len,
+                                                      uint8_t unused_secret_nonce[],
+                                                      const uint8_t ctext[],
+                                                      size_t ctext_len,
+                                                      const uint8_t ad[],
+                                                      size_t ad_len,
+                                                      const uint8_t nonce[],
+                                                      const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                               uint8_t mac[],
+                                                               unsigned long long* mac_len,
+                                                               const uint8_t ptext[],
+                                                               size_t ptext_len,
+                                                               const uint8_t ad[],
+                                                               size_t ad_len,
+                                                               const uint8_t unused_secret_nonce[],
+                                                               const uint8_t nonce[],
+                                                               const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                               uint8_t unused_secret_nonce[],
+                                                               const uint8_t ctext[],
+                                                               size_t ctext_len,
+                                                               const uint8_t mac[],
+                                                               const uint8_t ad[],
+                                                               size_t ad_len,
+                                                               const uint8_t nonce[],
+                                                               const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                                 unsigned long long* ctext_len,
+                                                 const uint8_t ptext[],
+                                                 size_t ptext_len,
+                                                 const uint8_t ad[],
+                                                 size_t ad_len,
+                                                 const uint8_t unused_secret_nonce[],
+                                                 const uint8_t nonce[],
+                                                 const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+                                                 unsigned long long* ptext_len,
+                                                 uint8_t unused_secret_nonce[],
+                                                 const uint8_t ctext[],
+                                                 size_t ctext_len,
+                                                 const uint8_t ad[],
+                                                 size_t ad_len,
+                                                 const uint8_t nonce[],
+                                                 const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                          uint8_t mac[],
+                                                          unsigned long long* mac_len,
+                                                          const uint8_t ptext[],
+                                                          size_t ptext_len,
+                                                          const uint8_t ad[],
+                                                          size_t ad_len,
+                                                          const uint8_t unused_secret_nonce[],
+                                                          const uint8_t nonce[],
+                                                          const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+                                                          uint8_t unused_secret_nonce[],
+                                                          const uint8_t ctext[],
+                                                          size_t ctext_len,
+                                                          const uint8_t mac[],
+                                                          const uint8_t ad[],
+                                                          size_t ad_len,
+                                                          const uint8_t nonce[],
+                                                          const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                                       unsigned long long* ctext_len,
+                                                       const uint8_t ptext[],
+                                                       size_t ptext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t unused_secret_nonce[],
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                                       unsigned long long* ptext_len,
+                                                       uint8_t unused_secret_nonce[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                                uint8_t mac[],
+                                                                unsigned long long* mac_len,
+                                                                const uint8_t ptext[],
+                                                                size_t ptext_len,
+                                                                const uint8_t ad[],
+                                                                size_t ad_len,
+                                                                const uint8_t unused_secret_nonce[],
+                                                                const uint8_t nonce[],
+                                                                const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                                uint8_t unused_secret_nonce[],
+                                                                const uint8_t ctext[],
+                                                                size_t ctext_len,
+                                                                const uint8_t mac[],
+                                                                const uint8_t ad[],
+                                                                size_t ad_len,
+                                                                const uint8_t nonce[],
+                                                                const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp b/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp
@@ -0,0 +1,131 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/mac.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+int Sodium::crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len)
+   {
+   auto sha512 = HashFunction::create_or_throw("SHA-512");
+   sha512->update(in, in_len);
+   sha512->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len)
+   {
+   auto sha256 = HashFunction::create_or_throw("SHA-256");
+   sha256->update(in, in_len);
+   sha256->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[],
+                                       size_t in_len, const uint8_t key[16])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("SipHash(2,4)");
+   mac->set_key(key, crypto_shorthash_siphash24_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_onetimeauth_poly1305(uint8_t out[],
+                                        const uint8_t in[],
+                                        size_t in_len,
+                                        const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("Poly1305");
+   mac->set_key(key, crypto_onetimeauth_poly1305_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_onetimeauth_poly1305_verify(const uint8_t mac[],
+                                               const uint8_t in[],
+                                               size_t in_len,
+                                               const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_onetimeauth_poly1305_BYTES);
+   crypto_onetimeauth_poly1305(computed.data(), in, in_len, key);
+   return crypto_verify_16(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha512(uint8_t out[],
+                                   const uint8_t in[],
+                                   size_t in_len,
+                                   const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   mac->set_key(key, crypto_auth_hmacsha512_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha512_verify(const uint8_t mac[],
+                                          const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha512_BYTES);
+   crypto_auth_hmacsha512(computed.data(), in, in_len, key);
+   return crypto_verify_64(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha512256(uint8_t out[],
+                                      const uint8_t in[],
+                                      size_t in_len,
+                                      const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   mac->set_key(key, crypto_auth_hmacsha512256_KEYBYTES);
+   mac->update(in, in_len);
+
+   secure_vector<uint8_t> buf(64);
+   mac->final(buf);
+
+   copy_mem(out, buf.data(), crypto_auth_hmacsha512256_BYTES);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha512256_verify(const uint8_t mac[],
+                                             const uint8_t in[],
+                                             size_t in_len,
+                                             const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha512256_BYTES);
+   crypto_auth_hmacsha512256(computed.data(), in, in_len, key);
+   return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha256(uint8_t out[],
+                                   const uint8_t in[],
+                                   size_t in_len,
+                                   const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   mac->set_key(key, crypto_auth_hmacsha256_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha256_verify(const uint8_t mac[],
+                                          const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha256_BYTES);
+   crypto_auth_hmacsha256(computed.data(), in, in_len, key);
+   return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_box.cpp b/third_party/botan/src/lib/compat/sodium/sodium_box.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_box.cpp
@@ -0,0 +1,100 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+                                                               uint8_t sk[32],
+                                                               const uint8_t seed[32])
+   {
+   secure_vector<uint8_t> digest(64);
+   crypto_hash_sha512(digest.data(), seed, 32);
+   copy_mem(sk, digest.data(), 32);
+   return crypto_scalarmult_curve25519_base(pk, sk);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+                                                          uint8_t sk[32])
+   {
+   randombytes_buf(sk, 32);
+   return crypto_scalarmult_curve25519_base(pk, sk);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+                                                           const uint8_t pk[32],
+                                                           const uint8_t sk[32])
+   {
+   const uint8_t zero[16] = { 0 };
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_scalarmult_curve25519(shared.data(), sk, pk) != 0)
+      return -1;
+
+   return crypto_core_hsalsa20(key, zero, shared.data(), nullptr);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t nonce[],
+                                                  const uint8_t pk[32],
+                                                  const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t pk[32],
+                                                       const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+                                const uint8_t ptext[], size_t ptext_len,
+                                const uint8_t nonce[], const uint8_t pk[32],
+                                const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_detached_afternm(ctext, mac, ptext, ptext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+                                     const uint8_t mac[],
+                                     size_t ctext_len,
+                                     const uint8_t nonce[],
+                                     const uint8_t pk[32],
+                                     const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_open_detached_afternm(ptext, ctext, mac, ctext_len, nonce, shared.data());
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp b/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp
@@ -0,0 +1,109 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/stream_cipher.h>
+
+namespace Botan {
+
+int Sodium::crypto_stream_chacha20(uint8_t out[], size_t out_len,
+                                   const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_chacha20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_chacha20_xor_ic(uint8_t out[], const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t nonce[], uint64_t ic,
+                                          const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+   chacha->seek(ic * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_ietf(uint8_t out[], size_t out_len,
+                                        const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_ietf_xor(uint8_t out[],
+                                            const uint8_t in[], size_t in_len,
+                                            const uint8_t nonce[],
+                                            const uint8_t key[])
+   {
+   return crypto_stream_chacha20_ietf_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_chacha20_ietf_xor_ic(uint8_t out[],
+                                               const uint8_t in[], size_t in_len,
+                                               const uint8_t nonce[], uint32_t ic,
+                                               const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+   chacha->seek(static_cast<uint64_t>(ic) * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xchacha20(uint8_t out[], size_t out_len,
+                                    const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_xchacha20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_xchacha20_xor_ic(uint8_t out[], const uint8_t in[],
+                                           size_t in_len,
+                                           const uint8_t nonce[], uint64_t ic,
+                                           const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+   chacha->seek(ic * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp b/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp
@@ -0,0 +1,124 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/salsa20.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+int Sodium::crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+                                 const uint8_t key[], const uint8_t c[])
+   {
+   uint32_t in32[16] = { 0 };
+
+   static const uint32_t SIGMA[] =
+      { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+   if(c == nullptr)
+      {
+      in32[0] = SIGMA[0];
+      in32[5] = SIGMA[1];
+      in32[10] = SIGMA[2];
+      in32[15] = SIGMA[3];
+      }
+   else
+      {
+      in32[0] = load_le<uint32_t>(c, 0);
+      in32[5] = load_le<uint32_t>(c, 1);
+      in32[10] = load_le<uint32_t>(c, 2);
+      in32[15] = load_le<uint32_t>(c, 3);
+      }
+
+   in32[1] = load_le<uint32_t>(key, 0);
+   in32[2] = load_le<uint32_t>(key, 1);
+   in32[3] = load_le<uint32_t>(key, 2);
+   in32[4] = load_le<uint32_t>(key, 3);
+
+   in32[6] = load_le<uint32_t>(in, 0);
+   in32[7] = load_le<uint32_t>(in, 1);
+   in32[8] = load_le<uint32_t>(in, 2);
+   in32[9] = load_le<uint32_t>(in, 3);
+
+   in32[11] = load_le<uint32_t>(key, 4);
+   in32[12] = load_le<uint32_t>(key, 5);
+   in32[13] = load_le<uint32_t>(key, 6);
+   in32[14] = load_le<uint32_t>(key, 7);
+
+   uint32_t out32[8] = { 0 };
+   Salsa20::hsalsa20(out32, in32);
+
+   copy_out_le(out, 32, out32);
+   return 0;
+   }
+
+int Sodium::crypto_stream_salsa20(uint8_t out[], size_t out_len,
+                                  const uint8_t nonce[], const uint8_t key[])
+   {
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+   salsa.write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_salsa20_xor(uint8_t out[], const uint8_t in[],
+                                      size_t in_len, const uint8_t nonce[],
+                                      const uint8_t key[])
+   {
+   return crypto_stream_salsa20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_salsa20_xor_ic(uint8_t out[], const uint8_t in[],
+                                         size_t in_len,
+                                         const uint8_t nonce[], uint64_t ic,
+                                         const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+   salsa.seek(ic * 64);
+   salsa.cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xsalsa20(uint8_t out[], size_t out_len,
+                                   const uint8_t nonce[], const uint8_t key[])
+   {
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+   salsa.write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_xsalsa20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_xsalsa20_xor_ic(uint8_t out[], const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t nonce[], uint64_t ic,
+                                          const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+   salsa.seek(ic * 64);
+   salsa.cipher(in, out, in_len);
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp b/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp
@@ -0,0 +1,123 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+int Sodium::crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+                                              const uint8_t ptext[],
+                                              size_t ptext_len,
+                                              const uint8_t nonce[],
+                                              const uint8_t key[])
+   {
+   if(ptext_len < 32)
+      return -1;
+
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   salsa->cipher(ptext + 32, ctext + 32, ptext_len - 32);
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext + 32, ptext_len - 32);
+   poly1305->final(ctext + 16);
+
+   clear_mem(ctext, 16);
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+                                                   const uint8_t ctext[],
+                                                   size_t ctext_len,
+                                                   const uint8_t nonce[],
+                                                   const uint8_t key[])
+   {
+   if(ctext_len < crypto_box_curve25519xsalsa20poly1305_ZEROBYTES)
+      {
+      return -1;
+      }
+
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext + 32, ctext_len - 32);
+   secure_vector<uint8_t> computed = poly1305->final();
+
+   if(!constant_time_compare(computed.data(), ctext + 16, 16))
+      return -1;
+
+   salsa->cipher(ctext + 32, ptext + 32, ctext_len - 32);
+
+   clear_mem(ptext, 32);
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+                                      const uint8_t ptext[],
+                                      size_t ptext_len,
+                                      const uint8_t nonce[],
+                                      const uint8_t key[])
+   {
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   salsa->cipher(ptext, ctext, ptext_len);
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext, ptext_len);
+   poly1305->final(mac);
+
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_open_detached(uint8_t ptext[],
+                                           const uint8_t ctext[],
+                                           const uint8_t mac[],
+                                           size_t ctext_len,
+                                           const uint8_t nonce[],
+                                           const uint8_t key[])
+   {
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext, ctext_len);
+   secure_vector<uint8_t> computed_mac = poly1305->final();
+
+   if(!constant_time_compare(mac, computed_mac.data(), computed_mac.size()))
+      return -1;
+
+   salsa->cipher(ctext, ptext, ctext_len);
+
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp b/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp
@@ -0,0 +1,160 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/chacha.h>
+#include <botan/mem_ops.h>
+#include <botan/system_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Sodium::randombytes_buf(void* buf, size_t len)
+   {
+   system_rng().randomize(static_cast<uint8_t*>(buf), len);
+   }
+
+uint32_t Sodium::randombytes_uniform(uint32_t upper_bound)
+   {
+   if(upper_bound <= 1)
+      return 0;
+
+   // Not completely uniform
+   uint64_t x;
+   randombytes_buf(&x, sizeof(x));
+   return x % upper_bound;
+   }
+
+void Sodium::randombytes_buf_deterministic(void* buf, size_t size, const uint8_t seed[randombytes_SEEDBYTES])
+   {
+   const unsigned char nonce[12] = {
+      'L', 'i', 'b', 's', 'o', 'd', 'i', 'u', 'm', 'D', 'R', 'G'
+   };
+
+   ChaCha chacha(20);
+   chacha.set_key(seed, randombytes_SEEDBYTES);
+   chacha.set_iv(nonce, sizeof(nonce));
+   chacha.write_keystream(static_cast<uint8_t*>(buf), size);
+   }
+
+int Sodium::crypto_verify_16(const uint8_t x[16], const uint8_t y[16])
+   {
+   return same_mem(x, y, 16);
+   }
+
+int Sodium::crypto_verify_32(const uint8_t x[32], const uint8_t y[32])
+   {
+   return same_mem(x, y, 32);
+   }
+
+int Sodium::crypto_verify_64(const uint8_t x[64], const uint8_t y[64])
+   {
+   return same_mem(x, y, 64);
+   }
+
+void Sodium::sodium_memzero(void* ptr, size_t len)
+   {
+   secure_scrub_memory(ptr, len);
+   }
+
+int Sodium::sodium_memcmp(const void* x, const void* y, size_t len)
+   {
+   const bool same = constant_time_compare(static_cast<const uint8_t*>(x), static_cast<const uint8_t*>(y), len);
+   return same ? 0 : -1;
+   }
+
+int Sodium::sodium_compare(const uint8_t x[], const uint8_t y[], size_t len)
+   {
+   const uint8_t LT = static_cast<uint8_t>(-1);
+   const uint8_t EQ = 0;
+   const uint8_t GT = 1;
+
+   uint8_t result = EQ; // until found otherwise
+
+   for(size_t i = 0; i != len; ++i)
+      {
+      const auto is_eq = CT::Mask<uint8_t>::is_equal(x[i], y[i]);
+      const auto is_lt = CT::Mask<uint8_t>::is_lt(x[i], y[i]);
+      result = is_eq.select(result, is_lt.select(LT, GT));
+      }
+
+   return static_cast<int8_t>(result);
+   }
+
+int Sodium::sodium_is_zero(const uint8_t b[], size_t len)
+   {
+   uint8_t sum = 0;
+   for(size_t i = 0; i != len; ++i)
+      sum |= b[i];
+   return static_cast<int>(CT::Mask<uint8_t>::expand(sum).if_not_set_return(1));
+   }
+
+void Sodium::sodium_increment(uint8_t b[], size_t len)
+   {
+   uint8_t carry = 1;
+   for(size_t i = 0; i != len; ++i)
+      {
+      b[i] += carry;
+      carry &= (b[i] == 0);
+      }
+   }
+
+void Sodium::sodium_add(uint8_t a[], const uint8_t b[], size_t len)
+   {
+   uint8_t carry = 0;
+   for(size_t i = 0; i != len; ++i)
+      {
+      a[i] += b[i] + carry;
+      carry = (a[i] < b[i]);
+      }
+   }
+
+void* Sodium::sodium_malloc(size_t size)
+   {
+   const uint64_t len = size;
+
+   if(size + sizeof(len) < size)
+      return nullptr;
+
+   uint8_t* p = static_cast<uint8_t*>(std::calloc(size + sizeof(len), 1));
+   store_le(len, p);
+   return p + 8;
+   }
+
+void Sodium::sodium_free(void* ptr)
+   {
+   if(ptr == nullptr)
+      return;
+
+   uint8_t* p = static_cast<uint8_t*>(ptr) - 8;
+   const uint64_t len = load_le<uint64_t>(p, 0);
+   secure_scrub_memory(ptr, static_cast<size_t>(len));
+   std::free(p);
+   }
+
+void* Sodium::sodium_allocarray(size_t count, size_t size)
+   {
+   const size_t bytes = count * size;
+   if(bytes < count || bytes < size)
+      return nullptr;
+   return sodium_malloc(bytes);
+   }
+
+int Sodium::sodium_mprotect_noaccess(void* ptr)
+   {
+   OS::page_prohibit_access(ptr);
+   return 0;
+   }
+
+int Sodium::sodium_mprotect_readwrite(void* ptr)
+   {
+   OS::page_allow_access(ptr);
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compression/bzip2/bzip2.cpp b/third_party/botan/src/lib/compression/bzip2/bzip2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/bzip2/bzip2.cpp
@@ -0,0 +1,110 @@
+/*
+* Bzip2 Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007,2014 Jack Lloyd
+*     2006 Matt Johnston
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bzip2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/compress_utils.h>
+
+#define BZ_NO_STDIO
+#include <bzlib.h>
+
+namespace Botan {
+
+namespace {
+
+class Bzip2_Stream : public Zlib_Style_Stream<bz_stream, char>
+   {
+   public:
+      Bzip2_Stream()
+         {
+         streamp()->opaque = alloc();
+         streamp()->bzalloc = Compression_Alloc_Info::malloc<int>;
+         streamp()->bzfree = Compression_Alloc_Info::free;
+         }
+
+      uint32_t run_flag() const override { return BZ_RUN; }
+      uint32_t flush_flag() const override { return BZ_FLUSH; }
+      uint32_t finish_flag() const override { return BZ_FINISH; }
+   };
+
+class Bzip2_Compression_Stream final : public Bzip2_Stream
+   {
+   public:
+      explicit Bzip2_Compression_Stream(size_t block_size)
+         {
+         /*
+         * Defaults to 900k blocks as the computation cost of
+         * compression is not overly affected by the size, though
+         * more memory is required.
+         */
+         if(block_size == 0 || block_size >= 9)
+            block_size = 9;
+
+         int rc = BZ2_bzCompressInit(streamp(), block_size, 0, 0);
+
+         if(rc != BZ_OK)
+            throw Compression_Error("BZ2_bzCompressInit", ErrorType::Bzip2Error, rc);
+         }
+
+      ~Bzip2_Compression_Stream()
+         {
+         BZ2_bzCompressEnd(streamp());
+         }
+
+      bool run(uint32_t flags) override
+         {
+         int rc = BZ2_bzCompress(streamp(), flags);
+
+         if(rc < 0)
+            throw Compression_Error("BZ2_bzCompress", ErrorType::Bzip2Error, rc);
+
+         return (rc == BZ_STREAM_END);
+         }
+   };
+
+class Bzip2_Decompression_Stream final : public Bzip2_Stream
+   {
+   public:
+      Bzip2_Decompression_Stream()
+         {
+         int rc = BZ2_bzDecompressInit(streamp(), 0, 0);
+
+         if(rc != BZ_OK)
+            throw Compression_Error("BZ2_bzDecompressInit", ErrorType::Bzip2Error, rc);
+         }
+
+      ~Bzip2_Decompression_Stream()
+         {
+         BZ2_bzDecompressEnd(streamp());
+         }
+
+      bool run(uint32_t) override
+         {
+         int rc = BZ2_bzDecompress(streamp());
+
+         if(rc != BZ_OK && rc != BZ_STREAM_END)
+            throw Compression_Error("BZ2_bzDecompress", ErrorType::Bzip2Error, rc);
+
+         return (rc == BZ_STREAM_END);
+         }
+   };
+
+}
+
+Compression_Stream* Bzip2_Compression::make_stream(size_t comp_level) const
+   {
+   return new Bzip2_Compression_Stream(comp_level);
+   }
+
+Compression_Stream* Bzip2_Decompression::make_stream() const
+   {
+   return new Bzip2_Decompression_Stream;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compression/bzip2/bzip2.h b/third_party/botan/src/lib/compression/bzip2/bzip2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/bzip2/bzip2.h
@@ -0,0 +1,40 @@
+/*
+* Bzip2 Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BZIP2_H_
+#define BOTAN_BZIP2_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* Bzip2 Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Bzip2_Compression final : public Stream_Compression
+   {
+   public:
+      std::string name() const override { return "Bzip2_Compression"; }
+   private:
+      Compression_Stream* make_stream(size_t comp_level) const override;
+   };
+
+/**
+* Bzip2 Deccompression
+*/
+class BOTAN_PUBLIC_API(2,0) Bzip2_Decompression final : public Stream_Decompression
+   {
+   public:
+      std::string name() const override { return "Bzip2_Decompression"; }
+   private:
+      Compression_Stream* make_stream() const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compression/compress_utils.cpp b/third_party/botan/src/lib/compression/compress_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/compress_utils.cpp
@@ -0,0 +1,196 @@
+/*
+* Compression Utils
+* (C) 2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <cstdlib>
+
+namespace Botan {
+
+void* Compression_Alloc_Info::do_malloc(size_t n, size_t size)
+   {
+   // TODO maximum length check here?
+   void* ptr = std::calloc(n, size);
+
+   /*
+   * Return null rather than throwing here as we are being called by a
+   * C library and it may not be possible for an exception to unwind
+   * the call stack from here. The compression library is expecting a
+   * function written in C and a null return on error, which it will
+   * send upwards to the compression wrappers.
+   */
+
+   if(ptr)
+      {
+      m_current_allocs[ptr] = n * size;
+      }
+
+   return ptr;
+   }
+
+void Compression_Alloc_Info::do_free(void* ptr)
+   {
+   if(ptr)
+      {
+      auto i = m_current_allocs.find(ptr);
+
+      if(i == m_current_allocs.end())
+         throw Internal_Error("Compression_Alloc_Info::free got pointer not allocated by us");
+
+      secure_scrub_memory(ptr, i->second);
+      std::free(ptr);
+      m_current_allocs.erase(i);
+      }
+   }
+
+void Stream_Compression::clear()
+   {
+   m_stream.reset();
+   }
+
+void Stream_Compression::start(size_t level)
+   {
+   m_stream.reset(make_stream(level));
+   }
+
+void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags)
+   {
+   BOTAN_ASSERT(m_stream, "Initialized");
+   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
+
+   // bzip doesn't like being called with no input and BZ_RUN
+   if(buf.size() == offset && flags == m_stream->run_flag())
+      {
+      return;
+      }
+
+   if(m_buffer.size() < buf.size() + offset)
+      m_buffer.resize(buf.size() + offset);
+
+   // If the output buffer has zero length, .data() might return nullptr. This would
+   // make some compression algorithms (notably those provided by zlib) fail.
+   // Any small positive value works fine, but we choose 32 as it is the smallest power
+   // of two that is large enough to hold all the headers and trailers of the common
+   // formats, preventing further resizings to make room for output data.
+   if(m_buffer.size() == 0)
+      m_buffer.resize(32);
+
+   m_stream->next_in(buf.data() + offset, buf.size() - offset);
+   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
+
+   while(true)
+      {
+      const bool stream_end = m_stream->run(flags);
+
+      if(stream_end)
+         {
+         BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
+         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+         break;
+         }
+      else if(m_stream->avail_out() == 0)
+         {
+         const size_t added = 8 + m_buffer.size();
+         m_buffer.resize(m_buffer.size() + added);
+         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
+         }
+      else if(m_stream->avail_in() == 0)
+         {
+         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+         break;
+         }
+      }
+
+   copy_mem(m_buffer.data(), buf.data(), offset);
+   buf.swap(m_buffer);
+   }
+
+void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush)
+   {
+   BOTAN_ASSERT(m_stream, "Initialized");
+   process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
+   }
+
+void Stream_Compression::finish(secure_vector<uint8_t>& buf, size_t offset)
+   {
+   BOTAN_ASSERT(m_stream, "Initialized");
+   process(buf, offset, m_stream->finish_flag());
+   clear();
+   }
+
+void Stream_Decompression::clear()
+   {
+   m_stream.reset();
+   }
+
+void Stream_Decompression::start()
+   {
+   m_stream.reset(make_stream());
+   }
+
+void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags)
+   {
+   BOTAN_ASSERT(m_stream, "Initialized");
+   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
+
+   if(m_buffer.size() < buf.size() + offset)
+      m_buffer.resize(buf.size() + offset);
+
+   m_stream->next_in(buf.data() + offset, buf.size() - offset);
+   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
+
+   while(true)
+      {
+      const bool stream_end = m_stream->run(flags);
+
+      if(stream_end)
+         {
+         if(m_stream->avail_in() == 0) // all data consumed?
+            {
+            m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+            clear();
+            break;
+            }
+
+         // More data follows: try to process as a following stream
+         const size_t read = (buf.size() - offset) - m_stream->avail_in();
+         start();
+         m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
+         }
+
+      if(m_stream->avail_out() == 0)
+         {
+         const size_t added = 8 + m_buffer.size();
+         m_buffer.resize(m_buffer.size() + added);
+         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
+         }
+      else if(m_stream->avail_in() == 0)
+         {
+         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+         break;
+         }
+      }
+
+   copy_mem(m_buffer.data(), buf.data(), offset);
+   buf.swap(m_buffer);
+   }
+
+void Stream_Decompression::update(secure_vector<uint8_t>& buf, size_t offset)
+   {
+   process(buf, offset, m_stream->run_flag());
+   }
+
+void Stream_Decompression::finish(secure_vector<uint8_t>& buf, size_t offset)
+   {
+   if(buf.size() != offset || m_stream.get())
+      process(buf, offset, m_stream->finish_flag());
+
+   if(m_stream.get())
+      throw Invalid_State(name() + " finished but not at stream end");
+   }
+
+}
diff --git a/third_party/botan/src/lib/compression/compress_utils.h b/third_party/botan/src/lib/compression/compress_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/compress_utils.h
@@ -0,0 +1,89 @@
+/*
+* Compression utility header
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_UTILS_H_
+#define BOTAN_COMPRESSION_UTILS_H_
+
+#include <botan/compression.h>
+#include <memory>
+#include <unordered_map>
+
+namespace Botan {
+
+/*
+* Allocation Size Tracking Helper for Zlib/Bzlib/LZMA
+*/
+class Compression_Alloc_Info final
+   {
+   public:
+      template<typename T>
+      static void* malloc(void* self, T n, T size)
+         {
+         return static_cast<Compression_Alloc_Info*>(self)->do_malloc(n, size);
+         }
+
+      static void free(void* self, void* ptr)
+         {
+         static_cast<Compression_Alloc_Info*>(self)->do_free(ptr);
+         }
+
+   private:
+      void* do_malloc(size_t n, size_t size);
+      void do_free(void* ptr);
+
+      std::unordered_map<void*, size_t> m_current_allocs;
+   };
+
+/**
+* Wrapper for Zlib/Bzlib/LZMA stream types
+*/
+template<typename Stream, typename ByteType>
+class Zlib_Style_Stream : public Compression_Stream
+   {
+   public:
+      void next_in(uint8_t* b, size_t len) override
+         {
+         m_stream.next_in = reinterpret_cast<ByteType*>(b);
+         m_stream.avail_in = len;
+         }
+
+      void next_out(uint8_t* b, size_t len) override
+         {
+         m_stream.next_out = reinterpret_cast<ByteType*>(b);
+         m_stream.avail_out = len;
+         }
+
+      size_t avail_in() const override { return m_stream.avail_in; }
+
+      size_t avail_out() const override { return m_stream.avail_out; }
+
+      Zlib_Style_Stream()
+         {
+         clear_mem(&m_stream, 1);
+         m_allocs.reset(new Compression_Alloc_Info);
+         }
+
+      ~Zlib_Style_Stream()
+         {
+         clear_mem(&m_stream, 1);
+         m_allocs.reset();
+         }
+
+   protected:
+      typedef Stream stream_t;
+
+      stream_t* streamp() { return &m_stream; }
+
+      Compression_Alloc_Info* alloc() { return m_allocs.get(); }
+   private:
+      stream_t m_stream;
+      std::unique_ptr<Compression_Alloc_Info> m_allocs;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compression/compression.cpp b/third_party/botan/src/lib/compression/compression.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/compression.cpp
@@ -0,0 +1,116 @@
+/*
+* Compression Factory
+* (C) 2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/compression.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+#include <cstdlib>
+
+#if defined(BOTAN_HAS_ZLIB)
+  #include <botan/zlib.h>
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+  #include <botan/bzip2.h>
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+  #include <botan/lzma.h>
+#endif
+
+namespace Botan {
+
+Compression_Algorithm* make_compressor(const std::string& name)
+   {
+#if defined(BOTAN_HAS_ZLIB)
+   if(name == "Zlib" || name == "zlib")
+      return new Zlib_Compression;
+   if(name == "Gzip" || name == "gzip" || name == "gz")
+      return new Gzip_Compression;
+   if(name == "Deflate" || name == "deflate")
+      return new Deflate_Compression;
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+   if(name == "bzip2" || name == "bz2" || name == "Bzip2")
+      return new Bzip2_Compression;
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+   if(name == "lzma" || name == "xz" || name == "LZMA")
+      return new LZMA_Compression;
+#endif
+
+   BOTAN_UNUSED(name);
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<Compression_Algorithm>
+Compression_Algorithm::create(const std::string& algo)
+   {
+   std::unique_ptr<Compression_Algorithm> compressor(make_compressor(algo));
+   return compressor;
+   }
+
+//static
+std::unique_ptr<Compression_Algorithm>
+Compression_Algorithm::create_or_throw(const std::string& algo)
+   {
+   if(auto compressor = Compression_Algorithm::create(algo))
+      {
+      return compressor;
+      }
+   throw Lookup_Error("Compression", algo, "");
+   }
+
+Decompression_Algorithm* make_decompressor(const std::string& name)
+   {
+#if defined(BOTAN_HAS_ZLIB)
+   if(name == "Zlib" || name == "zlib")
+      return new Zlib_Decompression;
+   if(name == "Gzip" || name == "gzip" || name == "gz")
+      return new Gzip_Decompression;
+   if(name == "Deflate" || name == "deflate")
+      return new Deflate_Decompression;
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+   if(name == "bzip2" || name == "bz2" || name == "Bzip2")
+      return new Bzip2_Decompression;
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+   if(name == "lzma" || name == "xz" || name == "LZMA")
+      return new LZMA_Decompression;
+#endif
+
+   BOTAN_UNUSED(name);
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<Decompression_Algorithm>
+Decompression_Algorithm::create(const std::string& algo)
+   {
+   std::unique_ptr<Decompression_Algorithm> decompressor(make_decompressor(algo));
+   return decompressor;
+   }
+
+//static
+std::unique_ptr<Decompression_Algorithm>
+Decompression_Algorithm::create_or_throw(const std::string& algo)
+   {
+   if(auto decompressor = Decompression_Algorithm::create(algo))
+      {
+      return decompressor;
+      }
+   throw Lookup_Error("Decompression", algo, "");
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/compression/compression.h b/third_party/botan/src/lib/compression/compression.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/compression.h
@@ -0,0 +1,238 @@
+/*
+* Compression Transform
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_TRANSFORM_H_
+#define BOTAN_COMPRESSION_TRANSFORM_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Interface for a compression algorithm.
+*/
+class BOTAN_PUBLIC_API(2,0) Compression_Algorithm
+   {
+   public:
+      /**
+      * Create an instance based on a name, or return null if the
+      * algo combination cannot be found.
+      */
+      static std::unique_ptr<Compression_Algorithm>
+         create(const std::string& algo_spec);
+
+      /**
+      * Create an instance based on a name
+      * @param algo_spec algorithm name
+      * Throws Lookup_Error if not found.
+      */
+      static std::unique_ptr<Compression_Algorithm>
+         create_or_throw(const std::string& algo_spec);
+
+      /**
+      * Begin compressing. Most compression algorithms offer a tunable
+      * time/compression tradeoff parameter generally represented by
+      * an integer in the range of 1 to 9.
+      *
+      * If 0 or a value out of range is provided, a compression algorithm
+      * specific default is used.
+      */
+      virtual void start(size_t comp_level = 0) = 0;
+
+      /**
+      * Process some data.
+      * @param buf in/out parameter which will possibly be resized or swapped
+      * @param offset an offset into blocks to begin processing
+      * @param flush if true the compressor will be told to flush state
+      */
+      virtual void update(secure_vector<uint8_t>& buf, size_t offset = 0, bool flush = false) = 0;
+
+      /**
+      * Finish compressing
+      *
+      * @param final_block in/out parameter
+      * @param offset an offset into final_block to begin processing
+      */
+      virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+      /**
+      * @return name of the compression algorithm
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Reset the state and abort the current message; start can be
+      * called again to process a new message.
+      */
+      virtual void clear() = 0;
+
+      virtual ~Compression_Algorithm() = default;
+   };
+
+/*
+* Interface for a decompression algorithm.
+*/
+class BOTAN_PUBLIC_API(2,0) Decompression_Algorithm
+   {
+   public:
+      /**
+      * Create an instance based on a name, or return null if the
+      * algo combination cannot be found.
+      */
+      static std::unique_ptr<Decompression_Algorithm>
+         create(const std::string& algo_spec);
+
+      /**
+      * Create an instance based on a name
+      * @param algo_spec algorithm name
+      * Throws Lookup_Error if not found.
+      */
+      static std::unique_ptr<Decompression_Algorithm>
+         create_or_throw(const std::string& algo_spec);
+
+      /**
+      * Begin decompressing.
+      * Decompression does not support levels, as compression does.
+      */
+      virtual void start() = 0;
+
+      /**
+      * Process some data.
+      * @param buf in/out parameter which will possibly be resized or swapped
+      * @param offset an offset into blocks to begin processing
+      */
+      virtual void update(secure_vector<uint8_t>& buf, size_t offset = 0) = 0;
+
+      /**
+      * Finish decompressing
+      *
+      * @param final_block in/out parameter
+      * @param offset an offset into final_block to begin processing
+      */
+      virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+      /**
+      * @return name of the decompression algorithm
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Reset the state and abort the current message; start can be
+      * called again to process a new message.
+      */
+      virtual void clear() = 0;
+
+      virtual ~Decompression_Algorithm() = default;
+   };
+
+BOTAN_PUBLIC_API(2,0) Compression_Algorithm* make_compressor(const std::string& type);
+BOTAN_PUBLIC_API(2,0) Decompression_Algorithm* make_decompressor(const std::string& type);
+
+/**
+* An error that occurred during compression (or decompression)
+*/
+class BOTAN_PUBLIC_API(2,9) Compression_Error : public Exception
+   {
+   public:
+
+      /**
+      * @param func_name the name of the compression API that was called
+      * (eg "BZ2_bzCompressInit" or "lzma_code")
+      * @param type what library this came from
+      * @param rc the error return code from the compression API. The
+      * interpretation of this value will depend on the library.
+      */
+     Compression_Error(const char* func_name, ErrorType type, int rc) :
+        Exception("Compression API " + std::string(func_name) +
+                  " failed with return code " + std::to_string(rc)),
+        m_type(type),
+        m_rc(rc)
+         {}
+
+      ErrorType error_type() const noexcept override { return m_type; }
+
+      int error_code() const noexcept override { return m_rc; }
+
+   private:
+      ErrorType m_type;
+      int m_rc;
+   };
+
+/**
+* Adapts a zlib style API
+*/
+class Compression_Stream
+   {
+   public:
+      virtual ~Compression_Stream() = default;
+
+      virtual void next_in(uint8_t* b, size_t len) = 0;
+
+      virtual void next_out(uint8_t* b, size_t len) = 0;
+
+      virtual size_t avail_in() const = 0;
+
+      virtual size_t avail_out() const = 0;
+
+      virtual uint32_t run_flag() const = 0;
+      virtual uint32_t flush_flag() const = 0;
+      virtual uint32_t finish_flag() const = 0;
+
+      virtual bool run(uint32_t flags) = 0;
+   };
+
+/**
+* Used to implement compression using Compression_Stream
+*/
+class Stream_Compression : public Compression_Algorithm
+   {
+   public:
+      void update(secure_vector<uint8_t>& buf, size_t offset, bool flush) final override;
+
+      void finish(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+      void clear() final override;
+
+   private:
+      void start(size_t level) final override;
+
+      void process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags);
+
+      virtual Compression_Stream* make_stream(size_t level) const = 0;
+
+      secure_vector<uint8_t> m_buffer;
+      std::unique_ptr<Compression_Stream> m_stream;
+   };
+
+/**
+* FIXME add doc
+*/
+class Stream_Decompression : public Decompression_Algorithm
+   {
+   public:
+      void update(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+      void finish(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+      void clear() final override;
+
+   private:
+      void start() final override;
+
+      void process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags);
+
+      virtual Compression_Stream* make_stream() const = 0;
+
+      secure_vector<uint8_t> m_buffer;
+      std::unique_ptr<Compression_Stream> m_stream;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compression/lzma/lzma.cpp b/third_party/botan/src/lib/compression/lzma/lzma.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/lzma/lzma.cpp
@@ -0,0 +1,95 @@
+/*
+* Lzma Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007,2014 Jack Lloyd
+*     2006 Matt Johnston
+*     2012 Vojtech Kral
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/lzma.h>
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <lzma.h>
+
+namespace Botan {
+
+namespace {
+
+class LZMA_Stream : public Zlib_Style_Stream<lzma_stream, uint8_t>
+   {
+   public:
+      LZMA_Stream()
+         {
+         m_allocator.opaque = alloc();
+         m_allocator.alloc = Compression_Alloc_Info::malloc<size_t>;
+         m_allocator.free = Compression_Alloc_Info::free;
+         streamp()->allocator = &m_allocator;
+         }
+
+      ~LZMA_Stream()
+         {
+         ::lzma_end(streamp());
+         }
+
+      bool run(uint32_t flags) override
+         {
+         lzma_ret rc = ::lzma_code(streamp(), static_cast<lzma_action>(flags));
+
+         if(rc != LZMA_OK && rc != LZMA_STREAM_END)
+            throw Compression_Error("lzma_code", ErrorType::LzmaError, rc);
+
+         return (rc == LZMA_STREAM_END);
+         }
+
+      uint32_t run_flag() const override { return LZMA_RUN; }
+      uint32_t flush_flag() const override { return LZMA_FULL_FLUSH; }
+      uint32_t finish_flag() const override { return LZMA_FINISH; }
+   private:
+      ::lzma_allocator m_allocator;
+   };
+
+class LZMA_Compression_Stream final : public LZMA_Stream
+   {
+   public:
+      explicit LZMA_Compression_Stream(size_t level)
+         {
+         if(level == 0)
+            level = 6; // default
+         else if(level > 9)
+            level = 9; // clamp to maximum allowed value
+
+         lzma_ret rc = ::lzma_easy_encoder(streamp(), level, LZMA_CHECK_CRC64);
+
+         if(rc != LZMA_OK)
+            throw Compression_Error("lzam_easy_encoder", ErrorType::LzmaError, rc);
+         }
+   };
+
+class LZMA_Decompression_Stream final : public LZMA_Stream
+   {
+   public:
+      LZMA_Decompression_Stream()
+         {
+         lzma_ret rc = ::lzma_stream_decoder(streamp(), UINT64_MAX,
+                                             LZMA_TELL_UNSUPPORTED_CHECK);
+
+         if(rc != LZMA_OK)
+            throw Compression_Error("lzma_stream_decoder", ErrorType::LzmaError, rc);
+         }
+   };
+
+}
+
+Compression_Stream* LZMA_Compression::make_stream(size_t level) const
+   {
+   return new LZMA_Compression_Stream(level);
+   }
+
+Compression_Stream* LZMA_Decompression::make_stream() const
+   {
+   return new LZMA_Decompression_Stream;
+   }
+
+}
diff --git a/third_party/botan/src/lib/compression/lzma/lzma.h b/third_party/botan/src/lib/compression/lzma/lzma.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/lzma/lzma.h
@@ -0,0 +1,42 @@
+/*
+* Lzma Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007 Jack Lloyd
+*     2012 Vojtech Kral
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LZMA_H_
+#define BOTAN_LZMA_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* LZMA Compression
+*/
+class BOTAN_PUBLIC_API(2,0) LZMA_Compression final : public Stream_Compression
+   {
+   public:
+      std::string name() const override { return "LZMA_Compression"; }
+
+   private:
+      Compression_Stream* make_stream(size_t level) const override;
+   };
+
+/**
+* LZMA Deccompression
+*/
+class BOTAN_PUBLIC_API(2,0) LZMA_Decompression final : public Stream_Decompression
+   {
+   public:
+      std::string name() const override { return "LZMA_Decompression"; }
+   private:
+      Compression_Stream* make_stream() const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/compression/zlib/zlib.cpp b/third_party/botan/src/lib/compression/zlib/zlib.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/zlib/zlib.cpp
@@ -0,0 +1,173 @@
+/*
+* Zlib Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007,2014 Jack Lloyd
+*     2006 Matt Johnston
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/zlib.h>
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <zlib.h>
+
+namespace Botan {
+
+namespace {
+
+class Zlib_Stream : public Zlib_Style_Stream<z_stream, Bytef>
+   {
+   public:
+      Zlib_Stream()
+         {
+         streamp()->opaque = alloc();
+         streamp()->zalloc = Compression_Alloc_Info::malloc<unsigned int>;
+         streamp()->zfree = Compression_Alloc_Info::free;
+         }
+
+      uint32_t run_flag() const override { return Z_NO_FLUSH; }
+      uint32_t flush_flag() const override { return Z_SYNC_FLUSH; }
+      uint32_t finish_flag() const override { return Z_FINISH; }
+
+      int compute_window_bits(int wbits, int wbits_offset) const
+         {
+         if(wbits_offset == -1)
+            return -wbits;
+         else
+            return wbits + wbits_offset;
+         }
+   };
+
+class Zlib_Compression_Stream : public Zlib_Stream
+   {
+   public:
+      Zlib_Compression_Stream(size_t level, int wbits, int wbits_offset = 0)
+         {
+         wbits = compute_window_bits(wbits, wbits_offset);
+
+         if(level >= 9)
+            level = 9;
+         else if(level == 0)
+            level = 6;
+
+         int rc = ::deflateInit2(streamp(), level, Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);
+
+         if(rc != Z_OK)
+            throw Compression_Error("deflateInit2", ErrorType::ZlibError, rc);
+         }
+
+      ~Zlib_Compression_Stream()
+         {
+         ::deflateEnd(streamp());
+         }
+
+      bool run(uint32_t flags) override
+         {
+         int rc = ::deflate(streamp(), flags);
+
+         if(rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR)
+            throw Compression_Error("zlib deflate", ErrorType::ZlibError, rc);
+
+         return (rc == Z_STREAM_END);
+         }
+   };
+
+class Zlib_Decompression_Stream : public Zlib_Stream
+   {
+   public:
+      Zlib_Decompression_Stream(int wbits, int wbits_offset = 0)
+         {
+         int rc = ::inflateInit2(streamp(), compute_window_bits(wbits, wbits_offset));
+
+         if(rc != Z_OK)
+            throw Compression_Error("inflateInit2", ErrorType::ZlibError, rc);
+         }
+
+      ~Zlib_Decompression_Stream()
+         {
+         ::inflateEnd(streamp());
+         }
+
+      bool run(uint32_t flags) override
+         {
+         int rc = ::inflate(streamp(), flags);
+
+         if(rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR)
+            throw Compression_Error("zlib inflate", ErrorType::ZlibError, rc);
+
+         return (rc == Z_STREAM_END);
+         }
+   };
+
+class Deflate_Compression_Stream final : public Zlib_Compression_Stream
+   {
+   public:
+      Deflate_Compression_Stream(size_t level, int wbits) :
+         Zlib_Compression_Stream(level, wbits, -1) {}
+   };
+
+class Deflate_Decompression_Stream final : public Zlib_Decompression_Stream
+   {
+   public:
+      explicit Deflate_Decompression_Stream(int wbits) : Zlib_Decompression_Stream(wbits, -1) {}
+   };
+
+class Gzip_Compression_Stream final : public Zlib_Compression_Stream
+   {
+   public:
+      Gzip_Compression_Stream(size_t level, int wbits, uint8_t os_code, uint64_t hdr_time) :
+         Zlib_Compression_Stream(level, wbits, 16)
+         {
+         clear_mem(&m_header, 1);
+         m_header.os = os_code;
+         m_header.time = static_cast<uLong>(hdr_time);
+
+         int rc = deflateSetHeader(streamp(), &m_header);
+         if(rc != Z_OK)
+            throw Compression_Error("deflateSetHeader", ErrorType::ZlibError, rc);
+         }
+
+   private:
+      ::gz_header m_header;
+   };
+
+class Gzip_Decompression_Stream final : public Zlib_Decompression_Stream
+   {
+   public:
+      explicit Gzip_Decompression_Stream(int wbits) : Zlib_Decompression_Stream(wbits, 16) {}
+   };
+
+}
+
+Compression_Stream* Zlib_Compression::make_stream(size_t level) const
+   {
+   return new Zlib_Compression_Stream(level, 15);
+   }
+
+Compression_Stream* Zlib_Decompression::make_stream() const
+   {
+   return new Zlib_Decompression_Stream(15);
+   }
+
+Compression_Stream* Deflate_Compression::make_stream(size_t level) const
+   {
+   return new Deflate_Compression_Stream(level, 15);
+   }
+
+Compression_Stream* Deflate_Decompression::make_stream() const
+   {
+   return new Deflate_Decompression_Stream(15);
+   }
+
+Compression_Stream* Gzip_Compression::make_stream(size_t level) const
+   {
+   return new Gzip_Compression_Stream(level, 15, m_os_code, m_hdr_time);
+   }
+
+Compression_Stream* Gzip_Decompression::make_stream() const
+   {
+   return new Gzip_Decompression_Stream(15);
+   }
+
+}
diff --git a/third_party/botan/src/lib/compression/zlib/zlib.h b/third_party/botan/src/lib/compression/zlib/zlib.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/compression/zlib/zlib.h
@@ -0,0 +1,89 @@
+/*
+* Zlib Compressor
+* (C) 2001 Peter J Jones
+*     2001-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ZLIB_H_
+#define BOTAN_ZLIB_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* Zlib Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Zlib_Compression final : public Stream_Compression
+   {
+   public:
+      std::string name() const override { return "Zlib_Compression"; }
+   private:
+      Compression_Stream* make_stream(size_t level) const override;
+   };
+
+/**
+* Zlib Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Zlib_Decompression final : public Stream_Decompression
+   {
+   public:
+      std::string name() const override { return "Zlib_Decompression"; }
+   private:
+      Compression_Stream* make_stream() const override;
+   };
+
+/**
+* Deflate Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Deflate_Compression final : public Stream_Compression
+   {
+   public:
+      std::string name() const override { return "Deflate_Compression"; }
+   private:
+      Compression_Stream* make_stream(size_t level) const override;
+   };
+
+/**
+* Deflate Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Deflate_Decompression final : public Stream_Decompression
+   {
+   public:
+      std::string name() const override { return "Deflate_Decompression"; }
+   private:
+      Compression_Stream* make_stream() const override;
+   };
+
+/**
+* Gzip Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Gzip_Compression final : public Stream_Compression
+   {
+   public:
+      explicit Gzip_Compression(uint8_t os_code = 255, uint64_t hdr_time = 0) :
+         m_hdr_time(hdr_time), m_os_code(os_code) {}
+
+      std::string name() const override { return "Gzip_Compression"; }
+   private:
+      Compression_Stream* make_stream(size_t level) const override;
+      const uint64_t m_hdr_time;
+      const uint8_t m_os_code;
+   };
+
+/**
+* Gzip Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Gzip_Decompression final : public Stream_Decompression
+   {
+   public:
+      std::string name() const override { return "Gzip_Decompression"; }
+   private:
+      Compression_Stream* make_stream() const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp b/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp
@@ -0,0 +1,122 @@
+/*
+* Reader of /dev/random and company
+* (C) 1999-2009,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/dev_random.h>
+#include <botan/exceptn.h>
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+namespace Botan {
+
+/**
+Device_EntropySource constructor
+Open a file descriptor to each (available) device in fsnames
+*/
+Device_EntropySource::Device_EntropySource(const std::vector<std::string>& fsnames)
+   {
+#ifndef O_NONBLOCK
+  #define O_NONBLOCK 0
+#endif
+
+#ifndef O_NOCTTY
+  #define O_NOCTTY 0
+#endif
+
+   const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY;
+
+   m_max_fd = 0;
+
+   for(auto fsname : fsnames)
+      {
+      int fd = ::open(fsname.c_str(), flags);
+
+      if(fd < 0)
+         {
+         /*
+         ENOENT or EACCES is normal as some of the named devices may not exist
+         on this system. But any other errno value probably indicates
+         either a bug in the application or file descriptor exhaustion.
+         */
+         if(errno != ENOENT && errno != EACCES)
+            throw System_Error("Opening OS RNG device failed", errno);
+         }
+      else
+         {
+         if(fd > FD_SETSIZE)
+            {
+            ::close(fd);
+            throw Invalid_State("Open of OS RNG succeeded but returned fd is too large for fd_set");
+            }
+
+         m_dev_fds.push_back(fd);
+         m_max_fd = std::max(m_max_fd, fd);
+         }
+      }
+   }
+
+/**
+Device_EntropySource destructor: close all open devices
+*/
+Device_EntropySource::~Device_EntropySource()
+   {
+   for(int fd : m_dev_fds)
+      {
+      // ignoring return value here, can't throw in destructor anyway
+      ::close(fd);
+      }
+   }
+
+/**
+* Gather entropy from a RNG device
+*/
+size_t Device_EntropySource::poll(RandomNumberGenerator& rng)
+   {
+   size_t bits = 0;
+
+   if(m_dev_fds.size() > 0)
+      {
+      fd_set read_set;
+      FD_ZERO(&read_set);
+
+      for(int dev_fd : m_dev_fds)
+         {
+         FD_SET(dev_fd, &read_set);
+         }
+
+      secure_vector<uint8_t> io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+
+      struct ::timeval timeout;
+      timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000);
+      timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000;
+
+      if(::select(m_max_fd + 1, &read_set, nullptr, nullptr, &timeout) > 0)
+         {
+         for(int dev_fd : m_dev_fds)
+            {
+            if(FD_ISSET(dev_fd, &read_set))
+               {
+               const ssize_t got = ::read(dev_fd, io_buf.data(), io_buf.size());
+
+               if(got > 0)
+                  {
+                  rng.add_entropy(io_buf.data(), static_cast<size_t>(got));
+                  bits += got * 8;
+                  }
+               }
+            }
+         }
+      }
+
+   return bits;
+   }
+
+}
diff --git a/third_party/botan/src/lib/entropy/dev_random/dev_random.h b/third_party/botan/src/lib/entropy/dev_random/dev_random.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/dev_random/dev_random.h
@@ -0,0 +1,37 @@
+/*
+* /dev/random EntropySource
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_DEVICE_H_
+#define BOTAN_ENTROPY_SRC_DEVICE_H_
+
+#include <botan/entropy_src.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Entropy source reading from kernel devices like /dev/random
+*/
+class Device_EntropySource final : public Entropy_Source
+   {
+   public:
+      std::string name() const override { return "dev_random"; }
+
+      size_t poll(RandomNumberGenerator& rng) override;
+
+      explicit Device_EntropySource(const std::vector<std::string>& fsnames);
+
+      ~Device_EntropySource();
+   private:
+      std::vector<int> m_dev_fds;
+      int m_max_fd;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/entropy_src.h b/third_party/botan/src/lib/entropy/entropy_src.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/entropy_src.h
@@ -0,0 +1,87 @@
+/*
+* EntropySource
+* (C) 2008,2009,2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_H_
+#define BOTAN_ENTROPY_H_
+
+#include <botan/secmem.h>
+#include <botan/rng.h>
+#include <string>
+#include <chrono>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Abstract interface to a source of entropy
+*/
+class BOTAN_PUBLIC_API(2,0) Entropy_Source
+   {
+   public:
+      /**
+      * Return a new entropy source of a particular type, or null
+      * Each entropy source may require substantial resources (eg, a file handle
+      * or socket instance), so try to share them among multiple RNGs, or just
+      * use the preconfigured global list accessed by Entropy_Sources::global_sources()
+      */
+      static std::unique_ptr<Entropy_Source> create(const std::string& type);
+
+      /**
+      * @return name identifying this entropy source
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Perform an entropy gathering poll
+      * @param rng will be provided with entropy via calls to add_entropy
+      * @return conservative estimate of actual entropy added to rng during poll
+      */
+      virtual size_t poll(RandomNumberGenerator& rng) = 0;
+
+      Entropy_Source() = default;
+      Entropy_Source(const Entropy_Source& other) = delete;
+      Entropy_Source(Entropy_Source&& other) = delete;
+      Entropy_Source& operator=(const Entropy_Source& other) = delete;
+
+      virtual ~Entropy_Source() = default;
+   };
+
+class BOTAN_PUBLIC_API(2,0) Entropy_Sources final
+   {
+   public:
+      static Entropy_Sources& global_sources();
+
+      void add_source(std::unique_ptr<Entropy_Source> src);
+
+      std::vector<std::string> enabled_sources() const;
+
+      size_t poll(RandomNumberGenerator& rng,
+                  size_t bits,
+                  std::chrono::milliseconds timeout);
+
+      /**
+      * Poll just a single named source. Ordinally only used for testing
+      */
+      size_t poll_just(RandomNumberGenerator& rng, const std::string& src);
+
+      Entropy_Sources() = default;
+      explicit Entropy_Sources(const std::vector<std::string>& sources);
+
+      Entropy_Sources(const Entropy_Sources& other) = delete;
+      Entropy_Sources(Entropy_Sources&& other) = delete;
+      Entropy_Sources& operator=(const Entropy_Sources& other) = delete;
+
+   private:
+      std::vector<std::unique_ptr<Entropy_Source>> m_srcs;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/entropy_srcs.cpp b/third_party/botan/src/lib/entropy/entropy_srcs.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/entropy_srcs.cpp
@@ -0,0 +1,235 @@
+/*
+* Entropy Source Polling
+* (C) 2008-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/entropy_src.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+  #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+  #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND)
+  #include <botan/internal/rdrand.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED)
+  #include <botan/internal/rdseed.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DARN)
+  #include <botan/internal/p9_darn.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM)
+  #include <botan/internal/dev_random.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
+  #include <botan/internal/es_win32.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER)
+  #include <botan/internal/proc_walk.h>
+  #include <botan/internal/os_utils.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+  #include <botan/internal/getentropy.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+
+class System_RNG_EntropySource final : public Entropy_Source
+   {
+   public:
+      size_t poll(RandomNumberGenerator& rng) override
+         {
+         const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS;
+         rng.reseed_from_rng(system_rng(), poll_bits);
+         return poll_bits;
+         }
+
+      std::string name() const override { return "system_rng"; }
+   };
+
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+
+class Processor_RNG_EntropySource final : public Entropy_Source
+   {
+   public:
+      size_t poll(RandomNumberGenerator& rng) override
+         {
+         /*
+         * Intel's documentation for RDRAND at
+         * https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
+         * claims that software can guarantee a reseed event by polling enough data:
+         * "There is an upper bound of 511 samples per seed in the implementation
+         * where samples are 128 bits in size and can provide two 64-bit random
+         * numbers each."
+         *
+         * By requesting 65536 bits we are asking for 512 samples and thus are assured
+         * that at some point in producing the output, at least one reseed of the
+         * internal state will occur.
+         *
+         * The reseeding conditions of the POWER and ARM processor RNGs are not known
+         * but probably work in a somewhat similar manner. The exact amount requested
+         * may be tweaked if and when such conditions become publically known.
+         */
+         const size_t poll_bits = 65536;
+         rng.reseed_from_rng(m_hwrng, poll_bits);
+         // Avoid trusting a black box, don't count this as contributing entropy:
+         return 0;
+         }
+
+      std::string name() const override { return m_hwrng.name(); }
+   private:
+      Processor_RNG m_hwrng;
+   };
+
+#endif
+
+}
+
+std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
+   {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   if(name == "system_rng" || name == "win32_cryptoapi")
+      {
+      return std::unique_ptr<Entropy_Source>(new System_RNG_EntropySource);
+      }
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   if(name == "hwrng" || name == "rdrand" || name == "p9_darn")
+      {
+      if(Processor_RNG::available())
+         {
+         return std::unique_ptr<Entropy_Source>(new Processor_RNG_EntropySource);
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED)
+   if(name == "rdseed")
+      {
+      return std::unique_ptr<Entropy_Source>(new Intel_Rdseed);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+   if(name == "getentropy")
+      {
+      return std::unique_ptr<Entropy_Source>(new Getentropy);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM)
+   if(name == "dev_random")
+      {
+      return std::unique_ptr<Entropy_Source>(new Device_EntropySource(BOTAN_SYSTEM_RNG_POLL_DEVICES));
+      }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER)
+   if(name == "proc_walk" && OS::running_in_privileged_state() == false)
+      {
+      const std::string root_dir = BOTAN_ENTROPY_PROC_FS_PATH;
+      if(!root_dir.empty())
+         return std::unique_ptr<Entropy_Source>(new ProcWalking_EntropySource(root_dir));
+      }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
+   if(name == "system_stats")
+      {
+      return std::unique_ptr<Entropy_Source>(new Win32_EntropySource);
+      }
+#endif
+
+   BOTAN_UNUSED(name);
+   return std::unique_ptr<Entropy_Source>();
+   }
+
+void Entropy_Sources::add_source(std::unique_ptr<Entropy_Source> src)
+   {
+   if(src.get())
+      {
+      m_srcs.push_back(std::move(src));
+      }
+   }
+
+std::vector<std::string> Entropy_Sources::enabled_sources() const
+   {
+   std::vector<std::string> sources;
+   for(size_t i = 0; i != m_srcs.size(); ++i)
+      {
+      sources.push_back(m_srcs[i]->name());
+      }
+   return sources;
+   }
+
+size_t Entropy_Sources::poll(RandomNumberGenerator& rng,
+                             size_t poll_bits,
+                             std::chrono::milliseconds timeout)
+   {
+   typedef std::chrono::system_clock clock;
+
+   auto deadline = clock::now() + timeout;
+
+   size_t bits_collected = 0;
+
+   for(size_t i = 0; i != m_srcs.size(); ++i)
+      {
+      bits_collected += m_srcs[i]->poll(rng);
+
+      if (bits_collected >= poll_bits || clock::now() > deadline)
+         break;
+      }
+
+   return bits_collected;
+   }
+
+size_t Entropy_Sources::poll_just(RandomNumberGenerator& rng, const std::string& the_src)
+   {
+   for(size_t i = 0; i != m_srcs.size(); ++i)
+      {
+      if(m_srcs[i]->name() == the_src)
+         {
+         return m_srcs[i]->poll(rng);
+         }
+      }
+
+   return 0;
+   }
+
+Entropy_Sources::Entropy_Sources(const std::vector<std::string>& sources)
+   {
+   for(auto&& src_name : sources)
+      {
+      add_source(Entropy_Source::create(src_name));
+      }
+   }
+
+Entropy_Sources& Entropy_Sources::global_sources()
+   {
+   static Entropy_Sources global_entropy_sources(BOTAN_ENTROPY_DEFAULT_SOURCES);
+
+   return global_entropy_sources;
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp b/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp
@@ -0,0 +1,35 @@
+/*
+* System Call getentropy(2)
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/getentropy.h>
+
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD) || defined(BOTAN_TARGET_OS_IS_FREEBSD)
+   #include <unistd.h>
+#else
+   #include <sys/random.h>
+#endif
+
+namespace Botan {
+
+/**
+* Gather 256 bytes entropy from getentropy(2).  Note that maximum
+* buffer size is limited to 256 bytes.  On OpenBSD this does neither
+* block nor fail.
+*/
+size_t Getentropy::poll(RandomNumberGenerator& rng)
+   {
+   secure_vector<uint8_t> buf(256);
+
+   if(::getentropy(buf.data(), buf.size()) == 0)
+      {
+      rng.add_entropy(buf.data(), buf.size());
+      return buf.size() * 8;
+      }
+
+   return 0;
+   }
+}
diff --git a/third_party/botan/src/lib/entropy/getentropy/getentropy.h b/third_party/botan/src/lib/entropy/getentropy/getentropy.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/getentropy/getentropy.h
@@ -0,0 +1,28 @@
+/*
+* Entropy Source Using OpenBSD getentropy(2) system call
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_GETENTROPY_H_
+#define BOTAN_ENTROPY_SRC_GETENTROPY_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Entropy source using the getentropy(2) system call first introduced in
+* OpenBSD 5.6 and added to Solaris 11.3.
+*/
+class Getentropy final : public Entropy_Source
+   {
+   public:
+      std::string name() const override { return "getentropy"; }
+      size_t poll(RandomNumberGenerator& rng) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp b/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp
@@ -0,0 +1,154 @@
+/*
+* Entropy source based on reading files in /proc on the assumption
+* that a remote attacker will have difficulty guessing some of them.
+*
+* (C) 1999-2008,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/proc_walk.h>
+#include <deque>
+
+#ifndef _POSIX_C_SOURCE
+  #define _POSIX_C_SOURCE 199309
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+namespace Botan {
+
+namespace {
+
+class Directory_Walker final : public File_Descriptor_Source
+   {
+   public:
+      explicit Directory_Walker(const std::string& root) :
+         m_cur_dir(std::make_pair<DIR*, std::string>(nullptr, ""))
+         {
+         if(DIR* root_dir = ::opendir(root.c_str()))
+            m_cur_dir = std::make_pair(root_dir, root);
+         }
+
+      ~Directory_Walker()
+         {
+         if(m_cur_dir.first)
+            ::closedir(m_cur_dir.first);
+         }
+
+      int next_fd() override;
+   private:
+      std::pair<struct dirent*, std::string> get_next_dirent();
+
+      std::pair<DIR*, std::string> m_cur_dir;
+      std::deque<std::string> m_dirlist;
+   };
+
+std::pair<struct dirent*, std::string> Directory_Walker::get_next_dirent()
+   {
+   while(m_cur_dir.first)
+      {
+      if(struct dirent* dir = ::readdir(m_cur_dir.first))
+         return std::make_pair(dir, m_cur_dir.second);
+
+      ::closedir(m_cur_dir.first);
+      m_cur_dir = std::make_pair<DIR*, std::string>(nullptr, "");
+
+      while(!m_dirlist.empty() && !m_cur_dir.first)
+         {
+         const std::string next_dir_name = m_dirlist[0];
+         m_dirlist.pop_front();
+
+         if(DIR* next_dir = ::opendir(next_dir_name.c_str()))
+            m_cur_dir = std::make_pair(next_dir, next_dir_name);
+         }
+      }
+
+   return std::make_pair<struct dirent*, std::string>(nullptr, ""); // nothing left
+   }
+
+int Directory_Walker::next_fd()
+   {
+   while(true)
+      {
+      std::pair<struct dirent*, std::string> entry = get_next_dirent();
+
+      if(!entry.first)
+         break; // no more dirs
+
+      const std::string filename = entry.first->d_name;
+
+      if(filename == "." || filename == "..")
+         continue;
+
+      const std::string full_path = entry.second + "/" + filename;
+
+      struct stat stat_buf;
+      if(::lstat(full_path.c_str(), &stat_buf) == -1)
+         continue;
+
+      if(S_ISDIR(stat_buf.st_mode))
+         {
+         m_dirlist.push_back(full_path);
+         }
+      else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH))
+         {
+         int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY);
+
+         if(fd >= 0)
+            return fd;
+         }
+      }
+
+   return -1;
+   }
+
+}
+
+size_t ProcWalking_EntropySource::poll(RandomNumberGenerator& rng)
+   {
+   const size_t MAX_FILES_READ_PER_POLL = 2048;
+
+   lock_guard_type<mutex_type> lock(m_mutex);
+
+   if(!m_dir)
+      m_dir.reset(new Directory_Walker(m_path));
+
+   m_buf.resize(4096);
+
+   size_t bits = 0;
+
+   for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i)
+      {
+      int fd = m_dir->next_fd();
+
+      // If we've exhaused this walk of the directory, halt the poll
+      if(fd == -1)
+         {
+         m_dir.reset();
+         break;
+         }
+
+      ssize_t got = ::read(fd, m_buf.data(), m_buf.size());
+      ::close(fd);
+
+      if(got > 0)
+         {
+         rng.add_entropy(m_buf.data(), static_cast<size_t>(got));
+
+         // Conservative estimate of 4 bits per file
+         bits += 4;
+         }
+
+      if(bits > 128)
+         break;
+      }
+
+   return bits;
+   }
+
+}
diff --git a/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h b/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h
@@ -0,0 +1,45 @@
+/*
+* File Tree Walking EntropySource
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_PROC_WALK_H_
+#define BOTAN_ENTROPY_SRC_PROC_WALK_H_
+
+#include <botan/entropy_src.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+class File_Descriptor_Source
+   {
+   public:
+      virtual int next_fd() = 0;
+      virtual ~File_Descriptor_Source() = default;
+   };
+
+/**
+* File Tree Walking Entropy Source
+*/
+class ProcWalking_EntropySource final : public Entropy_Source
+   {
+   public:
+      std::string name() const override { return "proc_walk"; }
+
+      size_t poll(RandomNumberGenerator& rng) override;
+
+      explicit ProcWalking_EntropySource(const std::string& root_dir) :
+         m_path(root_dir), m_dir(nullptr) {}
+
+   private:
+      const std::string m_path;
+      mutex_type m_mutex;
+      std::unique_ptr<File_Descriptor_Source> m_dir;
+      secure_vector<uint8_t> m_buf;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp b/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp
@@ -0,0 +1,97 @@
+/*
+* Entropy Source Using Intel's rdseed instruction
+* (C) 2015 Daniel Neus
+* (C) 2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/rdseed.h>
+#include <botan/cpuid.h>
+
+#include <immintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("rdseed")
+bool read_rdseed(secure_vector<uint32_t>& seed)
+   {
+   /*
+   * RDSEED is not guaranteed to generate an output within any specific number
+   * of attempts. However in testing on a Skylake system, with all hyperthreads
+   * occupied in tight RDSEED loops, RDSEED will still usually succeed in under
+   * 150 attempts. The maximum ever seen was 230 attempts until success. When
+   * idle, RDSEED usually succeeds in 1 or 2 attempts.
+   *
+   * We set an upper bound of 512 attempts, because it is possible that due
+   * to firmware issue RDSEED is simply broken and never succeeds. We do not
+   * want to loop forever in that case. If we exceed that limit, then we assume
+   * the hardware is actually just broken, and stop the poll.
+   */
+   const size_t RDSEED_RETRIES = 512;
+
+   for(size_t i = 0; i != RDSEED_RETRIES; ++i)
+      {
+      uint32_t r = 0;
+      int cf = 0;
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+      asm("rdseed %0; adcl $0,%1" :
+          "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc");
+#else
+      cf = _rdseed32_step(&r);
+#endif
+
+      if(1 == cf)
+         {
+         seed.push_back(r);
+         return true;
+         }
+
+      // Intel suggests pausing if RDSEED fails.
+      _mm_pause();
+      }
+
+   return false; // failed to produce an output after many attempts
+   }
+
+}
+
+size_t Intel_Rdseed::poll(RandomNumberGenerator& rng)
+   {
+   const size_t RDSEED_BYTES = 1024;
+   static_assert(RDSEED_BYTES % 4 == 0, "Bad RDSEED configuration");
+
+   if(CPUID::has_rdseed())
+      {
+      secure_vector<uint32_t> seed;
+      seed.reserve(RDSEED_BYTES / 4);
+
+      for(size_t p = 0; p != RDSEED_BYTES / 4; ++p)
+         {
+         /*
+         If at any point we exceed our retry count, we stop the entire seed
+         gathering process. This situation will only occur in situations of
+         extremely high RDSEED utilization. If RDSEED is currently so highly
+         contended, then the rest of the poll is likely to also face contention and
+         it is better to quit now rather than (presumably) face very high retry
+         times for the rest of the poll.
+         */
+         if(!read_rdseed(seed))
+            break;
+         }
+
+      if(seed.size() > 0)
+         {
+         rng.add_entropy(reinterpret_cast<const uint8_t*>(seed.data()),
+                         seed.size() * sizeof(uint32_t));
+         }
+      }
+
+   // RDSEED is used but not trusted
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/entropy/rdseed/rdseed.h b/third_party/botan/src/lib/entropy/rdseed/rdseed.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/rdseed/rdseed.h
@@ -0,0 +1,28 @@
+/*
+* Entropy Source Using Intel's rdseed instruction
+* (C) 2015 Jack Lloyd, Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_RDSEED_H_
+#define BOTAN_ENTROPY_SRC_RDSEED_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Entropy source using the rdseed instruction first introduced on
+* Intel's Broadwell architecture.
+*/
+class Intel_Rdseed final : public Entropy_Source
+   {
+   public:
+      std::string name() const override { return "rdseed"; }
+      size_t poll(RandomNumberGenerator& rng) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp b/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp
@@ -0,0 +1,59 @@
+/*
+* (C) 1999-2009,2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/es_win32.h>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+
+namespace Botan {
+
+size_t Win32_EntropySource::poll(RandomNumberGenerator& rng)
+   {
+   rng.add_entropy_T(::GetTickCount());
+   rng.add_entropy_T(::GetMessagePos());
+   rng.add_entropy_T(::GetMessageTime());
+   rng.add_entropy_T(::GetInputState());
+
+   rng.add_entropy_T(::GetCurrentProcessId());
+   rng.add_entropy_T(::GetCurrentThreadId());
+
+   SYSTEM_INFO sys_info;
+   ::GetSystemInfo(&sys_info);
+   rng.add_entropy_T(sys_info);
+
+   MEMORYSTATUSEX mem_info;
+   ::GlobalMemoryStatusEx(&mem_info);
+   rng.add_entropy_T(mem_info);
+
+   POINT point;
+   ::GetCursorPos(&point);
+   rng.add_entropy_T(point);
+
+   ::GetCaretPos(&point);
+   rng.add_entropy_T(point);
+
+   /*
+   Potential other sources to investigate
+
+   GetProductInfo
+   GetComputerNameExA
+   GetSystemFirmwareTable
+   GetVersionExA
+   GetProcessorSystemCycleTime
+   GetProcessHandleCount(GetCurrentProcess())
+   GetThreadTimes(GetCurrentThread())
+   QueryThreadCycleTime
+   QueryIdleProcessorCycleTime
+   QueryUnbiasedInterruptTime
+   */
+
+   // We assume all of the above is basically junk
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/entropy/win32_stats/es_win32.h b/third_party/botan/src/lib/entropy/win32_stats/es_win32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/entropy/win32_stats/es_win32.h
@@ -0,0 +1,27 @@
+/*
+* Win32 EntropySource
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_WIN32_H_
+#define BOTAN_ENTROPY_SRC_WIN32_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Win32 Entropy Source
+*/
+class Win32_EntropySource final : public Entropy_Source
+   {
+   public:
+      std::string name() const override { return "system_stats"; }
+      size_t poll(RandomNumberGenerator& rng) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/ffi/ffi.cpp b/third_party/botan/src/lib/ffi/ffi.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi.cpp
@@ -0,0 +1,293 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/os_utils.h>
+#include <botan/version.h>
+#include <botan/mem_ops.h>
+#include <botan/hex.h>
+#include <botan/base64.h>
+#include <cstdio>
+#include <cstdlib>
+
+namespace Botan_FFI {
+
+int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc)
+   {
+   std::string val;
+   if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") == true && val != "")
+      {
+      std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc);
+      }
+   return rc;
+   }
+
+namespace {
+
+int ffi_map_error_type(Botan::ErrorType err)
+   {
+   switch(err)
+      {
+      case Botan::ErrorType::Unknown:
+         return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+
+      case Botan::ErrorType::SystemError:
+      case Botan::ErrorType::IoError:
+      case Botan::ErrorType::OpenSSLError:
+      case Botan::ErrorType::Pkcs11Error:
+      case Botan::ErrorType::CommonCryptoError:
+      case Botan::ErrorType::TPMError:
+      case Botan::ErrorType::ZlibError:
+      case Botan::ErrorType::Bzip2Error:
+      case Botan::ErrorType::LzmaError:
+      case Botan::ErrorType::DatabaseError:
+         return BOTAN_FFI_ERROR_SYSTEM_ERROR;
+
+      case Botan::ErrorType::NotImplemented:
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+      case Botan::ErrorType::OutOfMemory:
+         return BOTAN_FFI_ERROR_OUT_OF_MEMORY;
+      case Botan::ErrorType::InternalError:
+         return BOTAN_FFI_ERROR_INTERNAL_ERROR;
+      case Botan::ErrorType::InvalidObjectState:
+         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
+      case Botan::ErrorType::KeyNotSet:
+         return BOTAN_FFI_ERROR_KEY_NOT_SET;
+      case Botan::ErrorType::InvalidArgument:
+      case Botan::ErrorType::InvalidNonceLength:
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      case Botan::ErrorType::EncodingFailure:
+      case Botan::ErrorType::DecodingFailure:
+         return BOTAN_FFI_ERROR_INVALID_INPUT;
+
+      case Botan::ErrorType::InvalidTag:
+         return BOTAN_FFI_ERROR_BAD_MAC;
+
+      case Botan::ErrorType::InvalidKeyLength:
+         return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
+      case Botan::ErrorType::LookupError:
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      case Botan::ErrorType::HttpError:
+         return BOTAN_FFI_ERROR_HTTP_ERROR;
+      case Botan::ErrorType::TLSError:
+         return BOTAN_FFI_ERROR_TLS_ERROR;
+      case Botan::ErrorType::RoughtimeError:
+         return BOTAN_FFI_ERROR_ROUGHTIME_ERROR;
+      }
+
+   return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+   }
+
+}
+
+int ffi_guard_thunk(const char* func_name, std::function<int ()> thunk)
+   {
+   try
+      {
+      return thunk();
+      }
+   catch(std::bad_alloc&)
+      {
+      return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY);
+      }
+   catch(Botan_FFI::FFI_Error& e)
+      {
+      return ffi_error_exception_thrown(func_name, e.what(), e.error_code());
+      }
+   catch(Botan::Exception& e)
+      {
+      return ffi_error_exception_thrown(func_name, e.what(), ffi_map_error_type(e.error_type()));
+      }
+   catch(std::exception& e)
+      {
+      return ffi_error_exception_thrown(func_name, e.what());
+      }
+   catch(...)
+      {
+      return ffi_error_exception_thrown(func_name, "unknown exception");
+      }
+
+   return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+   }
+
+}
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+const char* botan_error_description(int err)
+   {
+   switch(err)
+      {
+      case BOTAN_FFI_SUCCESS:
+         return "OK";
+
+      case BOTAN_FFI_INVALID_VERIFIER:
+         return "Invalid verifier";
+
+      case BOTAN_FFI_ERROR_INVALID_INPUT:
+         return "Invalid input";
+
+      case BOTAN_FFI_ERROR_BAD_MAC:
+         return "Invalid authentication code";
+
+      case BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE:
+         return "Insufficient buffer space";
+
+      case BOTAN_FFI_ERROR_EXCEPTION_THROWN:
+         return "Exception thrown";
+
+      case BOTAN_FFI_ERROR_OUT_OF_MEMORY:
+         return "Out of memory";
+
+      case BOTAN_FFI_ERROR_SYSTEM_ERROR:
+         return "Error while calling system API";
+
+      case BOTAN_FFI_ERROR_INTERNAL_ERROR:
+         return "Internal error";
+
+      case BOTAN_FFI_ERROR_BAD_FLAG:
+         return "Bad flag";
+
+      case BOTAN_FFI_ERROR_NULL_POINTER:
+         return "Null pointer argument";
+
+      case BOTAN_FFI_ERROR_BAD_PARAMETER:
+         return "Bad parameter";
+
+      case BOTAN_FFI_ERROR_KEY_NOT_SET:
+         return "Key not set on object";
+
+      case BOTAN_FFI_ERROR_INVALID_KEY_LENGTH:
+         return "Invalid key length";
+
+      case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE:
+         return "Invalid object state";
+
+      case BOTAN_FFI_ERROR_NOT_IMPLEMENTED:
+         return "Not implemented";
+
+      case BOTAN_FFI_ERROR_INVALID_OBJECT:
+         return "Invalid object handle";
+
+      case BOTAN_FFI_ERROR_TLS_ERROR:
+         return "TLS error";
+
+      case BOTAN_FFI_ERROR_HTTP_ERROR:
+         return "HTTP error";
+
+      case BOTAN_FFI_ERROR_UNKNOWN_ERROR:
+         return "Unknown error";
+      }
+
+   return "Unknown error";
+   }
+
+/*
+* Versioning
+*/
+uint32_t botan_ffi_api_version()
+   {
+   return BOTAN_HAS_FFI;
+   }
+
+int botan_ffi_supports_api(uint32_t api_version)
+   {
+   // This is the API introduced in 2.13
+   if(api_version == 20191214)
+      return BOTAN_FFI_SUCCESS;
+
+   // This is the API introduced in 2.8
+   if(api_version == 20180713)
+      return BOTAN_FFI_SUCCESS;
+
+   // This is the API introduced in 2.3
+   if(api_version == 20170815)
+      return BOTAN_FFI_SUCCESS;
+
+   // This is the API introduced in 2.1
+   if(api_version == 20170327)
+      return BOTAN_FFI_SUCCESS;
+
+   // This is the API introduced in 2.0
+   if(api_version == 20150515)
+      return BOTAN_FFI_SUCCESS;
+
+   // Something else:
+   return -1;
+   }
+
+const char* botan_version_string()
+   {
+   return Botan::version_cstr();
+   }
+
+uint32_t botan_version_major() { return Botan::version_major(); }
+uint32_t botan_version_minor() { return Botan::version_minor(); }
+uint32_t botan_version_patch() { return Botan::version_patch(); }
+uint32_t botan_version_datestamp()  { return Botan::version_datestamp(); }
+
+int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len)
+   {
+   return Botan::constant_time_compare(x, y, len) ? 0 : -1;
+   }
+
+int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len)
+   {
+   return botan_constant_time_compare(x, y, len);
+   }
+
+int botan_scrub_mem(void* mem, size_t bytes)
+   {
+   Botan::secure_scrub_memory(mem, bytes);
+   return BOTAN_FFI_SUCCESS;
+   }
+
+int botan_hex_encode(const uint8_t* in, size_t len, char* out, uint32_t flags)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0;
+      Botan::hex_encode(out, in, len, uppercase);
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::vector<uint8_t> bin = Botan::hex_decode(hex_str, in_len);
+      return Botan_FFI::write_vec_output(out, out_len, bin);
+      });
+   }
+
+int botan_base64_encode(const uint8_t* in, size_t len, char* out, size_t* out_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::string base64 = Botan::base64_encode(in, len);
+      return Botan_FFI::write_str_output(out, out_len, base64);
+      });
+   }
+
+int botan_base64_decode(const char* base64_str, size_t in_len,
+                        uint8_t* out, size_t* out_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(*out_len < Botan::base64_decode_max_output(in_len))
+         {
+         *out_len = Botan::base64_decode_max_output(in_len);
+         return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+         }
+
+      *out_len = Botan::base64_decode(out, std::string(base64_str, in_len));
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi.h b/third_party/botan/src/lib/ffi/ffi.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi.h
@@ -0,0 +1,1784 @@
+/*
+* FFI (C89 API)
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_H_
+#define BOTAN_FFI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+This header exports some of botan's functionality via a C89 interface. This API
+is uesd by the Python, OCaml, Rust and Ruby bindings via those languages
+respective ctypes/FFI libraries.
+
+The API is intended to be as easy as possible to call from other
+languages, which often have easy ways to call C, because C. But some C
+code is easier to deal with than others, so to make things easy this
+API follows a few simple rules:
+
+- All interactions are via pointers to opaque structs. No need to worry about
+  structure padding issues and the like.
+
+- All functions return an int error code (except the version calls, which are
+  assumed to always have something to say).
+
+- Use simple types: size_t for lengths, const char* NULL terminated strings,
+  uint8_t for binary.
+
+- No ownership of memory transfers across the API boundary. The API will
+  consume data from const pointers, and will produce output by writing to
+  buffers provided by (and allocated by) the caller.
+
+- If exporting a value (a string or a blob) the function takes a pointer to the
+  output array and a read/write pointer to the length. If the length is insufficient, an
+  error is returned. So passing nullptr/0 allows querying the final value.
+
+  Note this does not apply to all functions, like `botan_hash_final`
+  which is not idempotent and are documented specially. But it's a
+  general theory of operation.
+
+ TODO:
+ - Doxygen comments for all functions/params
+ - TLS
+*/
+
+#include <botan/build.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+* Error codes
+*
+* If you add a new value here be sure to also add it in
+* botan_error_description
+*/
+enum BOTAN_FFI_ERROR {
+   BOTAN_FFI_SUCCESS = 0,
+   BOTAN_FFI_INVALID_VERIFIER = 1,
+
+   BOTAN_FFI_ERROR_INVALID_INPUT = -1,
+   BOTAN_FFI_ERROR_BAD_MAC = -2,
+
+   BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10,
+
+   BOTAN_FFI_ERROR_EXCEPTION_THROWN = -20,
+   BOTAN_FFI_ERROR_OUT_OF_MEMORY = -21,
+   BOTAN_FFI_ERROR_SYSTEM_ERROR = -22,
+   BOTAN_FFI_ERROR_INTERNAL_ERROR = -23,
+
+   BOTAN_FFI_ERROR_BAD_FLAG = -30,
+   BOTAN_FFI_ERROR_NULL_POINTER = -31,
+   BOTAN_FFI_ERROR_BAD_PARAMETER = -32,
+   BOTAN_FFI_ERROR_KEY_NOT_SET = -33,
+   BOTAN_FFI_ERROR_INVALID_KEY_LENGTH = -34,
+   BOTAN_FFI_ERROR_INVALID_OBJECT_STATE = -35,
+
+   BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40,
+   BOTAN_FFI_ERROR_INVALID_OBJECT = -50,
+
+   BOTAN_FFI_ERROR_TLS_ERROR = -75,
+   BOTAN_FFI_ERROR_HTTP_ERROR = -76,
+   BOTAN_FFI_ERROR_ROUGHTIME_ERROR = -77,
+
+   BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100,
+};
+
+/**
+* Convert an error code into a string. Returns "Unknown error"
+* if the error code is not a known one.
+*/
+BOTAN_PUBLIC_API(2,8) const char* botan_error_description(int err);
+
+/**
+* Return 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.
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_ffi_api_version(void);
+
+/**
+* Return 0 (ok) if the version given is one this library supports.
+* botan_ffi_supports_api(botan_ffi_api_version()) will always return 0.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_ffi_supports_api(uint32_t api_version);
+
+/**
+* Return a free-form version string, e.g., 2.0.0
+*/
+BOTAN_PUBLIC_API(2,0) const char* botan_version_string(void);
+
+/**
+* Return the major version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_major(void);
+
+/**
+* Return the minor version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_minor(void);
+
+/**
+* Return the patch version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_patch(void);
+
+/**
+* Return the date this version was released as
+* an integer, or 0 if an unreleased version
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_datestamp(void);
+
+/**
+* Returns 0 if x[0..len] == y[0..len], or otherwise -1
+*/
+BOTAN_PUBLIC_API(2,3) int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len);
+
+/**
+* Deprecated equivalent to botan_constant_time_compare
+*/
+BOTAN_PUBLIC_API(2,0) int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len);
+
+/**
+* Clear out memory using a system specific approach to bypass elision by the
+* compiler (currently using RtlSecureZeroMemory or tricks with volatile pointers).
+*/
+BOTAN_PUBLIC_API(2,2) int botan_scrub_mem(void* mem, size_t bytes);
+
+#define BOTAN_FFI_HEX_LOWER_CASE 1
+
+/**
+* Perform hex encoding
+* @param x is some binary data
+* @param len length of x in bytes
+* @param out an array of at least x*2 bytes
+* @param flags flags out be upper or lower case?
+* @return 0 on success, 1 on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags);
+
+/**
+* Perform hex decoding
+* @param hex_str a string of hex chars (whitespace is ignored)
+* @param in_len the length of hex_str
+* @param out the output buffer should be at least strlen(hex_str)/2 bytes
+* @param out_len the size of out
+*/
+BOTAN_PUBLIC_API(2,3) int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len);
+
+/**
+* Perform base64 encoding
+*/
+BOTAN_PUBLIC_API(2,3) int botan_base64_encode(const uint8_t* x, size_t len, char* out, size_t* out_len);
+
+
+/**
+* Perform base64 decoding
+*/
+BOTAN_PUBLIC_API(2,3) int botan_base64_decode(const char* base64_str, size_t in_len,
+                                              uint8_t* out, size_t* out_len);
+
+/**
+* RNG type
+*/
+typedef struct botan_rng_struct* botan_rng_t;
+
+/**
+* Initialize a random number generator object
+* @param rng rng object
+* @param rng_type type of the rng, possible values:
+*    "system": system RNG
+*    "user": userspace RNG
+*    "user-threadsafe": userspace RNG, with internal locking
+*    "rdrand": directly read RDRAND
+* Set rng_type to null to let the library choose some default.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_init(botan_rng_t* rng, const char* rng_type);
+
+/**
+* Get random bytes from a random number generator
+* @param rng rng object
+* @param out output buffer of size out_len
+* @param out_len number of requested bytes
+* @return 0 on success, negative on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len);
+
+/**
+* Reseed a random number generator
+* Uses the System_RNG as a seed generator.
+*
+* @param rng rng object
+* @param bits number of bits to to reseed with
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_reseed(botan_rng_t rng, size_t bits);
+
+/**
+* Reseed a random number generator
+*
+* @param rng rng object
+* @param source_rng the rng that will be read from
+* @param bits number of bits to to reseed with
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,8) int botan_rng_reseed_from_rng(botan_rng_t rng,
+                                                    botan_rng_t source_rng,
+                                                    size_t bits);
+
+/**
+* Add some seed material to a random number generator
+*
+* @param rng rng object
+* @param entropy the data to add
+* @param entropy_len length of entropy buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,8) int botan_rng_add_entropy(botan_rng_t rng,
+                                                const uint8_t* entropy,
+                                                size_t entropy_len);
+
+/**
+* Frees all resources of the random number generator object
+* @param rng rng object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_destroy(botan_rng_t rng);
+
+/*
+* Hash type
+*/
+typedef struct botan_hash_struct* botan_hash_t;
+
+/**
+* Initialize a hash function object
+* @param hash hash object
+* @param hash_name name of the hash function, e.g., "SHA-384"
+* @param flags should be 0 in current API revision, all other uses are reserved
+*       and return BOTAN_FFI_ERROR_BAD_FLAG
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags);
+
+/**
+* Copy the state of a hash function object
+* @param dest destination hash object
+* @param source source hash object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,2) int botan_hash_copy_state(botan_hash_t *dest, const botan_hash_t source);
+
+/**
+* Writes the output length of the hash function to *output_length
+* @param hash hash object
+* @param output_length output buffer to hold the hash function output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_output_length(botan_hash_t hash, size_t* output_length);
+
+/**
+* Writes the block size of the hash function to *block_size
+* @param hash hash object
+* @param block_size output buffer to hold the hash function output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,2) int botan_hash_block_size(botan_hash_t hash, size_t* block_size);
+
+/**
+* Send more input to the hash function
+* @param hash hash object
+* @param in input buffer
+* @param in_len number of bytes to read from the input buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_update(botan_hash_t hash, const uint8_t* in, size_t in_len);
+
+/**
+* Finalizes the hash computation and writes the output to
+* out[0:botan_hash_output_length()] then reinitializes for computing
+* another digest as if botan_hash_clear had been called.
+* @param hash hash object
+* @param out output buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_final(botan_hash_t hash, uint8_t out[]);
+
+/**
+* Reinitializes the state of the hash computation. A hash can
+* be computed (with update/final) immediately.
+* @param hash hash object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_clear(botan_hash_t hash);
+
+/**
+* Frees all resources of the hash object
+* @param hash hash object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_destroy(botan_hash_t hash);
+
+/**
+* Get the name of this hash function
+* @param hash the object to read
+* @param name output buffer
+* @param name_len on input, the length of buffer, on success the number of bytes written
+*/
+BOTAN_PUBLIC_API(2,8) int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len);
+
+/*
+* Message Authentication type
+*/
+typedef struct botan_mac_struct* botan_mac_t;
+
+/**
+* Initialize a message authentication code object
+* @param mac mac object
+* @param mac_name name of the hash function, e.g., "HMAC(SHA-384)"
+* @param flags should be 0 in current API revision, all other uses are reserved
+*       and return a negative value (error code)
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags);
+
+/**
+* Writes the output length of the message authentication code to *output_length
+* @param mac mac object
+* @param output_length output buffer to hold the MAC output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_output_length(botan_mac_t mac, size_t* output_length);
+
+/**
+* Sets the key on the MAC
+* @param mac mac object
+* @param key buffer holding the key
+* @param key_len size of the key buffer in bytes
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len);
+
+/**
+* Send more input to the message authentication code
+* @param mac mac object
+* @param buf input buffer
+* @param len number of bytes to read from the input buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len);
+
+/**
+* Finalizes the MAC computation and writes the output to
+* out[0:botan_mac_output_length()] then reinitializes for computing
+* another MAC as if botan_mac_clear had been called.
+* @param mac mac object
+* @param out output buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_final(botan_mac_t mac, uint8_t out[]);
+
+/**
+* Reinitializes the state of the MAC computation. A MAC can
+* be computed (with update/final) immediately.
+* @param mac mac object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_clear(botan_mac_t mac);
+
+/**
+* Get the name of this MAC
+* @param mac the object to read
+* @param name output buffer
+* @param name_len on input, the length of buffer, on success the number of bytes written
+*/
+BOTAN_PUBLIC_API(2,8) int botan_mac_name(botan_mac_t mac, char* name, size_t* name_len);
+
+/**
+* Get the key length limits of this auth code
+* @param mac the object to read
+* @param out_minimum_keylength if non-NULL, will be set to minimum keylength of MAC
+* @param out_maximum_keylength if non-NULL, will be set to maximum keylength of MAC
+* @param out_keylength_modulo if non-NULL will be set to byte multiple of valid keys
+*/
+BOTAN_PUBLIC_API(2,8) int botan_mac_get_keyspec(botan_mac_t mac,
+                                                 size_t* out_minimum_keylength,
+                                                 size_t* out_maximum_keylength,
+                                                 size_t* out_keylength_modulo);
+
+/**
+* Frees all resources of the MAC object
+* @param mac mac object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_destroy(botan_mac_t mac);
+
+/*
+* Cipher modes
+*/
+typedef struct botan_cipher_struct* botan_cipher_t;
+
+#define BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION 1
+#define BOTAN_CIPHER_INIT_FLAG_ENCRYPT 0
+#define BOTAN_CIPHER_INIT_FLAG_DECRYPT 1
+
+/**
+* Initialize a cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_init(botan_cipher_t* cipher, const char* name, uint32_t flags);
+
+/**
+* Return the name of the cipher object
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len);
+
+/**
+* Return the output length of this cipher, for a particular input length.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len);
+
+/**
+* Return if the specified nonce length is valid for this cipher
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl);
+
+/**
+* Get the tag length of the cipher (0 for non-AEAD modes)
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_size);
+
+/**
+* Get the default nonce length of this cipher
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl);
+
+/**
+* Return the update granularity of the cipher; botan_cipher_update must be
+* called with blocks of this size, except for the final.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug);
+
+/**
+* Get information about the key lengths. Prefer botan_cipher_get_keyspec
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_query_keylen(botan_cipher_t,
+                                        size_t* out_minimum_keylength,
+                                        size_t* out_maximum_keylength);
+
+/**
+* Get information about the supported key lengths.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_get_keyspec(botan_cipher_t,
+                                                   size_t* min_keylen,
+                                                   size_t* max_keylen,
+                                                   size_t* mod_keylen);
+
+/**
+* Set the key for this cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_set_key(botan_cipher_t cipher,
+                                               const uint8_t* key, size_t key_len);
+
+/**
+* Reset the message specific state for this cipher.
+* Without resetting the keys, this resets the nonce, and any state
+* associated with any message bits that have been processed so far.
+*
+* It is conceptually equivalent to calling botan_cipher_clear followed
+* by botan_cipher_set_key with the original key.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_reset(botan_cipher_t cipher);
+
+/**
+* Set the associated data. Will fail if cipher is not an AEAD
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_set_associated_data(botan_cipher_t cipher,
+                                               const uint8_t* ad, size_t ad_len);
+
+/**
+* Begin processing a new message using the provided nonce
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_start(botan_cipher_t cipher,
+                                 const uint8_t* nonce, size_t nonce_len);
+
+#define BOTAN_CIPHER_UPDATE_FLAG_FINAL (1U << 0)
+
+/**
+* Encrypt some data
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_update(botan_cipher_t cipher,
+                                  uint32_t flags,
+                                  uint8_t output[],
+                                  size_t output_size,
+                                  size_t* output_written,
+                                  const uint8_t input_bytes[],
+                                  size_t input_size,
+                                  size_t* input_consumed);
+
+/**
+* Reset the key, nonce, AD and all other state on this cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_clear(botan_cipher_t hash);
+
+/**
+* Destroy the cipher object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_destroy(botan_cipher_t cipher);
+
+/*
+* Derive a key from a passphrase for a number of iterations
+* @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)"
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @param iterations the number of iterations to use (use 10K or more)
+* @return 0 on success, a negative value on failure
+*
+* Deprecated: use
+*  botan_pwdhash(pbkdf_algo, iterations, 0, 0, out, out_len,
+*                passphrase, 0, salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,0) int
+BOTAN_DEPRECATED("Use botan_pwdhash")
+botan_pbkdf(const char* pbkdf_algo,
+            uint8_t out[], size_t out_len,
+            const char* passphrase,
+            const uint8_t salt[], size_t salt_len,
+            size_t iterations);
+
+/**
+* Derive a key from a passphrase, running until msec time has elapsed.
+* @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)"
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @param milliseconds_to_run if iterations is zero, then instead the PBKDF is
+*        run until milliseconds_to_run milliseconds has passed
+* @param out_iterations_used set to the number iterations executed
+* @return 0 on success, a negative value on failure
+*
+* Deprecated: use
+*
+* botan_pwdhash_timed(pbkdf_algo,
+*                     static_cast<uint32_t>(ms_to_run),
+*                     iterations_used,
+*                     nullptr,
+*                     nullptr,
+*                     out, out_len,
+*                     password, 0,
+*                     salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pbkdf_timed(const char* pbkdf_algo,
+                                uint8_t out[], size_t out_len,
+                                const char* passphrase,
+                                const uint8_t salt[], size_t salt_len,
+                                size_t milliseconds_to_run,
+                                size_t* out_iterations_used);
+
+
+/*
+* Derive a key from a passphrase
+* @param algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)" or "Scrypt"
+* @param param1 the first PBKDF algorithm parameter
+* @param param2 the second PBKDF algorithm parameter (may be zero if unneeded)
+* @param param3 the third PBKDF algorithm parameter (may be zero if unneeded)
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param passphrase_len if > 0, specifies length of password. If len == 0, then
+*        strlen will be called on passphrase to compute the length.
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @return 0 on success, a negative value on failure
+*/
+int BOTAN_PUBLIC_API(2,8) botan_pwdhash(
+   const char* algo,
+   size_t param1,
+   size_t param2,
+   size_t param3,
+   uint8_t out[],
+   size_t out_len,
+   const char* passphrase,
+   size_t passphrase_len,
+   const uint8_t salt[],
+   size_t salt_len);
+
+/*
+* Derive a key from a passphrase
+* @param pbkdf_algo PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"
+* @param msec the desired runtime in milliseconds
+* @param param1 will be set to the first password hash parameter
+* @param param2 will be set to the second password hash parameter
+* @param param3 will be set to the third password hash parameter
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param passphrase_len if > 0, specifies length of password. If len == 0, then
+*        strlen will be called on passphrase to compute the length.
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @return 0 on success, a negative value on failure
+*/
+int BOTAN_PUBLIC_API(2,8) botan_pwdhash_timed(
+   const char* algo,
+   uint32_t msec,
+   size_t* param1,
+   size_t* param2,
+   size_t* param3,
+   uint8_t out[],
+   size_t out_len,
+   const char* passphrase,
+   size_t passphrase_len,
+   const uint8_t salt[],
+   size_t salt_len);
+
+/**
+* Derive a key using scrypt
+* Deprecated; use
+* botan_pwdhash("Scrypt", N, r, p, out, out_len, password, 0, salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,8) int
+BOTAN_DEPRECATED("Use botan_pwdhash")
+botan_scrypt(uint8_t out[], size_t out_len,
+             const char* passphrase,
+             const uint8_t salt[], size_t salt_len,
+             size_t N, size_t r, size_t p);
+
+/**
+* Derive a key
+* @param kdf_algo KDF algorithm, e.g., "SP800-56C"
+* @param out buffer holding the derived key, must be of length out_len
+* @param out_len the desired output length in bytes
+* @param secret the secret input
+* @param secret_len size of secret in bytes
+* @param salt a diversifier
+* @param salt_len size of salt in bytes
+* @param label purpose for the derived keying material
+* @param label_len size of label in bytes
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_kdf(const char* kdf_algo,
+                        uint8_t out[], size_t out_len,
+                        const uint8_t secret[], size_t secret_len,
+                        const uint8_t salt[], size_t salt_len,
+                        const uint8_t label[], size_t label_len);
+
+/*
+* Raw Block Cipher (PRP) interface
+*/
+typedef struct botan_block_cipher_struct* botan_block_cipher_t;
+
+/**
+* Initialize a block cipher object
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_init(botan_block_cipher_t* bc,
+                                                  const char* cipher_name);
+
+/**
+* Destroy a block cipher object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_destroy(botan_block_cipher_t bc);
+
+/**
+* Reinitializes the block cipher
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_clear(botan_block_cipher_t bc);
+
+/**
+* Set the key for a block cipher instance
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_set_key(botan_block_cipher_t bc,
+                                                     const uint8_t key[], size_t len);
+
+/**
+* Return the positive block size of this block cipher, or negative to
+* indicate an error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_block_size(botan_block_cipher_t bc);
+
+/**
+* Encrypt one or more blocks with the cipher
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc,
+                                                            const uint8_t in[],
+                                                            uint8_t out[],
+                                                            size_t blocks);
+
+/**
+* Decrypt one or more blocks with the cipher
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc,
+                                                            const uint8_t in[],
+                                                            uint8_t out[],
+                                                            size_t blocks);
+
+/**
+* Get the name of this block cipher
+* @param cipher the object to read
+* @param name output buffer
+* @param name_len on input, the length of buffer, on success the number of bytes written
+*/
+BOTAN_PUBLIC_API(2,8) int botan_block_cipher_name(botan_block_cipher_t cipher,
+                                                  char* name, size_t* name_len);
+
+
+/**
+* Get the key length limits of this block cipher
+* @param cipher the object to read
+* @param out_minimum_keylength if non-NULL, will be set to minimum keylength of cipher
+* @param out_maximum_keylength if non-NULL, will be set to maximum keylength of cipher
+* @param out_keylength_modulo if non-NULL will be set to byte multiple of valid keys
+*/
+BOTAN_PUBLIC_API(2,8) int botan_block_cipher_get_keyspec(botan_block_cipher_t cipher,
+                                                         size_t* out_minimum_keylength,
+                                                         size_t* out_maximum_keylength,
+                                                         size_t* out_keylength_modulo);
+
+/*
+* Multiple precision integers (MPI)
+*/
+typedef struct botan_mp_struct* botan_mp_t;
+
+/**
+* Initialize an MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_init(botan_mp_t* mp);
+
+/**
+* Destroy (deallocate) an MPI
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_destroy(botan_mp_t mp);
+
+/**
+* Convert the MPI to a hex string. Writes botan_mp_num_bytes(mp)*2 + 1 bytes
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_hex(const botan_mp_t mp, char* out);
+
+/**
+* Convert the MPI to a string. Currently base == 10 and base == 16 are supported.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_str(const botan_mp_t mp, uint8_t base, char* out, size_t* out_len);
+
+/**
+* Set the MPI to zero
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_clear(botan_mp_t mp);
+
+/**
+* Set the MPI value from an int
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_int(botan_mp_t mp, int initial_value);
+
+/**
+* Set the MPI value from another MP object
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_mp(botan_mp_t dest, const botan_mp_t source);
+
+/**
+* Set the MPI value from a string
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_str(botan_mp_t dest, const char* str);
+
+/**
+* Set the MPI value from a string with arbitrary radix.
+* For arbitrary being 10 or 16.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_radix_str(botan_mp_t dest, const char* str, size_t radix);
+
+/**
+* Return the number of significant bits in the MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_num_bits(const botan_mp_t n, size_t* bits);
+
+/**
+* Return the number of significant bytes in the MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_num_bytes(const botan_mp_t n, size_t* bytes);
+
+/*
+* Convert the MPI to a big-endian binary string. Writes botan_mp_num_bytes to vec
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_bin(const botan_mp_t mp, uint8_t vec[]);
+
+/*
+* Set an MP to the big-endian binary value
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_from_bin(const botan_mp_t mp, const uint8_t vec[], size_t vec_len);
+
+/*
+* Convert the MPI to a uint32_t, if possible. Fails if MPI is negative or too large.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val);
+
+/**
+* This function should have been named mp_is_non_negative. Returns 1
+* iff mp is greater than *or equal to* zero. Use botan_mp_is_negative
+* to detect negative numbers, botan_mp_is_zero to check for zero.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_positive(const botan_mp_t mp);
+
+/**
+* Return 1 iff mp is less than 0
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_negative(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_flip_sign(botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_zero(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)")
+int botan_mp_is_odd(const botan_mp_t mp);
+BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)")
+int botan_mp_is_even(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,8) int botan_mp_add_u32(botan_mp_t result, const botan_mp_t x, uint32_t y);
+BOTAN_PUBLIC_API(2,8) int botan_mp_sub_u32(botan_mp_t result, const botan_mp_t x, uint32_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_add(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+BOTAN_PUBLIC_API(2,1) int botan_mp_sub(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+BOTAN_PUBLIC_API(2,1) int botan_mp_mul(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_div(botan_mp_t quotient,
+                                       botan_mp_t remainder,
+                                       const botan_mp_t x, const botan_mp_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_mod_mul(botan_mp_t result, const botan_mp_t x,
+                                           const botan_mp_t y, const botan_mp_t mod);
+
+/*
+* Returns 0 if x != y
+* Returns 1 if x == y
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_equal(const botan_mp_t x, const botan_mp_t y);
+
+/*
+* Sets *result to comparison result:
+* -1 if x < y, 0 if x == y, 1 if x > y
+* Returns negative number on error or zero on success
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_cmp(int* result, const botan_mp_t x, const botan_mp_t y);
+
+/*
+* Swap two botan_mp_t
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_swap(botan_mp_t x, botan_mp_t y);
+
+/* Return (base^exponent) % modulus */
+BOTAN_PUBLIC_API(2,1) int botan_mp_powmod(botan_mp_t out, const botan_mp_t base, const botan_mp_t exponent, const botan_mp_t modulus);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_lshift(botan_mp_t out, const botan_mp_t in, size_t shift);
+BOTAN_PUBLIC_API(2,1) int botan_mp_rshift(botan_mp_t out, const botan_mp_t in, size_t shift);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_mod_inverse(botan_mp_t out, const botan_mp_t in, const botan_mp_t modulus);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_rand_range(botan_mp_t rand_out, botan_rng_t rng,
+                                  const botan_mp_t lower_bound, const botan_mp_t upper_bound);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_gcd(botan_mp_t out, const botan_mp_t x, const botan_mp_t y);
+
+/**
+* Returns 0 if n is not prime
+* Returns 1 if n is prime
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_prime(const botan_mp_t n, botan_rng_t rng, size_t test_prob);
+
+/**
+* Returns 0 if specified bit of n is not set
+* Returns 1 if specified bit of n is set
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_get_bit(const botan_mp_t n, size_t bit);
+
+/**
+* Set the specified bit
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_bit(botan_mp_t n, size_t bit);
+
+/**
+* Clear the specified bit
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_clear_bit(botan_mp_t n, size_t bit);
+
+/* Bcrypt password hashing */
+
+/**
+* Create a password hash using Bcrypt
+* @param out buffer holding the password hash, should be of length 64 bytes
+* @param out_len the desired output length in bytes
+* @param password the password
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param flags should be 0 in current API revision, all other uses are reserved
+*       and return BOTAN_FFI_ERROR_BAD_FLAG
+* @return 0 on success, a negative value on failure
+
+* Output is formatted bcrypt $2a$...
+*/
+BOTAN_PUBLIC_API(2,0) int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+                                    const char* password,
+                                    botan_rng_t rng,
+                                    size_t work_factor,
+                                    uint32_t flags);
+
+/**
+* Check a previously created password hash
+* @param pass the password to check against
+* @param hash the stored hash to check against
+* @return 0 if if this password/hash combination is valid,
+*       1 if the combination is not valid (but otherwise well formed),
+*       negative on error
+*/
+BOTAN_PUBLIC_API(2,0) int botan_bcrypt_is_valid(const char* pass, const char* hash);
+
+/*
+* Public/private key creation, import, ...
+*/
+typedef struct botan_privkey_struct* botan_privkey_t;
+
+/**
+* Create a new private key
+* @param key the new object will be placed here
+* @param algo_name something like "RSA" or "ECDSA"
+* @param algo_params is specific to the algorithm. For RSA, specifies
+*        the modulus bit length. For ECC is the name of the curve.
+* @param rng a random number generator
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_create(botan_privkey_t* key,
+                                               const char* algo_name,
+                                               const char* algo_params,
+                                               botan_rng_t rng);
+
+#define BOTAN_CHECK_KEY_EXPENSIVE_TESTS 1
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, const char* param);
+
+/**
+ * Generates DSA key pair. Gives to a caller control over key length
+ * and order of a subgroup 'q'.
+ *
+ * @param   key   handler to the resulting key
+ * @param   rng   initialized PRNG
+ * @param   pbits length of the key in bits. Must be between in range (1024, 3072)
+ *          and multiple of 64. Bit size of the prime 'p'
+ * @param   qbits order of the subgroup. Must be in range (160, 256) and multiple
+ *          of 8
+ *
+ * @returns BOTAN_FFI_SUCCESS Success, `key' initialized with DSA key
+ * @returns BOTAN_FFI_ERROR_NULL_POINTER  either `key' or `rng' is NULL
+ * @returns BOTAN_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or
+ *          `qbits'
+ * @returns BOTAN_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented
+ *
+*/
+BOTAN_PUBLIC_API(2,5) int botan_privkey_create_dsa(
+                                botan_privkey_t* key,
+                                botan_rng_t rng,
+                                size_t pbits,
+                                size_t qbits);
+
+/**
+ * Generates ElGamal key pair. Caller has a control over key length
+ * and order of a subgroup 'q'. Function is able to use two types of
+ * primes:
+ *    * if pbits-1 == qbits then safe primes are used for key generation
+ *    * otherwise generation uses group of prime order
+ *
+ * @param   key   handler to the resulting key
+ * @param   rng   initialized PRNG
+ * @param   pbits length of the key in bits. Must be at least 1024
+ * @param   qbits order of the subgroup. Must be at least 160
+ *
+ * @returns BOTAN_FFI_SUCCESS Success, `key' initialized with DSA key
+ * @returns BOTAN_FFI_ERROR_NULL_POINTER  either `key' or `rng' is NULL
+ * @returns BOTAN_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or
+ *          `qbits'
+ * @returns BOTAN_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented
+ *
+*/
+BOTAN_PUBLIC_API(2,5) int botan_privkey_create_elgamal(
+                            botan_privkey_t* key,
+                            botan_rng_t rng,
+                            size_t pbits,
+                            size_t qbits);
+
+/**
+* Input currently assumed to be PKCS #8 structure;
+* Set password to NULL to indicate no encryption expected
+* Starting in 2.8.0, the rng parameter is unused and may be set to null
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load(botan_privkey_t* key,
+                                             botan_rng_t rng,
+                                             const uint8_t bits[], size_t len,
+                                             const char* password);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_destroy(botan_privkey_t key);
+
+#define BOTAN_PRIVKEY_EXPORT_FLAG_DER 0
+#define BOTAN_PRIVKEY_EXPORT_FLAG_PEM 1
+
+/**
+* On input *out_len is number of bytes in out[]
+* On output *out_len is number of bytes written (or required)
+* If out is not big enough no output is written, *out_len is set and 1 is returned
+* Returns 0 on success and sets
+* If some other error occurs a negative integer is returned.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export(botan_privkey_t key,
+                                   uint8_t out[], size_t* out_len,
+                                   uint32_t flags);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_algo_name(botan_privkey_t key, char out[], size_t* out_len);
+
+/**
+* Set encryption_algo to NULL or "" to have the library choose a default (recommended)
+*/
+BOTAN_DEPRECATED("Use botan_privkey_export_encrypted_pbkdf_{msec,iter}")
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_encrypted(botan_privkey_t key,
+                                             uint8_t out[], size_t* out_len,
+                                             botan_rng_t rng,
+                                             const char* passphrase,
+                                             const char* encryption_algo,
+                                             uint32_t flags);
+
+/*
+* Export a private key, running PBKDF for specified amount of time
+* @param key the private key to export
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key,
+                                                        uint8_t out[], size_t* out_len,
+                                                        botan_rng_t rng,
+                                                        const char* passphrase,
+                                                        uint32_t pbkdf_msec_runtime,
+                                                        size_t* pbkdf_iterations_out,
+                                                        const char* cipher_algo,
+                                                        const char* pbkdf_algo,
+                                                        uint32_t flags);
+
+/**
+* Export a private key using the specified number of iterations.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key,
+                                                        uint8_t out[], size_t* out_len,
+                                                        botan_rng_t rng,
+                                                        const char* passphrase,
+                                                        size_t pbkdf_iterations,
+                                                        const char* cipher_algo,
+                                                        const char* pbkdf_algo,
+                                                        uint32_t flags);
+
+typedef struct botan_pubkey_struct* botan_pubkey_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load(botan_pubkey_t* key, const uint8_t bits[], size_t len);
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_pubkey(botan_pubkey_t* out, botan_privkey_t in);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len);
+
+/**
+* Returns 0 if key is valid, negative if invalid key or some other error
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash,
+                                       uint8_t out[], size_t* out_len);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_destroy(botan_pubkey_t key);
+
+/*
+* Get arbitrary named fields from public or privat keys
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_get_field(botan_mp_t output,
+                                     botan_pubkey_t key,
+                                     const char* field_name);
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_get_field(botan_mp_t output,
+                                      botan_privkey_t key,
+                                      const char* field_name);
+
+/*
+* Algorithm specific key operations: RSA
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_rsa(botan_privkey_t* key,
+                                     botan_mp_t p,
+                                     botan_mp_t q,
+                                     botan_mp_t e);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key,
+                                                       const uint8_t bits[],
+                                                       size_t len);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_rsa_get_privkey(botan_privkey_t rsa_key,
+                                                        uint8_t out[], size_t* out_len,
+                                                        uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_rsa(botan_pubkey_t* key,
+                                                botan_mp_t n,
+                                                botan_mp_t e);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key);
+
+/*
+* Algorithm specific key operations: DSA
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_dsa(botan_privkey_t* key,
+                                     botan_mp_t p,
+                                     botan_mp_t q,
+                                     botan_mp_t g,
+                                     botan_mp_t x);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_dsa(botan_pubkey_t* key,
+                                    botan_mp_t p,
+                                    botan_mp_t q,
+                                    botan_mp_t g,
+                                    botan_mp_t y);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_dsa_get_x(botan_mp_t n, botan_privkey_t key);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_g(botan_mp_t d, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key);
+
+/*
+* Loads Diffie Hellman private key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param x private key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_dh(botan_privkey_t* key,
+                                         botan_mp_t p,
+                                         botan_mp_t g,
+                                         botan_mp_t x);
+/**
+* Loads Diffie Hellman public key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param y public key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_dh(botan_pubkey_t* key,
+                                        botan_mp_t p,
+                                        botan_mp_t g,
+                                        botan_mp_t y);
+
+/*
+* Algorithm specific key operations: ElGamal
+*/
+
+/**
+* Loads ElGamal public key
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param y public key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_elgamal(botan_pubkey_t* key,
+                                        botan_mp_t p,
+                                        botan_mp_t g,
+                                        botan_mp_t y);
+
+/**
+* Loads ElGamal private key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param x private key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_elgamal(botan_privkey_t* key,
+                                         botan_mp_t p,
+                                         botan_mp_t g,
+                                         botan_mp_t x);
+
+/*
+* Algorithm specific key operations: Ed25519
+*/
+
+BOTAN_PUBLIC_API(2,2) int botan_privkey_load_ed25519(botan_privkey_t* key,
+                                         const uint8_t privkey[32]);
+
+BOTAN_PUBLIC_API(2,2) int botan_pubkey_load_ed25519(botan_pubkey_t* key,
+                                        const uint8_t pubkey[32]);
+
+BOTAN_PUBLIC_API(2,2) int botan_privkey_ed25519_get_privkey(botan_privkey_t key,
+                                                uint8_t output[64]);
+
+BOTAN_PUBLIC_API(2,2) int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key,
+                                              uint8_t pubkey[32]);
+
+/*
+* Algorithm specific key operations: X25519
+*/
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_load_x25519(botan_privkey_t* key,
+                                                    const uint8_t privkey[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_pubkey_load_x25519(botan_pubkey_t* key,
+                                                   const uint8_t pubkey[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_x25519_get_privkey(botan_privkey_t key,
+                                                           uint8_t output[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key,
+                                                         uint8_t pubkey[32]);
+
+/*
+* Algorithm specific key operations: ECDSA and ECDH
+*/
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_ecdsa(botan_privkey_t* key,
+                             const botan_mp_t scalar,
+                             const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_pubkey_load_ecdsa(botan_pubkey_t* key,
+                            const botan_mp_t public_x,
+                            const botan_mp_t public_y,
+                            const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_pubkey_load_ecdh(botan_pubkey_t* key,
+                           const botan_mp_t public_x,
+                           const botan_mp_t public_y,
+                           const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_ecdh(botan_privkey_t* key,
+                            const botan_mp_t scalar,
+                            const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_pubkey_load_sm2(botan_pubkey_t* key,
+                          const botan_mp_t public_x,
+                          const botan_mp_t public_y,
+                          const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_sm2(botan_privkey_t* key,
+                           const botan_mp_t scalar,
+                           const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_pubkey_load_sm2")
+int botan_pubkey_load_sm2_enc(botan_pubkey_t* key,
+                              const botan_mp_t public_x,
+                              const botan_mp_t public_y,
+                              const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_privkey_load_sm2")
+int botan_privkey_load_sm2_enc(botan_privkey_t* key,
+                               const botan_mp_t scalar,
+                               const char* curve_name);
+
+BOTAN_PUBLIC_API(2,3)
+int botan_pubkey_sm2_compute_za(uint8_t out[],
+                                size_t* out_len,
+                                const char* ident,
+                                const char* hash_algo,
+                                const botan_pubkey_t key);
+
+/*
+* Public Key Encryption
+*/
+typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+                                         botan_pubkey_t key,
+                                         const char* padding,
+                                         uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op,
+                                                            size_t ptext_len,
+                                                            size_t* ctext_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+                                              botan_rng_t rng,
+                                              uint8_t out[],
+                                              size_t* out_len,
+                                              const uint8_t plaintext[],
+                                              size_t plaintext_len);
+
+/*
+* Public Key Decryption
+*/
+typedef struct botan_pk_op_decrypt_struct* botan_pk_op_decrypt_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+                                         botan_privkey_t key,
+                                         const char* padding,
+                                         uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op,
+                                                            size_t ctext_len,
+                                                            size_t* ptext_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+                                  uint8_t out[], size_t* out_len,
+                                  const uint8_t ciphertext[], size_t ciphertext_len);
+
+/*
+* Signature Generation
+*/
+
+#define BOTAN_PUBKEY_DER_FORMAT_SIGNATURE 1
+
+typedef struct botan_pk_op_sign_struct* botan_pk_op_sign_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+                            botan_privkey_t key,
+                            const char* hash_and_padding,
+                            uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_sign_destroy(botan_pk_op_sign_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_sign_output_length(botan_pk_op_sign_t op, size_t* olen);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng,
+                            uint8_t sig[], size_t* sig_len);
+
+/*
+* Signature Verification
+*/
+typedef struct botan_pk_op_verify_struct* botan_pk_op_verify_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+                              botan_pubkey_t key,
+                              const char* hash_and_padding,
+                              uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_destroy(botan_pk_op_verify_t op);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len);
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len);
+
+/*
+* Key Agreement
+*/
+typedef struct botan_pk_op_ka_struct* botan_pk_op_ka_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+                                     botan_privkey_t key,
+                                     const char* kdf,
+                                     uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+                                                      uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_key_agreement_size(botan_pk_op_ka_t op, size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+                              uint8_t out[], size_t* out_len,
+                              const uint8_t other_key[], size_t other_key_len,
+                              const uint8_t salt[], size_t salt_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs_id[], size_t* pkcs_id_len);
+
+
+/*
+*
+* @param mce_key must be a McEliece key
+* ct_len should be pt_len + n/8 + a few?
+*/
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code")
+int botan_mceies_encrypt(botan_pubkey_t mce_key,
+                         botan_rng_t rng,
+                         const char* aead,
+                         const uint8_t pt[], size_t pt_len,
+                         const uint8_t ad[], size_t ad_len,
+                         uint8_t ct[], size_t* ct_len);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code")
+int botan_mceies_decrypt(botan_privkey_t mce_key,
+                         const char* aead,
+                         const uint8_t ct[], size_t ct_len,
+                         const uint8_t ad[], size_t ad_len,
+                         uint8_t pt[], size_t* pt_len);
+
+/*
+* X.509 certificates
+**************************/
+
+typedef struct botan_x509_cert_struct* botan_x509_cert_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert[], size_t cert_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* filename);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_destroy(botan_x509_cert_t cert);
+
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_dup(botan_x509_cert_t* new_cert, botan_x509_cert_t cert);
+
+/* Prefer botan_x509_cert_not_before and botan_x509_cert_not_after */
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch);
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert,
+                                                  uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_x509_cert_get_issuer_dn(botan_x509_cert_t cert,
+                                  const char* key, size_t index,
+                                  uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_x509_cert_get_subject_dn(botan_x509_cert_t cert,
+                                   const char* key, size_t index,
+                                   uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len);
+
+/* Must match values of Key_Constraints in key_constraints.h */
+enum botan_x509_cert_key_constraints {
+   NO_CONSTRAINTS     = 0,
+   DIGITAL_SIGNATURE  = 32768,
+   NON_REPUDIATION    = 16384,
+   KEY_ENCIPHERMENT   = 8192,
+   DATA_ENCIPHERMENT  = 4096,
+   KEY_AGREEMENT      = 2048,
+   KEY_CERT_SIGN      = 1024,
+   CRL_SIGN           = 512,
+   ENCIPHER_ONLY      = 256,
+   DECIPHER_ONLY      = 128
+};
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage);
+
+/**
+* Check if the certificate matches the specified hostname via alternative name or CN match.
+* RFC 5280 wildcards also supported.
+*/
+BOTAN_PUBLIC_API(2,5) int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname);
+
+/**
+* Returns 0 if the validation was successful, 1 if validation failed,
+* and negative on error. A status code with details is written to
+* *validation_result
+*
+* Intermediates or trusted lists can be null
+* Trusted path can be null
+*/
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_verify(
+   int* validation_result,
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates,
+   size_t intermediates_len,
+   const botan_x509_cert_t* trusted,
+   size_t trusted_len,
+   const char* trusted_path,
+   size_t required_strength,
+   const char* hostname,
+   uint64_t reference_time);
+
+/**
+* Returns a pointer to a static character string explaining the status code,
+* or else NULL if unknown.
+*/
+BOTAN_PUBLIC_API(2,8) const char* botan_x509_cert_validation_status(int code);
+
+/*
+* X.509 CRL
+**************************/
+
+typedef struct botan_x509_crl_struct* botan_x509_crl_t;
+
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path);
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len);
+
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_destroy(botan_x509_crl_t crl);
+
+/**
+ * Given a CRL and a certificate, 
+ * check if the certificate is revoked on that particular CRL
+ */
+BOTAN_PUBLIC_API(2,13) int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert);
+
+/**
+ * Different flavor of `botan_x509_cert_verify`, supports revocation lists.
+ * CRLs are passed as an array, same as intermediates and trusted CAs 
+ */
+BOTAN_PUBLIC_API(2,13) int botan_x509_cert_verify_with_crl(
+   int* validation_result,
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates,
+   size_t intermediates_len,
+   const botan_x509_cert_t* trusted,
+   size_t trusted_len,
+   const botan_x509_crl_t* crls,
+   size_t crls_len,
+   const char* trusted_path,
+   size_t required_strength,
+   const char* hostname,
+   uint64_t reference_time);
+
+
+typedef struct botan_x509_certstore_struct* botan_x509_certstore_t;
+
+BOTAN_PUBLIC_API(2,19) int botan_x509_certstore_load_file(botan_x509_certstore_t* certstore_obj, const char* certstore_path);
+
+BOTAN_PUBLIC_API(2,19) int botan_x509_certstore_destroy(botan_x509_certstore_t crl);
+
+BOTAN_PUBLIC_API(2,19)
+int botan_x509_cert_verify_with_certstore_crl(
+   int* result_code,
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates,
+   size_t intermediates_len,
+   const botan_x509_certstore_t store,
+   const botan_x509_crl_t* crls,
+   size_t crls_len,
+   size_t required_strength,
+   const char* hostname_cstr,
+   uint64_t reference_time);
+
+
+/**
+ * Key wrapping as per RFC 3394
+ */
+BOTAN_PUBLIC_API(2,2)
+int botan_key_wrap3394(const uint8_t key[], size_t key_len,
+                       const uint8_t kek[], size_t kek_len,
+                       uint8_t wrapped_key[], size_t *wrapped_key_len);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_key_unwrap3394(const uint8_t wrapped_key[], size_t wrapped_key_len,
+                         const uint8_t kek[], size_t kek_len,
+                         uint8_t key[], size_t *key_len);
+
+/**
+* HOTP
+*/
+
+typedef struct botan_hotp_struct* botan_hotp_t;
+
+/**
+* Initialize a HOTP instance
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_init(botan_hotp_t* hotp,
+                    const uint8_t key[], size_t key_len,
+                    const char* hash_algo,
+                    size_t digits);
+
+/**
+* Destroy a HOTP instance
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_destroy(botan_hotp_t hotp);
+
+/**
+* Generate a HOTP code for the provided counter
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_generate(botan_hotp_t hotp,
+                        uint32_t* hotp_code,
+                        uint64_t hotp_counter);
+
+/**
+* Verify a HOTP code
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_check(botan_hotp_t hotp,
+                     uint64_t* next_hotp_counter,
+                     uint32_t hotp_code,
+                     uint64_t hotp_counter,
+                     size_t resync_range);
+
+
+/**
+* TOTP
+*/
+
+typedef struct botan_totp_struct* botan_totp_t;
+
+/**
+* Initialize a TOTP instance
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_init(botan_totp_t* totp,
+                    const uint8_t key[], size_t key_len,
+                    const char* hash_algo,
+                    size_t digits,
+                    size_t time_step);
+
+/**
+* Destroy a TOTP instance
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_destroy(botan_totp_t totp);
+
+/**
+* Generate a TOTP code for the provided timestamp
+* @param totp the TOTP object
+* @param totp_code the OTP code will be written here
+* @param timestamp the current local timestamp
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_generate(botan_totp_t totp,
+                        uint32_t* totp_code,
+                        uint64_t timestamp);
+
+/**
+* Verify a TOTP code
+* @param totp the TOTP object
+* @param totp_code the presented OTP
+* @param timestamp the current local timestamp
+* @param acceptable_clock_drift specifies the acceptable amount
+* of clock drift (in terms of time steps) between the two hosts.
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_check(botan_totp_t totp,
+                     uint32_t totp_code,
+                     uint64_t timestamp,
+                     size_t acceptable_clock_drift);
+
+
+/**
+* Format Preserving Encryption
+*/
+
+typedef struct botan_fpe_struct* botan_fpe_t;
+
+#define BOTAN_FPE_FLAG_FE1_COMPAT_MODE 1
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_fe1_init(botan_fpe_t* fpe, botan_mp_t n,
+                       const uint8_t key[], size_t key_len,
+                       size_t rounds, uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_destroy(botan_fpe_t fpe);
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_encrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len);
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_decrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/ffi/ffi_block.cpp b/third_party/botan/src/lib/ffi/ffi_block.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_block.cpp
@@ -0,0 +1,112 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/block_cipher.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_block_cipher_struct, Botan::BlockCipher, 0x64C29716);
+
+int botan_block_cipher_init(botan_block_cipher_t* bc, const char* bc_name)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(bc == nullptr || bc_name == nullptr || *bc_name == 0)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      *bc = nullptr;
+
+      std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create(bc_name));
+      if(cipher == nullptr)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      *bc = new botan_block_cipher_struct(cipher.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+/**
+* Destroy a block cipher object
+*/
+int botan_block_cipher_destroy(botan_block_cipher_t bc)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(bc);
+   }
+
+int botan_block_cipher_clear(botan_block_cipher_t bc)
+   {
+   return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.clear(); });
+   }
+
+/**
+* Set the key for a block cipher instance
+*/
+int botan_block_cipher_set_key(botan_block_cipher_t bc,
+                               const uint8_t key[], size_t len)
+   {
+   if(key == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.set_key(key, len); });
+   }
+
+/**
+* Return the positive block size of this block cipher, or negative to
+* indicate an error
+*/
+int botan_block_cipher_block_size(botan_block_cipher_t bc)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BlockCipher, bc, b,
+                              { return static_cast<int>(b.block_size()); });
+   }
+
+int botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc,
+                                      const uint8_t in[],
+                                      uint8_t out[],
+                                      size_t blocks)
+   {
+   if(in == nullptr || out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.encrypt_n(in, out, blocks); });
+   }
+
+int botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc,
+                                      const uint8_t in[],
+                                      uint8_t out[],
+                                      size_t blocks)
+   {
+   if(in == nullptr || out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.decrypt_n(in, out, blocks); });
+   }
+
+int botan_block_cipher_name(botan_block_cipher_t cipher, char* name, size_t* name_len)
+   {
+   if(name_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::BlockCipher, cipher, bc, {
+      return write_str_output(name, name_len, bc.name()); });
+   }
+
+int botan_block_cipher_get_keyspec(botan_block_cipher_t cipher,
+                                   size_t* out_minimum_keylength,
+                                   size_t* out_maximum_keylength,
+                                   size_t* out_keylength_modulo)
+   {
+   return BOTAN_FFI_DO(Botan::BlockCipher, cipher, bc, {
+      if(out_minimum_keylength)
+         *out_minimum_keylength = bc.minimum_keylength();
+      if(out_maximum_keylength)
+         *out_maximum_keylength = bc.maximum_keylength();
+      if(out_keylength_modulo)
+         *out_keylength_modulo = bc.key_spec().keylength_multiple();
+      });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_cert.cpp b/third_party/botan/src/lib/ffi/ffi_cert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_cert.cpp
@@ -0,0 +1,609 @@
+/*
+* (C) 2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509cert.h>
+   #include <botan/x509path.h>
+   #include <botan/x509_crl.h>
+   #include <botan/certstor.h>
+   #include <botan/data_src.h>
+#endif
+
+#if defined(BOTAN_HAS_CERTSTOR_FLATFILE)
+   #include <botan/certstor_flatfile/certstor_flatfile.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
+
+#endif
+
+int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path)
+   {
+   if(!cert_obj || !cert_path)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(cert_path));
+      *cert_obj = new botan_x509_cert_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert)
+   {
+   if(!cert_obj)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(safe_get(cert)));
+      *cert_obj = new botan_x509_cert_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+
+#else
+   BOTAN_UNUSED(cert);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert_bits[], size_t cert_bits_len)
+   {
+   if(!cert_obj || !cert_bits)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DataSource_Memory bits(cert_bits, cert_bits_len);
+      std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(bits));
+      *cert_obj = new botan_x509_cert_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(cert_bits_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key)
+   {
+   if(key == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   *key = nullptr;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::Public_Key> publicKey = safe_get(cert).load_subject_public_key();
+      *key = new botan_pubkey_struct(publicKey.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(cert);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_issuer_dn(botan_x509_cert_t cert,
+                                  const char* key, size_t index,
+                                  uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.issuer_info(key).at(index)); });
+#else
+   BOTAN_UNUSED(cert, key, index, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_subject_dn(botan_x509_cert_t cert,
+                                   const char* key, size_t index,
+                                   uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.subject_info(key).at(index)); });
+#else
+   BOTAN_UNUSED(cert, key, index, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.to_string()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_RETURNING(Botan::X509_Certificate, cert, c, {
+      const Botan::Key_Constraints k = static_cast<Botan::Key_Constraints>(key_usage);
+      if(c.allowed_usage(k))
+         return BOTAN_FFI_SUCCESS;
+      return 1;
+      });
+#else
+   BOTAN_UNUSED(cert, key_usage);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_destroy(botan_x509_cert_t cert)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_CHECKED_DELETE(cert);
+#else
+   BOTAN_UNUSED(cert);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_before().to_string()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_after().to_string()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
+      *time_since_epoch = c.not_before().time_since_epoch();
+      });
+#else
+   BOTAN_UNUSED(cert, time_since_epoch);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
+      *time_since_epoch = c.not_after().time_since_epoch();
+      });
+#else
+   BOTAN_UNUSED(cert, time_since_epoch);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.serial_number()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.fingerprint(hash)); });
+#else
+   BOTAN_UNUSED(cert, hash, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.authority_key_id()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_key_id()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_public_key_bits()); });
+#else
+   BOTAN_UNUSED(cert, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname)
+   {
+   if(hostname == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c,
+                       { return c.matches_dns_name(hostname) ? 0 : -1; });
+#else
+   BOTAN_UNUSED(cert);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_verify(int* result_code,
+                           botan_x509_cert_t cert,
+                           const botan_x509_cert_t* intermediates,
+                           size_t intermediates_len,
+                           const botan_x509_cert_t* trusted,
+                           size_t trusted_len,
+                           const char* trusted_path,
+                           size_t required_strength,
+                           const char* hostname_cstr,
+                           uint64_t reference_time)
+   {
+   if(required_strength == 0)
+      required_strength = 110;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
+      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
+      const auto validation_time = reference_time == 0 ?
+         std::chrono::system_clock::now() :
+         std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
+
+      std::vector<Botan::X509_Certificate> end_certs;
+      end_certs.push_back(safe_get(cert));
+      for(size_t i = 0; i != intermediates_len; ++i)
+         end_certs.push_back(safe_get(intermediates[i]));
+
+      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
+      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
+      std::vector<Botan::Certificate_Store*> trusted_roots;
+
+      if(trusted_path && *trusted_path)
+         {
+         trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
+         trusted_roots.push_back(trusted_from_path.get());
+         }
+
+      if(trusted_len > 0)
+         {
+         trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
+         for(size_t i = 0; i != trusted_len; ++i)
+            {
+            trusted_extra->add_certificate(safe_get(trusted[i]));
+            }
+         trusted_roots.push_back(trusted_extra.get());
+         }
+
+      Botan::Path_Validation_Restrictions restrictions(false, required_strength);
+
+      auto validation_result = Botan::x509_path_validate(end_certs,
+                                                         restrictions,
+                                                         trusted_roots,
+                                                         hostname,
+                                                         usage,
+                                                         validation_time);
+
+      if(result_code)
+         *result_code = static_cast<int>(validation_result.result());
+
+      if(validation_result.successful_validation())
+         return 0;
+      else
+         return 1;
+      });
+#else
+   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
+   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+const char* botan_x509_cert_validation_status(int code)
+   {
+   if(code < 0)
+      return nullptr;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   Botan::Certificate_Status_Code sc = static_cast<Botan::Certificate_Status_Code>(code);
+   return Botan::to_string(sc);
+#else
+   return nullptr;
+#endif
+   }
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910);
+
+#endif
+
+int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path)
+   {
+   if(!crl_obj || !crl_path)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(crl_path));
+      *crl_obj = new botan_x509_crl_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len)
+   {
+   if(!crl_obj || !crl_bits)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DataSource_Memory bits(crl_bits, crl_bits_len);
+      std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(bits));
+      *crl_obj = new botan_x509_crl_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(crl_bits_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_crl_destroy(botan_x509_crl_t crl)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_CHECKED_DELETE(crl);
+#else
+   BOTAN_UNUSED(crl);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_RETURNING(Botan::X509_CRL, crl, c, {
+      return c.is_revoked(safe_get(cert)) ? 0 : -1;
+      });
+#else
+   BOTAN_UNUSED(cert);
+   BOTAN_UNUSED(crl);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_verify_with_crl(
+   int* result_code,
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates,
+   size_t intermediates_len,
+   const botan_x509_cert_t* trusted,
+   size_t trusted_len,
+   const botan_x509_crl_t* crls,
+   size_t crls_len,
+   const char* trusted_path,
+   size_t required_strength,
+   const char* hostname_cstr,
+   uint64_t reference_time)
+   {
+   if(required_strength == 0)
+      required_strength = 110;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
+      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
+      const auto validation_time = reference_time == 0 ?
+         std::chrono::system_clock::now() :
+         std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
+
+      std::vector<Botan::X509_Certificate> end_certs;
+      end_certs.push_back(safe_get(cert));
+      for(size_t i = 0; i != intermediates_len; ++i)
+         end_certs.push_back(safe_get(intermediates[i]));
+
+      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
+      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
+      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
+      std::vector<Botan::Certificate_Store*> trusted_roots;
+
+      if(trusted_path && *trusted_path)
+         {
+         trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
+         trusted_roots.push_back(trusted_from_path.get());
+         }
+
+      if(trusted_len > 0)
+         {
+         trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
+         for(size_t i = 0; i != trusted_len; ++i)
+            {
+            trusted_extra->add_certificate(safe_get(trusted[i]));
+            }
+         trusted_roots.push_back(trusted_extra.get());
+         }
+
+      if(crls_len > 0)
+         {
+         trusted_crls.reset(new Botan::Certificate_Store_In_Memory);
+         for(size_t i = 0; i != crls_len; ++i)
+            {
+            trusted_crls->add_crl(safe_get(crls[i]));
+            }
+         trusted_roots.push_back(trusted_crls.get());
+         }
+
+      Botan::Path_Validation_Restrictions restrictions(false, required_strength);
+
+      auto validation_result = Botan::x509_path_validate(end_certs,
+                                                         restrictions,
+                                                         trusted_roots,
+                                                         hostname,
+                                                         usage,
+                                                         validation_time);
+
+      if(result_code)
+         *result_code = static_cast<int>(validation_result.result());
+
+      if(validation_result.successful_validation())
+         return 0;
+      else
+         return 1;
+      });
+#else
+   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
+   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time, crls, crls_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_x509_certstore_struct, Botan::Certificate_Store, 0x8BD3442A);
+
+#endif
+
+int botan_x509_certstore_load_file(botan_x509_certstore_t* certstore_obj, const char* certstore_path)
+   {
+   if(!certstore_obj || !certstore_path)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_CERTSTOR_FLATFILE)
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::Certificate_Store> c(new Botan::Flatfile_Certificate_Store(certstore_path));
+      *certstore_obj = new botan_x509_certstore_struct(c.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_certstore_destroy(botan_x509_certstore_t certstore)
+   {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return BOTAN_FFI_CHECKED_DELETE(certstore);
+#else
+   BOTAN_UNUSED(certstore);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_x509_cert_verify_with_certstore_crl(
+   int* result_code,
+   botan_x509_cert_t cert,
+   const botan_x509_cert_t* intermediates,
+   size_t intermediates_len,
+   const botan_x509_certstore_t store,
+   const botan_x509_crl_t* crls,
+   size_t crls_len,
+   size_t required_strength,
+   const char* hostname_cstr,
+   uint64_t reference_time)
+   {
+   if(required_strength == 0)
+      required_strength = 110;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
+      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
+      const auto validation_time = reference_time == 0 ?
+         std::chrono::system_clock::now() :
+         std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
+
+      std::vector<Botan::X509_Certificate> end_certs;
+      end_certs.push_back(safe_get(cert));
+      for(size_t i = 0; i != intermediates_len; ++i)
+         end_certs.push_back(safe_get(intermediates[i]));
+
+      std::vector<Botan::Certificate_Store*> trusted_roots;
+      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
+      trusted_roots.push_back(&safe_get(store));
+
+      if(crls_len > 0)
+         {
+         trusted_crls.reset(new Botan::Certificate_Store_In_Memory);
+         for(size_t i = 0; i != crls_len; ++i)
+            {
+            trusted_crls->add_crl(safe_get(crls[i]));
+            }
+         trusted_roots.push_back(trusted_crls.get());
+         }
+
+      Botan::Path_Validation_Restrictions restrictions(false, required_strength);
+
+      auto validation_result = Botan::x509_path_validate(end_certs,
+                                                         restrictions,
+                                                         trusted_roots,
+                                                         hostname,
+                                                         usage,
+                                                         validation_time);
+
+      if(result_code)
+         *result_code = static_cast<int>(validation_result.result());
+
+      if(validation_result.successful_validation())
+         return 0;
+      else
+         return 1;
+      });
+#else
+   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
+   BOTAN_UNUSED(store, hostname_cstr, reference_time, crls, crls_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
diff --git a/third_party/botan/src/lib/ffi/ffi_cipher.cpp b/third_party/botan/src/lib/ffi/ffi_cipher.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_cipher.cpp
@@ -0,0 +1,233 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/aead.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+struct botan_cipher_struct final : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C>
+   {
+   explicit botan_cipher_struct(Botan::Cipher_Mode* x) : botan_struct(x) {}
+   Botan::secure_vector<uint8_t> m_buf;
+   };
+
+int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const bool encrypt_p = ((flags & BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION) == BOTAN_CIPHER_INIT_FLAG_ENCRYPT);
+      const Botan::Cipher_Dir dir = encrypt_p ? Botan::ENCRYPTION : Botan::DECRYPTION;
+      std::unique_ptr<Botan::Cipher_Mode> mode(Botan::Cipher_Mode::create(cipher_name, dir));
+      if(!mode)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+      *cipher = new botan_cipher_struct(mode.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_cipher_destroy(botan_cipher_t cipher)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(cipher);
+   }
+
+int botan_cipher_clear(botan_cipher_t cipher)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.clear(); });
+   }
+
+int botan_cipher_reset(botan_cipher_t cipher)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.reset(); });
+   }
+
+int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len)
+   {
+   if(out_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *out_len = c.output_length(in_len); });
+   }
+
+int botan_cipher_query_keylen(botan_cipher_t cipher,
+                              size_t* out_minimum_keylength,
+                              size_t* out_maximum_keylength)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+      *out_minimum_keylength = c.key_spec().minimum_keylength();
+      *out_maximum_keylength = c.key_spec().maximum_keylength();
+      });
+   }
+
+int botan_cipher_get_keyspec(botan_cipher_t cipher,
+                             size_t* out_minimum_keylength,
+                             size_t* out_maximum_keylength,
+                             size_t* out_keylength_modulo)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+      if(out_minimum_keylength)
+         *out_minimum_keylength = c.key_spec().minimum_keylength();
+      if(out_maximum_keylength)
+         *out_maximum_keylength = c.key_spec().maximum_keylength();
+      if(out_keylength_modulo)
+         *out_keylength_modulo = c.key_spec().keylength_multiple();
+      });
+   }
+
+int botan_cipher_set_key(botan_cipher_t cipher,
+                         const uint8_t* key, size_t key_len)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.set_key(key, key_len); });
+   }
+
+int botan_cipher_start(botan_cipher_t cipher_obj,
+                       const uint8_t* nonce, size_t nonce_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Cipher_Mode& cipher = safe_get(cipher_obj);
+      cipher.start(nonce, nonce_len);
+      cipher_obj->m_buf.reserve(cipher.update_granularity());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_cipher_update(botan_cipher_t cipher_obj,
+                        uint32_t flags,
+                        uint8_t output_ptr[],
+                        size_t orig_output_size,
+                        size_t* output_written,
+                        const uint8_t input_ptr[],
+                        size_t orig_input_size,
+                        size_t* input_consumed)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+
+      size_t input_size = orig_input_size;
+      size_t output_size = orig_output_size;
+      const uint8_t* input = input_ptr;
+      uint8_t* output = output_ptr;
+
+      using namespace Botan;
+      Cipher_Mode& cipher = safe_get(cipher_obj);
+      secure_vector<uint8_t>& mbuf = cipher_obj->m_buf;
+
+      const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL);
+
+      if(final_input)
+         {
+         mbuf.assign(input, input + input_size);
+         *input_consumed = input_size;
+         *output_written = 0;
+
+         try
+            {
+            cipher.finish(mbuf);
+            }
+         catch(Invalid_Authentication_Tag&)
+            {
+            return BOTAN_FFI_ERROR_BAD_MAC;
+            }
+
+         *output_written = mbuf.size();
+
+         if(mbuf.size() <= output_size)
+            {
+            copy_mem(output, mbuf.data(), mbuf.size());
+            mbuf.clear();
+            return BOTAN_FFI_SUCCESS;
+            }
+
+         return -1;
+         }
+
+      if(input_size == 0)
+         {
+         // Currently must take entire buffer in this case
+         *output_written = mbuf.size();
+         if(output_size >= mbuf.size())
+            {
+            copy_mem(output, mbuf.data(), mbuf.size());
+            mbuf.clear();
+            return BOTAN_FFI_SUCCESS;
+            }
+
+         return -1;
+         }
+
+      const size_t ud = cipher.update_granularity();
+      BOTAN_ASSERT(cipher.update_granularity() > cipher.minimum_final_size(), "logic error");
+
+      mbuf.resize(ud);
+      size_t taken = 0, written = 0;
+
+      while(input_size >= ud && output_size >= ud)
+         {
+         // FIXME we can use process here and avoid the copy
+         copy_mem(mbuf.data(), input, ud);
+         cipher.update(mbuf);
+
+         input_size -= ud;
+         copy_mem(output, mbuf.data(), ud);
+         input += ud;
+         taken += ud;
+
+         output_size -= ud;
+         output += ud;
+         written += ud;
+         }
+
+      *output_written = written;
+      *input_consumed = taken;
+
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_cipher_set_associated_data(botan_cipher_t cipher,
+                                     const uint8_t* ad,
+                                     size_t ad_len)
+   {
+   return BOTAN_FFI_RETURNING(Botan::Cipher_Mode, cipher, c, {
+      if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(&c))
+         {
+         aead->set_associated_data(ad, ad_len);
+         return BOTAN_FFI_SUCCESS;
+         }
+      return BOTAN_FFI_ERROR_BAD_PARAMETER;
+      });
+   }
+
+int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl)
+   {
+   return BOTAN_FFI_RETURNING(Botan::Cipher_Mode, cipher, c, {
+      return c.valid_nonce_length(nl) ? 1 : 0;
+      });
+   }
+
+int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *nl = c.default_nonce_length(); });
+   }
+
+int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *ug = c.update_granularity(); });
+   }
+
+int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *tl = c.tag_size(); });
+   }
+
+int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len)
+   {
+   return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+      return write_str_output(name, name_len, c.name()); });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_fpe.cpp b/third_party/botan/src/lib/ffi/ffi_fpe.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_fpe.cpp
@@ -0,0 +1,94 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_mp.h>
+#include <memory>
+
+#if defined(BOTAN_HAS_FPE_FE1)
+  #include <botan/fpe_fe1.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_FPE_FE1)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_fpe_struct, Botan::FPE_FE1, 0xD49FB820);
+
+#endif
+
+int botan_fpe_fe1_init(botan_fpe_t* fpe, botan_mp_t n,
+                       const uint8_t key[], size_t key_len,
+                       size_t rounds, uint32_t flags)
+   {
+#if defined(BOTAN_HAS_FPE_FE1)
+
+   return ffi_guard_thunk(__func__, [=]() {
+
+      if(fpe == nullptr || key == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      *fpe = nullptr;
+
+      if(flags != 0 && flags != BOTAN_FPE_FLAG_FE1_COMPAT_MODE)
+         return BOTAN_FFI_ERROR_BAD_FLAG;
+
+      const bool compat_mode = (flags & BOTAN_FPE_FLAG_FE1_COMPAT_MODE);
+
+      std::unique_ptr<Botan::FPE_FE1> fpe_obj(
+         new Botan::FPE_FE1(safe_get(n), rounds, compat_mode));
+
+      fpe_obj->set_key(key, key_len);
+
+      *fpe = new botan_fpe_struct(fpe_obj.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   *fpe = nullptr;
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_fpe_destroy(botan_fpe_t fpe)
+   {
+#if defined(BOTAN_HAS_FPE_FE1)
+   return BOTAN_FFI_CHECKED_DELETE(fpe);
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_fpe_encrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len)
+   {
+#if defined(BOTAN_HAS_FPE_FE1)
+   return ffi_guard_thunk(__func__, [=]() {
+      Botan::BigInt r = safe_get(fpe).encrypt(safe_get(x), tweak, tweak_len);
+      safe_get(x) = r;
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_fpe_decrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len)
+   {
+#if defined(BOTAN_HAS_FPE_FE1)
+   return ffi_guard_thunk(__func__, [=]() {
+      Botan::BigInt r = safe_get(fpe).decrypt(safe_get(x), tweak, tweak_len);
+      safe_get(x) = r;
+      return BOTAN_FFI_SUCCESS;
+      });
+
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_hash.cpp b/third_party/botan/src/lib/ffi/ffi_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_hash.cpp
@@ -0,0 +1,91 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/hash.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_hash_struct, Botan::HashFunction, 0x1F0A4F84);
+
+int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(hash == nullptr || hash_name == nullptr || *hash_name == 0)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+      if(flags != 0)
+         return BOTAN_FFI_ERROR_BAD_FLAG;
+
+      std::unique_ptr<Botan::HashFunction> h = Botan::HashFunction::create(hash_name);
+      if(h == nullptr)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      *hash = new botan_hash_struct(h.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_hash_destroy(botan_hash_t hash)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(hash);
+   }
+
+int botan_hash_output_length(botan_hash_t hash, size_t* out)
+   {
+   if(out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { *out = h.output_length(); });
+   }
+
+int botan_hash_block_size(botan_hash_t hash, size_t* out)
+   {
+   if(out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { *out = h.hash_block_size(); });
+   }
+
+int botan_hash_clear(botan_hash_t hash)
+   {
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.clear(); });
+   }
+
+int botan_hash_update(botan_hash_t hash, const uint8_t* buf, size_t len)
+   {
+   if(len == 0)
+      return 0;
+
+   if(buf == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.update(buf, len); });
+   }
+
+int botan_hash_final(botan_hash_t hash, uint8_t out[])
+   {
+   if(out == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.final(out); });
+   }
+
+int botan_hash_copy_state(botan_hash_t* dest, const botan_hash_t source)
+   {
+   return BOTAN_FFI_DO(Botan::HashFunction, source, src, {
+      *dest = new botan_hash_struct(src.copy_state().release()); });
+   }
+
+int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len)
+   {
+   if(name_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::HashFunction, hash, h, {
+      return write_str_output(name, name_len, h.name()); });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_hotp.cpp b/third_party/botan/src/lib/ffi/ffi_hotp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_hotp.cpp
@@ -0,0 +1,99 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_HOTP)
+   #include <botan/hotp.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_HOTP)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_hotp_struct, Botan::HOTP, 0x89CBF191);
+
+#endif
+
+int botan_hotp_init(botan_hotp_t* hotp,
+                    const uint8_t key[], size_t key_len,
+                    const char* hash_algo,
+                    size_t digits)
+   {
+   if(hotp == nullptr || key == nullptr || hash_algo == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   *hotp = nullptr;
+
+#if defined(BOTAN_HAS_HOTP)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+
+      *hotp = new botan_hotp_struct(
+         new Botan::HOTP(key, key_len, hash_algo, digits));
+
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(hotp, key, key_len, hash_algo, digits);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_hotp_destroy(botan_hotp_t hotp)
+   {
+#if defined(BOTAN_HAS_HOTP)
+   return BOTAN_FFI_CHECKED_DELETE(hotp);
+#else
+   BOTAN_UNUSED(hotp);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_hotp_generate(botan_hotp_t hotp,
+                        uint32_t* hotp_code,
+                        uint64_t hotp_counter)
+   {
+#if defined(BOTAN_HAS_HOTP)
+   if(hotp == nullptr || hotp_code == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::HOTP, hotp, h, {
+      *hotp_code = h.generate_hotp(hotp_counter);
+      });
+
+#else
+   BOTAN_UNUSED(hotp, hotp_code, hotp_counter);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_hotp_check(botan_hotp_t hotp,
+                     uint64_t* next_hotp_counter,
+                     uint32_t hotp_code,
+                     uint64_t hotp_counter,
+                     size_t resync_range)
+   {
+#if defined(BOTAN_HAS_HOTP)
+   return BOTAN_FFI_RETURNING(Botan::HOTP, hotp, h, {
+
+      auto resp = h.verify_hotp(hotp_code, hotp_counter, resync_range);
+
+      if(next_hotp_counter)
+         *next_hotp_counter = resp.second;
+
+      return (resp.first == true) ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER;
+      });
+
+#else
+   BOTAN_UNUSED(hotp, next_hotp_counter, hotp_code, hotp_counter, resync_range);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_kdf.cpp b/third_party/botan/src/lib/ffi/ffi_kdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_kdf.cpp
@@ -0,0 +1,189 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/kdf.h>
+
+#if defined(BOTAN_HAS_BCRYPT)
+  #include <botan/bcrypt.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_pbkdf(const char* algo, uint8_t out[], size_t out_len,
+                const char* pass, const uint8_t salt[], size_t salt_len,
+                size_t iterations)
+   {
+   return botan_pwdhash(algo,
+                        iterations,
+                        0,
+                        0,
+                        out, out_len,
+                        pass, 0,
+                        salt, salt_len);
+   }
+
+int botan_pbkdf_timed(const char* algo,
+                      uint8_t out[], size_t out_len,
+                      const char* password,
+                      const uint8_t salt[], size_t salt_len,
+                      size_t ms_to_run,
+                      size_t* iterations_used)
+   {
+   return botan_pwdhash_timed(algo,
+                              static_cast<uint32_t>(ms_to_run),
+                              iterations_used,
+                              nullptr,
+                              nullptr,
+                              out, out_len,
+                              password, 0,
+                              salt, salt_len);
+   }
+
+int botan_pwdhash(
+   const char* algo,
+   size_t param1,
+   size_t param2,
+   size_t param3,
+   uint8_t out[],
+   size_t out_len,
+   const char* password,
+   size_t password_len,
+   const uint8_t salt[],
+   size_t salt_len)
+   {
+   if(algo == nullptr || password == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(password_len == 0)
+      password_len = std::strlen(password);
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      auto pwdhash_fam = Botan::PasswordHashFamily::create(algo);
+
+      if(!pwdhash_fam)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      auto pwdhash = pwdhash_fam->from_params(param1, param2, param3);
+
+      pwdhash->derive_key(out, out_len,
+                          password, password_len,
+                          salt, salt_len);
+
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pwdhash_timed(
+   const char* algo,
+   uint32_t msec,
+   size_t* param1,
+   size_t* param2,
+   size_t* param3,
+   uint8_t out[],
+   size_t out_len,
+   const char* password,
+   size_t password_len,
+   const uint8_t salt[],
+   size_t salt_len)
+   {
+   if(algo == nullptr || password == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(password_len == 0)
+      password_len = std::strlen(password);
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+
+      auto pwdhash_fam = Botan::PasswordHashFamily::create(algo);
+
+      if(!pwdhash_fam)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      auto pwdhash = pwdhash_fam->tune(out_len, std::chrono::milliseconds(msec));
+
+      if(param1)
+         *param1 = pwdhash->iterations();
+      if(param2)
+         *param2 = pwdhash->parallelism();
+      if(param3)
+         *param3 = pwdhash->memory_param();
+
+      pwdhash->derive_key(out, out_len,
+                          password, password_len,
+                          salt, salt_len);
+
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_kdf(const char* kdf_algo,
+              uint8_t out[], size_t out_len,
+              const uint8_t secret[], size_t secret_len,
+              const uint8_t salt[], size_t salt_len,
+              const uint8_t label[], size_t label_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::KDF> kdf(Botan::get_kdf(kdf_algo));
+      kdf->kdf(out, out_len, secret, secret_len, salt, salt_len, label, label_len);
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_scrypt(uint8_t out[], size_t out_len,
+                 const char* password,
+                 const uint8_t salt[], size_t salt_len,
+                 size_t N, size_t r, size_t p)
+   {
+   return botan_pwdhash("Scrypt", N, r, p,
+                        out, out_len,
+                        password, 0,
+                        salt, salt_len);
+   }
+
+int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+                          const char* pass,
+                          botan_rng_t rng_obj, size_t wf,
+                          uint32_t flags)
+   {
+#if defined(BOTAN_HAS_BCRYPT)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(out == nullptr || out_len == nullptr || pass == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      if(flags != 0)
+         return BOTAN_FFI_ERROR_BAD_FLAG;
+
+      if(wf < 4 || wf > 18)
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+      const std::string bcrypt = Botan::generate_bcrypt(pass, rng, static_cast<uint16_t>(wf));
+      return write_str_output(out, out_len, bcrypt);
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_bcrypt_is_valid(const char* pass, const char* hash)
+   {
+#if defined(BOTAN_HAS_BCRYPT)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      return Botan::check_bcrypt(pass, hash) ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER;
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_keywrap.cpp b/third_party/botan/src/lib/ffi/ffi_keywrap.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_keywrap.cpp
@@ -0,0 +1,50 @@
+/*
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+   #include <botan/rfc3394.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_key_wrap3394(const uint8_t key[], size_t key_len,
+                       const uint8_t kek[], size_t kek_len,
+                       uint8_t wrapped_key[], size_t* wrapped_key_len)
+   {
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::SymmetricKey kek_sym(kek, kek_len);
+      const Botan::secure_vector<uint8_t> key_pt(key, key + key_len);
+      const Botan::secure_vector<uint8_t> key_ct = Botan::rfc3394_keywrap(key_pt, kek_sym);
+      return write_vec_output(wrapped_key, wrapped_key_len, key_ct);
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_key_unwrap3394(const uint8_t wrapped_key[], size_t wrapped_key_len,
+                         const uint8_t kek[], size_t kek_len,
+                         uint8_t key[], size_t* key_len)
+   {
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::SymmetricKey kek_sym(kek, kek_len);
+      const Botan::secure_vector<uint8_t> key_ct(wrapped_key, wrapped_key + wrapped_key_len);
+      const Botan::secure_vector<uint8_t> key_pt = Botan::rfc3394_keyunwrap(key_ct, kek_sym);
+      return write_vec_output(key, key_len, key_pt);
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_mac.cpp b/third_party/botan/src/lib/ffi/ffi_mac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_mac.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/mac.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_mac_struct, Botan::MessageAuthenticationCode, 0xA06E8FC1);
+
+int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(!mac || !mac_name || flags != 0)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      std::unique_ptr<Botan::MessageAuthenticationCode> m =
+         Botan::MessageAuthenticationCode::create(mac_name);
+
+      if(m == nullptr)
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      *mac = new botan_mac_struct(m.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_mac_destroy(botan_mac_t mac)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(mac);
+   }
+
+int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.set_key(key, key_len); });
+   }
+
+int botan_mac_output_length(botan_mac_t mac, size_t* out)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { *out = m.output_length(); });
+   }
+
+int botan_mac_clear(botan_mac_t mac)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.clear(); });
+   }
+
+int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.update(buf, len); });
+   }
+
+int botan_mac_final(botan_mac_t mac, uint8_t out[])
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.final(out); });
+   }
+
+int botan_mac_name(botan_mac_t mac, char* name, size_t* name_len)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, {
+      return write_str_output(name, name_len, m.name()); });
+   }
+
+int botan_mac_get_keyspec(botan_mac_t mac,
+                          size_t* out_minimum_keylength,
+                          size_t* out_maximum_keylength,
+                          size_t* out_keylength_modulo)
+   {
+   return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, {
+      if(out_minimum_keylength)
+         *out_minimum_keylength = m.minimum_keylength();
+      if(out_maximum_keylength)
+         *out_maximum_keylength = m.maximum_keylength();
+      if(out_keylength_modulo)
+         *out_keylength_modulo = m.key_spec().keylength_multiple();
+      });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_mp.cpp b/third_party/botan/src/lib/ffi/ffi_mp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_mp.cpp
@@ -0,0 +1,312 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/internal/ffi_mp.h>
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+#include <botan/divide.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_mp_init(botan_mp_t* mp_out)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(mp_out == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      *mp_out = new botan_mp_struct(new Botan::BigInt);
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_mp_clear(botan_mp_t mp)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.clear(); });
+   }
+
+int botan_mp_set_from_int(botan_mp_t mp, int initial_value)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+      if(initial_value >= 0)
+         {
+         bn = Botan::BigInt(static_cast<uint64_t>(initial_value));
+         }
+      else
+         {
+         bn = Botan::BigInt(static_cast<uint64_t>(-initial_value));
+         bn.flip_sign();
+         }
+      });
+   }
+
+int botan_mp_set_from_str(botan_mp_t mp, const char* str)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn = Botan::BigInt(str); });
+   }
+
+int botan_mp_set_from_radix_str(botan_mp_t mp, const char* str, size_t radix)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+      Botan::BigInt::Base base;
+      if(radix == 10)
+         base = Botan::BigInt::Decimal;
+      else if(radix == 16)
+         base = Botan::BigInt::Hexadecimal;
+      else
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+      const uint8_t* bytes = Botan::cast_char_ptr_to_uint8(str);
+      const size_t len = strlen(str);
+
+      bn = Botan::BigInt(bytes, len, base);
+      });
+   }
+
+int botan_mp_set_from_mp(botan_mp_t dest, const botan_mp_t source)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, dest, bn, { bn = safe_get(source); });
+   }
+
+int botan_mp_is_negative(const botan_mp_t mp)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_negative() ? 1 : 0; });
+   }
+
+int botan_mp_is_positive(const botan_mp_t mp)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_positive() ? 1 : 0; });
+   }
+
+int botan_mp_flip_sign(botan_mp_t mp)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.flip_sign(); });
+   }
+
+int botan_mp_from_bin(botan_mp_t mp, const uint8_t bin[], size_t bin_len)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_decode(bin, bin_len); });
+   }
+
+int botan_mp_to_hex(const botan_mp_t mp, char* out)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+      const std::string hex = bn.to_hex_string();
+      std::memcpy(out, hex.c_str(), 1 + hex.size());
+      });
+   }
+
+int botan_mp_to_str(const botan_mp_t mp, uint8_t digit_base, char* out, size_t* out_len)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, {
+
+      if(digit_base == 0 || digit_base == 10)
+         return write_str_output(out, out_len, bn.to_dec_string());
+      else if(digit_base == 16)
+         return write_str_output(out, out_len, bn.to_hex_string());
+      else
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+      });
+   }
+
+int botan_mp_to_bin(const botan_mp_t mp, uint8_t vec[])
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_encode(vec); });
+   }
+
+int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val)
+   {
+   if(val == nullptr)
+      {
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+      }
+   return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { *val = bn.to_u32bit(); });
+   }
+
+int botan_mp_destroy(botan_mp_t mp)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(mp);
+   }
+
+int botan_mp_add(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+      if(result == x)
+         res += safe_get(y);
+      else
+         res = safe_get(x) + safe_get(y);
+      });
+   }
+
+int botan_mp_sub(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+      if(result == x)
+         res -= safe_get(y);
+      else
+         res = safe_get(x) - safe_get(y);
+      });
+   }
+
+int botan_mp_add_u32(botan_mp_t result, const botan_mp_t x, uint32_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+      if(result == x)
+         res += static_cast<Botan::word>(y);
+      else
+         res = safe_get(x) + static_cast<Botan::word>(y);
+      });
+   }
+
+int botan_mp_sub_u32(botan_mp_t result, const botan_mp_t x, uint32_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+      if(result == x)
+         res -= static_cast<Botan::word>(y);
+      else
+         res = safe_get(x) - static_cast<Botan::word>(y);
+      });
+   }
+
+int botan_mp_mul(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+      if(result == x)
+         res *= safe_get(y);
+      else
+         res = safe_get(x) * safe_get(y);
+      });
+   }
+
+int botan_mp_div(botan_mp_t quotient,
+                 botan_mp_t remainder,
+                 const botan_mp_t x, const botan_mp_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, quotient, q, {
+      Botan::BigInt r;
+      Botan::divide(safe_get(x), safe_get(y), q, r);
+      safe_get(remainder) = r;
+      });
+   }
+
+int botan_mp_equal(const botan_mp_t x_w, const botan_mp_t y_w)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, x_w, x, { return x == safe_get(y_w); });
+   }
+
+int botan_mp_is_zero(const botan_mp_t mp)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_zero(); });
+   }
+
+int botan_mp_is_odd(const botan_mp_t mp)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_odd(); });
+   }
+
+int botan_mp_is_even(const botan_mp_t mp)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_even(); });
+   }
+
+int botan_mp_cmp(int* result, const botan_mp_t x_w, const botan_mp_t y_w)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { *result = x.cmp(safe_get(y_w)); });
+   }
+
+int botan_mp_swap(botan_mp_t x_w, botan_mp_t y_w)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { x.swap(safe_get(y_w)); });
+   }
+
+// Return (base^exponent) % modulus
+int botan_mp_powmod(botan_mp_t out, const botan_mp_t base, const botan_mp_t exponent, const botan_mp_t modulus)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o,
+                       { o = Botan::power_mod(safe_get(base), safe_get(exponent), safe_get(modulus)); });
+   }
+
+int botan_mp_lshift(botan_mp_t out, const botan_mp_t in, size_t shift)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) << shift; });
+   }
+
+int botan_mp_rshift(botan_mp_t out, const botan_mp_t in, size_t shift)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) >> shift; });
+   }
+
+int botan_mp_mod_inverse(botan_mp_t out, const botan_mp_t in, const botan_mp_t modulus)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = Botan::inverse_mod(safe_get(in), safe_get(modulus)); });
+   }
+
+int botan_mp_mod_mul(botan_mp_t out, const botan_mp_t x, const botan_mp_t y, const botan_mp_t modulus)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o, {
+      Botan::Modular_Reducer reducer(safe_get(modulus));
+      o = reducer.multiply(safe_get(x), safe_get(y));
+      });
+   }
+
+int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+      safe_get(rand_out).randomize(r, bits); });
+   }
+
+int botan_mp_rand_range(botan_mp_t rand_out,
+                        botan_rng_t rng,
+                        const botan_mp_t lower,
+                        const botan_mp_t upper)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+      safe_get(rand_out) = Botan::BigInt::random_integer(r, safe_get(lower), safe_get(upper)); });
+   }
+
+int botan_mp_gcd(botan_mp_t out, const botan_mp_t x, const botan_mp_t y)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, out, o, {
+      o = Botan::gcd(safe_get(x), safe_get(y)); });
+   }
+
+int botan_mp_is_prime(const botan_mp_t mp, botan_rng_t rng, size_t test_prob)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, n,
+                       { return (Botan::is_prime(n, safe_get(rng), test_prob)) ? 1 : 0; });
+   }
+
+int botan_mp_get_bit(const botan_mp_t mp, size_t bit)
+   {
+   return BOTAN_FFI_RETURNING(Botan::BigInt, mp, n, { return (n.get_bit(bit)); });
+   }
+
+int botan_mp_set_bit(botan_mp_t mp, size_t bit)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, n, { n.set_bit(bit); });
+   }
+
+int botan_mp_clear_bit(botan_mp_t mp, size_t bit)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, n, { n.clear_bit(bit); });
+   }
+
+int botan_mp_num_bits(const botan_mp_t mp, size_t* bits)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bits = n.bits(); });
+   }
+
+int botan_mp_num_bytes(const botan_mp_t mp, size_t* bytes)
+   {
+   return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bytes = n.bytes(); });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_mp.h b/third_party/botan/src/lib/ffi/ffi_mp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_mp.h
@@ -0,0 +1,19 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_MP_H_
+#define BOTAN_FFI_MP_H_
+
+#include <botan/bigint.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_mp_struct, Botan::BigInt, 0xC828B9D2);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/ffi/ffi_pk_op.cpp b/third_party/botan/src/lib/ffi/ffi_pk_op.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_pk_op.cpp
@@ -0,0 +1,255 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/pubkey.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_encrypt_struct, Botan::PK_Encryptor, 0x891F3FC3);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_decrypt_struct, Botan::PK_Decryptor, 0x912F3C37);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_sign_struct, Botan::PK_Signer, 0x1AF0C39F);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_verify_struct, Botan::PK_Verifier, 0x2B91F936);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_ka_struct, Botan::PK_Key_Agreement, 0x2939CAB1);
+
+int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+                               botan_pubkey_t key_obj,
+                               const char* padding,
+                               uint32_t flags)
+   {
+   if(op == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *op = nullptr;
+
+      std::unique_ptr<Botan::PK_Encryptor> pk(new Botan::PK_Encryptor_EME(safe_get(key_obj), Botan::system_rng(), padding));
+      *op = new botan_pk_op_encrypt_struct(pk.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(op);
+   }
+
+int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op, size_t ptext_len, size_t* ctext_len)
+   {
+   if(ctext_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::PK_Encryptor, op, o, { *ctext_len = o.ciphertext_length(ptext_len); });
+   }
+
+int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+                        botan_rng_t rng_obj,
+                        uint8_t out[], size_t* out_len,
+                        const uint8_t plaintext[], size_t plaintext_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Encryptor, op, o, {
+      return write_vec_output(out, out_len, o.encrypt(plaintext, plaintext_len, safe_get(rng_obj)));
+      });
+   }
+
+/*
+* Public Key Decryption
+*/
+int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+                               botan_privkey_t key_obj,
+                               const char* padding,
+                               uint32_t flags)
+   {
+   if(op == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(flags != 0)
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *op = nullptr;
+
+      std::unique_ptr<Botan::PK_Decryptor> pk(new Botan::PK_Decryptor_EME(safe_get(key_obj), Botan::system_rng(), padding));
+      *op = new botan_pk_op_decrypt_struct(pk.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(op);
+   }
+
+int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op, size_t ctext_len, size_t* ptext_len)
+   {
+   if(ptext_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   return BOTAN_FFI_DO(Botan::PK_Decryptor, op, o, { *ptext_len = o.plaintext_length(ctext_len); });
+   }
+
+int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+                        uint8_t out[], size_t* out_len,
+                        const uint8_t ciphertext[], size_t ciphertext_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Decryptor, op, o, {
+      return write_vec_output(out, out_len, o.decrypt(ciphertext, ciphertext_len));
+      });
+   }
+
+/*
+* Signature Generation
+*/
+int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+                            botan_privkey_t key_obj,
+                            const char* hash,
+                            uint32_t flags)
+   {
+   if(op == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *op = nullptr;
+
+      auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+      std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), Botan::system_rng(), hash, format));
+      *op = new botan_pk_op_sign_struct(pk.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pk_op_sign_destroy(botan_pk_op_sign_t op)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(op);
+   }
+
+int botan_pk_op_sign_output_length(botan_pk_op_sign_t op, size_t* sig_len)
+   {
+   if(sig_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::PK_Signer, op, o, { *sig_len = o.signature_length(); });
+   }
+
+int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Signer, op, o, { o.update(in, in_len); });
+   }
+
+int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng_obj, uint8_t out[], size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Signer, op, o, {
+      return write_vec_output(out, out_len, o.signature(safe_get(rng_obj)));
+      });
+   }
+
+int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+                              botan_pubkey_t key_obj,
+                              const char* hash,
+                              uint32_t flags)
+   {
+   if(op == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *op = nullptr;
+      auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+      std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash, format));
+      *op = new botan_pk_op_verify_struct(pk.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pk_op_verify_destroy(botan_pk_op_verify_t op)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(op);
+   }
+
+int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Verifier, op, o, { o.update(in, in_len); });
+   }
+
+int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len)
+   {
+   return BOTAN_FFI_RETURNING(Botan::PK_Verifier, op, o, {
+      const bool legit = o.check_signature(sig, sig_len);
+
+      if(legit)
+         return BOTAN_FFI_SUCCESS;
+      else
+         return BOTAN_FFI_INVALID_VERIFIER;
+      });
+   }
+
+int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+                                     botan_privkey_t key_obj,
+                                     const char* kdf,
+                                     uint32_t flags)
+   {
+   if(op == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(flags != 0)
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *op = nullptr;
+      std::unique_ptr<Botan::PK_Key_Agreement> pk(new Botan::PK_Key_Agreement(safe_get(key_obj), Botan::system_rng(), kdf));
+      *op = new botan_pk_op_ka_struct(pk.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(op);
+   }
+
+int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+                                            uint8_t out[], size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      if(auto kak = dynamic_cast<const Botan::PK_Key_Agreement_Key*>(&k))
+         return write_vec_output(out, out_len, kak->public_value());
+      return BOTAN_FFI_ERROR_BAD_FLAG;
+      });
+   }
+
+int botan_pk_op_key_agreement_size(botan_pk_op_ka_t op, size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, o, {
+      if(out_len == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+      *out_len = o.agreed_value_size();
+      });
+   }
+
+int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+                              uint8_t out[], size_t* out_len,
+                              const uint8_t other_key[], size_t other_key_len,
+                              const uint8_t salt[], size_t salt_len)
+   {
+   return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, o, {
+      auto k = o.derive_key(*out_len, other_key, other_key_len, salt, salt_len).bits_of();
+      return write_vec_output(out, out_len, k);
+      });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_pkey.cpp b/third_party/botan/src/lib/ffi/ffi_pkey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_pkey.cpp
@@ -0,0 +1,279 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/data_src.h>
+#include <botan/hash.h>
+#include <botan/pkcs8.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_key.h>
+#include <botan/pk_algs.h>
+
+#if defined(BOTAN_HAS_HASH_ID)
+  #include <botan/hash_id.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_privkey_create(botan_privkey_t* key_obj,
+                         const char* algo_name,
+                         const char* algo_params,
+                         botan_rng_t rng_obj)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(key_obj == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      *key_obj = nullptr;
+      if(rng_obj == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+      std::unique_ptr<Botan::Private_Key> key(
+         Botan::create_private_key(algo_name ? algo_name : "RSA",
+                                   rng,
+                                   algo_params ? algo_params : ""));
+
+      if(key)
+         {
+         *key_obj = new botan_privkey_struct(key.release());
+         return BOTAN_FFI_SUCCESS;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+         }
+      });
+   }
+
+int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
+                       const uint8_t bits[], size_t len,
+                       const char* password)
+   {
+   BOTAN_UNUSED(rng_obj);
+
+   *key = nullptr;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DataSource_Memory src(bits, len);
+
+      std::unique_ptr<Botan::Private_Key> pkcs8;
+
+      if(password == nullptr)
+         {
+         pkcs8 = Botan::PKCS8::load_key(src);
+         }
+      else
+         {
+         pkcs8 = Botan::PKCS8::load_key(src, std::string(password));
+         }
+
+      if(pkcs8)
+         {
+         *key = new botan_privkey_struct(pkcs8.release());
+         return BOTAN_FFI_SUCCESS;
+         }
+      return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+      });
+   }
+
+int botan_privkey_destroy(botan_privkey_t key)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(key);
+   }
+
+int botan_pubkey_load(botan_pubkey_t* key,
+                      const uint8_t bits[], size_t bits_len)
+   {
+   *key = nullptr;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DataSource_Memory src(bits, bits_len);
+      std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(src));
+
+      if(pubkey == nullptr)
+         return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+
+      *key = new botan_pubkey_struct(pubkey.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_pubkey_destroy(botan_pubkey_t key)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(key);
+   }
+
+int botan_privkey_export_pubkey(botan_pubkey_t* pubout, botan_privkey_t key_obj)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::Public_Key>
+         pubkey(Botan::X509::load_key(Botan::X509::BER_encode(safe_get(key_obj))));
+
+      *pubout = new botan_pubkey_struct(pubkey.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_privkey_algo_name(botan_privkey_t key, char out[], size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, { return write_str_output(out, out_len, k.algo_name()); });
+   }
+
+int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, { return write_str_output(out, out_len, k.algo_name()); });
+   }
+
+int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags)
+   {
+   const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS);
+
+   return BOTAN_FFI_RETURNING(Botan::Public_Key, key, k, {
+      return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT;
+      });
+   }
+
+int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags)
+   {
+   const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS);
+   return BOTAN_FFI_RETURNING(Botan::Private_Key, key, k, {
+      return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT;
+      });
+   }
+
+int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+   {
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+      if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+         return write_vec_output(out, out_len, Botan::X509::BER_encode(k));
+      else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+         return write_str_output(out, out_len, Botan::X509::PEM_encode(k));
+      else
+         return BOTAN_FFI_ERROR_BAD_FLAG;
+      });
+   }
+
+int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+   {
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+         return write_vec_output(out, out_len, Botan::PKCS8::BER_encode(k));
+      else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+         return write_str_output(out, out_len, Botan::PKCS8::PEM_encode(k));
+      else
+         return BOTAN_FFI_ERROR_BAD_FLAG;
+      });
+   }
+
+int botan_privkey_export_encrypted(botan_privkey_t key,
+                                   uint8_t out[], size_t* out_len,
+                                   botan_rng_t rng_obj,
+                                   const char* pass,
+                                   const char* /*ignored - pbe*/,
+                                   uint32_t flags)
+   {
+   return botan_privkey_export_encrypted_pbkdf_iter(key, out, out_len, rng_obj, pass, 100000, nullptr, nullptr, flags);
+   }
+
+int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key,
+                                              uint8_t out[], size_t* out_len,
+                                              botan_rng_t rng_obj,
+                                              const char* pass,
+                                              uint32_t pbkdf_msec,
+                                              size_t* pbkdf_iters_out,
+                                              const char* maybe_cipher,
+                                              const char* maybe_pbkdf_hash,
+                                              uint32_t flags)
+   {
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      const std::chrono::milliseconds pbkdf_time(pbkdf_msec);
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+      const std::string cipher = (maybe_cipher ? maybe_cipher : "");
+      const std::string pbkdf_hash = (maybe_pbkdf_hash ? maybe_pbkdf_hash : "");
+
+      if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+         {
+         return write_vec_output(out, out_len,
+                                 Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+         }
+      else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+         {
+         return write_str_output(out, out_len,
+                                 Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+         }
+      else
+         {
+         return -2;
+         }
+      });
+   }
+
+int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key,
+                                              uint8_t out[], size_t* out_len,
+                                              botan_rng_t rng_obj,
+                                              const char* pass,
+                                              size_t pbkdf_iter,
+                                              const char* maybe_cipher,
+                                              const char* maybe_pbkdf_hash,
+                                              uint32_t flags)
+   {
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+      const std::string cipher = (maybe_cipher ? maybe_cipher : "");
+      const std::string pbkdf_hash = (maybe_pbkdf_hash ? maybe_pbkdf_hash : "");
+
+      if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+         {
+         return write_vec_output(out, out_len,
+                                 Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+         }
+      else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+         {
+         return write_str_output(out, out_len,
+                                 Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+         }
+      else
+         {
+         return -2;
+         }
+      });
+   }
+
+int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate)
+   {
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, { *estimate = k.estimated_strength(); });
+   }
+
+int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash_fn,
+                             uint8_t out[], size_t* out_len)
+   {
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+      std::unique_ptr<Botan::HashFunction> h(Botan::HashFunction::create(hash_fn));
+      return write_vec_output(out, out_len, h->process(k.public_key_bits()));
+      });
+   }
+
+int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs_id[], size_t* pkcs_id_len)
+   {
+#if defined(BOTAN_HAS_HASH_ID)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::vector<uint8_t> hash_id = Botan::pkcs_hash_id(hash_name);
+      return write_output(pkcs_id, pkcs_id_len, hash_id.data(), hash_id.size());
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_pkey.h b/third_party/botan/src/lib/ffi/ffi_pkey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_pkey.h
@@ -0,0 +1,20 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_PKEY_H_
+#define BOTAN_FFI_PKEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_pubkey_struct, Botan::Public_Key, 0x2C286519);
+BOTAN_FFI_DECLARE_STRUCT(botan_privkey_struct, Botan::Private_Key, 0x7F96385E);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp b/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp
@@ -0,0 +1,980 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+* (C) 2017 Ribose Inc
+* (C) 2018 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/hash.h>
+#include <botan/pem.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/internal/ffi_mp.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+  #include <botan/ecc_key.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+  #include <botan/dl_algo.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+  #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+  #include <botan/elgamal.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+  #include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+  #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+  #include <botan/sm2.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+  #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+  #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+  #include <botan/ed25519.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+  #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_MCEIES)
+  #include <botan/mceies.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+  #include <botan/dh.h>
+#endif
+
+
+namespace {
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+// These are always called within an existing try/catch block
+
+template<class ECPrivateKey_t>
+int privkey_load_ec(std::unique_ptr<ECPrivateKey_t>& key,
+                    const Botan::BigInt& scalar,
+                    const char* curve_name)
+   {
+   if(curve_name == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   Botan::Null_RNG null_rng;
+   Botan::EC_Group grp(curve_name);
+   key.reset(new ECPrivateKey_t(null_rng, grp, scalar));
+   return BOTAN_FFI_SUCCESS;
+   }
+
+template<class ECPublicKey_t>
+int pubkey_load_ec(std::unique_ptr<ECPublicKey_t>& key,
+                   const Botan::BigInt& public_x,
+                   const Botan::BigInt& public_y,
+                   const char* curve_name)
+   {
+   if(curve_name == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   Botan::EC_Group grp(curve_name);
+   Botan::PointGFp uncompressed_point = grp.point(public_x, public_y);
+   key.reset(new ECPublicKey_t(grp, uncompressed_point));
+   return BOTAN_FFI_SUCCESS;
+   }
+
+#endif
+
+Botan::BigInt pubkey_get_field(const Botan::Public_Key& key,
+                               const std::string& field)
+   {
+   // Maybe this should be `return key.get_integer_field(field_name)`?
+
+#if defined(BOTAN_HAS_RSA)
+   if(const Botan::RSA_PublicKey* rsa = dynamic_cast<const Botan::RSA_PublicKey*>(&key))
+      {
+      if(field == "n")
+         return rsa->get_n();
+      else if(field == "e")
+         return rsa->get_e();
+      else
+         throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+      }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+   // Handles DSA, ElGamal, etc
+   if(const Botan::DL_Scheme_PublicKey* dl = dynamic_cast<const Botan::DL_Scheme_PublicKey*>(&key))
+      {
+      if(field == "p")
+         return dl->group_p();
+      else if(field == "q")
+         return dl->group_q();
+      else if(field == "g")
+         return dl->group_g();
+      else if(field == "y")
+         return dl->get_y();
+      else
+         throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+   if(const Botan::EC_PublicKey* ecc = dynamic_cast<const Botan::EC_PublicKey*>(&key))
+      {
+      if(field == "public_x")
+         return ecc->public_point().get_affine_x();
+      else if(field == "public_y")
+         return ecc->public_point().get_affine_y();
+      else if(field == "base_x")
+         return ecc->domain().get_g_x();
+      else if(field == "base_y")
+         return ecc->domain().get_g_y();
+      else if(field == "p")
+         return ecc->domain().get_p();
+      else if(field == "a")
+         return ecc->domain().get_a();
+      else if(field == "b")
+         return ecc->domain().get_b();
+      else if(field == "cofactor")
+         return ecc->domain().get_cofactor();
+      else if(field == "order")
+         return ecc->domain().get_order();
+      else
+         throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+      }
+#endif
+
+   // Some other algorithm type not supported by this function
+   throw Botan_FFI::FFI_Error("Field getter not implemented for this algorithm type",
+                              BOTAN_FFI_ERROR_NOT_IMPLEMENTED);
+   }
+
+Botan::BigInt privkey_get_field(const Botan::Private_Key& key,
+                                const std::string& field)
+   {
+   //return key.get_integer_field(field);
+
+#if defined(BOTAN_HAS_RSA)
+
+   if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&key))
+      {
+      if(field == "p")
+         return rsa->get_p();
+      else if(field == "q")
+         return rsa->get_q();
+      else if(field == "d")
+         return rsa->get_d();
+      else if(field == "c")
+         return rsa->get_c();
+      else if(field == "d1")
+         return rsa->get_d1();
+      else if(field == "d2")
+         return rsa->get_d2();
+      else
+         return pubkey_get_field(key, field);
+      }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+   // Handles DSA, ElGamal, etc
+   if(const Botan::DL_Scheme_PrivateKey* dl = dynamic_cast<const Botan::DL_Scheme_PrivateKey*>(&key))
+      {
+      if(field == "x")
+         return dl->get_x();
+      else
+         return pubkey_get_field(key, field);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+   if(const Botan::EC_PrivateKey* ecc = dynamic_cast<const Botan::EC_PrivateKey*>(&key))
+      {
+      if(field == "x")
+         return ecc->private_value();
+      else
+         return pubkey_get_field(key, field);
+      }
+#endif
+
+   return pubkey_get_field(key, field);
+   }
+
+}
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_pubkey_get_field(botan_mp_t output,
+                           botan_pubkey_t key,
+                           const char* field_name_cstr)
+   {
+   if(field_name_cstr == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   const std::string field_name(field_name_cstr);
+
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+      safe_get(output) = pubkey_get_field(k, field_name);
+      });
+   }
+
+int botan_privkey_get_field(botan_mp_t output,
+                            botan_privkey_t key,
+                            const char* field_name_cstr)
+   {
+   if(field_name_cstr == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   const std::string field_name(field_name_cstr);
+
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      safe_get(output) = privkey_get_field(k, field_name);
+      });
+   }
+
+/* RSA specific operations */
+
+int botan_privkey_create_rsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n_bits)
+   {
+   if(n_bits < 1024 || n_bits > 16*1024)
+      return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+   std::string n_str = std::to_string(n_bits);
+
+   return botan_privkey_create(key_obj, "RSA", n_str.c_str(), rng_obj);
+   }
+
+int botan_privkey_load_rsa(botan_privkey_t* key,
+                           botan_mp_t rsa_p, botan_mp_t rsa_q, botan_mp_t rsa_e)
+   {
+#if defined(BOTAN_HAS_RSA)
+   *key = nullptr;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(safe_get(rsa_p),
+                                                                safe_get(rsa_q),
+                                                                safe_get(rsa_e)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, rsa_p, rsa_q, rsa_e);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key,
+                                 const uint8_t bits[],
+                                 size_t len)
+   {
+#if defined(BOTAN_HAS_RSA)
+   *key = nullptr;
+
+   Botan::secure_vector<uint8_t> src(bits, bits + len);
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::AlgorithmIdentifier alg_id("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+      *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(alg_id, src));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, bits, len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_rsa(botan_pubkey_t* key,
+                          botan_mp_t n, botan_mp_t e)
+   {
+#if defined(BOTAN_HAS_RSA)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      *key = new botan_pubkey_struct(new Botan::RSA_PublicKey(safe_get(n), safe_get(e)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, n, e);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(p, key, "p");
+   }
+
+int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(q, key, "q");
+   }
+
+int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(n, key, "n");
+   }
+
+int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(e, key, "e");
+   }
+
+int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(d, key, "d");
+   }
+
+int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(e, key, "e");
+   }
+
+int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(n, key, "n");
+   }
+
+int botan_privkey_rsa_get_privkey(botan_privkey_t rsa_key,
+                                  uint8_t out[], size_t* out_len,
+                                  uint32_t flags)
+   {
+#if defined(BOTAN_HAS_RSA)
+   return BOTAN_FFI_DO(Botan::Private_Key, rsa_key, k, {
+      if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&k))
+         {
+         if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+            return write_vec_output(out, out_len, rsa->private_key_bits());
+         else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+            return write_str_output(out, out_len, Botan::PEM_Code::encode(rsa->private_key_bits(),
+                  "RSA PRIVATE KEY"));
+         else
+            return BOTAN_FFI_ERROR_BAD_FLAG;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+         }
+      });
+#else
+   BOTAN_UNUSED(rsa_key, out, out_len);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* DSA specific operations */
+int botan_privkey_create_dsa(botan_privkey_t* key, botan_rng_t rng_obj, size_t pbits, size_t qbits)
+   {
+#if defined(BOTAN_HAS_DSA)
+
+    if ((rng_obj == nullptr) || (key == nullptr))
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+    if ((pbits % 64) || (qbits % 8) ||
+        (pbits < 1024) || (pbits > 3072) ||
+        (qbits < 160) || (qbits > 256)) {
+      return BOTAN_FFI_ERROR_BAD_PARAMETER;
+    }
+
+    return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+      Botan::DL_Group group(rng, Botan::DL_Group::Prime_Subgroup, pbits, qbits);
+      *key = new botan_privkey_struct(new Botan::DSA_PrivateKey(rng, group));
+      return BOTAN_FFI_SUCCESS;
+    });
+#else
+    BOTAN_UNUSED(key, rng_obj, pbits, qbits);
+    return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_dsa(botan_privkey_t* key,
+                           botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t x)
+   {
+#if defined(BOTAN_HAS_DSA)
+   *key = nullptr;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Null_RNG null_rng;
+      Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g));
+      *key = new botan_privkey_struct(new Botan::DSA_PrivateKey(null_rng, group, safe_get(x)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, q, g, x);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_dsa(botan_pubkey_t* key,
+                          botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t y)
+   {
+#if defined(BOTAN_HAS_DSA)
+   *key = nullptr;
+
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g));
+      *key = new botan_pubkey_struct(new Botan::DSA_PublicKey(group, safe_get(y)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, q, g, y);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_dsa_get_x(botan_mp_t x, botan_privkey_t key)
+   {
+   return botan_privkey_get_field(x, key, "x");
+   }
+
+int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(p, key, "p");
+   }
+
+int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(q, key, "q");
+   }
+
+int botan_pubkey_dsa_get_g(botan_mp_t g, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(g, key, "g");
+   }
+
+int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key)
+   {
+   return botan_pubkey_get_field(y, key, "y");
+   }
+
+int botan_privkey_create_ecdsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
+   {
+   return botan_privkey_create(key_obj, "ECDSA", param_str, rng_obj);
+   }
+
+/* ECDSA specific operations */
+
+int botan_pubkey_load_ecdsa(botan_pubkey_t* key,
+                            const botan_mp_t public_x,
+                            const botan_mp_t public_y,
+                            const char* curve_name)
+   {
+#if defined(BOTAN_HAS_ECDSA)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::ECDSA_PublicKey> p_key;
+
+      int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name);
+      if(rc == BOTAN_FFI_SUCCESS)
+         *key = new botan_pubkey_struct(p_key.release());
+
+      return rc;
+      });
+#else
+   BOTAN_UNUSED(key, public_x, public_y, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_ecdsa(botan_privkey_t* key,
+                             const botan_mp_t scalar,
+                             const char* curve_name)
+   {
+#if defined(BOTAN_HAS_ECDSA)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::ECDSA_PrivateKey> p_key;
+      int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+      if(rc == BOTAN_FFI_SUCCESS)
+         *key = new botan_privkey_struct(p_key.release());
+      return rc;
+      });
+#else
+   BOTAN_UNUSED(key, scalar, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* ElGamal specific operations */
+int botan_privkey_create_elgamal(botan_privkey_t* key,
+                                 botan_rng_t rng_obj,
+                                 size_t pbits,
+                                 size_t qbits)
+   {
+#if defined(BOTAN_HAS_ELGAMAL)
+
+    if ((rng_obj == nullptr) || (key == nullptr))
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+    if ((pbits < 1024) || (qbits<160)) {
+      return BOTAN_FFI_ERROR_BAD_PARAMETER;
+    }
+
+    Botan::DL_Group::PrimeType prime_type = ((pbits-1) == qbits)
+      ? Botan::DL_Group::Strong
+      : Botan::DL_Group::Prime_Subgroup;
+
+    return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+      Botan::DL_Group group(rng, prime_type, pbits, qbits);
+      *key = new botan_privkey_struct(new Botan::ElGamal_PrivateKey(rng, group));
+      return BOTAN_FFI_SUCCESS;
+    });
+#else
+    BOTAN_UNUSED(key, rng_obj, pbits);
+    return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_elgamal(botan_pubkey_t* key,
+                              botan_mp_t p, botan_mp_t g, botan_mp_t y)
+   {
+#if defined(BOTAN_HAS_ELGAMAL)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DL_Group group(safe_get(p), safe_get(g));
+      *key = new botan_pubkey_struct(new Botan::ElGamal_PublicKey(group, safe_get(y)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, g, y);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_elgamal(botan_privkey_t* key,
+                               botan_mp_t p, botan_mp_t g, botan_mp_t x)
+   {
+#if defined(BOTAN_HAS_ELGAMAL)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Null_RNG null_rng;
+      Botan::DL_Group group(safe_get(p), safe_get(g));
+      *key = new botan_privkey_struct(new Botan::ElGamal_PrivateKey(null_rng, group, safe_get(x)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, g, x);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* Diffie Hellman specific operations */
+
+int botan_privkey_create_dh(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
+   {
+   return botan_privkey_create(key_obj, "DH", param_str, rng_obj);
+   }
+
+int botan_privkey_load_dh(botan_privkey_t* key,
+                          botan_mp_t p, botan_mp_t g, botan_mp_t x)
+   {
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Null_RNG null_rng;
+      Botan::DL_Group group(safe_get(p), safe_get(g));
+      *key = new botan_privkey_struct(new Botan::DH_PrivateKey(null_rng, group, safe_get(x)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, g, x);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_dh(botan_pubkey_t* key,
+                         botan_mp_t p, botan_mp_t g, botan_mp_t y)
+   {
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::DL_Group group(safe_get(p), safe_get(g));
+      *key = new botan_pubkey_struct(new Botan::DH_PublicKey(group, safe_get(y)));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, p, g, y);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* ECDH + x25519 specific operations */
+
+int botan_privkey_create_ecdh(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
+   {
+   if(param_str == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   const std::string params(param_str);
+
+   if(params == "curve25519")
+      return botan_privkey_create(key_obj, "Curve25519", "", rng_obj);
+
+   return botan_privkey_create(key_obj, "ECDH", param_str, rng_obj);
+   }
+
+int botan_pubkey_load_ecdh(botan_pubkey_t* key,
+                           const botan_mp_t public_x,
+                           const botan_mp_t public_y,
+                           const char* curve_name)
+   {
+#if defined(BOTAN_HAS_ECDH)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::ECDH_PublicKey> p_key;
+      int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name);
+
+      if(rc == BOTAN_FFI_SUCCESS)
+         *key = new botan_pubkey_struct(p_key.release());
+      return rc;
+      });
+#else
+   BOTAN_UNUSED(key, public_x, public_y, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_ecdh(botan_privkey_t* key,
+                            const botan_mp_t scalar,
+                            const char* curve_name)
+   {
+#if defined(BOTAN_HAS_ECDH)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::ECDH_PrivateKey> p_key;
+      int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+      if(rc == BOTAN_FFI_SUCCESS)
+         *key = new botan_privkey_struct(p_key.release());
+      return rc;
+      });
+#else
+   BOTAN_UNUSED(key, scalar, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* SM2 specific operations */
+
+int botan_pubkey_sm2_compute_za(uint8_t out[],
+                                size_t* out_len,
+                                const char* ident,
+                                const char* hash_algo,
+                                const botan_pubkey_t key)
+   {
+   if(out == nullptr || out_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+   if(ident == nullptr || hash_algo == nullptr || key == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_SM2)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::Public_Key& pub_key = safe_get(key);
+      const Botan::EC_PublicKey* ec_key = dynamic_cast<const Botan::EC_PublicKey*>(&pub_key);
+
+      if(ec_key == nullptr)
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      if(ec_key->algo_name() != "SM2")
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      const std::string ident_str(ident);
+      std::unique_ptr<Botan::HashFunction> hash =
+         Botan::HashFunction::create_or_throw(hash_algo);
+
+      const std::vector<uint8_t> za =
+         Botan::sm2_compute_za(*hash, ident_str, ec_key->domain(), ec_key->public_point());
+
+      return write_vec_output(out, out_len, za);
+      });
+#else
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_sm2(botan_pubkey_t* key,
+                          const botan_mp_t public_x,
+                          const botan_mp_t public_y,
+                          const char* curve_name)
+   {
+#if defined(BOTAN_HAS_SM2)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::SM2_PublicKey> p_key;
+      if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name))
+         {
+         *key = new botan_pubkey_struct(p_key.release());
+         return BOTAN_FFI_SUCCESS;
+         }
+      return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+      });
+#else
+   BOTAN_UNUSED(key, public_x, public_y, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_load_sm2(botan_privkey_t* key,
+                           const botan_mp_t scalar,
+                           const char* curve_name)
+   {
+#if defined(BOTAN_HAS_SM2)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      std::unique_ptr<Botan::SM2_PrivateKey> p_key;
+      int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+
+      if(rc == BOTAN_FFI_SUCCESS)
+         *key = new botan_privkey_struct(p_key.release());
+      return rc;
+      });
+#else
+   BOTAN_UNUSED(key, scalar, curve_name);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_sm2_enc(botan_pubkey_t* key,
+                              const botan_mp_t public_x,
+                              const botan_mp_t public_y,
+                              const char* curve_name)
+   {
+   return botan_pubkey_load_sm2(key, public_x, public_y, curve_name);
+   }
+
+int botan_privkey_load_sm2_enc(botan_privkey_t* key,
+                               const botan_mp_t scalar,
+                               const char* curve_name)
+   {
+   return botan_privkey_load_sm2(key, scalar, curve_name);
+   }
+
+/* Ed25519 specific operations */
+
+int botan_privkey_load_ed25519(botan_privkey_t* key,
+                               const uint8_t privkey[32])
+   {
+#if defined(BOTAN_HAS_ED25519)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::secure_vector<uint8_t> privkey_vec(privkey, privkey + 32);
+      *key = new botan_privkey_struct(new Botan::Ed25519_PrivateKey(privkey_vec));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, privkey);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_ed25519(botan_pubkey_t* key,
+                              const uint8_t pubkey[32])
+   {
+#if defined(BOTAN_HAS_ED25519)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::vector<uint8_t> pubkey_vec(pubkey, pubkey + 32);
+      *key = new botan_pubkey_struct(new Botan::Ed25519_PublicKey(pubkey_vec));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, pubkey);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_ed25519_get_privkey(botan_privkey_t key,
+                                      uint8_t output[64])
+   {
+#if defined(BOTAN_HAS_ED25519)
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      if(Botan::Ed25519_PrivateKey* ed = dynamic_cast<Botan::Ed25519_PrivateKey*>(&k))
+         {
+         const Botan::secure_vector<uint8_t>& ed_key = ed->get_private_key();
+         if(ed_key.size() != 64)
+            return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+         Botan::copy_mem(output, ed_key.data(), ed_key.size());
+         return BOTAN_FFI_SUCCESS;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+         }
+      });
+#else
+   BOTAN_UNUSED(key, output);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key,
+                                    uint8_t output[32])
+   {
+#if defined(BOTAN_HAS_ED25519)
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+      if(Botan::Ed25519_PublicKey* ed = dynamic_cast<Botan::Ed25519_PublicKey*>(&k))
+         {
+         const std::vector<uint8_t>& ed_key = ed->get_public_key();
+         if(ed_key.size() != 32)
+            return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+         Botan::copy_mem(output, ed_key.data(), ed_key.size());
+         return BOTAN_FFI_SUCCESS;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+         }
+      });
+#else
+   BOTAN_UNUSED(key, output);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+/* X25519 specific operations */
+
+int botan_privkey_load_x25519(botan_privkey_t* key,
+                              const uint8_t privkey[32])
+   {
+#if defined(BOTAN_HAS_X25519)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const Botan::secure_vector<uint8_t> privkey_vec(privkey, privkey + 32);
+      *key = new botan_privkey_struct(new Botan::X25519_PrivateKey(privkey_vec));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, privkey);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_load_x25519(botan_pubkey_t* key,
+                             const uint8_t pubkey[32])
+   {
+#if defined(BOTAN_HAS_X25519)
+   *key = nullptr;
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      const std::vector<uint8_t> pubkey_vec(pubkey, pubkey + 32);
+      *key = new botan_pubkey_struct(new Botan::X25519_PublicKey(pubkey_vec));
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(key, pubkey);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_x25519_get_privkey(botan_privkey_t key,
+                                     uint8_t output[32])
+   {
+#if defined(BOTAN_HAS_X25519)
+   return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+      if(Botan::X25519_PrivateKey* x25519 = dynamic_cast<Botan::X25519_PrivateKey*>(&k))
+         {
+         const Botan::secure_vector<uint8_t>& x25519_key = x25519->get_x();
+         if(x25519_key.size() != 32)
+            return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+         Botan::copy_mem(output, x25519_key.data(), x25519_key.size());
+         return BOTAN_FFI_SUCCESS;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+         }
+      });
+#else
+   BOTAN_UNUSED(key, output);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key,
+                                   uint8_t output[32])
+   {
+#if defined(BOTAN_HAS_X25519)
+   return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+      if(Botan::X25519_PublicKey* x25519 = dynamic_cast<Botan::X25519_PublicKey*>(&k))
+         {
+         const std::vector<uint8_t>& x25519_key = x25519->public_value();
+         if(x25519_key.size() != 32)
+            return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+         Botan::copy_mem(output, x25519_key.data(), x25519_key.size());
+         return BOTAN_FFI_SUCCESS;
+         }
+      else
+         {
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+         }
+      });
+#else
+   BOTAN_UNUSED(key, output);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_privkey_create_mceliece(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n, size_t t)
+   {
+   const std::string mce_params = std::to_string(n) + "," + std::to_string(t);
+   return botan_privkey_create(key_obj, "McEliece", mce_params.c_str(), rng_obj);
+   }
+
+int botan_mceies_decrypt(botan_privkey_t mce_key_obj,
+                         const char* aead,
+                         const uint8_t ct[], size_t ct_len,
+                         const uint8_t ad[], size_t ad_len,
+                         uint8_t out[], size_t* out_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Private_Key& key = safe_get(mce_key_obj);
+
+#if defined(BOTAN_HAS_MCELIECE) && defined(BOTAN_HAS_MCEIES)
+      Botan::McEliece_PrivateKey* mce = dynamic_cast<Botan::McEliece_PrivateKey*>(&key);
+      if(!mce)
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      const Botan::secure_vector<uint8_t> pt = mceies_decrypt(*mce, ct, ct_len, ad, ad_len, aead);
+      return write_vec_output(out, out_len, pt);
+#else
+      return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+      });
+   }
+
+int botan_mceies_encrypt(botan_pubkey_t mce_key_obj,
+                         botan_rng_t rng_obj,
+                         const char* aead,
+                         const uint8_t pt[], size_t pt_len,
+                         const uint8_t ad[], size_t ad_len,
+                         uint8_t out[], size_t* out_len)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      Botan::Public_Key& key = safe_get(mce_key_obj);
+      Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+#if defined(BOTAN_HAS_MCELIECE) && defined(BOTAN_HAS_MCEIES)
+      Botan::McEliece_PublicKey* mce = dynamic_cast<Botan::McEliece_PublicKey*>(&key);
+      if(!mce)
+         return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+      Botan::secure_vector<uint8_t> ct = mceies_encrypt(*mce, pt, pt_len, ad, ad_len, rng, aead);
+      return write_vec_output(out, out_len, ct);
+#else
+      return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+      });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_rng.cpp b/third_party/botan/src/lib/ffi/ffi_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_rng.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/system_rng.h>
+#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_rng_init(botan_rng_t* rng_out, const char* rng_type)
+   {
+   return ffi_guard_thunk(__func__, [=]() -> int {
+      if(rng_out == nullptr)
+         return BOTAN_FFI_ERROR_NULL_POINTER;
+
+      const std::string rng_type_s(rng_type ? rng_type : "system");
+
+      std::unique_ptr<Botan::RandomNumberGenerator> rng;
+
+      if(rng_type_s == "system")
+         {
+         rng.reset(new Botan::System_RNG);
+         }
+      else if(rng_type_s == "user" || rng_type_s == "user-threadsafe")
+         {
+         rng.reset(new Botan::AutoSeeded_RNG);
+         }
+      else if(rng_type_s == "null")
+         {
+         rng.reset(new Botan::Null_RNG);
+         }
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+      else if((rng_type_s == "rdrand" || rng_type_s == "hwrng") && Botan::Processor_RNG::available())
+         {
+         rng.reset(new Botan::Processor_RNG);
+         }
+#endif
+
+      if(!rng)
+         {
+         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+         }
+
+      *rng_out = new botan_rng_struct(rng.release());
+      return BOTAN_FFI_SUCCESS;
+      });
+   }
+
+int botan_rng_destroy(botan_rng_t rng)
+   {
+   return BOTAN_FFI_CHECKED_DELETE(rng);
+   }
+
+int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.randomize(out, out_len); });
+   }
+
+int botan_rng_reseed(botan_rng_t rng, size_t bits)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); });
+   }
+
+int botan_rng_add_entropy(botan_rng_t rng, const uint8_t* input, size_t len)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.add_entropy(input, len); });
+   }
+
+int botan_rng_reseed_from_rng(botan_rng_t rng, botan_rng_t source_rng, size_t bits)
+   {
+   return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(safe_get(source_rng), bits); });
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_rng.h b/third_party/botan/src/lib/ffi/ffi_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_rng.h
@@ -0,0 +1,19 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_RNG_H_
+#define BOTAN_FFI_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_rng_struct, Botan::RandomNumberGenerator, 0x4901F9C1);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/ffi/ffi_totp.cpp b/third_party/botan/src/lib/ffi/ffi_totp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_totp.cpp
@@ -0,0 +1,94 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_TOTP)
+   #include <botan/totp.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_TOTP)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_totp_struct, Botan::TOTP, 0x3D9D2CD1);
+
+#endif
+
+int botan_totp_init(botan_totp_t* totp,
+                    const uint8_t key[], size_t key_len,
+                    const char* hash_algo,
+                    size_t digits,
+                    size_t time_step)
+   {
+   if(totp == nullptr || key == nullptr || hash_algo == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   *totp = nullptr;
+
+#if defined(BOTAN_HAS_TOTP)
+   return ffi_guard_thunk(__func__, [=]() -> int {
+
+      *totp = new botan_totp_struct(
+         new Botan::TOTP(key, key_len, hash_algo, digits, time_step));
+
+      return BOTAN_FFI_SUCCESS;
+      });
+#else
+   BOTAN_UNUSED(totp, key, key_len, hash_algo, digits, time_step);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_totp_destroy(botan_totp_t totp)
+   {
+#if defined(BOTAN_HAS_TOTP)
+   return BOTAN_FFI_CHECKED_DELETE(totp);
+#else
+   BOTAN_UNUSED(totp);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_totp_generate(botan_totp_t totp,
+                        uint32_t* totp_code,
+                        uint64_t timestamp)
+   {
+#if defined(BOTAN_HAS_TOTP)
+   if(totp == nullptr || totp_code == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   return BOTAN_FFI_DO(Botan::TOTP, totp, t, {
+      *totp_code = t.generate_totp(timestamp);
+      });
+
+#else
+   BOTAN_UNUSED(totp, totp_code, timestamp);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+int botan_totp_check(botan_totp_t totp,
+                     uint32_t totp_code,
+                     uint64_t timestamp,
+                     size_t acceptable_clock_drift)
+   {
+#if defined(BOTAN_HAS_TOTP)
+   return BOTAN_FFI_RETURNING(Botan::TOTP, totp, t, {
+      const bool ok = t.verify_totp(totp_code, timestamp, acceptable_clock_drift);
+      return (ok ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER);
+      });
+
+#else
+   BOTAN_UNUSED(totp, totp_code, timestamp, acceptable_clock_drift);
+   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/ffi/ffi_util.h b/third_party/botan/src/lib/ffi/ffi_util.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/ffi/ffi_util.h
@@ -0,0 +1,182 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_UTILS_H_
+#define BOTAN_FFI_UTILS_H_
+
+#include <cstdint>
+#include <memory>
+#include <stdexcept>
+#include <functional>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+
+namespace Botan_FFI {
+
+class BOTAN_UNSTABLE_API FFI_Error final : public Botan::Exception
+   {
+   public:
+      FFI_Error(const std::string& what, int err_code) :
+         Exception("FFI error", what),
+         m_err_code(err_code)
+         {}
+
+      int error_code() const noexcept override { return m_err_code; }
+
+      Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::InvalidArgument; }
+
+   private:
+      int m_err_code;
+   };
+
+template<typename T, uint32_t MAGIC>
+struct botan_struct
+   {
+   public:
+      botan_struct(T* obj) : m_magic(MAGIC), m_obj(obj) {}
+      virtual ~botan_struct() { m_magic = 0; m_obj.reset(); }
+
+      bool magic_ok() const { return (m_magic == MAGIC); }
+
+      T* unsafe_get() const
+         {
+         return m_obj.get();
+         }
+   private:
+      uint32_t m_magic = 0;
+      std::unique_ptr<T> m_obj;
+   };
+
+#define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \
+   struct NAME final : public Botan_FFI::botan_struct<TYPE, MAGIC> { explicit NAME(TYPE* x) : botan_struct(x) {} }
+
+// Declared in ffi.cpp
+int ffi_error_exception_thrown(const char* func_name, const char* exn,
+                               int rc = BOTAN_FFI_ERROR_EXCEPTION_THROWN);
+
+template<typename T, uint32_t M>
+T& safe_get(botan_struct<T,M>* p)
+   {
+   if(!p)
+      throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER);
+   if(p->magic_ok() == false)
+      throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT);
+
+   if(T* t = p->unsafe_get())
+      return *t;
+
+   throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT);
+   }
+
+int ffi_guard_thunk(const char* func_name, std::function<int ()>);
+
+template<typename T, uint32_t M, typename F>
+int apply_fn(botan_struct<T, M>* o, const char* func_name, F func)
+   {
+   if(!o)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   if(o->magic_ok() == false)
+      return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+   T* p = o->unsafe_get();
+   if(p == nullptr)
+      return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+   return ffi_guard_thunk(func_name, [&]() { return func(*p); });
+   }
+
+#define BOTAN_FFI_DO(T, obj, param, block)                \
+   apply_fn(obj, __func__,                                \
+            [=](T& param) -> int { do { block } while(0); return BOTAN_FFI_SUCCESS; })
+
+/*
+* Like BOTAN_FFI_DO but with no trailing return with the expectation
+* that the block always returns a value. This exists because otherwise
+* MSVC warns about the dead return after the block in FFI_DO.
+*/
+#define BOTAN_FFI_RETURNING(T, obj, param, block)         \
+   apply_fn(obj, __func__,                                \
+            [=](T& param) -> int { do { block } while(0); })
+
+template<typename T, uint32_t M>
+int ffi_delete_object(botan_struct<T, M>* obj, const char* func_name)
+   {
+   try
+      {
+      if(obj == nullptr)
+         return BOTAN_FFI_SUCCESS; // ignore delete of null objects
+
+      if(obj->magic_ok() == false)
+         return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+      delete obj;
+      return BOTAN_FFI_SUCCESS;
+      }
+   catch(std::exception& e)
+      {
+      return ffi_error_exception_thrown(func_name, e.what());
+      }
+   catch(...)
+      {
+      return ffi_error_exception_thrown(func_name, "unknown exception");
+      }
+   }
+
+#define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__)
+
+inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len)
+   {
+   if(out_len == nullptr)
+      return BOTAN_FFI_ERROR_NULL_POINTER;
+
+   const size_t avail = *out_len;
+   *out_len = buf_len;
+
+   if((avail >= buf_len) && (out != nullptr))
+      {
+      Botan::copy_mem(out, buf, buf_len);
+      return BOTAN_FFI_SUCCESS;
+      }
+   else
+      {
+      if(out != nullptr)
+         {
+         Botan::clear_mem(out, avail);
+         }
+      return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+      }
+   }
+
+template<typename Alloc>
+int write_vec_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf)
+   {
+   return write_output(out, out_len, buf.data(), buf.size());
+   }
+
+inline int write_str_output(uint8_t out[], size_t* out_len, const std::string& str)
+   {
+   return write_output(out, out_len,
+                       Botan::cast_char_ptr_to_uint8(str.data()),
+                       str.size() + 1);
+   }
+
+inline int write_str_output(char out[], size_t* out_len, const std::string& str)
+   {
+   return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str);
+   }
+
+inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec)
+   {
+   return write_output(Botan::cast_char_ptr_to_uint8(out),
+                       out_len,
+                       str_vec.data(),
+                       str_vec.size());
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/algo_filt.cpp b/third_party/botan/src/lib/filters/algo_filt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/algo_filt.cpp
@@ -0,0 +1,96 @@
+/*
+* Filters
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <algorithm>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+StreamCipher_Filter::StreamCipher_Filter(StreamCipher* cipher) :
+   m_buffer(BOTAN_DEFAULT_BUFFER_SIZE),
+   m_cipher(cipher)
+   {
+   }
+
+StreamCipher_Filter::StreamCipher_Filter(StreamCipher* cipher, const SymmetricKey& key) :
+   StreamCipher_Filter(cipher)
+   {
+   m_cipher->set_key(key);
+   }
+
+StreamCipher_Filter::StreamCipher_Filter(const std::string& sc_name) :
+   m_buffer(BOTAN_DEFAULT_BUFFER_SIZE),
+   m_cipher(StreamCipher::create_or_throw(sc_name))
+   {
+   }
+
+StreamCipher_Filter::StreamCipher_Filter(const std::string& sc_name, const SymmetricKey& key) :
+   StreamCipher_Filter(sc_name)
+   {
+   m_cipher->set_key(key);
+   }
+
+void StreamCipher_Filter::write(const uint8_t input[], size_t length)
+   {
+   while(length)
+      {
+      size_t copied = std::min<size_t>(length, m_buffer.size());
+      m_cipher->cipher(input, m_buffer.data(), copied);
+      send(m_buffer, copied);
+      input += copied;
+      length -= copied;
+      }
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+Hash_Filter::Hash_Filter(const std::string& hash_name, size_t len) :
+   m_hash(HashFunction::create_or_throw(hash_name)),
+   m_out_len(len)
+   {
+   }
+
+void Hash_Filter::end_msg()
+   {
+   secure_vector<uint8_t> output = m_hash->final();
+   if(m_out_len)
+      send(output, std::min<size_t>(m_out_len, output.size()));
+   else
+      send(output);
+   }
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+
+MAC_Filter::MAC_Filter(const std::string& mac_name, size_t len) :
+   m_mac(MessageAuthenticationCode::create_or_throw(mac_name)),
+   m_out_len(len)
+   {
+   }
+
+MAC_Filter::MAC_Filter(const std::string& mac_name, const SymmetricKey& key, size_t len) :
+   MAC_Filter(mac_name, len)
+   {
+   m_mac->set_key(key);
+   }
+
+void MAC_Filter::end_msg()
+   {
+   secure_vector<uint8_t> output = m_mac->final();
+   if(m_out_len)
+      send(output, std::min<size_t>(m_out_len, output.size()));
+   else
+      send(output);
+   }
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/filters/b64_filt.cpp b/third_party/botan/src/lib/filters/b64_filt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/b64_filt.cpp
@@ -0,0 +1,182 @@
+/*
+* Base64 Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/base64.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Base64_Encoder Constructor
+*/
+Base64_Encoder::Base64_Encoder(bool breaks, size_t length, bool t_n) :
+   m_line_length(breaks ? length : 0),
+   m_trailing_newline(t_n && breaks),
+   m_in(48),
+   m_out(64),
+   m_position(0),
+   m_out_position(0)
+   {
+   }
+
+/*
+* Encode and send a block
+*/
+void Base64_Encoder::encode_and_send(const uint8_t input[], size_t length,
+                                     bool final_inputs)
+   {
+   while(length)
+      {
+      const size_t proc = std::min(length, m_in.size());
+
+      size_t consumed = 0;
+      size_t produced = base64_encode(cast_uint8_ptr_to_char(m_out.data()),
+                                      input, proc, consumed, final_inputs);
+
+      do_output(m_out.data(), produced);
+
+      // FIXME: s/proc/consumed/?
+      input += proc;
+      length -= proc;
+      }
+   }
+
+/*
+* Handle the output
+*/
+void Base64_Encoder::do_output(const uint8_t input[], size_t length)
+   {
+   if(m_line_length == 0)
+      send(input, length);
+   else
+      {
+      size_t remaining = length, offset = 0;
+      while(remaining)
+         {
+         size_t sent = std::min(m_line_length - m_out_position, remaining);
+         send(input + offset, sent);
+         m_out_position += sent;
+         remaining -= sent;
+         offset += sent;
+         if(m_out_position == m_line_length)
+            {
+            send('\n');
+            m_out_position = 0;
+            }
+         }
+      }
+   }
+
+/*
+* Convert some data into Base64
+*/
+void Base64_Encoder::write(const uint8_t input[], size_t length)
+   {
+   buffer_insert(m_in, m_position, input, length);
+   if(m_position + length >= m_in.size())
+      {
+      encode_and_send(m_in.data(), m_in.size());
+      input += (m_in.size() - m_position);
+      length -= (m_in.size() - m_position);
+      while(length >= m_in.size())
+         {
+         encode_and_send(input, m_in.size());
+         input += m_in.size();
+         length -= m_in.size();
+         }
+      copy_mem(m_in.data(), input, length);
+      m_position = 0;
+      }
+   m_position += length;
+   }
+
+/*
+* Flush buffers
+*/
+void Base64_Encoder::end_msg()
+   {
+   encode_and_send(m_in.data(), m_position, true);
+
+   if(m_trailing_newline || (m_out_position && m_line_length))
+      send('\n');
+
+   m_out_position = m_position = 0;
+   }
+
+/*
+* Base64_Decoder Constructor
+*/
+Base64_Decoder::Base64_Decoder(Decoder_Checking c) :
+   m_checking(c), m_in(64), m_out(48), m_position(0)
+   {
+   }
+
+/*
+* Convert some data from Base64
+*/
+void Base64_Decoder::write(const uint8_t input[], size_t length)
+   {
+   while(length)
+      {
+      size_t to_copy = std::min<size_t>(length, m_in.size() - m_position);
+      if(to_copy == 0)
+         {
+         m_in.resize(m_in.size()*2);
+         m_out.resize(m_out.size()*2);
+         }
+      copy_mem(&m_in[m_position], input, to_copy);
+      m_position += to_copy;
+
+      size_t consumed = 0;
+      size_t written = base64_decode(m_out.data(),
+                                     cast_uint8_ptr_to_char(m_in.data()),
+                                     m_position,
+                                     consumed,
+                                     false,
+                                     m_checking != FULL_CHECK);
+
+      send(m_out, written);
+
+      if(consumed != m_position)
+         {
+         copy_mem(m_in.data(), m_in.data() + consumed, m_position - consumed);
+         m_position = m_position - consumed;
+         }
+      else
+         m_position = 0;
+
+      length -= to_copy;
+      input += to_copy;
+      }
+   }
+
+/*
+* Flush buffers
+*/
+void Base64_Decoder::end_msg()
+   {
+   size_t consumed = 0;
+   size_t written = base64_decode(m_out.data(),
+                                  cast_uint8_ptr_to_char(m_in.data()),
+                                  m_position,
+                                  consumed,
+                                  true,
+                                  m_checking != FULL_CHECK);
+
+   send(m_out, written);
+
+   const bool not_full_bytes = consumed != m_position;
+
+   m_position = 0;
+
+   if(not_full_bytes)
+      throw Invalid_Argument("Base64_Decoder: Input not full bytes");
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/b64_filt.h b/third_party/botan/src/lib/filters/b64_filt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/b64_filt.h
@@ -0,0 +1,14 @@
+/*
+* Base64 Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE64_FILTER_H_
+#define BOTAN_BASE64_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(b64_filt.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/basefilt.cpp b/third_party/botan/src/lib/filters/basefilt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/basefilt.cpp
@@ -0,0 +1,52 @@
+/*
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+
+namespace Botan {
+
+/*
+* Chain Constructor
+*/
+Chain::Chain(Filter* f1, Filter* f2, Filter* f3, Filter* f4)
+   {
+   if(f1) { attach(f1); incr_owns(); }
+   if(f2) { attach(f2); incr_owns(); }
+   if(f3) { attach(f3); incr_owns(); }
+   if(f4) { attach(f4); incr_owns(); }
+   }
+
+/*
+* Chain Constructor
+*/
+Chain::Chain(Filter* filters[], size_t count)
+   {
+   for(size_t j = 0; j != count; ++j)
+      if(filters[j])
+         {
+         attach(filters[j]);
+         incr_owns();
+         }
+   }
+
+/*
+* Fork Constructor
+*/
+Fork::Fork(Filter* f1, Filter* f2, Filter* f3, Filter* f4)
+   {
+   Filter* filters[4] = { f1, f2, f3, f4 };
+   set_next(filters, 4);
+   }
+
+/*
+* Fork Constructor
+*/
+Fork::Fork(Filter* filters[], size_t count)
+   {
+   set_next(filters, count);
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/basefilt.h b/third_party/botan/src/lib/filters/basefilt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/basefilt.h
@@ -0,0 +1,18 @@
+/*
+* Basic Filters
+* (C) 1999-2007 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASEFILT_H_
+#define BOTAN_BASEFILT_H_
+
+// This header is deprecated and will be removed in a future major release
+
+#include <botan/filters.h>
+
+BOTAN_DEPRECATED_HEADER(basefilt.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/buf_filt.cpp b/third_party/botan/src/lib/filters/buf_filt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/buf_filt.cpp
@@ -0,0 +1,103 @@
+/*
+* Buffered Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/mem_ops.h>
+#include <botan/internal/rounding.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Buffered_Filter Constructor
+*/
+Buffered_Filter::Buffered_Filter(size_t b, size_t f) :
+   m_main_block_mod(b), m_final_minimum(f)
+   {
+   if(m_main_block_mod == 0)
+      throw Invalid_Argument("m_main_block_mod == 0");
+
+   if(m_final_minimum > m_main_block_mod)
+      throw Invalid_Argument("m_final_minimum > m_main_block_mod");
+
+   m_buffer.resize(2 * m_main_block_mod);
+   m_buffer_pos = 0;
+   }
+
+/*
+* Buffer input into blocks, trying to minimize copying
+*/
+void Buffered_Filter::write(const uint8_t input[], size_t input_size)
+   {
+   if(!input_size)
+      return;
+
+   if(m_buffer_pos + input_size >= m_main_block_mod + m_final_minimum)
+      {
+      size_t to_copy = std::min<size_t>(m_buffer.size() - m_buffer_pos, input_size);
+
+      copy_mem(&m_buffer[m_buffer_pos], input, to_copy);
+      m_buffer_pos += to_copy;
+
+      input += to_copy;
+      input_size -= to_copy;
+
+      size_t total_to_consume =
+         round_down(std::min(m_buffer_pos,
+                             m_buffer_pos + input_size - m_final_minimum),
+                    m_main_block_mod);
+
+      buffered_block(m_buffer.data(), total_to_consume);
+
+      m_buffer_pos -= total_to_consume;
+
+      copy_mem(m_buffer.data(), m_buffer.data() + total_to_consume, m_buffer_pos);
+      }
+
+   if(input_size >= m_final_minimum)
+      {
+      size_t full_blocks = (input_size - m_final_minimum) / m_main_block_mod;
+      size_t to_copy = full_blocks * m_main_block_mod;
+
+      if(to_copy)
+         {
+         buffered_block(input, to_copy);
+
+         input += to_copy;
+         input_size -= to_copy;
+         }
+      }
+
+   copy_mem(&m_buffer[m_buffer_pos], input, input_size);
+   m_buffer_pos += input_size;
+   }
+
+/*
+* Finish/flush operation
+*/
+void Buffered_Filter::end_msg()
+   {
+   if(m_buffer_pos < m_final_minimum)
+      throw Invalid_State("Buffered filter end_msg without enough input");
+
+   size_t spare_blocks = (m_buffer_pos - m_final_minimum) / m_main_block_mod;
+
+   if(spare_blocks)
+      {
+      size_t spare_bytes = m_main_block_mod * spare_blocks;
+      buffered_block(m_buffer.data(), spare_bytes);
+      buffered_final(&m_buffer[spare_bytes], m_buffer_pos - spare_bytes);
+      }
+   else
+      {
+      buffered_final(m_buffer.data(), m_buffer_pos);
+      }
+
+   m_buffer_pos = 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/buf_filt.h b/third_party/botan/src/lib/filters/buf_filt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/buf_filt.h
@@ -0,0 +1,14 @@
+/*
+* Buffered Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BUFFERED_FILTER_H_
+#define BOTAN_BUFFERED_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(buf_filt.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/cipher_filter.cpp b/third_party/botan/src/lib/filters/cipher_filter.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/cipher_filter.cpp
@@ -0,0 +1,103 @@
+/*
+* Filter interface for Cipher_Modes
+* (C) 2013,2014,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+namespace {
+
+size_t choose_update_size(size_t update_granularity)
+   {
+   const size_t target_size = 1024;
+
+   if(update_granularity >= target_size)
+      return update_granularity;
+
+   return round_up(target_size, update_granularity);
+   }
+
+}
+
+Cipher_Mode_Filter::Cipher_Mode_Filter(Cipher_Mode* mode) :
+   Buffered_Filter(choose_update_size(mode->update_granularity()),
+                   mode->minimum_final_size()),
+   m_mode(mode),
+   m_nonce(mode->default_nonce_length()),
+   m_buffer(m_mode->update_granularity())
+   {
+   }
+
+std::string Cipher_Mode_Filter::name() const
+   {
+   return m_mode->name();
+   }
+
+void Cipher_Mode_Filter::set_iv(const InitializationVector& iv)
+   {
+   m_nonce = unlock(iv.bits_of());
+   }
+
+void Cipher_Mode_Filter::set_key(const SymmetricKey& key)
+   {
+   m_mode->set_key(key);
+   }
+
+Key_Length_Specification Cipher_Mode_Filter::key_spec() const
+   {
+   return m_mode->key_spec();
+   }
+
+bool Cipher_Mode_Filter::valid_iv_length(size_t length) const
+   {
+   return m_mode->valid_nonce_length(length);
+   }
+
+void Cipher_Mode_Filter::write(const uint8_t input[], size_t input_length)
+   {
+   Buffered_Filter::write(input, input_length);
+   }
+
+void Cipher_Mode_Filter::end_msg()
+   {
+   Buffered_Filter::end_msg();
+   }
+
+void Cipher_Mode_Filter::start_msg()
+   {
+   if(m_nonce.empty() && !m_mode->valid_nonce_length(0))
+      throw Invalid_State("Cipher " + m_mode->name() + " requires a fresh nonce for each message");
+
+   m_mode->start(m_nonce);
+   m_nonce.clear();
+   }
+
+void Cipher_Mode_Filter::buffered_block(const uint8_t input[], size_t input_length)
+   {
+   while(input_length)
+      {
+      const size_t take = std::min(m_mode->update_granularity(), input_length);
+
+      m_buffer.assign(input, input + take);
+      m_mode->update(m_buffer);
+
+      send(m_buffer);
+
+      input += take;
+      input_length -= take;
+      }
+   }
+
+void Cipher_Mode_Filter::buffered_final(const uint8_t input[], size_t input_length)
+   {
+   secure_vector<uint8_t> buf(input, input + input_length);
+   m_mode->finish(buf);
+   send(buf);
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/cipher_filter.h b/third_party/botan/src/lib/filters/cipher_filter.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/cipher_filter.h
@@ -0,0 +1,14 @@
+/*
+* Filter interface for ciphers
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CIPHER_FILTER_H_
+#define BOTAN_CIPHER_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(cipher_filter.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/comp_filter.cpp b/third_party/botan/src/lib/filters/comp_filter.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/comp_filter.cpp
@@ -0,0 +1,122 @@
+/*
+* Filter interface for compression
+* (C) 2014,2015,2016 Jack Lloyd
+* (C) 2015 Matej Kenda
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_COMPRESSION)
+  #include <botan/compression.h>
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+Compression_Filter::Compression_Filter(const std::string& type, size_t level, size_t bs) :
+   m_comp(make_compressor(type)),
+   m_buffersize(std::max<size_t>(bs, 256)),
+   m_level(level)
+   {
+   if(!m_comp)
+      {
+      throw Invalid_Argument("Compression type '" + type + "' not found");
+      }
+   }
+
+Compression_Filter::~Compression_Filter() { /* for unique_ptr */ }
+
+std::string Compression_Filter::name() const
+   {
+   return m_comp->name();
+   }
+
+void Compression_Filter::start_msg()
+   {
+   m_comp->start(m_level);
+   }
+
+void Compression_Filter::write(const uint8_t input[], size_t input_length)
+   {
+   while(input_length)
+      {
+      const size_t take = std::min(m_buffersize, input_length);
+      BOTAN_ASSERT(take > 0, "Consumed something");
+
+      m_buffer.assign(input, input + take);
+      m_comp->update(m_buffer);
+
+      send(m_buffer);
+
+      input += take;
+      input_length -= take;
+      }
+   }
+
+void Compression_Filter::flush()
+   {
+   m_buffer.clear();
+   m_comp->update(m_buffer, 0, true);
+   send(m_buffer);
+   }
+
+void Compression_Filter::end_msg()
+   {
+   m_buffer.clear();
+   m_comp->finish(m_buffer);
+   send(m_buffer);
+   }
+
+Decompression_Filter::Decompression_Filter(const std::string& type, size_t bs) :
+   m_comp(make_decompressor(type)),
+   m_buffersize(std::max<size_t>(bs, 256))
+   {
+   if(!m_comp)
+      {
+      throw Invalid_Argument("Compression type '" + type + "' not found");
+      }
+   }
+
+Decompression_Filter::~Decompression_Filter() { /* for unique_ptr */ }
+
+std::string Decompression_Filter::name() const
+   {
+   return m_comp->name();
+   }
+
+void Decompression_Filter::start_msg()
+   {
+   m_comp->start();
+   }
+
+void Decompression_Filter::write(const uint8_t input[], size_t input_length)
+   {
+   while(input_length)
+      {
+      const size_t take = std::min(m_buffersize, input_length);
+      BOTAN_ASSERT(take > 0, "Consumed something");
+
+      m_buffer.assign(input, input + take);
+      m_comp->update(m_buffer);
+
+      send(m_buffer);
+
+      input += take;
+      input_length -= take;
+      }
+   }
+
+void Decompression_Filter::end_msg()
+   {
+   m_buffer.clear();
+   m_comp->finish(m_buffer);
+   send(m_buffer);
+   }
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/filters/comp_filter.h b/third_party/botan/src/lib/filters/comp_filter.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/comp_filter.h
@@ -0,0 +1,15 @@
+/*
+* Filter interface for compression
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_FILTER_H_
+#define BOTAN_COMPRESSION_FILTER_H_
+
+BOTAN_DEPRECATED_HEADER(comp_filter.h)
+
+#include <botan/filters.h>
+
+#endif
diff --git a/third_party/botan/src/lib/filters/data_snk.cpp b/third_party/botan/src/lib/filters/data_snk.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/data_snk.cpp
@@ -0,0 +1,75 @@
+/*
+* DataSink
+* (C) 1999-2007 Jack Lloyd
+*     2005 Matthew Gregan
+*     2017 Philippe Lieser
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/data_snk.h>
+#include <botan/exceptn.h>
+#include <ostream>
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+  #include <fstream>
+#endif
+
+namespace Botan {
+
+/*
+* Write to a stream
+*/
+void DataSink_Stream::write(const uint8_t out[], size_t length)
+   {
+   m_sink.write(cast_uint8_ptr_to_char(out), length);
+   if(!m_sink.good())
+      throw Stream_IO_Error("DataSink_Stream: Failure writing to " +
+                            m_identifier);
+   }
+
+/*
+* Flush the stream
+*/
+void DataSink_Stream::end_msg()
+   {
+   m_sink.flush();
+   }
+
+/*
+* DataSink_Stream Constructor
+*/
+DataSink_Stream::DataSink_Stream(std::ostream& out,
+                                 const std::string& name) :
+   m_identifier(name),
+   m_sink(out)
+   {
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* DataSink_Stream Constructor
+*/
+DataSink_Stream::DataSink_Stream(const std::string& path,
+                                 bool use_binary) :
+   m_identifier(path),
+   m_sink_memory(new std::ofstream(path, use_binary ? std::ios::binary : std::ios::out)),
+   m_sink(*m_sink_memory)
+   {
+   if(!m_sink.good())
+      {
+      throw Stream_IO_Error("DataSink_Stream: Failure opening " + path);
+      }
+   }
+#endif
+
+/*
+* DataSink_Stream Destructor
+*/
+DataSink_Stream::~DataSink_Stream()
+   {
+   // for ~unique_ptr
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/data_snk.h b/third_party/botan/src/lib/filters/data_snk.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/data_snk.h
@@ -0,0 +1,76 @@
+/*
+* DataSink
+* (C) 1999-2007 Jack Lloyd
+*     2017 Philippe Lieser
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_SINK_H_
+#define BOTAN_DATA_SINK_H_
+
+#include <botan/filter.h>
+#include <memory>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* This class represents abstract data sink objects.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSink : public Filter
+   {
+   public:
+      bool attachable() override { return false; }
+      DataSink() = default;
+      virtual ~DataSink() = default;
+
+      DataSink& operator=(const DataSink&) = delete;
+      DataSink(const DataSink&) = delete;
+   };
+
+/**
+* This class represents a data sink which writes its output to a stream.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSink_Stream final : public DataSink
+   {
+   public:
+      /**
+      * Construct a DataSink_Stream from a stream.
+      * @param stream the stream to write to
+      * @param name identifier
+      */
+      DataSink_Stream(std::ostream& stream,
+                      const std::string& name = "<std::ostream>");
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+      /**
+      * Construct a DataSink_Stream from a filesystem path name.
+      * @param pathname the name of the file to open a stream to
+      * @param use_binary indicates whether to treat the file
+      * as a binary file or not
+      */
+      DataSink_Stream(const std::string& pathname,
+                      bool use_binary = false);
+#endif
+
+      std::string name() const override { return m_identifier; }
+
+      void write(const uint8_t[], size_t) override;
+
+      void end_msg() override;
+
+      ~DataSink_Stream();
+
+   private:
+      const std::string m_identifier;
+
+      // May be null, if m_sink was an external reference
+      std::unique_ptr<std::ostream> m_sink_memory;
+      std::ostream& m_sink;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp b/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp
@@ -0,0 +1,55 @@
+/*
+* Pipe I/O for Unix
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/exceptn.h>
+#include <unistd.h>
+
+namespace Botan {
+
+/*
+* Write data from a pipe into a Unix fd
+*/
+int operator<<(int fd, Pipe& pipe)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(pipe.remaining())
+      {
+      size_t got = pipe.read(buffer.data(), buffer.size());
+      size_t position = 0;
+      while(got)
+         {
+         ssize_t ret = ::write(fd, &buffer[position], got);
+         if(ret < 0)
+            throw Stream_IO_Error("Pipe output operator (unixfd) has failed");
+
+         position += static_cast<size_t>(ret);
+         got -= static_cast<size_t>(ret);
+         }
+      }
+   return fd;
+   }
+
+/*
+* Read data from a Unix fd into a pipe
+*/
+int operator>>(int fd, Pipe& pipe)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(true)
+      {
+      ssize_t ret = ::read(fd, buffer.data(), buffer.size());
+      if(ret < 0)
+         throw Stream_IO_Error("Pipe input operator (unixfd) has failed");
+      else if(ret == 0)
+         break;
+      pipe.write(buffer.data(), static_cast<size_t>(ret));
+      }
+   return fd;
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/fd_unix/fd_unix.h b/third_party/botan/src/lib/filters/fd_unix/fd_unix.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/fd_unix/fd_unix.h
@@ -0,0 +1,35 @@
+/*
+* Pipe I/O for Unix
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PIPE_UNIXFD_H_
+#define BOTAN_PIPE_UNIXFD_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class Pipe;
+
+/**
+* Stream output operator; dumps the results from pipe's default
+* message to the output stream.
+* @param out file descriptor for an open output stream
+* @param pipe the pipe
+*/
+int BOTAN_PUBLIC_API(2,0) operator<<(int out, Pipe& pipe);
+
+/**
+* File descriptor input operator; dumps the remaining bytes of input
+* to the (assumed open) pipe message.
+* @param in file descriptor for an open input stream
+* @param pipe the pipe
+*/
+int BOTAN_PUBLIC_API(2,0) operator>>(int in, Pipe& pipe);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/filter.cpp b/third_party/botan/src/lib/filters/filter.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/filter.cpp
@@ -0,0 +1,129 @@
+/*
+* Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filter.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Filter Constructor
+*/
+Filter::Filter()
+   {
+   m_next.resize(1);
+   m_port_num = 0;
+   m_filter_owns = 0;
+   m_owned = false;
+   }
+
+/*
+* Send data to all ports
+*/
+void Filter::send(const uint8_t input[], size_t length)
+   {
+   if(!length)
+      return;
+
+   bool nothing_attached = true;
+   for(size_t j = 0; j != total_ports(); ++j)
+      if(m_next[j])
+         {
+         if(m_write_queue.size())
+            m_next[j]->write(m_write_queue.data(), m_write_queue.size());
+         m_next[j]->write(input, length);
+         nothing_attached = false;
+         }
+
+   if(nothing_attached)
+      m_write_queue += std::make_pair(input, length);
+   else
+      m_write_queue.clear();
+   }
+
+/*
+* Start a new message
+*/
+void Filter::new_msg()
+   {
+   start_msg();
+   for(size_t j = 0; j != total_ports(); ++j)
+      if(m_next[j])
+         m_next[j]->new_msg();
+   }
+
+/*
+* End the current message
+*/
+void Filter::finish_msg()
+   {
+   end_msg();
+   for(size_t j = 0; j != total_ports(); ++j)
+      if(m_next[j])
+         m_next[j]->finish_msg();
+   }
+
+/*
+* Attach a filter to the current port
+*/
+void Filter::attach(Filter* new_filter)
+   {
+   if(new_filter)
+      {
+      Filter* last = this;
+      while(last->get_next())
+         last = last->get_next();
+      last->m_next[last->current_port()] = new_filter;
+      }
+   }
+
+/*
+* Set the active port on a filter
+*/
+void Filter::set_port(size_t new_port)
+   {
+   if(new_port >= total_ports())
+      throw Invalid_Argument("Filter: Invalid port number");
+   m_port_num = new_port;
+   }
+
+/*
+* Return the next Filter in the logical chain
+*/
+Filter* Filter::get_next() const
+   {
+   if(m_port_num < m_next.size())
+      return m_next[m_port_num];
+   return nullptr;
+   }
+
+/*
+* Set the next Filters
+*/
+void Filter::set_next(Filter* filters[], size_t size)
+   {
+   m_next.clear();
+
+   m_port_num = 0;
+   m_filter_owns = 0;
+
+   while(size && filters && (filters[size-1] == nullptr))
+      --size;
+
+   if(filters && size)
+      m_next.assign(filters, filters + size);
+   }
+
+/*
+* Return the total number of ports
+*/
+size_t Filter::total_ports() const
+   {
+   return m_next.size();
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/filter.h b/third_party/botan/src/lib/filters/filter.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/filter.h
@@ -0,0 +1,175 @@
+/*
+* Filter
+* (C) 1999-2007 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FILTER_H_
+#define BOTAN_FILTER_H_
+
+#include <botan/secmem.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* This class represents general abstract filter objects.
+*/
+class BOTAN_PUBLIC_API(2,0) Filter
+   {
+   public:
+      /**
+      * @return descriptive name for this filter
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Write a portion of a message to this filter.
+      * @param input the input as a byte array
+      * @param length the length of the byte array input
+      */
+      virtual void write(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * Start a new message. Must be closed by end_msg() before another
+      * message can be started.
+      */
+      virtual void start_msg() { /* default empty */ }
+
+      /**
+      * Notify that the current message is finished; flush buffers and
+      * do end-of-message processing (if any).
+      */
+      virtual void end_msg() { /* default empty */ }
+
+      /**
+      * Check whether this filter is an attachable filter.
+      * @return true if this filter is attachable, false otherwise
+      */
+      virtual bool attachable() { return true; }
+
+      virtual ~Filter() = default;
+   protected:
+      /**
+      * @param in some input for the filter
+      * @param length the length of in
+      */
+      virtual void send(const uint8_t in[], size_t length);
+
+      /**
+      * @param in some input for the filter
+      */
+      void send(uint8_t in) { send(&in, 1); }
+
+      /**
+      * @param in some input for the filter
+      */
+      template<typename Alloc>
+      void send(const std::vector<uint8_t, Alloc>& in)
+         {
+         send(in.data(), in.size());
+         }
+
+      /**
+      * @param in some input for the filter
+      * @param length the number of bytes of in to send
+      */
+      template<typename Alloc>
+      void send(const std::vector<uint8_t, Alloc>& in, size_t length)
+         {
+         BOTAN_ASSERT_NOMSG(length <= in.size());
+         send(in.data(), length);
+         }
+
+      Filter();
+
+      Filter(const Filter&) = delete;
+
+      Filter& operator=(const Filter&) = delete;
+
+   private:
+      /**
+      * Start a new message in *this and all following filters. Only for
+      * internal use, not intended for use in client applications.
+      */
+      void new_msg();
+
+      /**
+      * End a new message in *this and all following filters. Only for
+      * internal use, not intended for use in client applications.
+      */
+      void finish_msg();
+
+      friend class Pipe;
+      friend class Fanout_Filter;
+
+      size_t total_ports() const;
+      size_t current_port() const { return m_port_num; }
+
+      /**
+      * Set the active port
+      * @param new_port the new value
+      */
+      void set_port(size_t new_port);
+
+      size_t owns() const { return m_filter_owns; }
+
+      /**
+      * Attach another filter to this one
+      * @param f filter to attach
+      */
+      void attach(Filter* f);
+
+      /**
+      * @param filters the filters to set
+      * @param count number of items in filters
+      */
+      void set_next(Filter* filters[], size_t count);
+      Filter* get_next() const;
+
+      secure_vector<uint8_t> m_write_queue;
+      std::vector<Filter*> m_next; // not owned
+      size_t m_port_num, m_filter_owns;
+
+      // true if filter belongs to a pipe --> prohibit filter sharing!
+      bool m_owned;
+   };
+
+/**
+* This is the abstract Fanout_Filter base class.
+**/
+class BOTAN_PUBLIC_API(2,0) Fanout_Filter : public Filter
+   {
+   protected:
+      /**
+      * Increment the number of filters past us that we own
+      */
+      void incr_owns() { ++m_filter_owns; }
+
+      void set_port(size_t n) { Filter::set_port(n); }
+
+      void set_next(Filter* f[], size_t n) { Filter::set_next(f, n); }
+
+      void attach(Filter* f) { Filter::attach(f); }
+
+   private:
+      friend class Threaded_Fork;
+      using Filter::m_write_queue;
+      using Filter::total_ports;
+      using Filter::m_next;
+   };
+
+/**
+* The type of checking to be performed by decoders:
+* NONE - no checks, IGNORE_WS - perform checks, but ignore
+* whitespaces, FULL_CHECK - perform checks, also complain
+* about white spaces.
+*/
+enum Decoder_Checking { NONE, IGNORE_WS, FULL_CHECK };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/filters.h b/third_party/botan/src/lib/filters/filters.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/filters.h
@@ -0,0 +1,741 @@
+/*
+* Common Filters
+* (C) 1999-2007,2015 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FILTERS_H_
+#define BOTAN_FILTERS_H_
+
+#include <botan/secmem.h>
+#include <botan/data_snk.h>
+#include <botan/pipe.h>
+#include <botan/symkey.h>
+#include <botan/cipher_mode.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+  #include <thread>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+   #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+   #include <botan/mac.h>
+#endif
+
+namespace Botan {
+
+/**
+* Filter mixin that breaks input into blocks, useful for
+* cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Buffered_Filter
+   {
+   public:
+      /**
+      * Write bytes into the buffered filter, which will them emit them
+      * in calls to buffered_block in the subclass
+      * @param in the input bytes
+      * @param length of in in bytes
+      */
+      void write(const uint8_t in[], size_t length);
+
+      template<typename Alloc>
+         void write(const std::vector<uint8_t, Alloc>& in, size_t length)
+         {
+         write(in.data(), length);
+         }
+
+      /**
+      * Finish a message, emitting to buffered_block and buffered_final
+      * Will throw an exception if less than final_minimum bytes were
+      * written into the filter.
+      */
+      void end_msg();
+
+      /**
+      * Initialize a Buffered_Filter
+      * @param block_size the function buffered_block will be called
+      *        with inputs which are a multiple of this size
+      * @param final_minimum the function buffered_final will be called
+      *        with at least this many bytes.
+      */
+      Buffered_Filter(size_t block_size, size_t final_minimum);
+
+      virtual ~Buffered_Filter() = default;
+   protected:
+      /**
+      * The block processor, implemented by subclasses
+      * @param input some input bytes
+      * @param length the size of input, guaranteed to be a multiple
+      *        of block_size
+      */
+      virtual void buffered_block(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * The final block, implemented by subclasses
+      * @param input some input bytes
+      * @param length the size of input, guaranteed to be at least
+      *        final_minimum bytes
+      */
+      virtual void buffered_final(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * @return block size of inputs
+      */
+      size_t buffered_block_size() const { return m_main_block_mod; }
+
+      /**
+      * @return current position in the buffer
+      */
+      size_t current_position() const { return m_buffer_pos; }
+
+      /**
+      * Reset the buffer position
+      */
+      void buffer_reset() { m_buffer_pos = 0; }
+   private:
+      size_t m_main_block_mod, m_final_minimum;
+
+      secure_vector<uint8_t> m_buffer;
+      size_t m_buffer_pos;
+   };
+
+/**
+* This class represents keyed filters, i.e. filters that have to be
+* fed with a key in order to function.
+*/
+class BOTAN_PUBLIC_API(2,0) Keyed_Filter : public Filter
+   {
+   public:
+      /**
+      * Set the key of this filter
+      * @param key the key to use
+      */
+      virtual void set_key(const SymmetricKey& key) = 0;
+
+      /**
+      * Set the initialization vector of this filter. Note: you should
+      * call set_iv() only after you have called set_key()
+      * @param iv the initialization vector to use
+      */
+      virtual void set_iv(const InitializationVector& iv)
+         {
+         if(iv.length() != 0)
+            throw Invalid_IV_Length(name(), iv.length());
+         }
+
+      /**
+      * Check whether a key length is valid for this filter
+      * @param length the key length to be checked for validity
+      * @return true if the key length is valid, false otherwise
+      */
+      bool valid_keylength(size_t length) const
+         {
+         return key_spec().valid_keylength(length);
+         }
+
+      /**
+      * @return object describing limits on key size
+      */
+      virtual Key_Length_Specification key_spec() const = 0;
+
+      /**
+      * Check whether an IV length is valid for this filter
+      * @param length the IV length to be checked for validity
+      * @return true if the IV length is valid, false otherwise
+      */
+      virtual bool valid_iv_length(size_t length) const
+         { return (length == 0); }
+   };
+
+/**
+* Filter interface for cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Cipher_Mode_Filter final : public Keyed_Filter,
+                                     private Buffered_Filter
+   {
+   public:
+      explicit Cipher_Mode_Filter(Cipher_Mode* t);
+
+      explicit Cipher_Mode_Filter(std::unique_ptr<Cipher_Mode> t) :
+         Cipher_Mode_Filter(t.release()) {}
+
+      void set_iv(const InitializationVector& iv) override;
+
+      void set_key(const SymmetricKey& key) override;
+
+      Key_Length_Specification key_spec() const override;
+
+      bool valid_iv_length(size_t length) const override;
+
+      std::string name() const override;
+
+   private:
+      void write(const uint8_t input[], size_t input_length) override;
+      void start_msg() override;
+      void end_msg() override;
+
+      void buffered_block(const uint8_t input[], size_t input_length) override;
+      void buffered_final(const uint8_t input[], size_t input_length) override;
+
+      std::unique_ptr<Cipher_Mode> m_mode;
+      std::vector<uint8_t> m_nonce;
+      secure_vector<uint8_t> m_buffer;
+   };
+
+// deprecated aliases, will be removed in a future major release
+typedef Cipher_Mode_Filter Transform_Filter;
+typedef Transform_Filter Transformation_Filter;
+
+/*
+* Get a cipher object
+*/
+
+/**
+* Factory method for general symmetric cipher filters. No key will be
+* set in the filter.
+*
+* @param algo_spec the name of the desired cipher
+* @param direction determines whether the filter will be an encrypting or
+* decrypting filter
+* @return pointer to the encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+                                Cipher_Dir direction)
+   {
+   std::unique_ptr<Cipher_Mode> c(Cipher_Mode::create_or_throw(algo_spec, direction));
+   return new Cipher_Mode_Filter(c.release());
+   }
+
+/**
+* Factory method for general symmetric cipher filters.
+* @param algo_spec the name of the desired cipher
+* @param key the key to be used for encryption/decryption performed by
+* the filter
+* @param direction determines whether the filter will be an encrypting
+* or decrypting filter
+* @return pointer to the encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+                                const SymmetricKey& key,
+                                Cipher_Dir direction)
+   {
+   Keyed_Filter* cipher = get_cipher(algo_spec, direction);
+   cipher->set_key(key);
+   return cipher;
+   }
+
+/**
+* Factory method for general symmetric cipher filters.
+* @param algo_spec the name of the desired cipher
+* @param key the key to be used for encryption/decryption performed by
+* the filter
+* @param iv the initialization vector to be used
+* @param direction determines whether the filter will be an encrypting
+* or decrypting filter
+* @return pointer to newly allocated encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+                                const SymmetricKey& key,
+                                const InitializationVector& iv,
+                                Cipher_Dir direction)
+   {
+   Keyed_Filter* cipher = get_cipher(algo_spec, key, direction);
+   if(iv.length())
+      cipher->set_iv(iv);
+   return cipher;
+   }
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+/**
+* Stream Cipher Filter
+*/
+class BOTAN_PUBLIC_API(2,0) StreamCipher_Filter final : public Keyed_Filter
+   {
+   public:
+
+      std::string name() const override { return m_cipher->name(); }
+
+      /**
+      * Write input data
+      * @param input data
+      * @param input_len length of input in bytes
+      */
+      void write(const uint8_t input[], size_t input_len) override;
+
+      bool valid_iv_length(size_t iv_len) const override
+         { return m_cipher->valid_iv_length(iv_len); }
+
+      /**
+      * Set the initialization vector for this filter.
+      * @param iv the initialization vector to set
+      */
+      void set_iv(const InitializationVector& iv) override
+         {
+         m_cipher->set_iv(iv.begin(), iv.length());
+         }
+
+      /**
+      * Set the key of this filter.
+      * @param key the key to set
+      */
+      void set_key(const SymmetricKey& key) override { m_cipher->set_key(key); }
+
+      Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); }
+
+      /**
+      * Construct a stream cipher filter.
+      * @param cipher a cipher object to use
+      */
+      explicit StreamCipher_Filter(StreamCipher* cipher);
+
+      /**
+      * Construct a stream cipher filter.
+      * @param cipher a cipher object to use
+      * @param key the key to use inside this filter
+      */
+      StreamCipher_Filter(StreamCipher* cipher, const SymmetricKey& key);
+
+      /**
+      * Construct a stream cipher filter.
+      * @param cipher the name of the desired cipher
+      */
+      explicit StreamCipher_Filter(const std::string& cipher);
+
+      /**
+      * Construct a stream cipher filter.
+      * @param cipher the name of the desired cipher
+      * @param key the key to use inside this filter
+      */
+      StreamCipher_Filter(const std::string& cipher, const SymmetricKey& key);
+   private:
+      secure_vector<uint8_t> m_buffer;
+      std::unique_ptr<StreamCipher> m_cipher;
+   };
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+/**
+* Hash Filter.
+*/
+class BOTAN_PUBLIC_API(2,0) Hash_Filter final : public Filter
+   {
+   public:
+      void write(const uint8_t input[], size_t len) override { m_hash->update(input, len); }
+      void end_msg() override;
+
+      std::string name() const override { return m_hash->name(); }
+
+      /**
+      * Construct a hash filter.
+      * @param hash the hash function to use
+      * @param len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the hashfunction
+      * hash. Otherwise, specify a smaller value here so that the
+      * output of the hash algorithm will be cut off.
+      */
+      Hash_Filter(HashFunction* hash, size_t len = 0) :
+         m_hash(hash), m_out_len(len) {}
+
+      /**
+      * Construct a hash filter.
+      * @param request the name of the hash algorithm to use
+      * @param len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the hashfunction
+      * hash. Otherwise, specify a smaller value here so that the
+      * output of the hash algorithm will be cut off.
+      */
+      Hash_Filter(const std::string& request, size_t len = 0);
+
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+      const size_t m_out_len;
+   };
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+
+/**
+* MessageAuthenticationCode Filter.
+*/
+class BOTAN_PUBLIC_API(2,0) MAC_Filter final : public Keyed_Filter
+   {
+   public:
+      void write(const uint8_t input[], size_t len) override { m_mac->update(input, len); }
+      void end_msg() override;
+
+      std::string name() const override { return m_mac->name(); }
+
+      /**
+      * Set the key of this filter.
+      * @param key the key to set
+      */
+      void set_key(const SymmetricKey& key) override { m_mac->set_key(key); }
+
+      Key_Length_Specification key_spec() const override { return m_mac->key_spec(); }
+
+      /**
+      * Construct a MAC filter. The MAC key will be left empty.
+      * @param mac the MAC to use
+      * @param out_len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the
+      * MAC. Otherwise, specify a smaller value here so that the
+      * output of the MAC will be cut off.
+      */
+      MAC_Filter(MessageAuthenticationCode* mac,
+                 size_t out_len = 0) :
+         m_mac(mac),
+         m_out_len(out_len)
+         {
+         }
+
+      /**
+      * Construct a MAC filter.
+      * @param mac the MAC to use
+      * @param key the MAC key to use
+      * @param out_len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the
+      * MAC. Otherwise, specify a smaller value here so that the
+      * output of the MAC will be cut off.
+      */
+      MAC_Filter(MessageAuthenticationCode* mac,
+                 const SymmetricKey& key,
+                 size_t out_len = 0) :
+         m_mac(mac),
+         m_out_len(out_len)
+         {
+         m_mac->set_key(key);
+         }
+
+      /**
+      * Construct a MAC filter. The MAC key will be left empty.
+      * @param mac the name of the MAC to use
+      * @param len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the
+      * MAC. Otherwise, specify a smaller value here so that the
+      * output of the MAC will be cut off.
+      */
+      MAC_Filter(const std::string& mac, size_t len = 0);
+
+      /**
+      * Construct a MAC filter.
+      * @param mac the name of the MAC to use
+      * @param key the MAC key to use
+      * @param len the output length of this filter. Leave the default
+      * value 0 if you want to use the full output of the
+      * MAC. Otherwise, specify a smaller value here so that the
+      * output of the MAC will be cut off.
+      */
+      MAC_Filter(const std::string& mac, const SymmetricKey& key,
+                 size_t len = 0);
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      const size_t m_out_len;
+   };
+#endif
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+class Compression_Algorithm;
+class Decompression_Algorithm;
+
+/**
+* Filter interface for compression
+*/
+class BOTAN_PUBLIC_API(2,0) Compression_Filter final : public Filter
+   {
+   public:
+      void start_msg() override;
+      void write(const uint8_t input[], size_t input_length) override;
+      void end_msg() override;
+
+      void flush();
+
+      std::string name() const override;
+
+      Compression_Filter(const std::string& type,
+                         size_t compression_level,
+                         size_t buffer_size = 4096);
+
+      ~Compression_Filter();
+   private:
+      std::unique_ptr<Compression_Algorithm> m_comp;
+      size_t m_buffersize, m_level;
+      secure_vector<uint8_t> m_buffer;
+   };
+
+/**
+* Filter interface for decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Decompression_Filter final : public Filter
+   {
+   public:
+      void start_msg() override;
+      void write(const uint8_t input[], size_t input_length) override;
+      void end_msg() override;
+
+      std::string name() const override;
+
+      Decompression_Filter(const std::string& type,
+                           size_t buffer_size = 4096);
+
+      ~Decompression_Filter();
+   private:
+      std::unique_ptr<Decompression_Algorithm> m_comp;
+      std::size_t m_buffersize;
+      secure_vector<uint8_t> m_buffer;
+   };
+
+#endif
+
+/**
+* This class represents a Base64 encoder.
+*/
+class BOTAN_PUBLIC_API(2,0) Base64_Encoder final : public Filter
+   {
+   public:
+      std::string name() const override { return "Base64_Encoder"; }
+
+      /**
+      * Input a part of a message to the encoder.
+      * @param input the message to input as a byte array
+      * @param length the length of the byte array input
+      */
+      void write(const uint8_t input[], size_t length) override;
+
+      /**
+      * Inform the Encoder that the current message shall be closed.
+      */
+      void end_msg() override;
+
+      /**
+      * Create a base64 encoder.
+      * @param breaks whether to use line breaks in the output
+      * @param length the length of the lines of the output
+      * @param t_n whether to use a trailing newline
+      */
+      Base64_Encoder(bool breaks = false, size_t length = 72,
+                     bool t_n = false);
+   private:
+      void encode_and_send(const uint8_t input[], size_t length,
+                           bool final_inputs = false);
+      void do_output(const uint8_t output[], size_t length);
+
+      const size_t m_line_length;
+      const bool m_trailing_newline;
+      std::vector<uint8_t> m_in, m_out;
+      size_t m_position, m_out_position;
+   };
+
+/**
+* This object represents a Base64 decoder.
+*/
+class BOTAN_PUBLIC_API(2,0) Base64_Decoder final : public Filter
+   {
+   public:
+      std::string name() const override { return "Base64_Decoder"; }
+
+      /**
+      * Input a part of a message to the decoder.
+      * @param input the message to input as a byte array
+      * @param length the length of the byte array input
+      */
+      void write(const uint8_t input[], size_t length) override;
+
+      /**
+      * Finish up the current message
+      */
+      void end_msg() override;
+
+      /**
+      * Create a base64 decoder.
+      * @param checking the type of checking that shall be performed by
+      * the decoder
+      */
+      explicit Base64_Decoder(Decoder_Checking checking = NONE);
+   private:
+      const Decoder_Checking m_checking;
+      std::vector<uint8_t> m_in, m_out;
+      size_t m_position;
+   };
+
+/**
+* Converts arbitrary binary data to hex strings, optionally with
+* newlines inserted
+*/
+class BOTAN_PUBLIC_API(2,0) Hex_Encoder final : public Filter
+   {
+   public:
+      /**
+      * Whether to use uppercase or lowercase letters for the encoded string.
+      */
+      enum Case { Uppercase, Lowercase };
+
+      std::string name() const override { return "Hex_Encoder"; }
+
+      void write(const uint8_t in[], size_t length) override;
+      void end_msg() override;
+
+      /**
+      * Create a hex encoder.
+      * @param the_case the case to use in the encoded strings.
+      */
+      explicit Hex_Encoder(Case the_case);
+
+      /**
+      * Create a hex encoder.
+      * @param newlines should newlines be used
+      * @param line_length if newlines are used, how long are lines
+      * @param the_case the case to use in the encoded strings
+      */
+      Hex_Encoder(bool newlines = false,
+                  size_t line_length = 72,
+                  Case the_case = Uppercase);
+   private:
+      void encode_and_send(const uint8_t[], size_t);
+
+      const Case m_casing;
+      const size_t m_line_length;
+      std::vector<uint8_t> m_in, m_out;
+      size_t m_position, m_counter;
+   };
+
+/**
+* Converts hex strings to bytes
+*/
+class BOTAN_PUBLIC_API(2,0) Hex_Decoder final : public Filter
+   {
+   public:
+      std::string name() const override { return "Hex_Decoder"; }
+
+      void write(const uint8_t[], size_t) override;
+      void end_msg() override;
+
+      /**
+      * Construct a Hex Decoder using the specified
+      * character checking.
+      * @param checking the checking to use during decoding.
+      */
+      explicit Hex_Decoder(Decoder_Checking checking = NONE);
+   private:
+      const Decoder_Checking m_checking;
+      std::vector<uint8_t> m_in, m_out;
+      size_t m_position;
+   };
+
+/**
+* BitBucket is a filter which simply discards all inputs
+*/
+class BOTAN_PUBLIC_API(2,0) BitBucket final : public Filter
+   {
+   public:
+      void write(const uint8_t[], size_t) override { /* discard */ }
+
+      std::string name() const override { return "BitBucket"; }
+   };
+
+/**
+* This class represents Filter chains. A Filter chain is an ordered
+* concatenation of Filters, the input to a Chain sequentially passes
+* through all the Filters contained in the Chain.
+*/
+
+class BOTAN_PUBLIC_API(2,0) Chain final : public Fanout_Filter
+   {
+   public:
+      void write(const uint8_t input[], size_t length) override { send(input, length); }
+
+      std::string name() const override { return "Chain"; }
+
+      /**
+      * Construct a chain of up to four filters. The filters are set
+      * up in the same order as the arguments.
+      */
+      Chain(Filter* = nullptr, Filter* = nullptr,
+            Filter* = nullptr, Filter* = nullptr);
+
+      /**
+      * Construct a chain from range of filters
+      * @param filter_arr the list of filters
+      * @param length how many filters
+      */
+      Chain(Filter* filter_arr[], size_t length);
+   };
+
+/**
+* This class represents a fork filter, whose purpose is to fork the
+* flow of data. It causes an input message to result in n messages at
+* the end of the filter, where n is the number of forks.
+*/
+class BOTAN_PUBLIC_API(2,0) Fork : public Fanout_Filter
+   {
+   public:
+      void write(const uint8_t input[], size_t length) override { send(input, length); }
+      void set_port(size_t n) { Fanout_Filter::set_port(n); }
+
+      std::string name() const override { return "Fork"; }
+
+      /**
+      * Construct a Fork filter with up to four forks.
+      */
+      Fork(Filter*, Filter*, Filter* = nullptr, Filter* = nullptr);
+
+      /**
+      * Construct a Fork from range of filters
+      * @param filter_arr the list of filters
+      * @param length how many filters
+      */
+      Fork(Filter* filter_arr[], size_t length);
+   };
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+
+/**
+* This class is a threaded version of the Fork filter. While this uses
+* threads, the class itself is NOT thread-safe. This is meant as a drop-
+* in replacement for Fork where performance gains are possible.
+*/
+class BOTAN_PUBLIC_API(2,0) Threaded_Fork final : public Fork
+   {
+   public:
+      std::string name() const override;
+
+      /**
+      * Construct a Threaded_Fork filter with up to four forks.
+      */
+      Threaded_Fork(Filter*, Filter*, Filter* = nullptr, Filter* = nullptr);
+
+      /**
+      * Construct a Threaded_Fork from range of filters
+      * @param filter_arr the list of filters
+      * @param length how many filters
+      */
+      Threaded_Fork(Filter* filter_arr[], size_t length);
+
+      ~Threaded_Fork();
+
+   private:
+      void set_next(Filter* f[], size_t n);
+      void send(const uint8_t in[], size_t length) override;
+      void thread_delegate_work(const uint8_t input[], size_t length);
+      void thread_entry(Filter* filter);
+
+      std::vector<std::shared_ptr<std::thread>> m_threads;
+      std::unique_ptr<struct Threaded_Fork_Data> m_thread_data;
+   };
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/hex_filt.cpp b/third_party/botan/src/lib/filters/hex_filt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/hex_filt.cpp
@@ -0,0 +1,170 @@
+/*
+* Hex Encoder/Decoder
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/hex.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+/**
+* Size used for internal buffer in hex encoder/decoder
+*/
+const size_t HEX_CODEC_BUFFER_SIZE = 256;
+
+/*
+* Hex_Encoder Constructor
+*/
+Hex_Encoder::Hex_Encoder(bool breaks, size_t length, Case c) :
+   m_casing(c), m_line_length(breaks ? length : 0)
+   {
+   m_in.resize(HEX_CODEC_BUFFER_SIZE);
+   m_out.resize(2*m_in.size());
+   m_counter = m_position = 0;
+   }
+
+/*
+* Hex_Encoder Constructor
+*/
+Hex_Encoder::Hex_Encoder(Case c) : m_casing(c), m_line_length(0)
+   {
+   m_in.resize(HEX_CODEC_BUFFER_SIZE);
+   m_out.resize(2*m_in.size());
+   m_counter = m_position = 0;
+   }
+
+/*
+* Encode and send a block
+*/
+void Hex_Encoder::encode_and_send(const uint8_t block[], size_t length)
+   {
+   hex_encode(cast_uint8_ptr_to_char(m_out.data()),
+              block, length,
+              m_casing == Uppercase);
+
+   if(m_line_length == 0)
+      send(m_out, 2*length);
+   else
+      {
+      size_t remaining = 2*length, offset = 0;
+      while(remaining)
+         {
+         size_t sent = std::min(m_line_length - m_counter, remaining);
+         send(&m_out[offset], sent);
+         m_counter += sent;
+         remaining -= sent;
+         offset += sent;
+         if(m_counter == m_line_length)
+            {
+            send('\n');
+            m_counter = 0;
+            }
+         }
+      }
+   }
+
+/*
+* Convert some data into hex format
+*/
+void Hex_Encoder::write(const uint8_t input[], size_t length)
+   {
+   buffer_insert(m_in, m_position, input, length);
+   if(m_position + length >= m_in.size())
+      {
+      encode_and_send(m_in.data(), m_in.size());
+      input += (m_in.size() - m_position);
+      length -= (m_in.size() - m_position);
+      while(length >= m_in.size())
+         {
+         encode_and_send(input, m_in.size());
+         input += m_in.size();
+         length -= m_in.size();
+         }
+      copy_mem(m_in.data(), input, length);
+      m_position = 0;
+      }
+   m_position += length;
+   }
+
+/*
+* Flush buffers
+*/
+void Hex_Encoder::end_msg()
+   {
+   encode_and_send(m_in.data(), m_position);
+   if(m_counter && m_line_length)
+      send('\n');
+   m_counter = m_position = 0;
+   }
+
+/*
+* Hex_Decoder Constructor
+*/
+Hex_Decoder::Hex_Decoder(Decoder_Checking c) : m_checking(c)
+   {
+   m_in.resize(HEX_CODEC_BUFFER_SIZE);
+   m_out.resize(m_in.size() / 2);
+   m_position = 0;
+   }
+
+/*
+* Convert some data from hex format
+*/
+void Hex_Decoder::write(const uint8_t input[], size_t length)
+   {
+   while(length)
+      {
+      size_t to_copy = std::min<size_t>(length, m_in.size() - m_position);
+      copy_mem(&m_in[m_position], input, to_copy);
+      m_position += to_copy;
+
+      size_t consumed = 0;
+      size_t written = hex_decode(m_out.data(),
+                                  cast_uint8_ptr_to_char(m_in.data()),
+                                  m_position,
+                                  consumed,
+                                  m_checking != FULL_CHECK);
+
+      send(m_out, written);
+
+      if(consumed != m_position)
+         {
+         copy_mem(m_in.data(), m_in.data() + consumed, m_position - consumed);
+         m_position = m_position - consumed;
+         }
+      else
+         m_position = 0;
+
+      length -= to_copy;
+      input += to_copy;
+      }
+   }
+
+/*
+* Flush buffers
+*/
+void Hex_Decoder::end_msg()
+   {
+   size_t consumed = 0;
+   size_t written = hex_decode(m_out.data(),
+                               cast_uint8_ptr_to_char(m_in.data()),
+                               m_position,
+                               consumed,
+                               m_checking != FULL_CHECK);
+
+   send(m_out, written);
+
+   const bool not_full_bytes = consumed != m_position;
+
+   m_position = 0;
+
+   if(not_full_bytes)
+      throw Invalid_Argument("Hex_Decoder: Input not full bytes");
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/hex_filt.h b/third_party/botan/src/lib/filters/hex_filt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/hex_filt.h
@@ -0,0 +1,14 @@
+/*
+* Hex Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HEX_FILTER_H_
+#define BOTAN_HEX_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(hex_filt.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/key_filt.h b/third_party/botan/src/lib/filters/key_filt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/key_filt.h
@@ -0,0 +1,14 @@
+/*
+* Keyed_Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEYED_FILTER_H_
+#define BOTAN_KEYED_FILTER_H_
+
+#include <botan/filter.h>
+BOTAN_DEPRECATED_HEADER(key_filt.h)
+
+#endif
diff --git a/third_party/botan/src/lib/filters/out_buf.cpp b/third_party/botan/src/lib/filters/out_buf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/out_buf.cpp
@@ -0,0 +1,121 @@
+/*
+* Pipe Output Buffer
+* (C) 1999-2007,2011 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/out_buf.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+/*
+* Read data from a message
+*/
+size_t Output_Buffers::read(uint8_t output[], size_t length,
+                            Pipe::message_id msg)
+   {
+   SecureQueue* q = get(msg);
+   if(q)
+      return q->read(output, length);
+   return 0;
+   }
+
+/*
+* Peek at data in a message
+*/
+size_t Output_Buffers::peek(uint8_t output[], size_t length,
+                            size_t stream_offset,
+                            Pipe::message_id msg) const
+   {
+   SecureQueue* q = get(msg);
+   if(q)
+      return q->peek(output, length, stream_offset);
+   return 0;
+   }
+
+/*
+* Check available bytes in a message
+*/
+size_t Output_Buffers::remaining(Pipe::message_id msg) const
+   {
+   SecureQueue* q = get(msg);
+   if(q)
+      return q->size();
+   return 0;
+   }
+
+/*
+* Return the total bytes of a message that have already been read.
+*/
+size_t Output_Buffers::get_bytes_read(Pipe::message_id msg) const
+   {
+   SecureQueue* q = get(msg);
+   if (q)
+      return q->get_bytes_read();
+   return 0;
+   }
+
+/*
+* Add a new output queue
+*/
+void Output_Buffers::add(SecureQueue* queue)
+   {
+   BOTAN_ASSERT(queue, "queue was provided");
+
+   BOTAN_ASSERT(m_buffers.size() < m_buffers.max_size(),
+                "Room was available in container");
+
+   m_buffers.push_back(std::unique_ptr<SecureQueue>(queue));
+   }
+
+/*
+* Retire old output queues
+*/
+void Output_Buffers::retire()
+   {
+   for(size_t i = 0; i != m_buffers.size(); ++i)
+      if(m_buffers[i] && m_buffers[i]->size() == 0)
+         {
+         m_buffers[i].reset();
+         }
+
+   while(m_buffers.size() && !m_buffers[0])
+      {
+      m_buffers.pop_front();
+      m_offset = m_offset + Pipe::message_id(1);
+      }
+   }
+
+/*
+* Get a particular output queue
+*/
+SecureQueue* Output_Buffers::get(Pipe::message_id msg) const
+   {
+   if(msg < m_offset)
+      return nullptr;
+
+   BOTAN_ASSERT(msg < message_count(), "Message number is in range");
+
+   return m_buffers[msg-m_offset].get();
+   }
+
+/*
+* Return the total number of messages
+*/
+Pipe::message_id Output_Buffers::message_count() const
+   {
+   return (m_offset + m_buffers.size());
+   }
+
+/*
+* Output_Buffers Constructor
+*/
+Output_Buffers::Output_Buffers()
+   {
+   m_offset = 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/out_buf.h b/third_party/botan/src/lib/filters/out_buf.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/out_buf.h
@@ -0,0 +1,44 @@
+/*
+* Output Buffer
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OUTPUT_BUFFER_H_
+#define BOTAN_OUTPUT_BUFFER_H_
+
+#include <botan/types.h>
+#include <botan/pipe.h>
+#include <deque>
+
+namespace Botan {
+
+/**
+* Container of output buffers for Pipe
+*/
+class Output_Buffers final
+   {
+   public:
+      size_t read(uint8_t[], size_t, Pipe::message_id);
+      size_t peek(uint8_t[], size_t, size_t, Pipe::message_id) const;
+      size_t get_bytes_read(Pipe::message_id) const;
+      size_t remaining(Pipe::message_id) const;
+
+      void add(class SecureQueue*);
+      void retire();
+
+      Pipe::message_id message_count() const;
+
+      Output_Buffers();
+   private:
+      class SecureQueue* get(Pipe::message_id) const;
+
+      std::deque<std::unique_ptr<SecureQueue>> m_buffers;
+      Pipe::message_id m_offset;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/pipe.cpp b/third_party/botan/src/lib/filters/pipe.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/pipe.cpp
@@ -0,0 +1,311 @@
+/*
+* Pipe
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/internal/out_buf.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* A Filter that does nothing
+*/
+class Null_Filter final : public Filter
+   {
+   public:
+      void write(const uint8_t input[], size_t length) override
+         { send(input, length); }
+
+      std::string name() const override { return "Null"; }
+   };
+
+}
+
+/*
+* Pipe Constructor
+*/
+Pipe::Pipe(Filter* f1, Filter* f2, Filter* f3, Filter* f4) :
+   Pipe({f1,f2,f3,f4})
+   {
+   }
+
+/*
+* Pipe Constructor
+*/
+Pipe::Pipe(std::initializer_list<Filter*> args)
+   {
+   m_outputs.reset(new Output_Buffers);
+   m_pipe = nullptr;
+   m_default_read = 0;
+   m_inside_msg = false;
+
+   for(auto i = args.begin(); i != args.end(); ++i)
+      do_append(*i);
+   }
+
+/*
+* Pipe Destructor
+*/
+Pipe::~Pipe()
+   {
+   destruct(m_pipe);
+   }
+
+/*
+* Reset the Pipe
+*/
+void Pipe::reset()
+   {
+   destruct(m_pipe);
+   m_pipe = nullptr;
+   m_inside_msg = false;
+   }
+
+/*
+* Destroy the Pipe
+*/
+void Pipe::destruct(Filter* to_kill)
+   {
+   if(!to_kill || dynamic_cast<SecureQueue*>(to_kill))
+      return;
+   for(size_t j = 0; j != to_kill->total_ports(); ++j)
+      destruct(to_kill->m_next[j]);
+   delete to_kill;
+   }
+
+/*
+* Test if the Pipe has any data in it
+*/
+bool Pipe::end_of_data() const
+   {
+   return (remaining() == 0);
+   }
+
+/*
+* Set the default read message
+*/
+void Pipe::set_default_msg(message_id msg)
+   {
+   if(msg >= message_count())
+      throw Invalid_Argument("Pipe::set_default_msg: msg number is too high");
+   m_default_read = msg;
+   }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const uint8_t input[], size_t length)
+   {
+   start_msg();
+   write(input, length);
+   end_msg();
+   }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const secure_vector<uint8_t>& input)
+   {
+   process_msg(input.data(), input.size());
+   }
+
+void Pipe::process_msg(const std::vector<uint8_t>& input)
+   {
+   process_msg(input.data(), input.size());
+   }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const std::string& input)
+   {
+   process_msg(cast_char_ptr_to_uint8(input.data()), input.length());
+   }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(DataSource& input)
+   {
+   start_msg();
+   write(input);
+   end_msg();
+   }
+
+/*
+* Start a new message
+*/
+void Pipe::start_msg()
+   {
+   if(m_inside_msg)
+      throw Invalid_State("Pipe::start_msg: Message was already started");
+   if(m_pipe == nullptr)
+      m_pipe = new Null_Filter;
+   find_endpoints(m_pipe);
+   m_pipe->new_msg();
+   m_inside_msg = true;
+   }
+
+/*
+* End the current message
+*/
+void Pipe::end_msg()
+   {
+   if(!m_inside_msg)
+      throw Invalid_State("Pipe::end_msg: Message was already ended");
+   m_pipe->finish_msg();
+   clear_endpoints(m_pipe);
+   if(dynamic_cast<Null_Filter*>(m_pipe))
+      {
+      delete m_pipe;
+      m_pipe = nullptr;
+      }
+   m_inside_msg = false;
+
+   m_outputs->retire();
+   }
+
+/*
+* Find the endpoints of the Pipe
+*/
+void Pipe::find_endpoints(Filter* f)
+   {
+   for(size_t j = 0; j != f->total_ports(); ++j)
+      if(f->m_next[j] && !dynamic_cast<SecureQueue*>(f->m_next[j]))
+         find_endpoints(f->m_next[j]);
+      else
+         {
+         SecureQueue* q = new SecureQueue;
+         f->m_next[j] = q;
+         m_outputs->add(q);
+         }
+   }
+
+/*
+* Remove the SecureQueues attached to the Filter
+*/
+void Pipe::clear_endpoints(Filter* f)
+   {
+   if(!f) return;
+   for(size_t j = 0; j != f->total_ports(); ++j)
+      {
+      if(f->m_next[j] && dynamic_cast<SecureQueue*>(f->m_next[j]))
+         f->m_next[j] = nullptr;
+      clear_endpoints(f->m_next[j]);
+      }
+   }
+
+void Pipe::append(Filter* filter)
+   {
+   do_append(filter);
+   }
+
+void Pipe::append_filter(Filter* filter)
+   {
+   if(m_outputs->message_count() != 0)
+      throw Invalid_State("Cannot call Pipe::append_filter after start_msg");
+
+   do_append(filter);
+   }
+
+void Pipe::prepend(Filter* filter)
+   {
+   do_prepend(filter);
+   }
+
+void Pipe::prepend_filter(Filter* filter)
+   {
+   if(m_outputs->message_count() != 0)
+      throw Invalid_State("Cannot call Pipe::prepend_filter after start_msg");
+
+   do_prepend(filter);
+   }
+
+/*
+* Append a Filter to the Pipe
+*/
+void Pipe::do_append(Filter* filter)
+   {
+   if(!filter)
+      return;
+   if(dynamic_cast<SecureQueue*>(filter))
+      throw Invalid_Argument("Pipe::append: SecureQueue cannot be used");
+   if(filter->m_owned)
+      throw Invalid_Argument("Filters cannot be shared among multiple Pipes");
+
+   if(m_inside_msg)
+      throw Invalid_State("Cannot append to a Pipe while it is processing");
+
+   filter->m_owned = true;
+
+   if(!m_pipe) m_pipe = filter;
+   else      m_pipe->attach(filter);
+   }
+
+/*
+* Prepend a Filter to the Pipe
+*/
+void Pipe::do_prepend(Filter* filter)
+   {
+   if(m_inside_msg)
+      throw Invalid_State("Cannot prepend to a Pipe while it is processing");
+   if(!filter)
+      return;
+   if(dynamic_cast<SecureQueue*>(filter))
+      throw Invalid_Argument("Pipe::prepend: SecureQueue cannot be used");
+   if(filter->m_owned)
+      throw Invalid_Argument("Filters cannot be shared among multiple Pipes");
+
+   filter->m_owned = true;
+
+   if(m_pipe) filter->attach(m_pipe);
+   m_pipe = filter;
+   }
+
+/*
+* Pop a Filter off the Pipe
+*/
+void Pipe::pop()
+   {
+   if(m_inside_msg)
+      throw Invalid_State("Cannot pop off a Pipe while it is processing");
+
+   if(!m_pipe)
+      return;
+
+   if(m_pipe->total_ports() > 1)
+      throw Invalid_State("Cannot pop off a Filter with multiple ports");
+
+   size_t to_remove = m_pipe->owns() + 1;
+
+   while(to_remove--)
+      {
+      std::unique_ptr<Filter> to_destroy(m_pipe);
+      m_pipe = m_pipe->m_next[0];
+      }
+   }
+
+/*
+* Return the number of messages in this Pipe
+*/
+Pipe::message_id Pipe::message_count() const
+   {
+   return m_outputs->message_count();
+   }
+
+/*
+* Static Member Variables
+*/
+const Pipe::message_id Pipe::LAST_MESSAGE =
+   static_cast<Pipe::message_id>(-2);
+
+const Pipe::message_id Pipe::DEFAULT_MESSAGE =
+   static_cast<Pipe::message_id>(-1);
+
+}
diff --git a/third_party/botan/src/lib/filters/pipe.h b/third_party/botan/src/lib/filters/pipe.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/pipe.h
@@ -0,0 +1,379 @@
+/*
+* Pipe
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PIPE_H_
+#define BOTAN_PIPE_H_
+
+#include <botan/data_src.h>
+#include <botan/exceptn.h>
+#include <initializer_list>
+#include <iosfwd>
+
+namespace Botan {
+
+class Filter;
+class Output_Buffers;
+
+/**
+* This class represents pipe objects.
+* A set of filters can be placed into a pipe, and information flows
+* through the pipe until it reaches the end, where the output is
+* collected for retrieval.  If you're familiar with the Unix shell
+* environment, this design will sound quite familiar.
+*/
+class BOTAN_PUBLIC_API(2,0) Pipe final : public DataSource
+   {
+   public:
+      /**
+      * An opaque type that identifies a message in this Pipe
+      */
+      typedef size_t message_id;
+
+      /**
+      * Exception if you use an invalid message as an argument to
+      * read, remaining, etc
+      */
+      class BOTAN_PUBLIC_API(2,0) Invalid_Message_Number final : public Invalid_Argument
+         {
+         public:
+            /**
+            * @param where the error occurred
+            * @param msg the invalid message id that was used
+            */
+            Invalid_Message_Number(const std::string& where, message_id msg) :
+               Invalid_Argument("Pipe::" + where + ": Invalid message number " +
+                                std::to_string(msg))
+               {}
+         };
+
+      /**
+      * A meta-id for whatever the last message is
+      */
+      static const message_id LAST_MESSAGE;
+
+      /**
+      * A meta-id for the default message (set with set_default_msg)
+      */
+      static const message_id DEFAULT_MESSAGE;
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in the byte array to write
+      * @param length the length of the byte array in
+      */
+      void write(const uint8_t in[], size_t length);
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in the secure_vector containing the data to write
+      */
+      void write(const secure_vector<uint8_t>& in)
+         { write(in.data(), in.size()); }
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in the std::vector containing the data to write
+      */
+      void write(const std::vector<uint8_t>& in)
+         { write(in.data(), in.size()); }
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in the string containing the data to write
+      */
+      void write(const std::string& in);
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in the DataSource to read the data from
+      */
+      void write(DataSource& in);
+
+      /**
+      * Write input to the pipe, i.e. to its first filter.
+      * @param in a single byte to be written
+      */
+      void write(uint8_t in);
+
+      /**
+      * Perform start_msg(), write() and end_msg() sequentially.
+      * @param in the byte array containing the data to write
+      * @param length the length of the byte array to write
+      */
+      void process_msg(const uint8_t in[], size_t length);
+
+      /**
+      * Perform start_msg(), write() and end_msg() sequentially.
+      * @param in the secure_vector containing the data to write
+      */
+      void process_msg(const secure_vector<uint8_t>& in);
+
+      /**
+      * Perform start_msg(), write() and end_msg() sequentially.
+      * @param in the secure_vector containing the data to write
+      */
+      void process_msg(const std::vector<uint8_t>& in);
+
+      /**
+      * Perform start_msg(), write() and end_msg() sequentially.
+      * @param in the string containing the data to write
+      */
+      void process_msg(const std::string& in);
+
+      /**
+      * Perform start_msg(), write() and end_msg() sequentially.
+      * @param in the DataSource providing the data to write
+      */
+      void process_msg(DataSource& in);
+
+      /**
+      * Find out how many bytes are ready to read.
+      * @param msg the number identifying the message
+      * for which the information is desired
+      * @return number of bytes that can still be read
+      */
+      size_t remaining(message_id msg = DEFAULT_MESSAGE) const BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read the default message from the pipe. Moves the internal
+      * offset so that every call to read will return a new portion of
+      * the message.
+      *
+      * @param output the byte array to write the read bytes to
+      * @param length the length of the byte array output
+      * @return number of bytes actually read into output
+      */
+      size_t read(uint8_t output[], size_t length) override BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read a specified message from the pipe. Moves the internal
+      * offset so that every call to read will return a new portion of
+      * the message.
+      * @param output the byte array to write the read bytes to
+      * @param length the length of the byte array output
+      * @param msg the number identifying the message to read from
+      * @return number of bytes actually read into output
+      */
+      size_t read(uint8_t output[], size_t length, message_id msg) BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read a single byte from the pipe. Moves the internal offset so
+      * that every call to read will return a new portion of the
+      * message.
+      *
+      * @param output the byte to write the result to
+      * @param msg the message to read from
+      * @return number of bytes actually read into output
+      */
+      size_t read(uint8_t& output, message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read the full contents of the pipe.
+      * @param msg the number identifying the message to read from
+      * @return secure_vector holding the contents of the pipe
+      */
+      secure_vector<uint8_t> read_all(message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read the full contents of the pipe.
+      * @param msg the number identifying the message to read from
+      * @return string holding the contents of the pipe
+      */
+      std::string read_all_as_string(message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * Read from the default message but do not modify the internal
+      * offset. Consecutive calls to peek() will return portions of
+      * the message starting at the same position.
+      * @param output the byte array to write the peeked message part to
+      * @param length the length of the byte array output
+      * @param offset the offset from the current position in message
+      * @return number of bytes actually peeked and written into output
+      */
+      size_t peek(uint8_t output[], size_t length, size_t offset) const override BOTAN_WARN_UNUSED_RESULT;
+
+      /** Read from the specified message but do not modify the
+      * internal offset. Consecutive calls to peek() will return
+      * portions of the message starting at the same position.
+      * @param output the byte array to write the peeked message part to
+      * @param length the length of the byte array output
+      * @param offset the offset from the current position in message
+      * @param msg the number identifying the message to peek from
+      * @return number of bytes actually peeked and written into output
+      */
+      size_t peek(uint8_t output[], size_t length,
+                  size_t offset, message_id msg) const BOTAN_WARN_UNUSED_RESULT;
+
+      /** Read a single byte from the specified message but do not
+      * modify the internal offset. Consecutive calls to peek() will
+      * return portions of the message starting at the same position.
+      * @param output the byte to write the peeked message byte to
+      * @param offset the offset from the current position in message
+      * @param msg the number identifying the message to peek from
+      * @return number of bytes actually peeked and written into output
+      */
+      size_t peek(uint8_t& output, size_t offset,
+                  message_id msg = DEFAULT_MESSAGE) const BOTAN_WARN_UNUSED_RESULT;
+
+      /**
+      * @return the number of bytes read from the default message.
+      */
+      size_t get_bytes_read() const override;
+
+      /**
+      * @return the number of bytes read from the specified message.
+      */
+      size_t get_bytes_read(message_id msg) const;
+
+      bool check_available(size_t n) override;
+      bool check_available_msg(size_t n, message_id msg);
+
+      /**
+      * @return currently set default message
+      */
+      size_t default_msg() const { return m_default_read; }
+
+      /**
+      * Set the default message
+      * @param msg the number identifying the message which is going to
+      * be the new default message
+      */
+      void set_default_msg(message_id msg);
+
+      /**
+      * Get the number of messages the are in this pipe.
+      * @return number of messages the are in this pipe
+      */
+      message_id message_count() const;
+
+      /**
+      * Test whether this pipe has any data that can be read from.
+      * @return true if there is more data to read, false otherwise
+      */
+      bool end_of_data() const override;
+
+      /**
+      * Start a new message in the pipe. A potential other message in this pipe
+      * must be closed with end_msg() before this function may be called.
+      */
+      void start_msg();
+
+      /**
+      * End the current message.
+      */
+      void end_msg();
+
+      /**
+      * Insert a new filter at the front of the pipe
+      * Deprecated because runtime modification of Pipes is deprecated.
+      * You can instead use prepend_filter which only works before the first
+      * message is processed.
+      * @param filt the new filter to insert
+      */
+      BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+      void prepend(Filter* filt);
+
+      /**
+      * Insert a new filter at the back of the pipe
+      * Deprecated because runtime modification of Pipes is deprecated.
+      * You can instead use append_filter which only works before the first
+      * message is processed.
+      * @param filt the new filter to insert
+      */
+      BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+      void append(Filter* filt);
+
+      /**
+      * Remove the first filter at the front of the pipe.
+      */
+      BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+      void pop();
+
+      /**
+      * Reset this pipe to an empty pipe.
+      */
+      BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+      void reset();
+
+      /**
+      * Append a new filter onto the filter sequence. This may only be
+      * called immediately after initial construction, before _any_
+      * calls to start_msg have been made.
+      *
+      * This function (unlike append) is not deprecated, as it allows
+      * only modification of the pipe at initialization (before use)
+      * rather than after messages have been processed.
+      */
+      void append_filter(Filter* filt);
+
+      /**
+      * Prepend a new filter onto the filter sequence. This may only be
+      * called immediately after initial construction, before _any_
+      * calls to start_msg have been made.
+      *
+      * This function (unlike prepend) is not deprecated, as it allows
+      * only modification of the pipe at initialization (before use)
+      * rather than after messages have been processed.
+      */
+      void prepend_filter(Filter* filt);
+
+      /**
+      * Construct a Pipe of up to four filters. The filters are set up
+      * in the same order as the arguments.
+      */
+      Pipe(Filter* = nullptr, Filter* = nullptr,
+           Filter* = nullptr, Filter* = nullptr);
+
+      /**
+      * Construct a Pipe from a list of filters
+      * @param filters the set of filters to use
+      */
+      explicit Pipe(std::initializer_list<Filter*> filters);
+
+      Pipe(const Pipe&) = delete;
+      Pipe& operator=(const Pipe&) = delete;
+
+      ~Pipe();
+   private:
+      void destruct(Filter*);
+      void do_append(Filter* filt);
+      void do_prepend(Filter* filt);
+      void find_endpoints(Filter*);
+      void clear_endpoints(Filter*);
+
+      message_id get_message_no(const std::string&, message_id) const;
+
+      Filter* m_pipe;
+      std::unique_ptr<Output_Buffers> m_outputs;
+      message_id m_default_read;
+      bool m_inside_msg;
+   };
+
+/**
+* Stream output operator; dumps the results from pipe's default
+* message to the output stream.
+* @param out an output stream
+* @param pipe the pipe
+*/
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& out, Pipe& pipe);
+
+/**
+* Stream input operator; dumps the remaining bytes of input
+* to the (assumed open) pipe message.
+* @param in the input stream
+* @param pipe the pipe
+*/
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream& in, Pipe& pipe);
+
+}
+
+#if defined(BOTAN_HAS_PIPE_UNIXFD_IO)
+  #include <botan/fd_unix.h>
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/filters/pipe_io.cpp b/third_party/botan/src/lib/filters/pipe_io.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/pipe_io.cpp
@@ -0,0 +1,47 @@
+/*
+* Pipe I/O
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <istream>
+#include <ostream>
+
+namespace Botan {
+
+/*
+* Write data from a pipe into an ostream
+*/
+std::ostream& operator<<(std::ostream& stream, Pipe& pipe)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(stream.good() && pipe.remaining())
+      {
+      const size_t got = pipe.read(buffer.data(), buffer.size());
+      stream.write(cast_uint8_ptr_to_char(buffer.data()), got);
+      }
+   if(!stream.good())
+      throw Stream_IO_Error("Pipe output operator (iostream) has failed");
+   return stream;
+   }
+
+/*
+* Read data from an istream into a pipe
+*/
+std::istream& operator>>(std::istream& stream, Pipe& pipe)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(stream.good())
+      {
+      stream.read(cast_uint8_ptr_to_char(buffer.data()), buffer.size());
+      const size_t got = static_cast<size_t>(stream.gcount());
+      pipe.write(buffer.data(), got);
+      }
+   if(stream.bad() || (stream.fail() && !stream.eof()))
+      throw Stream_IO_Error("Pipe input operator (iostream) has failed");
+   return stream;
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/pipe_rw.cpp b/third_party/botan/src/lib/filters/pipe_rw.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/pipe_rw.cpp
@@ -0,0 +1,181 @@
+/*
+* Pipe Reading/Writing
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/filter.h>
+#include <botan/internal/out_buf.h>
+
+namespace Botan {
+
+/*
+* Look up the canonical ID for a queue
+*/
+Pipe::message_id Pipe::get_message_no(const std::string& func_name,
+                                      message_id msg) const
+   {
+   if(msg == DEFAULT_MESSAGE)
+      msg = default_msg();
+   else if(msg == LAST_MESSAGE)
+      msg = message_count() - 1;
+
+   if(msg >= message_count())
+      throw Invalid_Message_Number(func_name, msg);
+
+   return msg;
+   }
+
+/*
+* Write into a Pipe
+*/
+void Pipe::write(const uint8_t input[], size_t length)
+   {
+   if(!m_inside_msg)
+      throw Invalid_State("Cannot write to a Pipe while it is not processing");
+   m_pipe->write(input, length);
+   }
+
+/*
+* Write a string into a Pipe
+*/
+void Pipe::write(const std::string& str)
+   {
+   write(cast_char_ptr_to_uint8(str.data()), str.size());
+   }
+
+/*
+* Write a single byte into a Pipe
+*/
+void Pipe::write(uint8_t input)
+   {
+   write(&input, 1);
+   }
+
+/*
+* Write the contents of a DataSource into a Pipe
+*/
+void Pipe::write(DataSource& source)
+   {
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(!source.end_of_data())
+      {
+      size_t got = source.read(buffer.data(), buffer.size());
+      write(buffer.data(), got);
+      }
+   }
+
+/*
+* Read some data from the pipe
+*/
+size_t Pipe::read(uint8_t output[], size_t length, message_id msg)
+   {
+   return m_outputs->read(output, length, get_message_no("read", msg));
+   }
+
+/*
+* Read some data from the pipe
+*/
+size_t Pipe::read(uint8_t output[], size_t length)
+   {
+   return read(output, length, DEFAULT_MESSAGE);
+   }
+
+/*
+* Read a single byte from the pipe
+*/
+size_t Pipe::read(uint8_t& out, message_id msg)
+   {
+   return read(&out, 1, msg);
+   }
+
+/*
+* Return all data in the pipe
+*/
+secure_vector<uint8_t> Pipe::read_all(message_id msg)
+   {
+   msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg());
+   secure_vector<uint8_t> buffer(remaining(msg));
+   size_t got = read(buffer.data(), buffer.size(), msg);
+   buffer.resize(got);
+   return buffer;
+   }
+
+/*
+* Return all data in the pipe as a string
+*/
+std::string Pipe::read_all_as_string(message_id msg)
+   {
+   msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg());
+   secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+   std::string str;
+   str.reserve(remaining(msg));
+
+   while(true)
+      {
+      size_t got = read(buffer.data(), buffer.size(), msg);
+      if(got == 0)
+         break;
+      str.append(cast_uint8_ptr_to_char(buffer.data()), got);
+      }
+
+   return str;
+   }
+
+/*
+* Find out how many bytes are ready to read
+*/
+size_t Pipe::remaining(message_id msg) const
+   {
+   return m_outputs->remaining(get_message_no("remaining", msg));
+   }
+
+/*
+* Peek at some data in the pipe
+*/
+size_t Pipe::peek(uint8_t output[], size_t length,
+                  size_t offset, message_id msg) const
+   {
+   return m_outputs->peek(output, length, offset, get_message_no("peek", msg));
+   }
+
+/*
+* Peek at some data in the pipe
+*/
+size_t Pipe::peek(uint8_t output[], size_t length, size_t offset) const
+   {
+   return peek(output, length, offset, DEFAULT_MESSAGE);
+   }
+
+/*
+* Peek at a byte in the pipe
+*/
+size_t Pipe::peek(uint8_t& out, size_t offset, message_id msg) const
+   {
+   return peek(&out, 1, offset, msg);
+   }
+
+size_t Pipe::get_bytes_read() const
+   {
+   return m_outputs->get_bytes_read(default_msg());
+   }
+
+size_t Pipe::get_bytes_read(message_id msg) const
+   {
+   return m_outputs->get_bytes_read(msg);
+   }
+
+bool Pipe::check_available(size_t n)
+   {
+   return (n <= remaining(default_msg()));
+   }
+
+bool Pipe::check_available_msg(size_t n, message_id msg)
+   {
+   return (n <= remaining(msg));
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/secqueue.cpp b/third_party/botan/src/lib/filters/secqueue.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/secqueue.cpp
@@ -0,0 +1,232 @@
+/*
+* SecureQueue
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/secqueue.h>
+#include <algorithm>
+
+namespace Botan {
+
+/**
+* A node in a SecureQueue
+*/
+class SecureQueueNode final
+   {
+   public:
+      SecureQueueNode() : m_buffer(BOTAN_DEFAULT_BUFFER_SIZE)
+         { m_next = nullptr; m_start = m_end = 0; }
+
+      ~SecureQueueNode() { m_next = nullptr; m_start = m_end = 0; }
+
+      size_t write(const uint8_t input[], size_t length)
+         {
+         size_t copied = std::min<size_t>(length, m_buffer.size() - m_end);
+         copy_mem(m_buffer.data() + m_end, input, copied);
+         m_end += copied;
+         return copied;
+         }
+
+      size_t read(uint8_t output[], size_t length)
+         {
+         size_t copied = std::min(length, m_end - m_start);
+         copy_mem(output, m_buffer.data() + m_start, copied);
+         m_start += copied;
+         return copied;
+         }
+
+      size_t peek(uint8_t output[], size_t length, size_t offset = 0)
+         {
+         const size_t left = m_end - m_start;
+         if(offset >= left) return 0;
+         size_t copied = std::min(length, left - offset);
+         copy_mem(output, m_buffer.data() + m_start + offset, copied);
+         return copied;
+         }
+
+      size_t size() const { return (m_end - m_start); }
+   private:
+      friend class SecureQueue;
+      SecureQueueNode* m_next;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_start, m_end;
+   };
+
+/*
+* Create a SecureQueue
+*/
+SecureQueue::SecureQueue()
+   {
+   m_bytes_read = 0;
+   set_next(nullptr, 0);
+   m_head = m_tail = new SecureQueueNode;
+   }
+
+/*
+* Copy a SecureQueue
+*/
+SecureQueue::SecureQueue(const SecureQueue& input) :
+   Fanout_Filter(), DataSource()
+   {
+   m_bytes_read = 0;
+   set_next(nullptr, 0);
+
+   m_head = m_tail = new SecureQueueNode;
+   SecureQueueNode* temp = input.m_head;
+   while(temp)
+      {
+      write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start);
+      temp = temp->m_next;
+      }
+   }
+
+/*
+* Destroy this SecureQueue
+*/
+void SecureQueue::destroy()
+   {
+   SecureQueueNode* temp = m_head;
+   while(temp)
+      {
+      SecureQueueNode* holder = temp->m_next;
+      delete temp;
+      temp = holder;
+      }
+   m_head = m_tail = nullptr;
+   }
+
+/*
+* Copy a SecureQueue
+*/
+SecureQueue& SecureQueue::operator=(const SecureQueue& input)
+   {
+   if(this == &input)
+      return *this;
+
+   destroy();
+   m_bytes_read = input.get_bytes_read();
+   m_head = m_tail = new SecureQueueNode;
+   SecureQueueNode* temp = input.m_head;
+   while(temp)
+      {
+      write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start);
+      temp = temp->m_next;
+      }
+   return (*this);
+   }
+
+/*
+* Add some bytes to the queue
+*/
+void SecureQueue::write(const uint8_t input[], size_t length)
+   {
+   if(!m_head)
+      m_head = m_tail = new SecureQueueNode;
+   while(length)
+      {
+      const size_t n = m_tail->write(input, length);
+      input += n;
+      length -= n;
+      if(length)
+         {
+         m_tail->m_next = new SecureQueueNode;
+         m_tail = m_tail->m_next;
+         }
+      }
+   }
+
+/*
+* Read some bytes from the queue
+*/
+size_t SecureQueue::read(uint8_t output[], size_t length)
+   {
+   size_t got = 0;
+   while(length && m_head)
+      {
+      const size_t n = m_head->read(output, length);
+      output += n;
+      got += n;
+      length -= n;
+      if(m_head->size() == 0)
+         {
+         SecureQueueNode* holder = m_head->m_next;
+         delete m_head;
+         m_head = holder;
+         }
+      }
+   m_bytes_read += got;
+   return got;
+   }
+
+/*
+* Read data, but do not remove it from queue
+*/
+size_t SecureQueue::peek(uint8_t output[], size_t length, size_t offset) const
+   {
+   SecureQueueNode* current = m_head;
+
+   while(offset && current)
+      {
+      if(offset >= current->size())
+         {
+         offset -= current->size();
+         current = current->m_next;
+         }
+      else
+         break;
+      }
+
+   size_t got = 0;
+   while(length && current)
+      {
+      const size_t n = current->peek(output, length, offset);
+      offset = 0;
+      output += n;
+      got += n;
+      length -= n;
+      current = current->m_next;
+      }
+   return got;
+   }
+
+/**
+* Return how many bytes have been read so far.
+*/
+size_t SecureQueue::get_bytes_read() const
+   {
+   return m_bytes_read;
+   }
+
+/*
+* Return how many bytes the queue holds
+*/
+size_t SecureQueue::size() const
+   {
+   SecureQueueNode* current = m_head;
+   size_t count = 0;
+
+   while(current)
+      {
+      count += current->size();
+      current = current->m_next;
+      }
+   return count;
+   }
+
+/*
+* Test if the queue has any data in it
+*/
+bool SecureQueue::end_of_data() const
+   {
+   return (size() == 0);
+   }
+
+bool SecureQueue::empty() const
+   {
+   return (size() == 0);
+   }
+
+}
diff --git a/third_party/botan/src/lib/filters/secqueue.h b/third_party/botan/src/lib/filters/secqueue.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/secqueue.h
@@ -0,0 +1,74 @@
+/*
+* SecureQueue
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SECURE_QUEUE_H_
+#define BOTAN_SECURE_QUEUE_H_
+
+#include <botan/data_src.h>
+#include <botan/filter.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(secqueue.h)
+
+namespace Botan {
+
+/**
+* A queue that knows how to zeroize itself
+*/
+class BOTAN_PUBLIC_API(2,0) SecureQueue final : public Fanout_Filter, public DataSource
+   {
+   public:
+      std::string name() const override { return "Queue"; }
+
+      void write(const uint8_t[], size_t) override;
+
+      size_t read(uint8_t[], size_t) override;
+      size_t peek(uint8_t[], size_t, size_t = 0) const override;
+      size_t get_bytes_read() const override;
+
+      bool end_of_data() const override;
+
+      bool empty() const;
+
+      bool check_available(size_t n) override { return n <= size(); }
+
+      /**
+      * @return number of bytes available in the queue
+      */
+      size_t size() const;
+
+      bool attachable() override { return false; }
+
+      /**
+      * SecureQueue assignment
+      * @param other the queue to copy
+      */
+      SecureQueue& operator=(const SecureQueue& other);
+
+      /**
+      * SecureQueue default constructor (creates empty queue)
+      */
+      SecureQueue();
+
+      /**
+      * SecureQueue copy constructor
+      * @param other the queue to copy
+      */
+      SecureQueue(const SecureQueue& other);
+
+      ~SecureQueue() { destroy(); }
+
+   private:
+      void destroy();
+      size_t m_bytes_read;
+      class SecureQueueNode* m_head;
+      class SecureQueueNode* m_tail;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/filters/threaded_fork.cpp b/third_party/botan/src/lib/filters/threaded_fork.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/filters/threaded_fork.cpp
@@ -0,0 +1,153 @@
+/*
+* Threaded Fork
+* (C) 2013 Joel Low
+*     2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+
+#include <botan/internal/semaphore.h>
+#include <botan/internal/barrier.h>
+#include <functional>
+
+namespace Botan {
+
+struct Threaded_Fork_Data
+   {
+   /*
+   * Semaphore for indicating that there is work to be done (or to
+   * quit)
+   */
+   Semaphore m_input_ready_semaphore;
+
+   /*
+   * Synchronises all threads to complete processing data in lock-step.
+   */
+   Barrier m_input_complete_barrier;
+
+   /*
+   * The work that needs to be done. This should be only when the threads
+   * are NOT running (i.e. before notifying the work condition, after
+   * the input_complete_barrier has reset.)
+   */
+   const uint8_t* m_input = nullptr;
+
+   /*
+   * The length of the work that needs to be done.
+   */
+   size_t m_input_length = 0;
+   };
+
+/*
+* Threaded_Fork constructor
+*/
+Threaded_Fork::Threaded_Fork(Filter* f1, Filter* f2, Filter* f3, Filter* f4) :
+   Fork(nullptr, static_cast<size_t>(0)),
+   m_thread_data(new Threaded_Fork_Data)
+   {
+   Filter* filters[4] = { f1, f2, f3, f4 };
+   set_next(filters, 4);
+   }
+
+/*
+* Threaded_Fork constructor
+*/
+Threaded_Fork::Threaded_Fork(Filter* filters[], size_t count) :
+   Fork(nullptr, static_cast<size_t>(0)),
+   m_thread_data(new Threaded_Fork_Data)
+   {
+   set_next(filters, count);
+   }
+
+Threaded_Fork::~Threaded_Fork()
+   {
+   m_thread_data->m_input = nullptr;
+   m_thread_data->m_input_length = 0;
+
+   m_thread_data->m_input_ready_semaphore.release(m_threads.size());
+
+   for(auto& thread : m_threads)
+     thread->join();
+   }
+
+std::string Threaded_Fork::name() const
+   {
+   return "Threaded Fork";
+   }
+
+void Threaded_Fork::set_next(Filter* f[], size_t n)
+   {
+   Fork::set_next(f, n);
+   n = m_next.size();
+
+   if(n < m_threads.size())
+      m_threads.resize(n);
+   else
+      {
+      m_threads.reserve(n);
+      for(size_t i = m_threads.size(); i != n; ++i)
+         {
+         m_threads.push_back(
+            std::shared_ptr<std::thread>(
+               new std::thread(
+                  std::bind(&Threaded_Fork::thread_entry, this, m_next[i]))));
+         }
+      }
+   }
+
+void Threaded_Fork::send(const uint8_t input[], size_t length)
+   {
+   if(m_write_queue.size())
+      thread_delegate_work(m_write_queue.data(), m_write_queue.size());
+   thread_delegate_work(input, length);
+
+   bool nothing_attached = true;
+   for(size_t j = 0; j != total_ports(); ++j)
+      if(m_next[j])
+         nothing_attached = false;
+
+   if(nothing_attached)
+      m_write_queue += std::make_pair(input, length);
+   else
+      m_write_queue.clear();
+   }
+
+void Threaded_Fork::thread_delegate_work(const uint8_t input[], size_t length)
+   {
+   //Set the data to do.
+   m_thread_data->m_input = input;
+   m_thread_data->m_input_length = length;
+
+   //Let the workers start processing.
+   m_thread_data->m_input_complete_barrier.wait(total_ports() + 1);
+   m_thread_data->m_input_ready_semaphore.release(total_ports());
+
+   //Wait for all the filters to finish processing.
+   m_thread_data->m_input_complete_barrier.sync();
+
+   //Reset the thread data
+   m_thread_data->m_input = nullptr;
+   m_thread_data->m_input_length = 0;
+   }
+
+void Threaded_Fork::thread_entry(Filter* filter)
+   {
+   while(true)
+      {
+      m_thread_data->m_input_ready_semaphore.acquire();
+
+      if(!m_thread_data->m_input)
+         break;
+
+      filter->write(m_thread_data->m_input, m_thread_data->m_input_length);
+      m_thread_data->m_input_complete_barrier.sync();
+      }
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/blake2/blake2b.cpp b/third_party/botan/src/lib/hash/blake2/blake2b.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/blake2/blake2b.cpp
@@ -0,0 +1,207 @@
+/*
+* BLAKE2b
+* (C) 2016 cynecx
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blake2b.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+enum blake2b_constant {
+  BLAKE2B_BLOCKBYTES = 128,
+  BLAKE2B_IVU64COUNT = 8
+};
+
+const uint64_t blake2b_IV[BLAKE2B_IVU64COUNT] = {
+   0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
+   0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+   0x510e527fade682d1, 0x9b05688c2b3e6c1f,
+   0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
+};
+
+}
+
+BLAKE2b::BLAKE2b(size_t output_bits) :
+   m_output_bits(output_bits),
+   m_buffer(BLAKE2B_BLOCKBYTES),
+   m_bufpos(0),
+   m_H(BLAKE2B_IVU64COUNT)
+   {
+   if(output_bits == 0 || output_bits > 512 || output_bits % 8 != 0)
+      {
+      throw Invalid_Argument("Bad output bits size for BLAKE2b");
+      }
+
+   state_init();
+   }
+
+void BLAKE2b::state_init()
+   {
+   copy_mem(m_H.data(), blake2b_IV, BLAKE2B_IVU64COUNT);
+   m_H[0] ^= 0x01010000 ^ static_cast<uint8_t>(output_length());
+   m_T[0] = m_T[1] = 0;
+   m_F[0] = m_F[1] = 0;
+   m_bufpos = 0;
+   }
+
+namespace {
+
+BOTAN_FORCE_INLINE void G(uint64_t& a, uint64_t& b, uint64_t& c, uint64_t& d,
+                          uint64_t M0, uint64_t M1)
+   {
+   a = a + b + M0;
+   d = rotr<32>(d ^ a);
+   c = c + d;
+   b = rotr<24>(b ^ c);
+   a = a + b + M1;
+   d = rotr<16>(d ^ a);
+   c = c + d;
+   b = rotr<63>(b ^ c);
+   }
+
+template<size_t i0, size_t i1, size_t i2, size_t i3, size_t i4, size_t i5, size_t i6, size_t i7,
+         size_t i8, size_t i9, size_t iA, size_t iB, size_t iC, size_t iD, size_t iE, size_t iF>
+BOTAN_FORCE_INLINE void ROUND(uint64_t* v, const uint64_t* M)
+   {
+   G(v[ 0], v[ 4], v[ 8], v[12], M[i0], M[i1]);
+   G(v[ 1], v[ 5], v[ 9], v[13], M[i2], M[i3]);
+   G(v[ 2], v[ 6], v[10], v[14], M[i4], M[i5]);
+   G(v[ 3], v[ 7], v[11], v[15], M[i6], M[i7]);
+   G(v[ 0], v[ 5], v[10], v[15], M[i8], M[i9]);
+   G(v[ 1], v[ 6], v[11], v[12], M[iA], M[iB]);
+   G(v[ 2], v[ 7], v[ 8], v[13], M[iC], M[iD]);
+   G(v[ 3], v[ 4], v[ 9], v[14], M[iE], M[iF]);
+   }
+
+
+}
+
+void BLAKE2b::compress(const uint8_t* input, size_t blocks, uint64_t increment)
+   {
+   for(size_t b = 0; b != blocks; ++b)
+      {
+      m_T[0] += increment;
+      if(m_T[0] < increment)
+         {
+         m_T[1]++;
+         }
+
+      uint64_t M[16];
+      uint64_t v[16];
+      load_le(M, input, 16);
+
+      input += BLAKE2B_BLOCKBYTES;
+
+      for(size_t i = 0; i < 8; i++)
+         v[i] = m_H[i];
+      for(size_t i = 0; i != 8; ++i)
+         v[i + 8] = blake2b_IV[i];
+
+      v[12] ^= m_T[0];
+      v[13] ^= m_T[1];
+      v[14] ^= m_F[0];
+      v[15] ^= m_F[1];
+
+      ROUND< 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15>(v, M);
+      ROUND<14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3>(v, M);
+      ROUND<11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4>(v, M);
+      ROUND< 7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8>(v, M);
+      ROUND< 9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13>(v, M);
+      ROUND< 2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9>(v, M);
+      ROUND<12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11>(v, M);
+      ROUND<13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10>(v, M);
+      ROUND< 6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5>(v, M);
+      ROUND<10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13,  0>(v, M);
+      ROUND< 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15>(v, M);
+      ROUND<14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3>(v, M);
+
+      for(size_t i = 0; i < 8; i++)
+         {
+         m_H[i] ^= v[i] ^ v[i + 8];
+         }
+      }
+   }
+
+void BLAKE2b::add_data(const uint8_t input[], size_t length)
+   {
+   if(length == 0)
+      return;
+
+   if(m_bufpos > 0)
+      {
+      if(m_bufpos < BLAKE2B_BLOCKBYTES)
+         {
+         const size_t take = std::min(BLAKE2B_BLOCKBYTES - m_bufpos, length);
+         copy_mem(&m_buffer[m_bufpos], input, take);
+         m_bufpos += take;
+         length -= take;
+         input += take;
+         }
+
+      if(m_bufpos == m_buffer.size() && length > 0)
+         {
+         compress(m_buffer.data(), 1, BLAKE2B_BLOCKBYTES);
+         m_bufpos = 0;
+         }
+      }
+
+   if(length > BLAKE2B_BLOCKBYTES)
+      {
+      const size_t full_blocks = ((length-1) / BLAKE2B_BLOCKBYTES);
+      compress(input, full_blocks, BLAKE2B_BLOCKBYTES);
+
+      input += full_blocks * BLAKE2B_BLOCKBYTES;
+      length -= full_blocks * BLAKE2B_BLOCKBYTES;
+      }
+
+   if(length > 0)
+      {
+      copy_mem(&m_buffer[m_bufpos], input, length);
+      m_bufpos += length;
+      }
+   }
+
+void BLAKE2b::final_result(uint8_t output[])
+   {
+   if(m_bufpos != BLAKE2B_BLOCKBYTES)
+      clear_mem(&m_buffer[m_bufpos], BLAKE2B_BLOCKBYTES - m_bufpos);
+   m_F[0] = 0xFFFFFFFFFFFFFFFF;
+   compress(m_buffer.data(), 1, m_bufpos);
+   copy_out_vec_le(output, output_length(), m_H);
+   state_init();
+   }
+
+std::string BLAKE2b::name() const
+   {
+   return "BLAKE2b(" + std::to_string(m_output_bits) + ")";
+   }
+
+HashFunction* BLAKE2b::clone() const
+   {
+   return new BLAKE2b(m_output_bits);
+   }
+
+std::unique_ptr<HashFunction> BLAKE2b::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new BLAKE2b(*this));
+   }
+
+void BLAKE2b::clear()
+   {
+   zeroise(m_H);
+   zeroise(m_buffer);
+   m_bufpos = 0;
+   state_init();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/blake2/blake2b.h b/third_party/botan/src/lib/hash/blake2/blake2b.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/blake2/blake2b.h
@@ -0,0 +1,60 @@
+/*
+* BLAKE2b
+* (C) 2016 cynecx
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLAKE2B_H_
+#define BOTAN_BLAKE2B_H_
+
+#include <botan/hash.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blake2b.h)
+
+namespace Botan {
+
+/**
+* BLAKE2B
+*/
+class BOTAN_PUBLIC_API(2,0) BLAKE2b final : public HashFunction
+   {
+   public:
+      /**
+      * @param output_bits the output size of BLAKE2b in bits
+      */
+      explicit BLAKE2b(size_t output_bits = 512);
+
+      size_t hash_block_size() const override { return 128; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::string name() const override;
+      void clear() override;
+
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      void state_init();
+      void compress(const uint8_t* data, size_t blocks, uint64_t increment);
+
+      const size_t m_output_bits;
+
+      secure_vector<uint8_t> m_buffer;
+      size_t m_bufpos;
+
+      secure_vector<uint64_t> m_H;
+      uint64_t m_T[2];
+      uint64_t m_F[2];
+   };
+
+typedef BLAKE2b Blake2b;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp b/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp
@@ -0,0 +1,86 @@
+/*
+* Adler32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/adler32.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+void adler32_update(const uint8_t input[], size_t length,
+                    uint16_t& S1, uint16_t& S2)
+   {
+   uint32_t S1x = S1;
+   uint32_t S2x = S2;
+
+   while(length >= 16)
+      {
+      S1x += input[ 0]; S2x += S1x;
+      S1x += input[ 1]; S2x += S1x;
+      S1x += input[ 2]; S2x += S1x;
+      S1x += input[ 3]; S2x += S1x;
+      S1x += input[ 4]; S2x += S1x;
+      S1x += input[ 5]; S2x += S1x;
+      S1x += input[ 6]; S2x += S1x;
+      S1x += input[ 7]; S2x += S1x;
+      S1x += input[ 8]; S2x += S1x;
+      S1x += input[ 9]; S2x += S1x;
+      S1x += input[10]; S2x += S1x;
+      S1x += input[11]; S2x += S1x;
+      S1x += input[12]; S2x += S1x;
+      S1x += input[13]; S2x += S1x;
+      S1x += input[14]; S2x += S1x;
+      S1x += input[15]; S2x += S1x;
+      input += 16;
+      length -= 16;
+      }
+
+   for(size_t j = 0; j != length; ++j)
+      {
+      S1x += input[j];
+      S2x += S1x;
+      }
+
+   S1 = S1x % 65521;
+   S2 = S2x % 65521;
+   }
+
+}
+
+/*
+* Update an Adler32 Checksum
+*/
+void Adler32::add_data(const uint8_t input[], size_t length)
+   {
+   const size_t PROCESS_AMOUNT = 5552;
+
+   while(length >= PROCESS_AMOUNT)
+      {
+      adler32_update(input, PROCESS_AMOUNT, m_S1, m_S2);
+      input += PROCESS_AMOUNT;
+      length -= PROCESS_AMOUNT;
+      }
+
+   adler32_update(input, length, m_S1, m_S2);
+   }
+
+/*
+* Finalize an Adler32 Checksum
+*/
+void Adler32::final_result(uint8_t output[])
+   {
+   store_be(output, m_S2, m_S1);
+   clear();
+   }
+
+std::unique_ptr<HashFunction> Adler32::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new Adler32(*this));
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/checksum/adler32/adler32.h b/third_party/botan/src/lib/hash/checksum/adler32/adler32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/adler32/adler32.h
@@ -0,0 +1,40 @@
+/*
+* Adler32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ADLER32_H_
+#define BOTAN_ADLER32_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(adler32.h)
+
+namespace Botan {
+
+/**
+* The Adler32 checksum, used in zlib
+*/
+class BOTAN_PUBLIC_API(2,0) Adler32 final : public HashFunction
+   {
+   public:
+      std::string name() const override { return "Adler32"; }
+      size_t output_length() const override { return 4; }
+      HashFunction* clone() const override { return new Adler32; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override { m_S1 = 1; m_S2 = 0; }
+
+      Adler32() { clear(); }
+      ~Adler32() { clear(); }
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      uint16_t m_S1, m_S2;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp b/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp
@@ -0,0 +1,252 @@
+/*
+* CRC24
+* (C) 1999-2007 Jack Lloyd
+* (C) 2017 [Ribose Inc](https://www.ribose.com). Performed by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crc24.h>
+#include <botan/loadstor.h>
+#include <botan/bswap.h>
+
+namespace Botan {
+
+namespace {
+
+const uint32_t CRC24_T0[256] = {
+   0x00000000, 0x00FB4C86, 0x000DD58A, 0x00F6990C, 0x00E1E693, 0x001AAA15, 0x00EC3319,
+   0x00177F9F, 0x003981A1, 0x00C2CD27, 0x0034542B, 0x00CF18AD, 0x00D86732, 0x00232BB4,
+   0x00D5B2B8, 0x002EFE3E, 0x00894EC5, 0x00720243, 0x00849B4F, 0x007FD7C9, 0x0068A856,
+   0x0093E4D0, 0x00657DDC, 0x009E315A, 0x00B0CF64, 0x004B83E2, 0x00BD1AEE, 0x00465668,
+   0x005129F7, 0x00AA6571, 0x005CFC7D, 0x00A7B0FB, 0x00E9D10C, 0x00129D8A, 0x00E40486,
+   0x001F4800, 0x0008379F, 0x00F37B19, 0x0005E215, 0x00FEAE93, 0x00D050AD, 0x002B1C2B,
+   0x00DD8527, 0x0026C9A1, 0x0031B63E, 0x00CAFAB8, 0x003C63B4, 0x00C72F32, 0x00609FC9,
+   0x009BD34F, 0x006D4A43, 0x009606C5, 0x0081795A, 0x007A35DC, 0x008CACD0, 0x0077E056,
+   0x00591E68, 0x00A252EE, 0x0054CBE2, 0x00AF8764, 0x00B8F8FB, 0x0043B47D, 0x00B52D71,
+   0x004E61F7, 0x00D2A319, 0x0029EF9F, 0x00DF7693, 0x00243A15, 0x0033458A, 0x00C8090C,
+   0x003E9000, 0x00C5DC86, 0x00EB22B8, 0x00106E3E, 0x00E6F732, 0x001DBBB4, 0x000AC42B,
+   0x00F188AD, 0x000711A1, 0x00FC5D27, 0x005BEDDC, 0x00A0A15A, 0x00563856, 0x00AD74D0,
+   0x00BA0B4F, 0x004147C9, 0x00B7DEC5, 0x004C9243, 0x00626C7D, 0x009920FB, 0x006FB9F7,
+   0x0094F571, 0x00838AEE, 0x0078C668, 0x008E5F64, 0x007513E2, 0x003B7215, 0x00C03E93,
+   0x0036A79F, 0x00CDEB19, 0x00DA9486, 0x0021D800, 0x00D7410C, 0x002C0D8A, 0x0002F3B4,
+   0x00F9BF32, 0x000F263E, 0x00F46AB8, 0x00E31527, 0x001859A1, 0x00EEC0AD, 0x00158C2B,
+   0x00B23CD0, 0x00497056, 0x00BFE95A, 0x0044A5DC, 0x0053DA43, 0x00A896C5, 0x005E0FC9,
+   0x00A5434F, 0x008BBD71, 0x0070F1F7, 0x008668FB, 0x007D247D, 0x006A5BE2, 0x00911764,
+   0x00678E68, 0x009CC2EE, 0x00A44733, 0x005F0BB5, 0x00A992B9, 0x0052DE3F, 0x0045A1A0,
+   0x00BEED26, 0x0048742A, 0x00B338AC, 0x009DC692, 0x00668A14, 0x00901318, 0x006B5F9E,
+   0x007C2001, 0x00876C87, 0x0071F58B, 0x008AB90D, 0x002D09F6, 0x00D64570, 0x0020DC7C,
+   0x00DB90FA, 0x00CCEF65, 0x0037A3E3, 0x00C13AEF, 0x003A7669, 0x00148857, 0x00EFC4D1,
+   0x00195DDD, 0x00E2115B, 0x00F56EC4, 0x000E2242, 0x00F8BB4E, 0x0003F7C8, 0x004D963F,
+   0x00B6DAB9, 0x004043B5, 0x00BB0F33, 0x00AC70AC, 0x00573C2A, 0x00A1A526, 0x005AE9A0,
+   0x0074179E, 0x008F5B18, 0x0079C214, 0x00828E92, 0x0095F10D, 0x006EBD8B, 0x00982487,
+   0x00636801, 0x00C4D8FA, 0x003F947C, 0x00C90D70, 0x003241F6, 0x00253E69, 0x00DE72EF,
+   0x0028EBE3, 0x00D3A765, 0x00FD595B, 0x000615DD, 0x00F08CD1, 0x000BC057, 0x001CBFC8,
+   0x00E7F34E, 0x00116A42, 0x00EA26C4, 0x0076E42A, 0x008DA8AC, 0x007B31A0, 0x00807D26,
+   0x009702B9, 0x006C4E3F, 0x009AD733, 0x00619BB5, 0x004F658B, 0x00B4290D, 0x0042B001,
+   0x00B9FC87, 0x00AE8318, 0x0055CF9E, 0x00A35692, 0x00581A14, 0x00FFAAEF, 0x0004E669,
+   0x00F27F65, 0x000933E3, 0x001E4C7C, 0x00E500FA, 0x001399F6, 0x00E8D570, 0x00C62B4E,
+   0x003D67C8, 0x00CBFEC4, 0x0030B242, 0x0027CDDD, 0x00DC815B, 0x002A1857, 0x00D154D1,
+   0x009F3526, 0x006479A0, 0x0092E0AC, 0x0069AC2A, 0x007ED3B5, 0x00859F33, 0x0073063F,
+   0x00884AB9, 0x00A6B487, 0x005DF801, 0x00AB610D, 0x00502D8B, 0x00475214, 0x00BC1E92,
+   0x004A879E, 0x00B1CB18, 0x00167BE3, 0x00ED3765, 0x001BAE69, 0x00E0E2EF, 0x00F79D70,
+   0x000CD1F6, 0x00FA48FA, 0x0001047C, 0x002FFA42, 0x00D4B6C4, 0x00222FC8, 0x00D9634E,
+   0x00CE1CD1, 0x00355057, 0x00C3C95B, 0x003885DD };
+
+const uint32_t CRC24_T1[256] = {
+   0x00000000, 0x00488F66, 0x00901ECD, 0x00D891AB, 0x00DB711C, 0x0093FE7A, 0x004B6FD1,
+   0x0003E0B7, 0x00B6E338, 0x00FE6C5E, 0x0026FDF5, 0x006E7293, 0x006D9224, 0x00251D42,
+   0x00FD8CE9, 0x00B5038F, 0x006CC771, 0x00244817, 0x00FCD9BC, 0x00B456DA, 0x00B7B66D,
+   0x00FF390B, 0x0027A8A0, 0x006F27C6, 0x00DA2449, 0x0092AB2F, 0x004A3A84, 0x0002B5E2,
+   0x00015555, 0x0049DA33, 0x00914B98, 0x00D9C4FE, 0x00D88EE3, 0x00900185, 0x0048902E,
+   0x00001F48, 0x0003FFFF, 0x004B7099, 0x0093E132, 0x00DB6E54, 0x006E6DDB, 0x0026E2BD,
+   0x00FE7316, 0x00B6FC70, 0x00B51CC7, 0x00FD93A1, 0x0025020A, 0x006D8D6C, 0x00B44992,
+   0x00FCC6F4, 0x0024575F, 0x006CD839, 0x006F388E, 0x0027B7E8, 0x00FF2643, 0x00B7A925,
+   0x0002AAAA, 0x004A25CC, 0x0092B467, 0x00DA3B01, 0x00D9DBB6, 0x009154D0, 0x0049C57B,
+   0x00014A1D, 0x004B5141, 0x0003DE27, 0x00DB4F8C, 0x0093C0EA, 0x0090205D, 0x00D8AF3B,
+   0x00003E90, 0x0048B1F6, 0x00FDB279, 0x00B53D1F, 0x006DACB4, 0x002523D2, 0x0026C365,
+   0x006E4C03, 0x00B6DDA8, 0x00FE52CE, 0x00279630, 0x006F1956, 0x00B788FD, 0x00FF079B,
+   0x00FCE72C, 0x00B4684A, 0x006CF9E1, 0x00247687, 0x00917508, 0x00D9FA6E, 0x00016BC5,
+   0x0049E4A3, 0x004A0414, 0x00028B72, 0x00DA1AD9, 0x009295BF, 0x0093DFA2, 0x00DB50C4,
+   0x0003C16F, 0x004B4E09, 0x0048AEBE, 0x000021D8, 0x00D8B073, 0x00903F15, 0x00253C9A,
+   0x006DB3FC, 0x00B52257, 0x00FDAD31, 0x00FE4D86, 0x00B6C2E0, 0x006E534B, 0x0026DC2D,
+   0x00FF18D3, 0x00B797B5, 0x006F061E, 0x00278978, 0x002469CF, 0x006CE6A9, 0x00B47702,
+   0x00FCF864, 0x0049FBEB, 0x0001748D, 0x00D9E526, 0x00916A40, 0x00928AF7, 0x00DA0591,
+   0x0002943A, 0x004A1B5C, 0x0096A282, 0x00DE2DE4, 0x0006BC4F, 0x004E3329, 0x004DD39E,
+   0x00055CF8, 0x00DDCD53, 0x00954235, 0x002041BA, 0x0068CEDC, 0x00B05F77, 0x00F8D011,
+   0x00FB30A6, 0x00B3BFC0, 0x006B2E6B, 0x0023A10D, 0x00FA65F3, 0x00B2EA95, 0x006A7B3E,
+   0x0022F458, 0x002114EF, 0x00699B89, 0x00B10A22, 0x00F98544, 0x004C86CB, 0x000409AD,
+   0x00DC9806, 0x00941760, 0x0097F7D7, 0x00DF78B1, 0x0007E91A, 0x004F667C, 0x004E2C61,
+   0x0006A307, 0x00DE32AC, 0x0096BDCA, 0x00955D7D, 0x00DDD21B, 0x000543B0, 0x004DCCD6,
+   0x00F8CF59, 0x00B0403F, 0x0068D194, 0x00205EF2, 0x0023BE45, 0x006B3123, 0x00B3A088,
+   0x00FB2FEE, 0x0022EB10, 0x006A6476, 0x00B2F5DD, 0x00FA7ABB, 0x00F99A0C, 0x00B1156A,
+   0x006984C1, 0x00210BA7, 0x00940828, 0x00DC874E, 0x000416E5, 0x004C9983, 0x004F7934,
+   0x0007F652, 0x00DF67F9, 0x0097E89F, 0x00DDF3C3, 0x00957CA5, 0x004DED0E, 0x00056268,
+   0x000682DF, 0x004E0DB9, 0x00969C12, 0x00DE1374, 0x006B10FB, 0x00239F9D, 0x00FB0E36,
+   0x00B38150, 0x00B061E7, 0x00F8EE81, 0x00207F2A, 0x0068F04C, 0x00B134B2, 0x00F9BBD4,
+   0x00212A7F, 0x0069A519, 0x006A45AE, 0x0022CAC8, 0x00FA5B63, 0x00B2D405, 0x0007D78A,
+   0x004F58EC, 0x0097C947, 0x00DF4621, 0x00DCA696, 0x009429F0, 0x004CB85B, 0x0004373D,
+   0x00057D20, 0x004DF246, 0x009563ED, 0x00DDEC8B, 0x00DE0C3C, 0x0096835A, 0x004E12F1,
+   0x00069D97, 0x00B39E18, 0x00FB117E, 0x002380D5, 0x006B0FB3, 0x0068EF04, 0x00206062,
+   0x00F8F1C9, 0x00B07EAF, 0x0069BA51, 0x00213537, 0x00F9A49C, 0x00B12BFA, 0x00B2CB4D,
+   0x00FA442B, 0x0022D580, 0x006A5AE6, 0x00DF5969, 0x0097D60F, 0x004F47A4, 0x0007C8C2,
+   0x00042875, 0x004CA713, 0x009436B8, 0x00DCB9DE };
+
+const uint32_t CRC24_T2[256] = {
+   0x00000000, 0x00D70983, 0x00555F80, 0x00825603, 0x0051F286, 0x0086FB05, 0x0004AD06,
+   0x00D3A485, 0x0059A88B, 0x008EA108, 0x000CF70B, 0x00DBFE88, 0x00085A0D, 0x00DF538E,
+   0x005D058D, 0x008A0C0E, 0x00491C91, 0x009E1512, 0x001C4311, 0x00CB4A92, 0x0018EE17,
+   0x00CFE794, 0x004DB197, 0x009AB814, 0x0010B41A, 0x00C7BD99, 0x0045EB9A, 0x0092E219,
+   0x0041469C, 0x00964F1F, 0x0014191C, 0x00C3109F, 0x006974A4, 0x00BE7D27, 0x003C2B24,
+   0x00EB22A7, 0x00388622, 0x00EF8FA1, 0x006DD9A2, 0x00BAD021, 0x0030DC2F, 0x00E7D5AC,
+   0x006583AF, 0x00B28A2C, 0x00612EA9, 0x00B6272A, 0x00347129, 0x00E378AA, 0x00206835,
+   0x00F761B6, 0x007537B5, 0x00A23E36, 0x00719AB3, 0x00A69330, 0x0024C533, 0x00F3CCB0,
+   0x0079C0BE, 0x00AEC93D, 0x002C9F3E, 0x00FB96BD, 0x00283238, 0x00FF3BBB, 0x007D6DB8,
+   0x00AA643B, 0x0029A4CE, 0x00FEAD4D, 0x007CFB4E, 0x00ABF2CD, 0x00785648, 0x00AF5FCB,
+   0x002D09C8, 0x00FA004B, 0x00700C45, 0x00A705C6, 0x002553C5, 0x00F25A46, 0x0021FEC3,
+   0x00F6F740, 0x0074A143, 0x00A3A8C0, 0x0060B85F, 0x00B7B1DC, 0x0035E7DF, 0x00E2EE5C,
+   0x00314AD9, 0x00E6435A, 0x00641559, 0x00B31CDA, 0x003910D4, 0x00EE1957, 0x006C4F54,
+   0x00BB46D7, 0x0068E252, 0x00BFEBD1, 0x003DBDD2, 0x00EAB451, 0x0040D06A, 0x0097D9E9,
+   0x00158FEA, 0x00C28669, 0x001122EC, 0x00C62B6F, 0x00447D6C, 0x009374EF, 0x001978E1,
+   0x00CE7162, 0x004C2761, 0x009B2EE2, 0x00488A67, 0x009F83E4, 0x001DD5E7, 0x00CADC64,
+   0x0009CCFB, 0x00DEC578, 0x005C937B, 0x008B9AF8, 0x00583E7D, 0x008F37FE, 0x000D61FD,
+   0x00DA687E, 0x00506470, 0x00876DF3, 0x00053BF0, 0x00D23273, 0x000196F6, 0x00D69F75,
+   0x0054C976, 0x0083C0F5, 0x00A9041B, 0x007E0D98, 0x00FC5B9B, 0x002B5218, 0x00F8F69D,
+   0x002FFF1E, 0x00ADA91D, 0x007AA09E, 0x00F0AC90, 0x0027A513, 0x00A5F310, 0x0072FA93,
+   0x00A15E16, 0x00765795, 0x00F40196, 0x00230815, 0x00E0188A, 0x00371109, 0x00B5470A,
+   0x00624E89, 0x00B1EA0C, 0x0066E38F, 0x00E4B58C, 0x0033BC0F, 0x00B9B001, 0x006EB982,
+   0x00ECEF81, 0x003BE602, 0x00E84287, 0x003F4B04, 0x00BD1D07, 0x006A1484, 0x00C070BF,
+   0x0017793C, 0x00952F3F, 0x004226BC, 0x00918239, 0x00468BBA, 0x00C4DDB9, 0x0013D43A,
+   0x0099D834, 0x004ED1B7, 0x00CC87B4, 0x001B8E37, 0x00C82AB2, 0x001F2331, 0x009D7532,
+   0x004A7CB1, 0x00896C2E, 0x005E65AD, 0x00DC33AE, 0x000B3A2D, 0x00D89EA8, 0x000F972B,
+   0x008DC128, 0x005AC8AB, 0x00D0C4A5, 0x0007CD26, 0x00859B25, 0x005292A6, 0x00813623,
+   0x00563FA0, 0x00D469A3, 0x00036020, 0x0080A0D5, 0x0057A956, 0x00D5FF55, 0x0002F6D6,
+   0x00D15253, 0x00065BD0, 0x00840DD3, 0x00530450, 0x00D9085E, 0x000E01DD, 0x008C57DE,
+   0x005B5E5D, 0x0088FAD8, 0x005FF35B, 0x00DDA558, 0x000AACDB, 0x00C9BC44, 0x001EB5C7,
+   0x009CE3C4, 0x004BEA47, 0x00984EC2, 0x004F4741, 0x00CD1142, 0x001A18C1, 0x009014CF,
+   0x00471D4C, 0x00C54B4F, 0x001242CC, 0x00C1E649, 0x0016EFCA, 0x0094B9C9, 0x0043B04A,
+   0x00E9D471, 0x003EDDF2, 0x00BC8BF1, 0x006B8272, 0x00B826F7, 0x006F2F74, 0x00ED7977,
+   0x003A70F4, 0x00B07CFA, 0x00677579, 0x00E5237A, 0x00322AF9, 0x00E18E7C, 0x003687FF,
+   0x00B4D1FC, 0x0063D87F, 0x00A0C8E0, 0x0077C163, 0x00F59760, 0x00229EE3, 0x00F13A66,
+   0x002633E5, 0x00A465E6, 0x00736C65, 0x00F9606B, 0x002E69E8, 0x00AC3FEB, 0x007B3668,
+   0x00A892ED, 0x007F9B6E, 0x00FDCD6D, 0x002AC4EE };
+
+const uint32_t CRC24_T3[256] = {
+   0x00000000, 0x00520936, 0x00A4126C, 0x00F61B5A, 0x004825D8, 0x001A2CEE, 0x00EC37B4,
+   0x00BE3E82, 0x006B0636, 0x00390F00, 0x00CF145A, 0x009D1D6C, 0x002323EE, 0x00712AD8,
+   0x00873182, 0x00D538B4, 0x00D60C6C, 0x0084055A, 0x00721E00, 0x00201736, 0x009E29B4,
+   0x00CC2082, 0x003A3BD8, 0x006832EE, 0x00BD0A5A, 0x00EF036C, 0x00191836, 0x004B1100,
+   0x00F52F82, 0x00A726B4, 0x00513DEE, 0x000334D8, 0x00AC19D8, 0x00FE10EE, 0x00080BB4,
+   0x005A0282, 0x00E43C00, 0x00B63536, 0x00402E6C, 0x0012275A, 0x00C71FEE, 0x009516D8,
+   0x00630D82, 0x003104B4, 0x008F3A36, 0x00DD3300, 0x002B285A, 0x0079216C, 0x007A15B4,
+   0x00281C82, 0x00DE07D8, 0x008C0EEE, 0x0032306C, 0x0060395A, 0x00962200, 0x00C42B36,
+   0x00111382, 0x00431AB4, 0x00B501EE, 0x00E708D8, 0x0059365A, 0x000B3F6C, 0x00FD2436,
+   0x00AF2D00, 0x00A37F36, 0x00F17600, 0x00076D5A, 0x0055646C, 0x00EB5AEE, 0x00B953D8,
+   0x004F4882, 0x001D41B4, 0x00C87900, 0x009A7036, 0x006C6B6C, 0x003E625A, 0x00805CD8,
+   0x00D255EE, 0x00244EB4, 0x00764782, 0x0075735A, 0x00277A6C, 0x00D16136, 0x00836800,
+   0x003D5682, 0x006F5FB4, 0x009944EE, 0x00CB4DD8, 0x001E756C, 0x004C7C5A, 0x00BA6700,
+   0x00E86E36, 0x005650B4, 0x00045982, 0x00F242D8, 0x00A04BEE, 0x000F66EE, 0x005D6FD8,
+   0x00AB7482, 0x00F97DB4, 0x00474336, 0x00154A00, 0x00E3515A, 0x00B1586C, 0x006460D8,
+   0x003669EE, 0x00C072B4, 0x00927B82, 0x002C4500, 0x007E4C36, 0x0088576C, 0x00DA5E5A,
+   0x00D96A82, 0x008B63B4, 0x007D78EE, 0x002F71D8, 0x00914F5A, 0x00C3466C, 0x00355D36,
+   0x00675400, 0x00B26CB4, 0x00E06582, 0x00167ED8, 0x004477EE, 0x00FA496C, 0x00A8405A,
+   0x005E5B00, 0x000C5236, 0x0046FF6C, 0x0014F65A, 0x00E2ED00, 0x00B0E436, 0x000EDAB4,
+   0x005CD382, 0x00AAC8D8, 0x00F8C1EE, 0x002DF95A, 0x007FF06C, 0x0089EB36, 0x00DBE200,
+   0x0065DC82, 0x0037D5B4, 0x00C1CEEE, 0x0093C7D8, 0x0090F300, 0x00C2FA36, 0x0034E16C,
+   0x0066E85A, 0x00D8D6D8, 0x008ADFEE, 0x007CC4B4, 0x002ECD82, 0x00FBF536, 0x00A9FC00,
+   0x005FE75A, 0x000DEE6C, 0x00B3D0EE, 0x00E1D9D8, 0x0017C282, 0x0045CBB4, 0x00EAE6B4,
+   0x00B8EF82, 0x004EF4D8, 0x001CFDEE, 0x00A2C36C, 0x00F0CA5A, 0x0006D100, 0x0054D836,
+   0x0081E082, 0x00D3E9B4, 0x0025F2EE, 0x0077FBD8, 0x00C9C55A, 0x009BCC6C, 0x006DD736,
+   0x003FDE00, 0x003CEAD8, 0x006EE3EE, 0x0098F8B4, 0x00CAF182, 0x0074CF00, 0x0026C636,
+   0x00D0DD6C, 0x0082D45A, 0x0057ECEE, 0x0005E5D8, 0x00F3FE82, 0x00A1F7B4, 0x001FC936,
+   0x004DC000, 0x00BBDB5A, 0x00E9D26C, 0x00E5805A, 0x00B7896C, 0x00419236, 0x00139B00,
+   0x00ADA582, 0x00FFACB4, 0x0009B7EE, 0x005BBED8, 0x008E866C, 0x00DC8F5A, 0x002A9400,
+   0x00789D36, 0x00C6A3B4, 0x0094AA82, 0x0062B1D8, 0x0030B8EE, 0x00338C36, 0x00618500,
+   0x00979E5A, 0x00C5976C, 0x007BA9EE, 0x0029A0D8, 0x00DFBB82, 0x008DB2B4, 0x00588A00,
+   0x000A8336, 0x00FC986C, 0x00AE915A, 0x0010AFD8, 0x0042A6EE, 0x00B4BDB4, 0x00E6B482,
+   0x00499982, 0x001B90B4, 0x00ED8BEE, 0x00BF82D8, 0x0001BC5A, 0x0053B56C, 0x00A5AE36,
+   0x00F7A700, 0x00229FB4, 0x00709682, 0x00868DD8, 0x00D484EE, 0x006ABA6C, 0x0038B35A,
+   0x00CEA800, 0x009CA136, 0x009F95EE, 0x00CD9CD8, 0x003B8782, 0x00698EB4, 0x00D7B036,
+   0x0085B900, 0x0073A25A, 0x0021AB6C, 0x00F493D8, 0x00A69AEE, 0x005081B4, 0x00028882,
+   0x00BCB600, 0x00EEBF36, 0x0018A46C, 0x004AAD5A };
+
+inline uint32_t process8(uint32_t crc, uint8_t data)
+   {
+   return (crc >> 8) ^ CRC24_T0[(crc & 0xff) ^ data];
+   }
+
+inline uint32_t process32(uint32_t crc, uint32_t word)
+   {
+   crc ^= word;
+   crc = CRC24_T3[(crc >>  0) & 0xff]
+       ^ CRC24_T2[(crc >>  8) & 0xff]
+       ^ CRC24_T1[(crc >> 16) & 0xff]
+       ^ CRC24_T0[(crc >> 24) & 0xff];
+   return crc;
+   }
+}
+
+std::unique_ptr<HashFunction> CRC24::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new CRC24(*this));
+   }
+
+/*
+* Update a CRC24 Checksum
+*
+* Implementation uses Slicing-by-N algorithm described in
+* "Novel Table Lookup-Based Algorithms for High-Performance
+* CRC Generation", by M.Kounavis.
+*
+* This algorithm uses 4 precomputed look-up tables. First
+* table T0 is computed same way as in a method proposed
+* by D. Sarwate (1988). Then T_1, T2 and T3 are computed
+* in following way:
+*
+*    T1[j] = (T0[j] >> 8) ^ T0[ T0[j] & 0xFF ]
+*    T2[j] = (T1[j] >> 8) ^ T0[ T1[j] & 0xFF ]
+*    T3[j] = (T2[j] >> 8) ^ T0[ T2[j] & 0xFF ]
+*
+*/
+void CRC24::add_data(const uint8_t input[], size_t length)
+   {
+   uint32_t d[4];
+   uint32_t tmp = m_crc;
+
+   // Input is word aligned if WA & input == 0
+   static const uint8_t WA = (BOTAN_MP_WORD_BITS/8) - 1;
+
+   // Ensure input is word aligned before processing in parallel
+   for(;length && (reinterpret_cast<uintptr_t>(input) & WA); length--)
+      tmp = process8(tmp, *input++);
+
+   while(length >= 16)
+      {
+      load_le(d, input, 4);
+      tmp = process32(tmp, d[0]);
+      tmp = process32(tmp, d[1]);
+      tmp = process32(tmp, d[2]);
+      tmp = process32(tmp, d[3]);
+
+      input += 16;
+      length -= 16;
+      }
+
+   while(length--)
+      tmp = process8(tmp, *input++);
+
+   m_crc = tmp & 0xffffff;
+   }
+
+/*
+* Finalize a CRC24 Checksum
+*/
+void CRC24::final_result(uint8_t output[])
+   {
+   output[0] = get_byte(3, m_crc);
+   output[1] = get_byte(2, m_crc);
+   output[2] = get_byte(1, m_crc);
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/checksum/crc24/crc24.h b/third_party/botan/src/lib/hash/checksum/crc24/crc24.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/crc24/crc24.h
@@ -0,0 +1,41 @@
+/*
+* CRC24
+* (C) 1999-2007 Jack Lloyd
+* (C) 2017 [Ribose Inc](https://www.ribose.com). Performed by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRC24_H_
+#define BOTAN_CRC24_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(crc24.h)
+
+namespace Botan {
+
+/**
+* 24-bit cyclic redundancy check
+*/
+class BOTAN_PUBLIC_API(2,0) CRC24 final : public HashFunction
+   {
+   public:
+      std::string name() const override { return "CRC24"; }
+      size_t output_length() const override { return 3; }
+      HashFunction* clone() const override { return new CRC24; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override { m_crc = 0XCE04B7L; }
+
+      CRC24() { clear(); }
+      ~CRC24() { clear(); }
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      uint32_t m_crc;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp b/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp
@@ -0,0 +1,107 @@
+/*
+* CRC32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crc32.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> CRC32::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new CRC32(*this));
+   }
+
+/*
+* Update a CRC32 Checksum
+*/
+void CRC32::add_data(const uint8_t input[], size_t length)
+   {
+   const uint32_t TABLE[256] = {
+      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+      0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+      0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+      0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+      0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+      0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+      0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+      0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+      0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+      0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+      0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+      0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+      0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+      0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+      0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+      0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+      0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+      0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+      0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+      0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+      0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+      0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+      0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+      0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+      0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+      0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+      0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+      0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+      0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+      0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+      0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+      0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+      0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+      0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+      0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+      0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+      0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+      0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+      0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+      0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+      0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };
+
+   uint32_t tmp = m_crc;
+   while(length >= 16)
+      {
+      tmp = TABLE[(tmp ^ input[ 0]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 1]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 2]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 3]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 4]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 5]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 6]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 7]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 8]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[ 9]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[10]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[11]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[12]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[13]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[14]) & 0xFF] ^ (tmp >> 8);
+      tmp = TABLE[(tmp ^ input[15]) & 0xFF] ^ (tmp >> 8);
+      input += 16;
+      length -= 16;
+      }
+
+   for(size_t i = 0; i != length; ++i)
+      tmp = TABLE[(tmp ^ input[i]) & 0xFF] ^ (tmp >> 8);
+
+   m_crc = tmp;
+   }
+
+/*
+* Finalize a CRC32 Checksum
+*/
+void CRC32::final_result(uint8_t output[])
+   {
+   m_crc ^= 0xFFFFFFFF;
+   store_be(m_crc, output);
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/checksum/crc32/crc32.h b/third_party/botan/src/lib/hash/checksum/crc32/crc32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/checksum/crc32/crc32.h
@@ -0,0 +1,40 @@
+/*
+* CRC32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRC32_H_
+#define BOTAN_CRC32_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(crc32.h)
+
+namespace Botan {
+
+/**
+* 32-bit cyclic redundancy check
+*/
+class BOTAN_PUBLIC_API(2,0) CRC32 final : public HashFunction
+   {
+   public:
+      std::string name() const override { return "CRC32"; }
+      size_t output_length() const override { return 4; }
+      HashFunction* clone() const override { return new CRC32; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override { m_crc = 0xFFFFFFFF; }
+
+      CRC32() { clear(); }
+      ~CRC32() { clear(); }
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      uint32_t m_crc;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/comb4p/comb4p.cpp b/third_party/botan/src/lib/hash/comb4p/comb4p.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/comb4p/comb4p.cpp
@@ -0,0 +1,110 @@
+/*
+* Comb4P hash combiner
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/comb4p.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+void comb4p_round(secure_vector<uint8_t>& out,
+                  const secure_vector<uint8_t>& in,
+                  uint8_t round_no,
+                  HashFunction& h1,
+                  HashFunction& h2)
+   {
+   h1.update(round_no);
+   h2.update(round_no);
+
+   h1.update(in.data(), in.size());
+   h2.update(in.data(), in.size());
+
+   secure_vector<uint8_t> h_buf = h1.final();
+   xor_buf(out.data(), h_buf.data(), std::min(out.size(), h_buf.size()));
+
+   h_buf = h2.final();
+   xor_buf(out.data(), h_buf.data(), std::min(out.size(), h_buf.size()));
+   }
+
+}
+
+Comb4P::Comb4P(HashFunction* h1, HashFunction* h2) :
+   m_hash1(h1), m_hash2(h2)
+   {
+   if(m_hash1->name() == m_hash2->name())
+      throw Invalid_Argument("Comb4P: Must use two distinct hashes");
+
+   if(m_hash1->output_length() != m_hash2->output_length())
+      throw Invalid_Argument("Comb4P: Incompatible hashes " +
+                                  m_hash1->name() + " and " +
+                                  m_hash2->name());
+
+   clear();
+   }
+
+size_t Comb4P::hash_block_size() const
+   {
+   if(m_hash1->hash_block_size() == m_hash2->hash_block_size())
+      return m_hash1->hash_block_size();
+
+   /*
+   * Return LCM of the block sizes? This would probably be OK for
+   * HMAC, which is the main thing relying on knowing the block size.
+   */
+   return 0;
+   }
+
+void Comb4P::clear()
+   {
+   m_hash1->clear();
+   m_hash2->clear();
+
+   // Prep for processing next message, if any
+   m_hash1->update(0);
+   m_hash2->update(0);
+   }
+
+std::unique_ptr<HashFunction> Comb4P::copy_state() const
+   {
+   std::unique_ptr<Comb4P> copy(new Comb4P);
+   copy->m_hash1 = m_hash1->copy_state();
+   copy->m_hash2 = m_hash2->copy_state();
+   // work around GCC 4.8 bug
+   return std::unique_ptr<HashFunction>(copy.release());
+   }
+
+void Comb4P::add_data(const uint8_t input[], size_t length)
+   {
+   m_hash1->update(input, length);
+   m_hash2->update(input, length);
+   }
+
+void Comb4P::final_result(uint8_t out[])
+   {
+   secure_vector<uint8_t> h1 = m_hash1->final();
+   secure_vector<uint8_t> h2 = m_hash2->final();
+
+   // First round
+   xor_buf(h1.data(), h2.data(), std::min(h1.size(), h2.size()));
+
+   // Second round
+   comb4p_round(h2, h1, 1, *m_hash1, *m_hash2);
+
+   // Third round
+   comb4p_round(h1, h2, 2, *m_hash1, *m_hash2);
+
+   copy_mem(out            , h1.data(), h1.size());
+   copy_mem(out + h1.size(), h2.data(), h2.size());
+
+   // Prep for processing next message, if any
+   m_hash1->update(0);
+   m_hash2->update(0);
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/hash/comb4p/comb4p.h b/third_party/botan/src/lib/hash/comb4p/comb4p.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/comb4p/comb4p.h
@@ -0,0 +1,61 @@
+/*
+* Comb4P hash combiner
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMB4P_H_
+#define BOTAN_COMB4P_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(comb4p.h)
+
+namespace Botan {
+
+/**
+* Combines two hash functions using a Feistel scheme. Described in
+* "On the Security of Hash Function Combiners", Anja Lehmann
+*/
+class BOTAN_PUBLIC_API(2,0) Comb4P final : public HashFunction
+   {
+   public:
+      /**
+      * @param h1 the first hash
+      * @param h2 the second hash
+      */
+      Comb4P(HashFunction* h1, HashFunction* h2);
+
+      size_t hash_block_size() const override;
+
+      size_t output_length() const override
+         {
+         return m_hash1->output_length() + m_hash2->output_length();
+         }
+
+      HashFunction* clone() const override
+         {
+         return new Comb4P(m_hash1->clone(), m_hash2->clone());
+         }
+
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      std::string name() const override
+         {
+         return "Comb4P(" + m_hash1->name() + "," + m_hash2->name() + ")";
+         }
+
+      void clear() override;
+   private:
+      Comb4P() = default;
+
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      std::unique_ptr<HashFunction> m_hash1, m_hash2;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp b/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp
@@ -0,0 +1,248 @@
+/*
+* GOST 34.11
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_3411.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/**
+* GOST 34.11 Constructor
+*/
+GOST_34_11::GOST_34_11() :
+   m_cipher(GOST_28147_89_Params("R3411_CryptoPro")),
+   m_buffer(32),
+   m_sum(32),
+   m_hash(32)
+   {
+   m_count = 0;
+   m_position = 0;
+   }
+
+void GOST_34_11::clear()
+   {
+   m_cipher.clear();
+   zeroise(m_sum);
+   zeroise(m_hash);
+   m_count = 0;
+   m_position = 0;
+   }
+
+std::unique_ptr<HashFunction> GOST_34_11::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new GOST_34_11(*this));
+   }
+
+/**
+* Hash additional inputs
+*/
+void GOST_34_11::add_data(const uint8_t input[], size_t length)
+   {
+   m_count += length;
+
+   if(m_position)
+      {
+      buffer_insert(m_buffer, m_position, input, length);
+
+      if(m_position + length >= hash_block_size())
+         {
+         compress_n(m_buffer.data(), 1);
+         input += (hash_block_size() - m_position);
+         length -= (hash_block_size() - m_position);
+         m_position = 0;
+         }
+      }
+
+   const size_t full_blocks = length / hash_block_size();
+   const size_t remaining   = length % hash_block_size();
+
+   if(full_blocks)
+      compress_n(input, full_blocks);
+
+   buffer_insert(m_buffer, m_position, input + full_blocks * hash_block_size(), remaining);
+   m_position += remaining;
+   }
+
+/**
+* The GOST 34.11 compression function
+*/
+void GOST_34_11::compress_n(const uint8_t input[], size_t blocks)
+   {
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      for(uint16_t j = 0, carry = 0; j != 32; ++j)
+         {
+         uint16_t s = m_sum[j] + input[32*i+j] + carry;
+         carry = get_byte(0, s);
+         m_sum[j] = get_byte(1, s);
+         }
+
+      uint8_t S[32] = { 0 };
+
+      uint64_t U[4], V[4];
+      load_be(U, m_hash.data(), 4);
+      load_be(V, input + 32*i, 4);
+
+      for(size_t j = 0; j != 4; ++j)
+         {
+         uint8_t key[32] = { 0 };
+
+         // P transformation
+         for(size_t k = 0; k != 4; ++k)
+            {
+            const uint64_t UVk = U[k] ^ V[k];
+            for(size_t l = 0; l != 8; ++l)
+               key[4*l+k] = get_byte(l, UVk);
+            }
+
+         m_cipher.set_key(key, 32);
+         m_cipher.encrypt(&m_hash[8*j], S + 8*j);
+
+         if(j == 3)
+            break;
+
+         // A(x)
+         uint64_t A_U = U[0];
+         U[0] = U[1];
+         U[1] = U[2];
+         U[2] = U[3];
+         U[3] = U[0] ^ A_U;
+
+         if(j == 1) // C_3
+            {
+            U[0] ^= 0x00FF00FF00FF00FF;
+            U[1] ^= 0xFF00FF00FF00FF00;
+            U[2] ^= 0x00FFFF00FF0000FF;
+            U[3] ^= 0xFF000000FFFF00FF;
+            }
+
+         // A(A(x))
+         uint64_t AA_V_1 = V[0] ^ V[1];
+         uint64_t AA_V_2 = V[1] ^ V[2];
+         V[0] = V[2];
+         V[1] = V[3];
+         V[2] = AA_V_1;
+         V[3] = AA_V_2;
+         }
+
+      uint8_t S2[32] = { 0 };
+
+      // 12 rounds of psi
+      S2[ 0] = S[24];
+      S2[ 1] = S[25];
+      S2[ 2] = S[26];
+      S2[ 3] = S[27];
+      S2[ 4] = S[28];
+      S2[ 5] = S[29];
+      S2[ 6] = S[30];
+      S2[ 7] = S[31];
+      S2[ 8] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[ 6] ^ S[24] ^ S[30];
+      S2[ 9] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[ 7] ^ S[25] ^ S[31];
+      S2[10] = S[ 0] ^ S[ 8] ^ S[24] ^ S[26] ^ S[30];
+      S2[11] = S[ 1] ^ S[ 9] ^ S[25] ^ S[27] ^ S[31];
+      S2[12] = S[ 0] ^ S[ 4] ^ S[ 6] ^ S[10] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+      S2[13] = S[ 1] ^ S[ 5] ^ S[ 7] ^ S[11] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+      S2[14] = S[ 0] ^ S[ 4] ^ S[ 8] ^ S[12] ^ S[24] ^ S[26] ^ S[28];
+      S2[15] = S[ 1] ^ S[ 5] ^ S[ 9] ^ S[13] ^ S[25] ^ S[27] ^ S[29];
+      S2[16] = S[ 2] ^ S[ 6] ^ S[10] ^ S[14] ^ S[26] ^ S[28] ^ S[30];
+      S2[17] = S[ 3] ^ S[ 7] ^ S[11] ^ S[15] ^ S[27] ^ S[29] ^ S[31];
+      S2[18] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[12] ^ S[16] ^ S[24] ^ S[28];
+      S2[19] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[13] ^ S[17] ^ S[25] ^ S[29];
+      S2[20] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[14] ^ S[18] ^ S[26] ^ S[30];
+      S2[21] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[15] ^ S[19] ^ S[27] ^ S[31];
+      S2[22] = S[ 0] ^ S[ 2] ^ S[10] ^ S[12] ^ S[16] ^ S[20] ^ S[24] ^ S[28] ^ S[30];
+      S2[23] = S[ 1] ^ S[ 3] ^ S[11] ^ S[13] ^ S[17] ^ S[21] ^ S[25] ^ S[29] ^ S[31];
+      S2[24] = S[ 0] ^ S[ 6] ^ S[12] ^ S[14] ^ S[18] ^ S[22] ^ S[24] ^ S[26];
+      S2[25] = S[ 1] ^ S[ 7] ^ S[13] ^ S[15] ^ S[19] ^ S[23] ^ S[25] ^ S[27];
+      S2[26] = S[ 2] ^ S[ 8] ^ S[14] ^ S[16] ^ S[20] ^ S[24] ^ S[26] ^ S[28];
+      S2[27] = S[ 3] ^ S[ 9] ^ S[15] ^ S[17] ^ S[21] ^ S[25] ^ S[27] ^ S[29];
+      S2[28] = S[ 4] ^ S[10] ^ S[16] ^ S[18] ^ S[22] ^ S[26] ^ S[28] ^ S[30];
+      S2[29] = S[ 5] ^ S[11] ^ S[17] ^ S[19] ^ S[23] ^ S[27] ^ S[29] ^ S[31];
+      S2[30] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[12] ^ S[18] ^ S[20] ^ S[28];
+      S2[31] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[13] ^ S[19] ^ S[21] ^ S[29];
+
+      xor_buf(S, S2, input + 32*i, 32);
+
+      S2[0] = S[0] ^ S[2] ^ S[4] ^ S[6] ^ S[24] ^ S[30];
+      S2[1] = S[1] ^ S[3] ^ S[5] ^ S[7] ^ S[25] ^ S[31];
+
+      copy_mem(S, S+2, 30);
+      S[30] = S2[0];
+      S[31] = S2[1];
+
+      xor_buf(S, m_hash.data(), 32);
+
+      // 61 rounds of psi
+      S2[ 0] = S[ 2] ^ S[ 6] ^ S[14] ^ S[20] ^ S[22] ^ S[26] ^ S[28] ^ S[30];
+      S2[ 1] = S[ 3] ^ S[ 7] ^ S[15] ^ S[21] ^ S[23] ^ S[27] ^ S[29] ^ S[31];
+      S2[ 2] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[16] ^ S[22] ^ S[28];
+      S2[ 3] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[17] ^ S[23] ^ S[29];
+      S2[ 4] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[18] ^ S[24] ^ S[30];
+      S2[ 5] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[19] ^ S[25] ^ S[31];
+      S2[ 6] = S[ 0] ^ S[ 2] ^ S[10] ^ S[12] ^ S[20] ^ S[24] ^ S[26] ^ S[30];
+      S2[ 7] = S[ 1] ^ S[ 3] ^ S[11] ^ S[13] ^ S[21] ^ S[25] ^ S[27] ^ S[31];
+      S2[ 8] = S[ 0] ^ S[ 6] ^ S[12] ^ S[14] ^ S[22] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+      S2[ 9] = S[ 1] ^ S[ 7] ^ S[13] ^ S[15] ^ S[23] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+      S2[10] = S[ 0] ^ S[ 4] ^ S[ 6] ^ S[ 8] ^ S[14] ^ S[16] ^ S[26] ^ S[28];
+      S2[11] = S[ 1] ^ S[ 5] ^ S[ 7] ^ S[ 9] ^ S[15] ^ S[17] ^ S[27] ^ S[29];
+      S2[12] = S[ 2] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[16] ^ S[18] ^ S[28] ^ S[30];
+      S2[13] = S[ 3] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[17] ^ S[19] ^ S[29] ^ S[31];
+      S2[14] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[12] ^ S[18] ^ S[20] ^ S[24];
+      S2[15] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[13] ^ S[19] ^ S[21] ^ S[25];
+      S2[16] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[12] ^ S[14] ^ S[20] ^ S[22] ^ S[26];
+      S2[17] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[13] ^ S[15] ^ S[21] ^ S[23] ^ S[27];
+      S2[18] = S[ 4] ^ S[ 6] ^ S[10] ^ S[12] ^ S[14] ^ S[16] ^ S[22] ^ S[24] ^ S[28];
+      S2[19] = S[ 5] ^ S[ 7] ^ S[11] ^ S[13] ^ S[15] ^ S[17] ^ S[23] ^ S[25] ^ S[29];
+      S2[20] = S[ 6] ^ S[ 8] ^ S[12] ^ S[14] ^ S[16] ^ S[18] ^ S[24] ^ S[26] ^ S[30];
+      S2[21] = S[ 7] ^ S[ 9] ^ S[13] ^ S[15] ^ S[17] ^ S[19] ^ S[25] ^ S[27] ^ S[31];
+      S2[22] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[14] ^ S[16] ^
+               S[18] ^ S[20] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+      S2[23] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[15] ^ S[17] ^
+               S[19] ^ S[21] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+      S2[24] = S[ 0] ^ S[ 8] ^ S[10] ^ S[12] ^ S[16] ^ S[18] ^ S[20] ^ S[22] ^
+               S[24] ^ S[26] ^ S[28];
+      S2[25] = S[ 1] ^ S[ 9] ^ S[11] ^ S[13] ^ S[17] ^ S[19] ^ S[21] ^ S[23] ^
+               S[25] ^ S[27] ^ S[29];
+      S2[26] = S[ 2] ^ S[10] ^ S[12] ^ S[14] ^ S[18] ^ S[20] ^ S[22] ^ S[24] ^
+               S[26] ^ S[28] ^ S[30];
+      S2[27] = S[ 3] ^ S[11] ^ S[13] ^ S[15] ^ S[19] ^ S[21] ^ S[23] ^ S[25] ^
+               S[27] ^ S[29] ^ S[31];
+      S2[28] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[12] ^ S[14] ^ S[16] ^ S[20] ^ S[22] ^ S[26] ^ S[28];
+      S2[29] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[13] ^ S[15] ^ S[17] ^ S[21] ^ S[23] ^ S[27] ^ S[29];
+      S2[30] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[14] ^ S[16] ^ S[18] ^ S[22] ^ S[24] ^ S[28] ^ S[30];
+      S2[31] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[15] ^ S[17] ^ S[19] ^ S[23] ^ S[25] ^ S[29] ^ S[31];
+
+      copy_mem(m_hash.data(), S2, 32);
+      }
+   }
+
+/**
+* Produce the final GOST 34.11 output
+*/
+void GOST_34_11::final_result(uint8_t out[])
+   {
+   if(m_position)
+      {
+      clear_mem(m_buffer.data() + m_position, m_buffer.size() - m_position);
+      compress_n(m_buffer.data(), 1);
+      }
+
+   secure_vector<uint8_t> length_buf(32);
+   const uint64_t bit_count = m_count * 8;
+   store_le(bit_count, length_buf.data());
+
+   secure_vector<uint8_t> sum_buf = m_sum;
+
+   compress_n(length_buf.data(), 1);
+   compress_n(sum_buf.data(), 1);
+
+   copy_mem(out, m_hash.data(), 32);
+
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/gost_3411/gost_3411.h b/third_party/botan/src/lib/hash/gost_3411/gost_3411.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/gost_3411/gost_3411.h
@@ -0,0 +1,47 @@
+/*
+* GOST 34.11
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_3411_H_
+#define BOTAN_GOST_3411_H_
+
+#include <botan/hash.h>
+#include <botan/gost_28147.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gost_3411.h)
+
+namespace Botan {
+
+/**
+* GOST 34.11
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_34_11 final : public HashFunction
+   {
+   public:
+      std::string name() const override { return "GOST-R-34.11-94" ; }
+      size_t output_length() const override { return 32; }
+      size_t hash_block_size() const override { return 32; }
+      HashFunction* clone() const override { return new GOST_34_11; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      GOST_34_11();
+   private:
+      void compress_n(const uint8_t input[], size_t blocks);
+
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+
+      GOST_28147_89 m_cipher;
+      secure_vector<uint8_t> m_buffer, m_sum, m_hash;
+      size_t m_position;
+      uint64_t m_count;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/hash.cpp b/third_party/botan/src/lib/hash/hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/hash.cpp
@@ -0,0 +1,360 @@
+/*
+* Hash Functions
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_ADLER32)
+  #include <botan/adler32.h>
+#endif
+
+#if defined(BOTAN_HAS_CRC24)
+  #include <botan/crc24.h>
+#endif
+
+#if defined(BOTAN_HAS_CRC32)
+  #include <botan/crc32.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_11)
+  #include <botan/gost_3411.h>
+#endif
+
+#if defined(BOTAN_HAS_KECCAK)
+  #include <botan/keccak.h>
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+  #include <botan/md4.h>
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+  #include <botan/md5.h>
+#endif
+
+#if defined(BOTAN_HAS_RIPEMD_160)
+  #include <botan/rmd160.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA1)
+  #include <botan/sha160.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32)
+  #include <botan/sha2_32.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64)
+  #include <botan/sha2_64.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA3)
+  #include <botan/sha3.h>
+#endif
+
+#if defined(BOTAN_HAS_SHAKE)
+  #include <botan/shake.h>
+#endif
+
+#if defined(BOTAN_HAS_SKEIN_512)
+  #include <botan/skein_512.h>
+#endif
+
+#if defined(BOTAN_HAS_STREEBOG)
+  #include <botan/streebog.h>
+#endif
+
+#if defined(BOTAN_HAS_SM3)
+  #include <botan/sm3.h>
+#endif
+
+#if defined(BOTAN_HAS_TIGER)
+  #include <botan/tiger.h>
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+  #include <botan/whrlpool.h>
+#endif
+
+#if defined(BOTAN_HAS_PARALLEL_HASH)
+  #include <botan/par_hash.h>
+#endif
+
+#if defined(BOTAN_HAS_COMB4P)
+  #include <botan/comb4p.h>
+#endif
+
+#if defined(BOTAN_HAS_BLAKE2B)
+  #include <botan/blake2b.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+  #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> HashFunction::create(const std::string& algo_spec,
+                                                   const std::string& provider)
+   {
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+   if(provider.empty() || provider == "commoncrypto")
+      {
+      if(auto hash = make_commoncrypto_hash(algo_spec))
+         return hash;
+
+      if(!provider.empty())
+         return nullptr;
+      }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider.empty() || provider == "openssl")
+      {
+      if(auto hash = make_openssl_hash(algo_spec))
+         return hash;
+
+      if(!provider.empty())
+         return nullptr;
+      }
+#endif
+
+   if(provider.empty() == false && provider != "base")
+      return nullptr; // unknown provider
+
+#if defined(BOTAN_HAS_SHA1)
+   if(algo_spec == "SHA-160" ||
+      algo_spec == "SHA-1" ||
+      algo_spec == "SHA1")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_160);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32)
+   if(algo_spec == "SHA-224")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_224);
+      }
+
+   if(algo_spec == "SHA-256")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64)
+   if(algo_spec == "SHA-384")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_384);
+      }
+
+   if(algo_spec == "SHA-512")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_512);
+      }
+
+   if(algo_spec == "SHA-512-256")
+      {
+      return std::unique_ptr<HashFunction>(new SHA_512_256);
+      }
+#endif
+
+#if defined(BOTAN_HAS_RIPEMD_160)
+   if(algo_spec == "RIPEMD-160")
+      {
+      return std::unique_ptr<HashFunction>(new RIPEMD_160);
+      }
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+   if(algo_spec == "Whirlpool")
+      {
+      return std::unique_ptr<HashFunction>(new Whirlpool);
+      }
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+   if(algo_spec == "MD5")
+      {
+      return std::unique_ptr<HashFunction>(new MD5);
+      }
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+   if(algo_spec == "MD4")
+      {
+      return std::unique_ptr<HashFunction>(new MD4);
+      }
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_11)
+   if(algo_spec == "GOST-R-34.11-94" || algo_spec == "GOST-34.11")
+      {
+      return std::unique_ptr<HashFunction>(new GOST_34_11);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ADLER32)
+   if(algo_spec == "Adler32")
+      {
+      return std::unique_ptr<HashFunction>(new Adler32);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CRC24)
+   if(algo_spec == "CRC24")
+      {
+      return std::unique_ptr<HashFunction>(new CRC24);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CRC32)
+   if(algo_spec == "CRC32")
+      {
+      return std::unique_ptr<HashFunction>(new CRC32);
+      }
+#endif
+
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_TIGER)
+   if(req.algo_name() == "Tiger")
+      {
+      return std::unique_ptr<HashFunction>(
+         new Tiger(req.arg_as_integer(0, 24),
+                   req.arg_as_integer(1, 3)));
+      }
+#endif
+
+#if defined(BOTAN_HAS_SKEIN_512)
+   if(req.algo_name() == "Skein-512")
+      {
+      return std::unique_ptr<HashFunction>(
+         new Skein_512(req.arg_as_integer(0, 512), req.arg(1, "")));
+      }
+#endif
+
+#if defined(BOTAN_HAS_BLAKE2B)
+   if(req.algo_name() == "Blake2b" || req.algo_name() == "BLAKE2b")
+      {
+      return std::unique_ptr<HashFunction>(
+         new Blake2b(req.arg_as_integer(0, 512)));
+   }
+#endif
+
+#if defined(BOTAN_HAS_KECCAK)
+   if(req.algo_name() == "Keccak-1600")
+      {
+      return std::unique_ptr<HashFunction>(
+         new Keccak_1600(req.arg_as_integer(0, 512)));
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA3)
+   if(req.algo_name() == "SHA-3")
+      {
+      return std::unique_ptr<HashFunction>(
+         new SHA_3(req.arg_as_integer(0, 512)));
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHAKE)
+   if(req.algo_name() == "SHAKE-128")
+      {
+      return std::unique_ptr<HashFunction>(new SHAKE_128(req.arg_as_integer(0, 128)));
+      }
+   if(req.algo_name() == "SHAKE-256")
+      {
+      return std::unique_ptr<HashFunction>(new SHAKE_256(req.arg_as_integer(0, 256)));
+      }
+#endif
+
+#if defined(BOTAN_HAS_STREEBOG)
+   if(algo_spec == "Streebog-256")
+      {
+      return std::unique_ptr<HashFunction>(new Streebog_256);
+      }
+   if(algo_spec == "Streebog-512")
+      {
+      return std::unique_ptr<HashFunction>(new Streebog_512);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SM3)
+   if(algo_spec == "SM3")
+      {
+      return std::unique_ptr<HashFunction>(new SM3);
+      }
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+   if(req.algo_name() == "Whirlpool")
+      {
+      return std::unique_ptr<HashFunction>(new Whirlpool);
+      }
+#endif
+
+#if defined(BOTAN_HAS_PARALLEL_HASH)
+   if(req.algo_name() == "Parallel")
+      {
+      std::vector<std::unique_ptr<HashFunction>> hashes;
+
+      for(size_t i = 0; i != req.arg_count(); ++i)
+         {
+         auto h = HashFunction::create(req.arg(i));
+         if(!h)
+            {
+            return nullptr;
+            }
+         hashes.push_back(std::move(h));
+         }
+
+      return std::unique_ptr<HashFunction>(new Parallel(hashes));
+      }
+#endif
+
+#if defined(BOTAN_HAS_COMB4P)
+   if(req.algo_name() == "Comb4P" && req.arg_count() == 2)
+      {
+      std::unique_ptr<HashFunction> h1(HashFunction::create(req.arg(0)));
+      std::unique_ptr<HashFunction> h2(HashFunction::create(req.arg(1)));
+
+      if(h1 && h2)
+         return std::unique_ptr<HashFunction>(new Comb4P(h1.release(), h2.release()));
+      }
+#endif
+
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<HashFunction>
+HashFunction::create_or_throw(const std::string& algo,
+                              const std::string& provider)
+   {
+   if(auto hash = HashFunction::create(algo, provider))
+      {
+      return hash;
+      }
+   throw Lookup_Error("Hash", algo, provider);
+   }
+
+std::vector<std::string> HashFunction::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<HashFunction>(algo_spec, {"base", "openssl", "commoncrypto"});
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/hash/hash.h b/third_party/botan/src/lib/hash/hash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/hash.h
@@ -0,0 +1,91 @@
+/*
+* Hash Function Base Class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HASH_FUNCTION_BASE_CLASS_H_
+#define BOTAN_HASH_FUNCTION_BASE_CLASS_H_
+
+#include <botan/buf_comp.h>
+#include <string>
+#include <memory>
+
+namespace Botan {
+
+/**
+* This class represents hash function (message digest) objects
+*/
+class BOTAN_PUBLIC_API(2,0) HashFunction : public Buffered_Computation
+   {
+   public:
+      /**
+      * Create an instance based on a name, or return null if the
+      * algo/provider combination cannot be found. If provider is
+      * empty then best available is chosen.
+      */
+      static std::unique_ptr<HashFunction>
+         create(const std::string& algo_spec,
+                const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to use
+      * Throws Lookup_Error if not found.
+      */
+      static std::unique_ptr<HashFunction>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      * @param algo_spec algorithm name
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * @return new object representing the same algorithm as *this
+      */
+      virtual HashFunction* clone() const = 0;
+
+      /**
+      * @return provider information about this implementation. Default is "base",
+      * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+      */
+      virtual std::string provider() const { return "base"; }
+
+      virtual ~HashFunction() = default;
+
+      /**
+      * Reset the state.
+      */
+      virtual void clear() = 0;
+
+      /**
+      * @return the hash function name
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * @return hash block size as defined for this algorithm
+      */
+      virtual size_t hash_block_size() const { return 0; }
+
+      /**
+      * Return a new hash object with the same state as *this. This
+      * allows computing the hash of several messages with a common
+      * prefix more efficiently than would otherwise be possible.
+      *
+      * This function should be called `clone` but that was already
+      * used for the case of returning an uninitialized object.
+      * @return new hash object
+      */
+      virtual std::unique_ptr<HashFunction> copy_state() const = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/keccak/keccak.cpp b/third_party/botan/src/lib/hash/keccak/keccak.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/keccak/keccak.cpp
@@ -0,0 +1,68 @@
+/*
+* Keccak
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/keccak.h>
+#include <botan/sha3.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Keccak_1600::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new Keccak_1600(*this));
+   }
+
+Keccak_1600::Keccak_1600(size_t output_bits) :
+   m_output_bits(output_bits),
+   m_bitrate(1600 - 2*output_bits),
+   m_S(25),
+   m_S_pos(0)
+   {
+   // We only support the parameters for the SHA-3 proposal
+
+   if(output_bits != 224 && output_bits != 256 &&
+      output_bits != 384 && output_bits != 512)
+      throw Invalid_Argument("Keccak_1600: Invalid output length " +
+                             std::to_string(output_bits));
+   }
+
+std::string Keccak_1600::name() const
+   {
+   return "Keccak-1600(" + std::to_string(m_output_bits) + ")";
+   }
+
+HashFunction* Keccak_1600::clone() const
+   {
+   return new Keccak_1600(m_output_bits);
+   }
+
+void Keccak_1600::clear()
+   {
+   zeroise(m_S);
+   m_S_pos = 0;
+   }
+
+void Keccak_1600::add_data(const uint8_t input[], size_t length)
+   {
+   m_S_pos = SHA_3::absorb(m_bitrate, m_S, m_S_pos, input, length);
+   }
+
+void Keccak_1600::final_result(uint8_t output[])
+   {
+   SHA_3::finish(m_bitrate, m_S, m_S_pos, 0x01, 0x80);
+
+   /*
+   * We never have to run the permutation again because we only support
+   * limited output lengths
+   */
+   copy_out_vec_le(output, m_output_bits/8, m_S);
+
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/keccak/keccak.h b/third_party/botan/src/lib/hash/keccak/keccak.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/keccak/keccak.h
@@ -0,0 +1,51 @@
+/*
+* Keccak
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KECCAK_H_
+#define BOTAN_KECCAK_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+BOTAN_FUTURE_INTERNAL_HEADER(keccak.h)
+
+/**
+* Keccak[1600], a SHA-3 candidate
+*/
+class BOTAN_PUBLIC_API(2,0) Keccak_1600 final : public HashFunction
+   {
+   public:
+
+      /**
+      * @param output_bits the size of the hash output; must be one of
+      *                    224, 256, 384, or 512
+      */
+      explicit Keccak_1600(size_t output_bits = 512);
+
+      size_t hash_block_size() const override { return m_bitrate / 8; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string name() const override;
+      void clear() override;
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      size_t m_output_bits, m_bitrate;
+      secure_vector<uint64_t> m_S;
+      size_t m_S_pos;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/md4/md4.cpp b/third_party/botan/src/lib/hash/md4/md4.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/md4/md4.cpp
@@ -0,0 +1,144 @@
+/*
+* MD4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/md4.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> MD4::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new MD4(*this));
+   }
+
+namespace {
+
+inline void FF4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+                uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+   {
+   A += (D ^ (B & (C ^ D))) + M0;
+   A = rotl<3>(A);
+
+   D += (C ^ (A & (B ^ C))) + M1;
+   D = rotl<7>(D);
+
+   C += (B ^ (D & (A ^ B))) + M2;
+   C = rotl<11>(C);
+
+   B += (A ^ (C & (D ^ A))) + M3;
+   B = rotl<19>(B);
+   }
+
+inline void GG4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+                uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+   {
+   A += ((B & C) | (D & (B | C))) + M0 + 0x5A827999;
+   A = rotl<3>(A);
+
+   D += ((A & B) | (C & (A | B))) + M1 + 0x5A827999;
+   D = rotl<5>(D);
+
+   C += ((D & A) | (B & (D | A))) + M2 + 0x5A827999;
+   C = rotl<9>(C);
+
+   B += ((C & D) | (A & (C | D))) + M3 + 0x5A827999;
+   B = rotl<13>(B);
+   }
+
+inline void HH4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+                uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+   {
+   A += (B ^ C ^ D) + M0 + 0x6ED9EBA1;
+   A = rotl<3>(A);
+
+   D += (A ^ B ^ C) + M1 + 0x6ED9EBA1;
+   D = rotl<9>(D);
+
+   C += (A ^ B ^ D) + M2 + 0x6ED9EBA1;
+   C = rotl<11>(C);
+
+   B += (A ^ C ^ D) + M3 + 0x6ED9EBA1;
+   B = rotl<15>(B);
+   }
+
+}
+
+/*
+* MD4 Compression Function
+*/
+void MD4::compress_n(const uint8_t input[], size_t blocks)
+   {
+   uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t M00 = load_le<uint32_t>(input, 0);
+      uint32_t M01 = load_le<uint32_t>(input, 1);
+      uint32_t M02 = load_le<uint32_t>(input, 2);
+      uint32_t M03 = load_le<uint32_t>(input, 3);
+      uint32_t M04 = load_le<uint32_t>(input, 4);
+      uint32_t M05 = load_le<uint32_t>(input, 5);
+      uint32_t M06 = load_le<uint32_t>(input, 6);
+      uint32_t M07 = load_le<uint32_t>(input, 7);
+      uint32_t M08 = load_le<uint32_t>(input, 8);
+      uint32_t M09 = load_le<uint32_t>(input, 9);
+      uint32_t M10 = load_le<uint32_t>(input, 10);
+      uint32_t M11 = load_le<uint32_t>(input, 11);
+      uint32_t M12 = load_le<uint32_t>(input, 12);
+      uint32_t M13 = load_le<uint32_t>(input, 13);
+      uint32_t M14 = load_le<uint32_t>(input, 14);
+      uint32_t M15 = load_le<uint32_t>(input, 15);
+
+      FF4(A, B, C, D, M00, M01, M02, M03);
+      FF4(A, B, C, D, M04, M05, M06, M07);
+      FF4(A, B, C, D, M08, M09, M10, M11);
+      FF4(A, B, C, D, M12, M13, M14, M15);
+
+      GG4(A, B, C, D, M00, M04, M08, M12);
+      GG4(A, B, C, D, M01, M05, M09, M13);
+      GG4(A, B, C, D, M02, M06, M10, M14);
+      GG4(A, B, C, D, M03, M07, M11, M15);
+
+      HH4(A, B, C, D, M00, M08, M04, M12);
+      HH4(A, B, C, D, M02, M10, M06, M14);
+      HH4(A, B, C, D, M01, M09, M05, M13);
+      HH4(A, B, C, D, M03, M11, M07, M15);
+
+      A = (m_digest[0] += A);
+      B = (m_digest[1] += B);
+      C = (m_digest[2] += C);
+      D = (m_digest[3] += D);
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void MD4::copy_out(uint8_t output[])
+   {
+   copy_out_vec_le(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void MD4::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0x67452301;
+   m_digest[1] = 0xEFCDAB89;
+   m_digest[2] = 0x98BADCFE;
+   m_digest[3] = 0x10325476;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/md4/md4.h b/third_party/botan/src/lib/hash/md4/md4.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/md4/md4.h
@@ -0,0 +1,45 @@
+/*
+* MD4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MD4_H_
+#define BOTAN_MD4_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(md4.h)
+
+namespace Botan {
+
+/**
+* MD4
+*/
+class BOTAN_PUBLIC_API(2,0) MD4 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "MD4"; }
+      size_t output_length() const override { return 16; }
+      HashFunction* clone() const override { return new MD4; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      MD4() : MDx_HashFunction(64, false, true), m_digest(4)
+         { clear(); }
+
+   private:
+      void compress_n(const uint8_t input[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      /**
+      * The digest value
+      */
+      secure_vector<uint32_t> m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/md5/md5.cpp b/third_party/botan/src/lib/hash/md5/md5.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/md5/md5.cpp
@@ -0,0 +1,140 @@
+/*
+* MD5
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/md5.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> MD5::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new MD5(*this));
+   }
+
+namespace {
+
+/*
+* MD5 FF Function
+*/
+template<size_t S>
+inline void FF(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+   {
+   A += (D ^ (B & (C ^ D))) + M;
+   A  = rotl<S>(A) + B;
+   }
+
+/*
+* MD5 GG Function
+*/
+template<size_t S>
+inline void GG(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+   {
+   A += (C ^ (D & (B ^ C))) + M;
+   A  = rotl<S>(A) + B;
+   }
+
+/*
+* MD5 HH Function
+*/
+template<size_t S>
+inline void HH(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+   {
+   A += (B ^ C ^ D) + M;
+   A  = rotl<S>(A) + B;
+   }
+
+/*
+* MD5 II Function
+*/
+template<size_t S>
+inline void II(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+   {
+   A += (C ^ (B | ~D)) + M;
+   A  = rotl<S>(A) + B;
+   }
+
+}
+
+/*
+* MD5 Compression Function
+*/
+void MD5::compress_n(const uint8_t input[], size_t blocks)
+   {
+   uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      load_le(m_M.data(), input, m_M.size());
+
+      FF< 7>(A,B,C,D,m_M[ 0]+0xD76AA478);   FF<12>(D,A,B,C,m_M[ 1]+0xE8C7B756);
+      FF<17>(C,D,A,B,m_M[ 2]+0x242070DB);   FF<22>(B,C,D,A,m_M[ 3]+0xC1BDCEEE);
+      FF< 7>(A,B,C,D,m_M[ 4]+0xF57C0FAF);   FF<12>(D,A,B,C,m_M[ 5]+0x4787C62A);
+      FF<17>(C,D,A,B,m_M[ 6]+0xA8304613);   FF<22>(B,C,D,A,m_M[ 7]+0xFD469501);
+      FF< 7>(A,B,C,D,m_M[ 8]+0x698098D8);   FF<12>(D,A,B,C,m_M[ 9]+0x8B44F7AF);
+      FF<17>(C,D,A,B,m_M[10]+0xFFFF5BB1);   FF<22>(B,C,D,A,m_M[11]+0x895CD7BE);
+      FF< 7>(A,B,C,D,m_M[12]+0x6B901122);   FF<12>(D,A,B,C,m_M[13]+0xFD987193);
+      FF<17>(C,D,A,B,m_M[14]+0xA679438E);   FF<22>(B,C,D,A,m_M[15]+0x49B40821);
+
+      GG< 5>(A,B,C,D,m_M[ 1]+0xF61E2562);   GG< 9>(D,A,B,C,m_M[ 6]+0xC040B340);
+      GG<14>(C,D,A,B,m_M[11]+0x265E5A51);   GG<20>(B,C,D,A,m_M[ 0]+0xE9B6C7AA);
+      GG< 5>(A,B,C,D,m_M[ 5]+0xD62F105D);   GG< 9>(D,A,B,C,m_M[10]+0x02441453);
+      GG<14>(C,D,A,B,m_M[15]+0xD8A1E681);   GG<20>(B,C,D,A,m_M[ 4]+0xE7D3FBC8);
+      GG< 5>(A,B,C,D,m_M[ 9]+0x21E1CDE6);   GG< 9>(D,A,B,C,m_M[14]+0xC33707D6);
+      GG<14>(C,D,A,B,m_M[ 3]+0xF4D50D87);   GG<20>(B,C,D,A,m_M[ 8]+0x455A14ED);
+      GG< 5>(A,B,C,D,m_M[13]+0xA9E3E905);   GG< 9>(D,A,B,C,m_M[ 2]+0xFCEFA3F8);
+      GG<14>(C,D,A,B,m_M[ 7]+0x676F02D9);   GG<20>(B,C,D,A,m_M[12]+0x8D2A4C8A);
+
+      HH< 4>(A,B,C,D,m_M[ 5]+0xFFFA3942);   HH<11>(D,A,B,C,m_M[ 8]+0x8771F681);
+      HH<16>(C,D,A,B,m_M[11]+0x6D9D6122);   HH<23>(B,C,D,A,m_M[14]+0xFDE5380C);
+      HH< 4>(A,B,C,D,m_M[ 1]+0xA4BEEA44);   HH<11>(D,A,B,C,m_M[ 4]+0x4BDECFA9);
+      HH<16>(C,D,A,B,m_M[ 7]+0xF6BB4B60);   HH<23>(B,C,D,A,m_M[10]+0xBEBFBC70);
+      HH< 4>(A,B,C,D,m_M[13]+0x289B7EC6);   HH<11>(D,A,B,C,m_M[ 0]+0xEAA127FA);
+      HH<16>(C,D,A,B,m_M[ 3]+0xD4EF3085);   HH<23>(B,C,D,A,m_M[ 6]+0x04881D05);
+      HH< 4>(A,B,C,D,m_M[ 9]+0xD9D4D039);   HH<11>(D,A,B,C,m_M[12]+0xE6DB99E5);
+      HH<16>(C,D,A,B,m_M[15]+0x1FA27CF8);   HH<23>(B,C,D,A,m_M[ 2]+0xC4AC5665);
+
+      II< 6>(A,B,C,D,m_M[ 0]+0xF4292244);   II<10>(D,A,B,C,m_M[ 7]+0x432AFF97);
+      II<15>(C,D,A,B,m_M[14]+0xAB9423A7);   II<21>(B,C,D,A,m_M[ 5]+0xFC93A039);
+      II< 6>(A,B,C,D,m_M[12]+0x655B59C3);   II<10>(D,A,B,C,m_M[ 3]+0x8F0CCC92);
+      II<15>(C,D,A,B,m_M[10]+0xFFEFF47D);   II<21>(B,C,D,A,m_M[ 1]+0x85845DD1);
+      II< 6>(A,B,C,D,m_M[ 8]+0x6FA87E4F);   II<10>(D,A,B,C,m_M[15]+0xFE2CE6E0);
+      II<15>(C,D,A,B,m_M[ 6]+0xA3014314);   II<21>(B,C,D,A,m_M[13]+0x4E0811A1);
+      II< 6>(A,B,C,D,m_M[ 4]+0xF7537E82);   II<10>(D,A,B,C,m_M[11]+0xBD3AF235);
+      II<15>(C,D,A,B,m_M[ 2]+0x2AD7D2BB);   II<21>(B,C,D,A,m_M[ 9]+0xEB86D391);
+
+      A = (m_digest[0] += A);
+      B = (m_digest[1] += B);
+      C = (m_digest[2] += C);
+      D = (m_digest[3] += D);
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void MD5::copy_out(uint8_t output[])
+   {
+   copy_out_vec_le(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void MD5::clear()
+   {
+   MDx_HashFunction::clear();
+   zeroise(m_M);
+   m_digest[0] = 0x67452301;
+   m_digest[1] = 0xEFCDAB89;
+   m_digest[2] = 0x98BADCFE;
+   m_digest[3] = 0x10325476;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/md5/md5.h b/third_party/botan/src/lib/hash/md5/md5.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/md5/md5.h
@@ -0,0 +1,50 @@
+/*
+* MD5
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MD5_H_
+#define BOTAN_MD5_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(md5.h)
+
+namespace Botan {
+
+/**
+* MD5
+*/
+class BOTAN_PUBLIC_API(2,0) MD5 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "MD5"; }
+      size_t output_length() const override { return 16; }
+      HashFunction* clone() const override { return new MD5; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      MD5() : MDx_HashFunction(64, false, true), m_M(16), m_digest(4)
+         { clear(); }
+
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      /**
+      * The message buffer
+      */
+      secure_vector<uint32_t> m_M;
+
+      /**
+      * The digest value
+      */
+      secure_vector<uint32_t> m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp b/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp
@@ -0,0 +1,121 @@
+/*
+* Merkle-Damgard Hash Function
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mdx_hash.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+/*
+* MDx_HashFunction Constructor
+*/
+MDx_HashFunction::MDx_HashFunction(size_t block_len,
+                                   bool byte_big_endian,
+                                   bool bit_big_endian,
+                                   uint8_t cnt_size) :
+   m_pad_char(bit_big_endian == true ? 0x80 : 0x01),
+   m_counter_size(cnt_size),
+   m_block_bits(ceil_log2(block_len)),
+   m_count_big_endian(byte_big_endian),
+   m_count(0),
+   m_buffer(block_len),
+   m_position(0)
+   {
+   if(!is_power_of_2(block_len))
+      throw Invalid_Argument("MDx_HashFunction block length must be a power of 2");
+   if(m_block_bits < 3 || m_block_bits > 16)
+      throw Invalid_Argument("MDx_HashFunction block size too large or too small");
+   if(m_counter_size < 8 || m_counter_size > block_len)
+      throw Invalid_State("MDx_HashFunction invalid counter length");
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void MDx_HashFunction::clear()
+   {
+   zeroise(m_buffer);
+   m_count = m_position = 0;
+   }
+
+/*
+* Update the hash
+*/
+void MDx_HashFunction::add_data(const uint8_t input[], size_t length)
+   {
+   const size_t block_len = static_cast<size_t>(1) << m_block_bits;
+
+   m_count += length;
+
+   if(m_position)
+      {
+      buffer_insert(m_buffer, m_position, input, length);
+
+      if(m_position + length >= block_len)
+         {
+         compress_n(m_buffer.data(), 1);
+         input += (block_len - m_position);
+         length -= (block_len - m_position);
+         m_position = 0;
+         }
+      }
+
+   // Just in case the compiler can't figure out block_len is a power of 2
+   const size_t full_blocks = length >> m_block_bits;
+   const size_t remaining   = length & (block_len - 1);
+
+   if(full_blocks > 0)
+      {
+      compress_n(input, full_blocks);
+      }
+
+   buffer_insert(m_buffer, m_position, input + full_blocks * block_len, remaining);
+   m_position += remaining;
+   }
+
+/*
+* Finalize a hash
+*/
+void MDx_HashFunction::final_result(uint8_t output[])
+   {
+   const size_t block_len = static_cast<size_t>(1) << m_block_bits;
+
+   clear_mem(&m_buffer[m_position], block_len - m_position);
+   m_buffer[m_position] = m_pad_char;
+
+   if(m_position >= block_len - m_counter_size)
+      {
+      compress_n(m_buffer.data(), 1);
+      zeroise(m_buffer);
+      }
+
+   write_count(&m_buffer[block_len - m_counter_size]);
+
+   compress_n(m_buffer.data(), 1);
+   copy_out(output);
+   clear();
+   }
+
+/*
+* Write the count bits to the buffer
+*/
+void MDx_HashFunction::write_count(uint8_t out[])
+   {
+   BOTAN_ASSERT_NOMSG(m_counter_size <= output_length());
+   BOTAN_ASSERT_NOMSG(m_counter_size >= 8);
+
+   const uint64_t bit_count = m_count * 8;
+
+   if(m_count_big_endian)
+      store_be(bit_count, out + m_counter_size - 8);
+   else
+      store_le(bit_count, out + m_counter_size - 8);
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h b/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h
@@ -0,0 +1,73 @@
+/*
+* MDx Hash Function
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MDX_BASE_H_
+#define BOTAN_MDX_BASE_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mdx_hash.h)
+
+namespace Botan {
+
+/**
+* MDx Hash Function Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) MDx_HashFunction : public HashFunction
+   {
+   public:
+      /**
+      * @param block_length is the number of bytes per block, which must
+      *        be a power of 2 and at least 8.
+      * @param big_byte_endian specifies if the hash uses big-endian bytes
+      * @param big_bit_endian specifies if the hash uses big-endian bits
+      * @param counter_size specifies the size of the counter var in bytes
+      */
+      MDx_HashFunction(size_t block_length,
+                       bool big_byte_endian,
+                       bool big_bit_endian,
+                       uint8_t counter_size = 8);
+
+      size_t hash_block_size() const override final { return m_buffer.size(); }
+   protected:
+      void add_data(const uint8_t input[], size_t length) override final;
+      void final_result(uint8_t output[]) override final;
+
+      /**
+      * Run the hash's compression function over a set of blocks
+      * @param blocks the input
+      * @param block_n the number of blocks
+      */
+      virtual void compress_n(const uint8_t blocks[], size_t block_n) = 0;
+
+      void clear() override;
+
+      /**
+      * Copy the output to the buffer
+      * @param buffer to put the output into
+      */
+      virtual void copy_out(uint8_t buffer[]) = 0;
+
+      /**
+      * Write the count, if used, to this spot
+      * @param out where to write the counter to
+      */
+      virtual void write_count(uint8_t out[]);
+   private:
+      const uint8_t m_pad_char;
+      const uint8_t m_counter_size;
+      const uint8_t m_block_bits;
+      const bool m_count_big_endian;
+
+      uint64_t m_count;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_position;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/par_hash/par_hash.cpp b/third_party/botan/src/lib/hash/par_hash/par_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/par_hash/par_hash.cpp
@@ -0,0 +1,86 @@
+/*
+* Parallel Hash
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/par_hash.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+void Parallel::add_data(const uint8_t input[], size_t length)
+   {
+   for(auto&& hash : m_hashes)
+       hash->update(input, length);
+   }
+
+void Parallel::final_result(uint8_t out[])
+   {
+   size_t offset = 0;
+
+   for(auto&& hash : m_hashes)
+      {
+      hash->final(out + offset);
+      offset += hash->output_length();
+      }
+   }
+
+size_t Parallel::output_length() const
+   {
+   size_t sum = 0;
+
+   for(auto&& hash : m_hashes)
+      sum += hash->output_length();
+   return sum;
+   }
+
+std::string Parallel::name() const
+   {
+   std::vector<std::string> names;
+
+   for(auto&& hash : m_hashes)
+      names.push_back(hash->name());
+
+   return "Parallel(" + string_join(names, ',') + ")";
+   }
+
+HashFunction* Parallel::clone() const
+   {
+   std::vector<std::unique_ptr<HashFunction>> hash_copies;
+
+   for(auto&& hash : m_hashes)
+      hash_copies.push_back(std::unique_ptr<HashFunction>(hash->clone()));
+
+   return new Parallel(hash_copies);
+   }
+
+std::unique_ptr<HashFunction> Parallel::copy_state() const
+   {
+   std::vector<std::unique_ptr<HashFunction>> hash_clones;
+
+   for(const std::unique_ptr<HashFunction>& hash : m_hashes)
+      {
+      hash_clones.push_back(hash->copy_state());
+      }
+
+   return std::unique_ptr<HashFunction>(new Parallel(hash_clones));
+   }
+
+void Parallel::clear()
+   {
+   for(auto&& hash : m_hashes)
+      hash->clear();
+   }
+
+Parallel::Parallel(std::vector<std::unique_ptr<HashFunction>>& h)
+   {
+   for(size_t i = 0; i != h.size(); ++i)
+      {
+      m_hashes.push_back(std::unique_ptr<HashFunction>(h[i].release()));
+      }
+   }
+
+
+}
diff --git a/third_party/botan/src/lib/hash/par_hash/par_hash.h b/third_party/botan/src/lib/hash/par_hash/par_hash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/par_hash/par_hash.h
@@ -0,0 +1,50 @@
+/*
+* Parallel Hash
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PARALLEL_HASH_H_
+#define BOTAN_PARALLEL_HASH_H_
+
+#include <botan/hash.h>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(par_hash.h)
+
+namespace Botan {
+
+/**
+* Parallel Hashes
+*/
+class BOTAN_PUBLIC_API(2,0) Parallel final : public HashFunction
+   {
+   public:
+      void clear() override;
+      std::string name() const override;
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      size_t output_length() const override;
+
+      /**
+      * @param hashes a set of hashes to compute in parallel
+      * Takes ownership of all pointers
+      */
+      explicit Parallel(std::vector<std::unique_ptr<HashFunction>>& hashes);
+
+      Parallel(const Parallel&) = delete;
+      Parallel& operator=(const Parallel&) = delete;
+   private:
+      Parallel() = delete;
+
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+
+      std::vector<std::unique_ptr<HashFunction>> m_hashes;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/rmd160/rmd160.cpp b/third_party/botan/src/lib/hash/rmd160/rmd160.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/rmd160/rmd160.cpp
@@ -0,0 +1,221 @@
+/*
+* RIPEMD-160
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rmd160.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> RIPEMD_160::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new RIPEMD_160(*this));
+   }
+
+namespace {
+
+/*
+* RIPEMD-160 F1 Function
+*/
+template<size_t S>
+inline void F1(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+               uint32_t M)
+   {
+   A += (B ^ C ^ D) + M;
+   A  = rotl<S>(A) + E;
+   C  = rotl<10>(C);
+   }
+
+/*
+* RIPEMD-160 F2 Function
+*/
+template<size_t S>
+inline void F2(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+               uint32_t M)
+   {
+   A += (D ^ (B & (C ^ D))) + M;
+   A  = rotl<S>(A) + E;
+   C  = rotl<10>(C);
+   }
+
+/*
+* RIPEMD-160 F3 Function
+*/
+template<size_t S>
+inline void F3(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+               uint32_t M)
+   {
+   A += (D ^ (B | ~C)) + M;
+   A  = rotl<S>(A) + E;
+   C  = rotl<10>(C);
+   }
+
+/*
+* RIPEMD-160 F4 Function
+*/
+template<size_t S>
+inline void F4(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+               uint32_t M)
+   {
+   A += (C ^ (D & (B ^ C))) + M;
+   A  = rotl<S>(A) + E;
+   C  = rotl<10>(C);
+   }
+
+/*
+* RIPEMD-160 F5 Function
+*/
+template<size_t S>
+inline void F5(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+               uint32_t M)
+   {
+   A += (B ^ (C | ~D)) + M;
+   A  = rotl<S>(A) + E;
+   C  = rotl<10>(C);
+   }
+
+}
+
+/*
+* RIPEMD-160 Compression Function
+*/
+void RIPEMD_160::compress_n(const uint8_t input[], size_t blocks)
+   {
+   const uint32_t MAGIC2 = 0x5A827999, MAGIC3 = 0x6ED9EBA1,
+                  MAGIC4 = 0x8F1BBCDC, MAGIC5 = 0xA953FD4E,
+                  MAGIC6 = 0x50A28BE6, MAGIC7 = 0x5C4DD124,
+                  MAGIC8 = 0x6D703EF3, MAGIC9 = 0x7A6D76E9;
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      load_le(m_M.data(), input, m_M.size());
+
+      uint32_t A1 = m_digest[0], A2 = A1,
+               B1 = m_digest[1], B2 = B1,
+               C1 = m_digest[2], C2 = C1,
+               D1 = m_digest[3], D2 = D1,
+               E1 = m_digest[4], E2 = E1;
+
+      F1<11>(A1,B1,C1,D1,E1,m_M[ 0]       );  F5< 8>(A2,B2,C2,D2,E2,m_M[ 5]+MAGIC6);
+      F1<14>(E1,A1,B1,C1,D1,m_M[ 1]       );  F5< 9>(E2,A2,B2,C2,D2,m_M[14]+MAGIC6);
+      F1<15>(D1,E1,A1,B1,C1,m_M[ 2]       );  F5< 9>(D2,E2,A2,B2,C2,m_M[ 7]+MAGIC6);
+      F1<12>(C1,D1,E1,A1,B1,m_M[ 3]       );  F5<11>(C2,D2,E2,A2,B2,m_M[ 0]+MAGIC6);
+      F1< 5>(B1,C1,D1,E1,A1,m_M[ 4]       );  F5<13>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC6);
+      F1< 8>(A1,B1,C1,D1,E1,m_M[ 5]       );  F5<15>(A2,B2,C2,D2,E2,m_M[ 2]+MAGIC6);
+      F1< 7>(E1,A1,B1,C1,D1,m_M[ 6]       );  F5<15>(E2,A2,B2,C2,D2,m_M[11]+MAGIC6);
+      F1< 9>(D1,E1,A1,B1,C1,m_M[ 7]       );  F5< 5>(D2,E2,A2,B2,C2,m_M[ 4]+MAGIC6);
+      F1<11>(C1,D1,E1,A1,B1,m_M[ 8]       );  F5< 7>(C2,D2,E2,A2,B2,m_M[13]+MAGIC6);
+      F1<13>(B1,C1,D1,E1,A1,m_M[ 9]       );  F5< 7>(B2,C2,D2,E2,A2,m_M[ 6]+MAGIC6);
+      F1<14>(A1,B1,C1,D1,E1,m_M[10]       );  F5< 8>(A2,B2,C2,D2,E2,m_M[15]+MAGIC6);
+      F1<15>(E1,A1,B1,C1,D1,m_M[11]       );  F5<11>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC6);
+      F1< 6>(D1,E1,A1,B1,C1,m_M[12]       );  F5<14>(D2,E2,A2,B2,C2,m_M[ 1]+MAGIC6);
+      F1< 7>(C1,D1,E1,A1,B1,m_M[13]       );  F5<14>(C2,D2,E2,A2,B2,m_M[10]+MAGIC6);
+      F1< 9>(B1,C1,D1,E1,A1,m_M[14]       );  F5<12>(B2,C2,D2,E2,A2,m_M[ 3]+MAGIC6);
+      F1< 8>(A1,B1,C1,D1,E1,m_M[15]       );  F5< 6>(A2,B2,C2,D2,E2,m_M[12]+MAGIC6);
+
+      F2< 7>(E1,A1,B1,C1,D1,m_M[ 7]+MAGIC2);  F4< 9>(E2,A2,B2,C2,D2,m_M[ 6]+MAGIC7);
+      F2< 6>(D1,E1,A1,B1,C1,m_M[ 4]+MAGIC2);  F4<13>(D2,E2,A2,B2,C2,m_M[11]+MAGIC7);
+      F2< 8>(C1,D1,E1,A1,B1,m_M[13]+MAGIC2);  F4<15>(C2,D2,E2,A2,B2,m_M[ 3]+MAGIC7);
+      F2<13>(B1,C1,D1,E1,A1,m_M[ 1]+MAGIC2);  F4< 7>(B2,C2,D2,E2,A2,m_M[ 7]+MAGIC7);
+      F2<11>(A1,B1,C1,D1,E1,m_M[10]+MAGIC2);  F4<12>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC7);
+      F2< 9>(E1,A1,B1,C1,D1,m_M[ 6]+MAGIC2);  F4< 8>(E2,A2,B2,C2,D2,m_M[13]+MAGIC7);
+      F2< 7>(D1,E1,A1,B1,C1,m_M[15]+MAGIC2);  F4< 9>(D2,E2,A2,B2,C2,m_M[ 5]+MAGIC7);
+      F2<15>(C1,D1,E1,A1,B1,m_M[ 3]+MAGIC2);  F4<11>(C2,D2,E2,A2,B2,m_M[10]+MAGIC7);
+      F2< 7>(B1,C1,D1,E1,A1,m_M[12]+MAGIC2);  F4< 7>(B2,C2,D2,E2,A2,m_M[14]+MAGIC7);
+      F2<12>(A1,B1,C1,D1,E1,m_M[ 0]+MAGIC2);  F4< 7>(A2,B2,C2,D2,E2,m_M[15]+MAGIC7);
+      F2<15>(E1,A1,B1,C1,D1,m_M[ 9]+MAGIC2);  F4<12>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC7);
+      F2< 9>(D1,E1,A1,B1,C1,m_M[ 5]+MAGIC2);  F4< 7>(D2,E2,A2,B2,C2,m_M[12]+MAGIC7);
+      F2<11>(C1,D1,E1,A1,B1,m_M[ 2]+MAGIC2);  F4< 6>(C2,D2,E2,A2,B2,m_M[ 4]+MAGIC7);
+      F2< 7>(B1,C1,D1,E1,A1,m_M[14]+MAGIC2);  F4<15>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC7);
+      F2<13>(A1,B1,C1,D1,E1,m_M[11]+MAGIC2);  F4<13>(A2,B2,C2,D2,E2,m_M[ 1]+MAGIC7);
+      F2<12>(E1,A1,B1,C1,D1,m_M[ 8]+MAGIC2);  F4<11>(E2,A2,B2,C2,D2,m_M[ 2]+MAGIC7);
+
+      F3<11>(D1,E1,A1,B1,C1,m_M[ 3]+MAGIC3);  F3< 9>(D2,E2,A2,B2,C2,m_M[15]+MAGIC8);
+      F3<13>(C1,D1,E1,A1,B1,m_M[10]+MAGIC3);  F3< 7>(C2,D2,E2,A2,B2,m_M[ 5]+MAGIC8);
+      F3< 6>(B1,C1,D1,E1,A1,m_M[14]+MAGIC3);  F3<15>(B2,C2,D2,E2,A2,m_M[ 1]+MAGIC8);
+      F3< 7>(A1,B1,C1,D1,E1,m_M[ 4]+MAGIC3);  F3<11>(A2,B2,C2,D2,E2,m_M[ 3]+MAGIC8);
+      F3<14>(E1,A1,B1,C1,D1,m_M[ 9]+MAGIC3);  F3< 8>(E2,A2,B2,C2,D2,m_M[ 7]+MAGIC8);
+      F3< 9>(D1,E1,A1,B1,C1,m_M[15]+MAGIC3);  F3< 6>(D2,E2,A2,B2,C2,m_M[14]+MAGIC8);
+      F3<13>(C1,D1,E1,A1,B1,m_M[ 8]+MAGIC3);  F3< 6>(C2,D2,E2,A2,B2,m_M[ 6]+MAGIC8);
+      F3<15>(B1,C1,D1,E1,A1,m_M[ 1]+MAGIC3);  F3<14>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC8);
+      F3<14>(A1,B1,C1,D1,E1,m_M[ 2]+MAGIC3);  F3<12>(A2,B2,C2,D2,E2,m_M[11]+MAGIC8);
+      F3< 8>(E1,A1,B1,C1,D1,m_M[ 7]+MAGIC3);  F3<13>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC8);
+      F3<13>(D1,E1,A1,B1,C1,m_M[ 0]+MAGIC3);  F3< 5>(D2,E2,A2,B2,C2,m_M[12]+MAGIC8);
+      F3< 6>(C1,D1,E1,A1,B1,m_M[ 6]+MAGIC3);  F3<14>(C2,D2,E2,A2,B2,m_M[ 2]+MAGIC8);
+      F3< 5>(B1,C1,D1,E1,A1,m_M[13]+MAGIC3);  F3<13>(B2,C2,D2,E2,A2,m_M[10]+MAGIC8);
+      F3<12>(A1,B1,C1,D1,E1,m_M[11]+MAGIC3);  F3<13>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC8);
+      F3< 7>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC3);  F3< 7>(E2,A2,B2,C2,D2,m_M[ 4]+MAGIC8);
+      F3< 5>(D1,E1,A1,B1,C1,m_M[12]+MAGIC3);  F3< 5>(D2,E2,A2,B2,C2,m_M[13]+MAGIC8);
+
+      F4<11>(C1,D1,E1,A1,B1,m_M[ 1]+MAGIC4);  F2<15>(C2,D2,E2,A2,B2,m_M[ 8]+MAGIC9);
+      F4<12>(B1,C1,D1,E1,A1,m_M[ 9]+MAGIC4);  F2< 5>(B2,C2,D2,E2,A2,m_M[ 6]+MAGIC9);
+      F4<14>(A1,B1,C1,D1,E1,m_M[11]+MAGIC4);  F2< 8>(A2,B2,C2,D2,E2,m_M[ 4]+MAGIC9);
+      F4<15>(E1,A1,B1,C1,D1,m_M[10]+MAGIC4);  F2<11>(E2,A2,B2,C2,D2,m_M[ 1]+MAGIC9);
+      F4<14>(D1,E1,A1,B1,C1,m_M[ 0]+MAGIC4);  F2<14>(D2,E2,A2,B2,C2,m_M[ 3]+MAGIC9);
+      F4<15>(C1,D1,E1,A1,B1,m_M[ 8]+MAGIC4);  F2<14>(C2,D2,E2,A2,B2,m_M[11]+MAGIC9);
+      F4< 9>(B1,C1,D1,E1,A1,m_M[12]+MAGIC4);  F2< 6>(B2,C2,D2,E2,A2,m_M[15]+MAGIC9);
+      F4< 8>(A1,B1,C1,D1,E1,m_M[ 4]+MAGIC4);  F2<14>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC9);
+      F4< 9>(E1,A1,B1,C1,D1,m_M[13]+MAGIC4);  F2< 6>(E2,A2,B2,C2,D2,m_M[ 5]+MAGIC9);
+      F4<14>(D1,E1,A1,B1,C1,m_M[ 3]+MAGIC4);  F2< 9>(D2,E2,A2,B2,C2,m_M[12]+MAGIC9);
+      F4< 5>(C1,D1,E1,A1,B1,m_M[ 7]+MAGIC4);  F2<12>(C2,D2,E2,A2,B2,m_M[ 2]+MAGIC9);
+      F4< 6>(B1,C1,D1,E1,A1,m_M[15]+MAGIC4);  F2< 9>(B2,C2,D2,E2,A2,m_M[13]+MAGIC9);
+      F4< 8>(A1,B1,C1,D1,E1,m_M[14]+MAGIC4);  F2<12>(A2,B2,C2,D2,E2,m_M[ 9]+MAGIC9);
+      F4< 6>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC4);  F2< 5>(E2,A2,B2,C2,D2,m_M[ 7]+MAGIC9);
+      F4< 5>(D1,E1,A1,B1,C1,m_M[ 6]+MAGIC4);  F2<15>(D2,E2,A2,B2,C2,m_M[10]+MAGIC9);
+      F4<12>(C1,D1,E1,A1,B1,m_M[ 2]+MAGIC4);  F2< 8>(C2,D2,E2,A2,B2,m_M[14]+MAGIC9);
+
+      F5< 9>(B1,C1,D1,E1,A1,m_M[ 4]+MAGIC5);  F1< 8>(B2,C2,D2,E2,A2,m_M[12]       );
+      F5<15>(A1,B1,C1,D1,E1,m_M[ 0]+MAGIC5);  F1< 5>(A2,B2,C2,D2,E2,m_M[15]       );
+      F5< 5>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC5);  F1<12>(E2,A2,B2,C2,D2,m_M[10]       );
+      F5<11>(D1,E1,A1,B1,C1,m_M[ 9]+MAGIC5);  F1< 9>(D2,E2,A2,B2,C2,m_M[ 4]       );
+      F5< 6>(C1,D1,E1,A1,B1,m_M[ 7]+MAGIC5);  F1<12>(C2,D2,E2,A2,B2,m_M[ 1]       );
+      F5< 8>(B1,C1,D1,E1,A1,m_M[12]+MAGIC5);  F1< 5>(B2,C2,D2,E2,A2,m_M[ 5]       );
+      F5<13>(A1,B1,C1,D1,E1,m_M[ 2]+MAGIC5);  F1<14>(A2,B2,C2,D2,E2,m_M[ 8]       );
+      F5<12>(E1,A1,B1,C1,D1,m_M[10]+MAGIC5);  F1< 6>(E2,A2,B2,C2,D2,m_M[ 7]       );
+      F5< 5>(D1,E1,A1,B1,C1,m_M[14]+MAGIC5);  F1< 8>(D2,E2,A2,B2,C2,m_M[ 6]       );
+      F5<12>(C1,D1,E1,A1,B1,m_M[ 1]+MAGIC5);  F1<13>(C2,D2,E2,A2,B2,m_M[ 2]       );
+      F5<13>(B1,C1,D1,E1,A1,m_M[ 3]+MAGIC5);  F1< 6>(B2,C2,D2,E2,A2,m_M[13]       );
+      F5<14>(A1,B1,C1,D1,E1,m_M[ 8]+MAGIC5);  F1< 5>(A2,B2,C2,D2,E2,m_M[14]       );
+      F5<11>(E1,A1,B1,C1,D1,m_M[11]+MAGIC5);  F1<15>(E2,A2,B2,C2,D2,m_M[ 0]       );
+      F5< 8>(D1,E1,A1,B1,C1,m_M[ 6]+MAGIC5);  F1<13>(D2,E2,A2,B2,C2,m_M[ 3]       );
+      F5< 5>(C1,D1,E1,A1,B1,m_M[15]+MAGIC5);  F1<11>(C2,D2,E2,A2,B2,m_M[ 9]       );
+      F5< 6>(B1,C1,D1,E1,A1,m_M[13]+MAGIC5);  F1<11>(B2,C2,D2,E2,A2,m_M[11]       );
+
+      C1          = m_digest[1] + C1 + D2;
+      m_digest[1] = m_digest[2] + D1 + E2;
+      m_digest[2] = m_digest[3] + E1 + A2;
+      m_digest[3] = m_digest[4] + A1 + B2;
+      m_digest[4] = m_digest[0] + B1 + C2;
+      m_digest[0] = C1;
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void RIPEMD_160::copy_out(uint8_t output[])
+   {
+   copy_out_vec_le(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void RIPEMD_160::clear()
+   {
+   MDx_HashFunction::clear();
+   zeroise(m_M);
+   m_digest[0] = 0x67452301;
+   m_digest[1] = 0xEFCDAB89;
+   m_digest[2] = 0x98BADCFE;
+   m_digest[3] = 0x10325476;
+   m_digest[4] = 0xC3D2E1F0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/rmd160/rmd160.h b/third_party/botan/src/lib/hash/rmd160/rmd160.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/rmd160/rmd160.h
@@ -0,0 +1,41 @@
+/*
+* RIPEMD-160
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RIPEMD_160_H_
+#define BOTAN_RIPEMD_160_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rmd160.h)
+
+namespace Botan {
+
+/**
+* RIPEMD-160
+*/
+class BOTAN_PUBLIC_API(2,0) RIPEMD_160 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "RIPEMD-160"; }
+      size_t output_length() const override { return 20; }
+      HashFunction* clone() const override { return new RIPEMD_160; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      RIPEMD_160() : MDx_HashFunction(64, false, true), m_M(16), m_digest(5)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      secure_vector<uint32_t> m_M, m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sha1/sha160.cpp b/third_party/botan/src/lib/hash/sha1/sha160.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha1/sha160.cpp
@@ -0,0 +1,190 @@
+/*
+* SHA-160
+* (C) 1999-2008,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> SHA_160::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_160(*this));
+   }
+
+namespace SHA1_F {
+
+namespace {
+
+/*
+* SHA-160 F1 Function
+*/
+inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (D ^ (B & (C ^ D))) + msg + 0x5A827999 + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F2 Function
+*/
+inline void F2(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (B ^ C ^ D) + msg + 0x6ED9EBA1 + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F3 Function
+*/
+inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += ((B & C) | ((B | C) & D)) + msg + 0x8F1BBCDC + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F4 Function
+*/
+inline void F4(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (B ^ C ^ D) + msg + 0xCA62C1D6 + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+}
+
+}
+
+/*
+* SHA-160 Compression Function
+*/
+void SHA_160::compress_n(const uint8_t input[], size_t blocks)
+   {
+   using namespace SHA1_F;
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+   if(CPUID::has_intel_sha())
+      {
+      return sha1_compress_x86(m_digest, input, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+   if(CPUID::has_arm_sha1())
+      {
+      return sha1_armv8_compress_n(m_digest, input, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA1_SSE2)
+   if(CPUID::has_sse2())
+      {
+      return sse2_compress_n(m_digest, input, blocks);
+      }
+
+#endif
+
+   uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2],
+          D = m_digest[3], E = m_digest[4];
+
+   m_W.resize(80);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      load_be(m_W.data(), input, 16);
+
+      for(size_t j = 16; j != 80; j += 8)
+         {
+         m_W[j  ] = rotl<1>(m_W[j-3] ^ m_W[j-8] ^ m_W[j-14] ^ m_W[j-16]);
+         m_W[j+1] = rotl<1>(m_W[j-2] ^ m_W[j-7] ^ m_W[j-13] ^ m_W[j-15]);
+         m_W[j+2] = rotl<1>(m_W[j-1] ^ m_W[j-6] ^ m_W[j-12] ^ m_W[j-14]);
+         m_W[j+3] = rotl<1>(m_W[j  ] ^ m_W[j-5] ^ m_W[j-11] ^ m_W[j-13]);
+         m_W[j+4] = rotl<1>(m_W[j+1] ^ m_W[j-4] ^ m_W[j-10] ^ m_W[j-12]);
+         m_W[j+5] = rotl<1>(m_W[j+2] ^ m_W[j-3] ^ m_W[j- 9] ^ m_W[j-11]);
+         m_W[j+6] = rotl<1>(m_W[j+3] ^ m_W[j-2] ^ m_W[j- 8] ^ m_W[j-10]);
+         m_W[j+7] = rotl<1>(m_W[j+4] ^ m_W[j-1] ^ m_W[j- 7] ^ m_W[j- 9]);
+         }
+
+      F1(A, B, C, D, E, m_W[ 0]);   F1(E, A, B, C, D, m_W[ 1]);
+      F1(D, E, A, B, C, m_W[ 2]);   F1(C, D, E, A, B, m_W[ 3]);
+      F1(B, C, D, E, A, m_W[ 4]);   F1(A, B, C, D, E, m_W[ 5]);
+      F1(E, A, B, C, D, m_W[ 6]);   F1(D, E, A, B, C, m_W[ 7]);
+      F1(C, D, E, A, B, m_W[ 8]);   F1(B, C, D, E, A, m_W[ 9]);
+      F1(A, B, C, D, E, m_W[10]);   F1(E, A, B, C, D, m_W[11]);
+      F1(D, E, A, B, C, m_W[12]);   F1(C, D, E, A, B, m_W[13]);
+      F1(B, C, D, E, A, m_W[14]);   F1(A, B, C, D, E, m_W[15]);
+      F1(E, A, B, C, D, m_W[16]);   F1(D, E, A, B, C, m_W[17]);
+      F1(C, D, E, A, B, m_W[18]);   F1(B, C, D, E, A, m_W[19]);
+
+      F2(A, B, C, D, E, m_W[20]);   F2(E, A, B, C, D, m_W[21]);
+      F2(D, E, A, B, C, m_W[22]);   F2(C, D, E, A, B, m_W[23]);
+      F2(B, C, D, E, A, m_W[24]);   F2(A, B, C, D, E, m_W[25]);
+      F2(E, A, B, C, D, m_W[26]);   F2(D, E, A, B, C, m_W[27]);
+      F2(C, D, E, A, B, m_W[28]);   F2(B, C, D, E, A, m_W[29]);
+      F2(A, B, C, D, E, m_W[30]);   F2(E, A, B, C, D, m_W[31]);
+      F2(D, E, A, B, C, m_W[32]);   F2(C, D, E, A, B, m_W[33]);
+      F2(B, C, D, E, A, m_W[34]);   F2(A, B, C, D, E, m_W[35]);
+      F2(E, A, B, C, D, m_W[36]);   F2(D, E, A, B, C, m_W[37]);
+      F2(C, D, E, A, B, m_W[38]);   F2(B, C, D, E, A, m_W[39]);
+
+      F3(A, B, C, D, E, m_W[40]);   F3(E, A, B, C, D, m_W[41]);
+      F3(D, E, A, B, C, m_W[42]);   F3(C, D, E, A, B, m_W[43]);
+      F3(B, C, D, E, A, m_W[44]);   F3(A, B, C, D, E, m_W[45]);
+      F3(E, A, B, C, D, m_W[46]);   F3(D, E, A, B, C, m_W[47]);
+      F3(C, D, E, A, B, m_W[48]);   F3(B, C, D, E, A, m_W[49]);
+      F3(A, B, C, D, E, m_W[50]);   F3(E, A, B, C, D, m_W[51]);
+      F3(D, E, A, B, C, m_W[52]);   F3(C, D, E, A, B, m_W[53]);
+      F3(B, C, D, E, A, m_W[54]);   F3(A, B, C, D, E, m_W[55]);
+      F3(E, A, B, C, D, m_W[56]);   F3(D, E, A, B, C, m_W[57]);
+      F3(C, D, E, A, B, m_W[58]);   F3(B, C, D, E, A, m_W[59]);
+
+      F4(A, B, C, D, E, m_W[60]);   F4(E, A, B, C, D, m_W[61]);
+      F4(D, E, A, B, C, m_W[62]);   F4(C, D, E, A, B, m_W[63]);
+      F4(B, C, D, E, A, m_W[64]);   F4(A, B, C, D, E, m_W[65]);
+      F4(E, A, B, C, D, m_W[66]);   F4(D, E, A, B, C, m_W[67]);
+      F4(C, D, E, A, B, m_W[68]);   F4(B, C, D, E, A, m_W[69]);
+      F4(A, B, C, D, E, m_W[70]);   F4(E, A, B, C, D, m_W[71]);
+      F4(D, E, A, B, C, m_W[72]);   F4(C, D, E, A, B, m_W[73]);
+      F4(B, C, D, E, A, m_W[74]);   F4(A, B, C, D, E, m_W[75]);
+      F4(E, A, B, C, D, m_W[76]);   F4(D, E, A, B, C, m_W[77]);
+      F4(C, D, E, A, B, m_W[78]);   F4(B, C, D, E, A, m_W[79]);
+
+      A = (m_digest[0] += A);
+      B = (m_digest[1] += B);
+      C = (m_digest[2] += C);
+      D = (m_digest[3] += D);
+      E = (m_digest[4] += E);
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void SHA_160::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_160::clear()
+   {
+   MDx_HashFunction::clear();
+   zeroise(m_W);
+   m_digest[0] = 0x67452301;
+   m_digest[1] = 0xEFCDAB89;
+   m_digest[2] = 0x98BADCFE;
+   m_digest[3] = 0x10325476;
+   m_digest[4] = 0xC3D2E1F0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sha1/sha160.h b/third_party/botan/src/lib/hash/sha1/sha160.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha1/sha160.h
@@ -0,0 +1,75 @@
+/*
+* SHA-160
+* (C) 1999-2007,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_160_H_
+#define BOTAN_SHA_160_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha160.h)
+
+namespace Botan {
+
+/**
+* NIST's SHA-160
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_160 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-160"; }
+      size_t output_length() const override { return 20; }
+      HashFunction* clone() const override { return new SHA_160; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      SHA_160() : MDx_HashFunction(64, true, true), m_digest(5)
+         {
+         clear();
+         }
+
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+      static void sha1_armv8_compress_n(secure_vector<uint32_t>& digest,
+                                        const uint8_t blocks[],
+                                        size_t block_count);
+#endif
+
+#if defined(BOTAN_HAS_SHA1_SSE2)
+      static void sse2_compress_n(secure_vector<uint32_t>& digest,
+                                  const uint8_t blocks[],
+                                  size_t block_count);
+#endif
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+      // Using x86 SHA instructions in Intel Goldmont and Cannonlake
+      static void sha1_compress_x86(secure_vector<uint32_t>& digest,
+                                    const uint8_t blocks[],
+                                    size_t block_count);
+#endif
+
+
+      void copy_out(uint8_t[]) override;
+
+      /**
+      * The digest value
+      */
+      secure_vector<uint32_t> m_digest;
+
+      /**
+      * The message buffer
+      */
+      secure_vector<uint32_t> m_W;
+   };
+
+typedef SHA_160 SHA_1;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp b/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp
@@ -0,0 +1,207 @@
+/*
+* SHA-1 using CPU instructions in ARMv8
+*
+* Contributed by Jeffrey Walton. Based on public domain code by
+* Johannes Schneiders, Skip Hovsmith and Barry O'Rourke.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+/*
+* SHA-1 using CPU instructions in ARMv8
+*/
+//static
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+BOTAN_FUNC_ISA("+crypto")
+void SHA_160::sha1_armv8_compress_n(secure_vector<uint32_t>& digest, const uint8_t input8[], size_t blocks)
+   {
+   uint32x4_t ABCD;
+   uint32_t E0;
+
+   // Load magic constants
+   const uint32x4_t C0 = vdupq_n_u32(0x5A827999);
+   const uint32x4_t C1 = vdupq_n_u32(0x6ED9EBA1);
+   const uint32x4_t C2 = vdupq_n_u32(0x8F1BBCDC);
+   const uint32x4_t C3 = vdupq_n_u32(0xCA62C1D6);
+
+   ABCD = vld1q_u32(&digest[0]);
+   E0 = digest[4];
+
+   // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670
+   const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+
+   while (blocks)
+      {
+      // Save current hash
+      const uint32x4_t ABCD_SAVED = ABCD;
+      const uint32_t E0_SAVED = E0;
+
+      uint32x4_t MSG0, MSG1, MSG2, MSG3;
+      uint32x4_t TMP0, TMP1;
+      uint32_t E1;
+
+      MSG0 = vld1q_u32(input32 + 0);
+      MSG1 = vld1q_u32(input32 + 4);
+      MSG2 = vld1q_u32(input32 + 8);
+      MSG3 = vld1q_u32(input32 + 12);
+
+      MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0)));
+      MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1)));
+      MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2)));
+      MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3)));
+
+      TMP0 = vaddq_u32(MSG0, C0);
+      TMP1 = vaddq_u32(MSG1, C0);
+
+      // Rounds 0-3
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG2, C0);
+      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+      // Rounds 4-7
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1cq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG3, C0);
+      MSG0 = vsha1su1q_u32(MSG0, MSG3);
+      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+      // Rounds 8-11
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG0, C0);
+      MSG1 = vsha1su1q_u32(MSG1, MSG0);
+      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+      // Rounds 12-15
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1cq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG1, C1);
+      MSG2 = vsha1su1q_u32(MSG2, MSG1);
+      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+      // Rounds 16-19
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG2, C1);
+      MSG3 = vsha1su1q_u32(MSG3, MSG2);
+      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+      // Rounds 20-23
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG3, C1);
+      MSG0 = vsha1su1q_u32(MSG0, MSG3);
+      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+      // Rounds 24-27
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG0, C1);
+      MSG1 = vsha1su1q_u32(MSG1, MSG0);
+      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+      // Rounds 28-31
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG1, C1);
+      MSG2 = vsha1su1q_u32(MSG2, MSG1);
+      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+      // Rounds 32-35
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG2, C2);
+      MSG3 = vsha1su1q_u32(MSG3, MSG2);
+      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+      // Rounds 36-39
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG3, C2);
+      MSG0 = vsha1su1q_u32(MSG0, MSG3);
+      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+      // Rounds 40-43
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG0, C2);
+      MSG1 = vsha1su1q_u32(MSG1, MSG0);
+      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+      // Rounds 44-47
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1mq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG1, C2);
+      MSG2 = vsha1su1q_u32(MSG2, MSG1);
+      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+      // Rounds 48-51
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG2, C2);
+      MSG3 = vsha1su1q_u32(MSG3, MSG2);
+      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+      // Rounds 52-55
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1mq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG3, C3);
+      MSG0 = vsha1su1q_u32(MSG0, MSG3);
+      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+      // Rounds 56-59
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG0, C3);
+      MSG1 = vsha1su1q_u32(MSG1, MSG0);
+      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+      // Rounds 60-63
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG1, C3);
+      MSG2 = vsha1su1q_u32(MSG2, MSG1);
+      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+      // Rounds 64-67
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+      TMP0 = vaddq_u32(MSG2, C3);
+      MSG3 = vsha1su1q_u32(MSG3, MSG2);
+      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+      // Rounds 68-71
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+      TMP1 = vaddq_u32(MSG3, C3);
+      MSG0 = vsha1su1q_u32(MSG0, MSG3);
+
+      // Rounds 72-75
+      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+
+      // Rounds 76-79
+      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+
+      // Add state back
+      E0 += E0_SAVED;
+      ABCD = vaddq_u32(ABCD_SAVED, ABCD);
+
+      input32 += 64/4;
+      blocks--;
+      }
+
+   // Save digest
+   vst1q_u32(&digest[0], ABCD);
+   digest[4] = E0;
+   }
+#endif
+
+}
diff --git a/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp b/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp
@@ -0,0 +1,336 @@
+/*
+* SHA-1 using SSE2
+* Based on public domain code by Dean Gaudet
+*    (http://arctic.org/~dean/crypto/sha1.html)
+* (C) 2009-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <botan/rotate.h>
+#include <emmintrin.h>
+
+namespace Botan {
+
+namespace SHA1_SSE2_F {
+
+namespace {
+
+/*
+* First 16 bytes just need byte swapping. Preparing just means
+* adding in the round constants.
+*/
+
+#define prep00_15(P, W)                                      \
+   do {                                                      \
+      W = _mm_shufflehi_epi16(W, _MM_SHUFFLE(2, 3, 0, 1));   \
+      W = _mm_shufflelo_epi16(W, _MM_SHUFFLE(2, 3, 0, 1));   \
+      W = _mm_or_si128(_mm_slli_epi16(W, 8),                 \
+                       _mm_srli_epi16(W, 8));                \
+      P.u128 = _mm_add_epi32(W, K00_19);                     \
+   } while(0)
+
+/*
+For each multiple of 4, t, we want to calculate this:
+
+W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
+W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1);
+W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1);
+W[t+3] = rol(W[t]   ^ W[t-5] ^ W[t-11] ^ W[t-13], 1);
+
+we'll actually calculate this:
+
+W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
+W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1);
+W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1);
+W[t+3] = rol(  0    ^ W[t-5] ^ W[t-11] ^ W[t-13], 1);
+W[t+3] ^= rol(W[t+0], 1);
+
+the parameters are:
+
+W0 = &W[t-16];
+W1 = &W[t-12];
+W2 = &W[t- 8];
+W3 = &W[t- 4];
+
+and on output:
+prepared = W0 + K
+W0 = W[t]..W[t+3]
+*/
+
+/* note that there is a step here where i want to do a rol by 1, which
+* normally would look like this:
+*
+* r1 = psrld r0,$31
+* r0 = pslld r0,$1
+* r0 = por r0,r1
+*
+* but instead i do this:
+*
+* r1 = pcmpltd r0,zero
+* r0 = paddd r0,r0
+* r0 = psub r0,r1
+*
+* because pcmpltd and paddd are available in both MMX units on
+* efficeon, pentium-m, and opteron but shifts are available in
+* only one unit.
+*/
+#define prep(prep, XW0, XW1, XW2, XW3, K)                               \
+   do {                                                                 \
+      __m128i r0, r1, r2, r3;                                           \
+                                                                        \
+      /* load W[t-4] 16-byte aligned, and shift */                      \
+      r3 = _mm_srli_si128((XW3), 4);                                    \
+      r0 = (XW0);                                                       \
+      /* get high 64-bits of XW0 into low 64-bits */                    \
+      r1 = _mm_shuffle_epi32((XW0), _MM_SHUFFLE(1,0,3,2));              \
+      /* load high 64-bits of r1 */                                     \
+      r1 = _mm_unpacklo_epi64(r1, (XW1));                               \
+      r2 = (XW2);                                                       \
+                                                                        \
+      r0 = _mm_xor_si128(r1, r0);                                       \
+      r2 = _mm_xor_si128(r3, r2);                                       \
+      r0 = _mm_xor_si128(r2, r0);                                       \
+      /* unrotated W[t]..W[t+2] in r0 ... still need W[t+3] */          \
+                                                                        \
+      r2 = _mm_slli_si128(r0, 12);                                      \
+      r1 = _mm_cmplt_epi32(r0, _mm_setzero_si128());                    \
+      r0 = _mm_add_epi32(r0, r0);   /* shift left by 1 */               \
+      r0 = _mm_sub_epi32(r0, r1);   /* r0 has W[t]..W[t+2] */           \
+                                                                        \
+      r3 = _mm_srli_epi32(r2, 30);                                      \
+      r2 = _mm_slli_epi32(r2, 2);                                       \
+                                                                        \
+      r0 = _mm_xor_si128(r0, r3);                                       \
+      r0 = _mm_xor_si128(r0, r2);   /* r0 now has W[t+3] */             \
+                                                                        \
+      (XW0) = r0;                                                       \
+      (prep).u128 = _mm_add_epi32(r0, K);                               \
+   } while(0)
+
+/*
+* SHA-160 F1 Function
+*/
+inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (D ^ (B & (C ^ D))) + msg + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F2 Function
+*/
+inline void F2(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (B ^ C ^ D) + msg + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F3 Function
+*/
+inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += ((B & C) | ((B | C) & D)) + msg + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+/*
+* SHA-160 F4 Function
+*/
+inline void F4(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+   {
+   E += (B ^ C ^ D) + msg + rotl<5>(A);
+   B  = rotl<30>(B);
+   }
+
+}
+
+}
+
+/*
+* SHA-160 Compression Function using SSE for message expansion
+*/
+//static
+BOTAN_FUNC_ISA("sse2")
+void SHA_160::sse2_compress_n(secure_vector<uint32_t>& digest, const uint8_t input[], size_t blocks)
+   {
+   using namespace SHA1_SSE2_F;
+
+   const __m128i K00_19 = _mm_set1_epi32(0x5A827999);
+   const __m128i K20_39 = _mm_set1_epi32(0x6ED9EBA1);
+   const __m128i K40_59 = _mm_set1_epi32(0x8F1BBCDC);
+   const __m128i K60_79 = _mm_set1_epi32(0xCA62C1D6);
+
+   uint32_t A = digest[0],
+          B = digest[1],
+          C = digest[2],
+          D = digest[3],
+          E = digest[4];
+
+   const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      union v4si {
+         uint32_t u32[4];
+         __m128i u128;
+         };
+
+      v4si P0, P1, P2, P3;
+
+      __m128i W0 = _mm_loadu_si128(&input_mm[0]);
+      prep00_15(P0, W0);
+
+      __m128i W1 = _mm_loadu_si128(&input_mm[1]);
+      prep00_15(P1, W1);
+
+      __m128i W2 = _mm_loadu_si128(&input_mm[2]);
+      prep00_15(P2, W2);
+
+      __m128i W3 = _mm_loadu_si128(&input_mm[3]);
+      prep00_15(P3, W3);
+
+      /*
+      Using SSE4; slower on Core2 and Nehalem
+      #define GET_P_32(P, i) _mm_extract_epi32(P.u128, i)
+
+      Much slower on all tested platforms
+      #define GET_P_32(P,i) _mm_cvtsi128_si32(_mm_srli_si128(P.u128, i*4))
+      */
+
+#define GET_P_32(P, i) P.u32[i]
+
+      F1(A, B, C, D, E, GET_P_32(P0, 0));
+      F1(E, A, B, C, D, GET_P_32(P0, 1));
+      F1(D, E, A, B, C, GET_P_32(P0, 2));
+      F1(C, D, E, A, B, GET_P_32(P0, 3));
+      prep(P0, W0, W1, W2, W3, K00_19);
+
+      F1(B, C, D, E, A, GET_P_32(P1, 0));
+      F1(A, B, C, D, E, GET_P_32(P1, 1));
+      F1(E, A, B, C, D, GET_P_32(P1, 2));
+      F1(D, E, A, B, C, GET_P_32(P1, 3));
+      prep(P1, W1, W2, W3, W0, K20_39);
+
+      F1(C, D, E, A, B, GET_P_32(P2, 0));
+      F1(B, C, D, E, A, GET_P_32(P2, 1));
+      F1(A, B, C, D, E, GET_P_32(P2, 2));
+      F1(E, A, B, C, D, GET_P_32(P2, 3));
+      prep(P2, W2, W3, W0, W1, K20_39);
+
+      F1(D, E, A, B, C, GET_P_32(P3, 0));
+      F1(C, D, E, A, B, GET_P_32(P3, 1));
+      F1(B, C, D, E, A, GET_P_32(P3, 2));
+      F1(A, B, C, D, E, GET_P_32(P3, 3));
+      prep(P3, W3, W0, W1, W2, K20_39);
+
+      F1(E, A, B, C, D, GET_P_32(P0, 0));
+      F1(D, E, A, B, C, GET_P_32(P0, 1));
+      F1(C, D, E, A, B, GET_P_32(P0, 2));
+      F1(B, C, D, E, A, GET_P_32(P0, 3));
+      prep(P0, W0, W1, W2, W3, K20_39);
+
+      F2(A, B, C, D, E, GET_P_32(P1, 0));
+      F2(E, A, B, C, D, GET_P_32(P1, 1));
+      F2(D, E, A, B, C, GET_P_32(P1, 2));
+      F2(C, D, E, A, B, GET_P_32(P1, 3));
+      prep(P1, W1, W2, W3, W0, K20_39);
+
+      F2(B, C, D, E, A, GET_P_32(P2, 0));
+      F2(A, B, C, D, E, GET_P_32(P2, 1));
+      F2(E, A, B, C, D, GET_P_32(P2, 2));
+      F2(D, E, A, B, C, GET_P_32(P2, 3));
+      prep(P2, W2, W3, W0, W1, K40_59);
+
+      F2(C, D, E, A, B, GET_P_32(P3, 0));
+      F2(B, C, D, E, A, GET_P_32(P3, 1));
+      F2(A, B, C, D, E, GET_P_32(P3, 2));
+      F2(E, A, B, C, D, GET_P_32(P3, 3));
+      prep(P3, W3, W0, W1, W2, K40_59);
+
+      F2(D, E, A, B, C, GET_P_32(P0, 0));
+      F2(C, D, E, A, B, GET_P_32(P0, 1));
+      F2(B, C, D, E, A, GET_P_32(P0, 2));
+      F2(A, B, C, D, E, GET_P_32(P0, 3));
+      prep(P0, W0, W1, W2, W3, K40_59);
+
+      F2(E, A, B, C, D, GET_P_32(P1, 0));
+      F2(D, E, A, B, C, GET_P_32(P1, 1));
+      F2(C, D, E, A, B, GET_P_32(P1, 2));
+      F2(B, C, D, E, A, GET_P_32(P1, 3));
+      prep(P1, W1, W2, W3, W0, K40_59);
+
+      F3(A, B, C, D, E, GET_P_32(P2, 0));
+      F3(E, A, B, C, D, GET_P_32(P2, 1));
+      F3(D, E, A, B, C, GET_P_32(P2, 2));
+      F3(C, D, E, A, B, GET_P_32(P2, 3));
+      prep(P2, W2, W3, W0, W1, K40_59);
+
+      F3(B, C, D, E, A, GET_P_32(P3, 0));
+      F3(A, B, C, D, E, GET_P_32(P3, 1));
+      F3(E, A, B, C, D, GET_P_32(P3, 2));
+      F3(D, E, A, B, C, GET_P_32(P3, 3));
+      prep(P3, W3, W0, W1, W2, K60_79);
+
+      F3(C, D, E, A, B, GET_P_32(P0, 0));
+      F3(B, C, D, E, A, GET_P_32(P0, 1));
+      F3(A, B, C, D, E, GET_P_32(P0, 2));
+      F3(E, A, B, C, D, GET_P_32(P0, 3));
+      prep(P0, W0, W1, W2, W3, K60_79);
+
+      F3(D, E, A, B, C, GET_P_32(P1, 0));
+      F3(C, D, E, A, B, GET_P_32(P1, 1));
+      F3(B, C, D, E, A, GET_P_32(P1, 2));
+      F3(A, B, C, D, E, GET_P_32(P1, 3));
+      prep(P1, W1, W2, W3, W0, K60_79);
+
+      F3(E, A, B, C, D, GET_P_32(P2, 0));
+      F3(D, E, A, B, C, GET_P_32(P2, 1));
+      F3(C, D, E, A, B, GET_P_32(P2, 2));
+      F3(B, C, D, E, A, GET_P_32(P2, 3));
+      prep(P2, W2, W3, W0, W1, K60_79);
+
+      F4(A, B, C, D, E, GET_P_32(P3, 0));
+      F4(E, A, B, C, D, GET_P_32(P3, 1));
+      F4(D, E, A, B, C, GET_P_32(P3, 2));
+      F4(C, D, E, A, B, GET_P_32(P3, 3));
+      prep(P3, W3, W0, W1, W2, K60_79);
+
+      F4(B, C, D, E, A, GET_P_32(P0, 0));
+      F4(A, B, C, D, E, GET_P_32(P0, 1));
+      F4(E, A, B, C, D, GET_P_32(P0, 2));
+      F4(D, E, A, B, C, GET_P_32(P0, 3));
+
+      F4(C, D, E, A, B, GET_P_32(P1, 0));
+      F4(B, C, D, E, A, GET_P_32(P1, 1));
+      F4(A, B, C, D, E, GET_P_32(P1, 2));
+      F4(E, A, B, C, D, GET_P_32(P1, 3));
+
+      F4(D, E, A, B, C, GET_P_32(P2, 0));
+      F4(C, D, E, A, B, GET_P_32(P2, 1));
+      F4(B, C, D, E, A, GET_P_32(P2, 2));
+      F4(A, B, C, D, E, GET_P_32(P2, 3));
+
+      F4(E, A, B, C, D, GET_P_32(P3, 0));
+      F4(D, E, A, B, C, GET_P_32(P3, 1));
+      F4(C, D, E, A, B, GET_P_32(P3, 2));
+      F4(B, C, D, E, A, GET_P_32(P3, 3));
+
+      A = (digest[0] += A);
+      B = (digest[1] += B);
+      C = (digest[2] += C);
+      D = (digest[3] += D);
+      E = (digest[4] += E);
+
+      input_mm += (64 / 16);
+      }
+
+#undef GET_P_32
+   }
+
+#undef prep00_15
+#undef prep
+
+}
diff --git a/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp b/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp
@@ -0,0 +1,216 @@
+/*
+* SHA-1 using Intel SHA intrinsic
+*
+* Based on public domain code by Sean Gulley
+* (https://github.com/mitls/hacl-star/tree/master/experimental/hash)
+* Adapted to Botan by Jeffrey Walton.
+*
+* Further changes
+*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+BOTAN_FUNC_ISA("sha,ssse3,sse4.1")
+void SHA_160::sha1_compress_x86(secure_vector<uint32_t>& digest,
+                                const uint8_t input[],
+                                size_t blocks)
+   {
+   const __m128i MASK = _mm_set_epi64x(0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL);
+   const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+   uint32_t* state = digest.data();
+
+   // Load initial values
+   __m128i ABCD = _mm_loadu_si128(reinterpret_cast<__m128i*>(state));
+   __m128i E0 = _mm_set_epi32(state[4], 0, 0, 0);
+   ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
+
+   while (blocks)
+      {
+      // Save current hash
+      const __m128i ABCD_SAVE = ABCD;
+      const __m128i E0_SAVE = E0;
+
+      __m128i MSG0, MSG1, MSG2, MSG3;
+      __m128i E1;
+
+      // Rounds 0-3
+      MSG0 = _mm_loadu_si128(input_mm+0);
+      MSG0 = _mm_shuffle_epi8(MSG0, MASK);
+      E0 = _mm_add_epi32(E0, MSG0);
+      E1 = ABCD;
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+
+      // Rounds 4-7
+      MSG1 = _mm_loadu_si128(input_mm+1);
+      MSG1 = _mm_shuffle_epi8(MSG1, MASK);
+      E1 = _mm_sha1nexte_epu32(E1, MSG1);
+      E0 = ABCD;
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
+      MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+
+      // Rounds 8-11
+      MSG2 = _mm_loadu_si128(input_mm+2);
+      MSG2 = _mm_shuffle_epi8(MSG2, MASK);
+      E0 = _mm_sha1nexte_epu32(E0, MSG2);
+      E1 = ABCD;
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+      MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+      MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+      // Rounds 12-15
+      MSG3 = _mm_loadu_si128(input_mm+3);
+      MSG3 = _mm_shuffle_epi8(MSG3, MASK);
+      E1 = _mm_sha1nexte_epu32(E1, MSG3);
+      E0 = ABCD;
+      MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
+      MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+      MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+      // Rounds 16-19
+      E0 = _mm_sha1nexte_epu32(E0, MSG0);
+      E1 = ABCD;
+      MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+      MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+      MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+      // Rounds 20-23
+      E1 = _mm_sha1nexte_epu32(E1, MSG1);
+      E0 = ABCD;
+      MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+      MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+      MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+      // Rounds 24-27
+      E0 = _mm_sha1nexte_epu32(E0, MSG2);
+      E1 = ABCD;
+      MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
+      MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+      MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+      // Rounds 28-31
+      E1 = _mm_sha1nexte_epu32(E1, MSG3);
+      E0 = ABCD;
+      MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+      MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+      MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+      // Rounds 32-35
+      E0 = _mm_sha1nexte_epu32(E0, MSG0);
+      E1 = ABCD;
+      MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
+      MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+      MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+      // Rounds 36-39
+      E1 = _mm_sha1nexte_epu32(E1, MSG1);
+      E0 = ABCD;
+      MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+      MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+      MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+      // Rounds 40-43
+      E0 = _mm_sha1nexte_epu32(E0, MSG2);
+      E1 = ABCD;
+      MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+      MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+      MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+      // Rounds 44-47
+      E1 = _mm_sha1nexte_epu32(E1, MSG3);
+      E0 = ABCD;
+      MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
+      MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+      MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+      // Rounds 48-51
+      E0 = _mm_sha1nexte_epu32(E0, MSG0);
+      E1 = ABCD;
+      MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+      MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+      MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+      // Rounds 52-55
+      E1 = _mm_sha1nexte_epu32(E1, MSG1);
+      E0 = ABCD;
+      MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
+      MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+      MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+      // Rounds 56-59
+      E0 = _mm_sha1nexte_epu32(E0, MSG2);
+      E1 = ABCD;
+      MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+      MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+      MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+      // Rounds 60-63
+      E1 = _mm_sha1nexte_epu32(E1, MSG3);
+      E0 = ABCD;
+      MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+      MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+      MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+      // Rounds 64-67
+      E0 = _mm_sha1nexte_epu32(E0, MSG0);
+      E1 = ABCD;
+      MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
+      MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+      MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+      // Rounds 68-71
+      E1 = _mm_sha1nexte_epu32(E1, MSG1);
+      E0 = ABCD;
+      MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+      MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+      // Rounds 72-75
+      E0 = _mm_sha1nexte_epu32(E0, MSG2);
+      E1 = ABCD;
+      MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
+
+      // Rounds 76-79
+      E1 = _mm_sha1nexte_epu32(E1, MSG3);
+      E0 = ABCD;
+      ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+
+      // Add values back to state
+      E0 = _mm_sha1nexte_epu32(E0, E0_SAVE);
+      ABCD = _mm_add_epi32(ABCD, ABCD_SAVE);
+
+      input_mm += 4;
+      blocks--;
+      }
+
+   // Save state
+   ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
+   _mm_storeu_si128(reinterpret_cast<__m128i*>(state), ABCD);
+   state[4] = _mm_extract_epi32(E0, 3);
+   }
+#endif
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp b/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp
@@ -0,0 +1,278 @@
+/*
+* SHA-{224,256}
+* (C) 1999-2010,2017 Jack Lloyd
+*     2007 FlexSecure GmbH
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+std::string sha256_provider()
+   {
+#if defined(BOTAN_HAS_SHA2_32_X86)
+   if(CPUID::has_intel_sha())
+      {
+      return "shani";
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return "bmi2";
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+   if(CPUID::has_arm_sha2())
+      {
+      return "armv8";
+      }
+#endif
+
+   return "base";
+   }
+
+}
+
+std::unique_ptr<HashFunction> SHA_224::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_224(*this));
+   }
+
+std::unique_ptr<HashFunction> SHA_256::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_256(*this));
+   }
+
+/*
+* SHA-256 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_32_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) do {               \
+   uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A); \
+   uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E); \
+   uint32_t M2_sigma = rotr<17>(M2) ^ rotr<19>(M2) ^ (M2 >> 10);    \
+   uint32_t M4_sigma = rotr<7>(M4) ^ rotr<18>(M4) ^ (M4 >> 3);      \
+   H += magic + E_rho + ((E & F) ^ (~E & G)) + M1;                                  \
+   D += H;                                                                          \
+   H += A_rho + ((A & B) | ((A | B) & C));                                          \
+   M1 += M2_sigma + M3 + M4_sigma;                                                  \
+   } while(0);
+
+/*
+* SHA-224 / SHA-256 compression function
+*/
+void SHA_256::compress_digest(secure_vector<uint32_t>& digest,
+                              const uint8_t input[], size_t blocks)
+   {
+#if defined(BOTAN_HAS_SHA2_32_X86)
+   if(CPUID::has_intel_sha())
+      {
+      return SHA_256::compress_digest_x86(digest, input, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return SHA_256::compress_digest_x86_bmi2(digest, input, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+   if(CPUID::has_arm_sha2())
+      {
+      return SHA_256::compress_digest_armv8(digest, input, blocks);
+      }
+#endif
+
+   uint32_t A = digest[0], B = digest[1], C = digest[2],
+            D = digest[3], E = digest[4], F = digest[5],
+            G = digest[6], H = digest[7];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t W00 = load_be<uint32_t>(input,  0);
+      uint32_t W01 = load_be<uint32_t>(input,  1);
+      uint32_t W02 = load_be<uint32_t>(input,  2);
+      uint32_t W03 = load_be<uint32_t>(input,  3);
+      uint32_t W04 = load_be<uint32_t>(input,  4);
+      uint32_t W05 = load_be<uint32_t>(input,  5);
+      uint32_t W06 = load_be<uint32_t>(input,  6);
+      uint32_t W07 = load_be<uint32_t>(input,  7);
+      uint32_t W08 = load_be<uint32_t>(input,  8);
+      uint32_t W09 = load_be<uint32_t>(input,  9);
+      uint32_t W10 = load_be<uint32_t>(input, 10);
+      uint32_t W11 = load_be<uint32_t>(input, 11);
+      uint32_t W12 = load_be<uint32_t>(input, 12);
+      uint32_t W13 = load_be<uint32_t>(input, 13);
+      uint32_t W14 = load_be<uint32_t>(input, 14);
+      uint32_t W15 = load_be<uint32_t>(input, 15);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x71374491);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCF);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA5);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25B);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B01);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A7);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C1);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC6);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DC);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C8);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF3);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x14292967);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A85);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B2138);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D13);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A7354);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C85);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A1);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664B);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A3);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD6990624);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E3585);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA070);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C08);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774C);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4A);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC70208);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEB);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2);
+
+      A = (digest[0] += A);
+      B = (digest[1] += B);
+      C = (digest[2] += C);
+      D = (digest[3] += D);
+      E = (digest[4] += E);
+      F = (digest[5] += F);
+      G = (digest[6] += G);
+      H = (digest[7] += H);
+
+      input += 64;
+      }
+   }
+
+std::string SHA_224::provider() const
+   {
+   return sha256_provider();
+   }
+
+std::string SHA_256::provider() const
+   {
+   return sha256_provider();
+   }
+
+/*
+* SHA-224 compression function
+*/
+void SHA_224::compress_n(const uint8_t input[], size_t blocks)
+   {
+   SHA_256::compress_digest(m_digest, input, blocks);
+   }
+
+/*
+* Copy out the digest
+*/
+void SHA_224::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_224::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0xC1059ED8;
+   m_digest[1] = 0x367CD507;
+   m_digest[2] = 0x3070DD17;
+   m_digest[3] = 0xF70E5939;
+   m_digest[4] = 0xFFC00B31;
+   m_digest[5] = 0x68581511;
+   m_digest[6] = 0x64F98FA7;
+   m_digest[7] = 0xBEFA4FA4;
+   }
+
+/*
+* SHA-256 compression function
+*/
+void SHA_256::compress_n(const uint8_t input[], size_t blocks)
+   {
+   SHA_256::compress_digest(m_digest, input, blocks);
+   }
+
+/*
+* Copy out the digest
+*/
+void SHA_256::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_256::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0x6A09E667;
+   m_digest[1] = 0xBB67AE85;
+   m_digest[2] = 0x3C6EF372;
+   m_digest[3] = 0xA54FF53A;
+   m_digest[4] = 0x510E527F;
+   m_digest[5] = 0x9B05688C;
+   m_digest[6] = 0x1F83D9AB;
+   m_digest[7] = 0x5BE0CD19;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_32/sha2_32.h b/third_party/botan/src/lib/hash/sha2_32/sha2_32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_32/sha2_32.h
@@ -0,0 +1,95 @@
+/*
+* SHA-{224,256}
+* (C) 1999-2011 Jack Lloyd
+*     2007 FlexSecure GmbH
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_224_256_H_
+#define BOTAN_SHA_224_256_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha2_32.h)
+
+namespace Botan {
+
+/**
+* SHA-224
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_224 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-224"; }
+      size_t output_length() const override { return 28; }
+      HashFunction* clone() const override { return new SHA_224; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+
+      SHA_224() : MDx_HashFunction(64, true, true), m_digest(8)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      secure_vector<uint32_t> m_digest;
+   };
+
+/**
+* SHA-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_256 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-256"; }
+      size_t output_length() const override { return 32; }
+      HashFunction* clone() const override { return new SHA_256; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      std::string provider() const override;
+
+      SHA_256() : MDx_HashFunction(64, true, true), m_digest(8)
+         { clear(); }
+
+      /*
+      * Perform a SHA-256 compression. For internal use
+      */
+      static void compress_digest(secure_vector<uint32_t>& digest,
+                                  const uint8_t input[],
+                                  size_t blocks);
+
+   private:
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+      static void compress_digest_armv8(secure_vector<uint32_t>& digest,
+                                        const uint8_t input[],
+                                        size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86_BMI2)
+      static void compress_digest_x86_bmi2(secure_vector<uint32_t>& digest,
+                                           const uint8_t input[],
+                                           size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86)
+      static void compress_digest_x86(secure_vector<uint32_t>& digest,
+                                      const uint8_t input[],
+                                      size_t blocks);
+#endif
+
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      secure_vector<uint32_t> m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp b/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp
@@ -0,0 +1,204 @@
+/*
+* SHA-256 using CPU instructions in ARMv8
+*
+* Contributed by Jeffrey Walton. Based on public domain code by
+* Johannes Schneiders, Skip Hovsmith and Barry O'Rourke.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+/*
+* SHA-256 using CPU instructions in ARMv8
+*/
+//static
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+BOTAN_FUNC_ISA("+crypto")
+void SHA_256::compress_digest_armv8(secure_vector<uint32_t>& digest, const uint8_t input8[], size_t blocks)
+   {
+   static const uint32_t K[] = {
+      0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+      0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+      0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+      0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+      0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+      0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+      0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+      0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+      0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+      0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+      0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+      0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+      0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+      0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+      0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+      0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
+   };
+
+   uint32x4_t STATE0, STATE1, ABEF_SAVE, CDGH_SAVE;
+   uint32x4_t MSG0, MSG1, MSG2, MSG3;
+   uint32x4_t TMP0, TMP1, TMP2;
+
+   // Load initial values
+   STATE0 = vld1q_u32(&digest[0]);
+   STATE1 = vld1q_u32(&digest[4]);
+
+   // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670
+   const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+
+   while (blocks)
+      {
+      // Save current state
+      ABEF_SAVE = STATE0;
+      CDGH_SAVE = STATE1;
+
+      MSG0 = vld1q_u32(input32 + 0);
+      MSG1 = vld1q_u32(input32 + 4);
+      MSG2 = vld1q_u32(input32 + 8);
+      MSG3 = vld1q_u32(input32 + 12);
+
+      MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0)));
+      MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1)));
+      MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2)));
+      MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3)));
+
+      TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x00]));
+
+      // Rounds 0-3
+      MSG0 = vsha256su0q_u32(MSG0, MSG1);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x04]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+      // Rounds 4-7
+      MSG1 = vsha256su0q_u32(MSG1, MSG2);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x08]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+      // Rounds 8-11
+      MSG2 = vsha256su0q_u32(MSG2, MSG3);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x0c]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+      // Rounds 12-15
+      MSG3 = vsha256su0q_u32(MSG3, MSG0);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x10]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+      // Rounds 16-19
+      MSG0 = vsha256su0q_u32(MSG0, MSG1);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x14]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+      // Rounds 20-23
+      MSG1 = vsha256su0q_u32(MSG1, MSG2);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x18]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+      // Rounds 24-27
+      MSG2 = vsha256su0q_u32(MSG2, MSG3);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x1c]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+      // Rounds 28-31
+      MSG3 = vsha256su0q_u32(MSG3, MSG0);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x20]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+      // Rounds 32-35
+      MSG0 = vsha256su0q_u32(MSG0, MSG1);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x24]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+      // Rounds 36-39
+      MSG1 = vsha256su0q_u32(MSG1, MSG2);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x28]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+      // Rounds 40-43
+      MSG2 = vsha256su0q_u32(MSG2, MSG3);
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x2c]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+      MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+      // Rounds 44-47
+      MSG3 = vsha256su0q_u32(MSG3, MSG0);
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x30]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+      MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+      // Rounds 48-51
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x34]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+
+      // Rounds 52-55
+      TMP2 = STATE0;
+      TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x38]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+
+      // Rounds 56-59
+      TMP2 = STATE0;
+      TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x3c]));
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+
+      // Rounds 60-63
+      TMP2 = STATE0;
+      STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+      STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+
+      // Add back to state
+      STATE0 = vaddq_u32(STATE0, ABEF_SAVE);
+      STATE1 = vaddq_u32(STATE1, CDGH_SAVE);
+
+      input32 += 64/4;
+      blocks--;
+      }
+
+   // Save state
+   vst1q_u32(&digest[0], STATE0);
+   vst1q_u32(&digest[4], STATE1);
+   }
+#endif
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp b/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp
@@ -0,0 +1,140 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+/*
+Your eyes do not decieve you; this is currently just a copy of the
+baseline SHA-256 implementation. Because we compile it with BMI2
+flags, GCC and Clang use the BMI2 instructions without further help.
+
+Likely instruction scheduling could be improved by using inline asm.
+*/
+
+#define SHA2_32_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) do {   \
+   uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A);             \
+   uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E);             \
+   uint32_t M2_sigma = rotr<17>(M2) ^ rotr<19>(M2) ^ (M2 >> 10);        \
+   uint32_t M4_sigma = rotr<7>(M4) ^ rotr<18>(M4) ^ (M4 >> 3);          \
+   H += magic + E_rho + ((E & F) ^ (~E & G)) + M1;                      \
+   D += H;                                                              \
+   H += A_rho + ((A & B) | ((A | B) & C));                              \
+   M1 += M2_sigma + M3 + M4_sigma;                                      \
+   } while(0);
+
+void SHA_256::compress_digest_x86_bmi2(secure_vector<uint32_t>& digest,
+                                       const uint8_t input[],
+                                       size_t blocks)
+   {
+   uint32_t A = digest[0], B = digest[1], C = digest[2],
+            D = digest[3], E = digest[4], F = digest[5],
+            G = digest[6], H = digest[7];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t W00 = load_be<uint32_t>(input,  0);
+      uint32_t W01 = load_be<uint32_t>(input,  1);
+      uint32_t W02 = load_be<uint32_t>(input,  2);
+      uint32_t W03 = load_be<uint32_t>(input,  3);
+      uint32_t W04 = load_be<uint32_t>(input,  4);
+      uint32_t W05 = load_be<uint32_t>(input,  5);
+      uint32_t W06 = load_be<uint32_t>(input,  6);
+      uint32_t W07 = load_be<uint32_t>(input,  7);
+      uint32_t W08 = load_be<uint32_t>(input,  8);
+      uint32_t W09 = load_be<uint32_t>(input,  9);
+      uint32_t W10 = load_be<uint32_t>(input, 10);
+      uint32_t W11 = load_be<uint32_t>(input, 11);
+      uint32_t W12 = load_be<uint32_t>(input, 12);
+      uint32_t W13 = load_be<uint32_t>(input, 13);
+      uint32_t W14 = load_be<uint32_t>(input, 14);
+      uint32_t W15 = load_be<uint32_t>(input, 15);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x71374491);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCF);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA5);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25B);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B01);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A7);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C1);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC6);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DC);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C8);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF3);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x14292967);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A85);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B2138);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D13);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A7354);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C85);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A1);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664B);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A3);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD6990624);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E3585);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA070);
+
+      SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C08);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774C);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4A);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3);
+      SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE);
+      SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F);
+      SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814);
+      SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC70208);
+      SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA);
+      SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEB);
+      SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7);
+      SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2);
+
+      A = (digest[0] += A);
+      B = (digest[1] += B);
+      C = (digest[2] += C);
+      D = (digest[3] += D);
+      E = (digest[4] += E);
+      F = (digest[5] += F);
+      G = (digest[6] += G);
+      H = (digest[7] += H);
+
+      input += 64;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp b/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp
@@ -0,0 +1,215 @@
+/*
+* Support for SHA-256 x86 instrinsic
+* Based on public domain code by Sean Gulley
+*    (https://github.com/mitls/hacl-star/tree/master/experimental/hash)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+// called from sha2_32.cpp
+#if defined(BOTAN_HAS_SHA2_32_X86)
+BOTAN_FUNC_ISA("sha,sse4.1,ssse3")
+void SHA_256::compress_digest_x86(secure_vector<uint32_t>& digest, const uint8_t input[], size_t blocks)
+   {
+   __m128i STATE0, STATE1;
+   __m128i MSG, TMP, MASK;
+   __m128i TMSG0, TMSG1, TMSG2, TMSG3;
+   __m128i ABEF_SAVE, CDGH_SAVE;
+
+   uint32_t* state = &digest[0];
+
+   const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+   // Load initial values
+   TMP = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[0]));
+   STATE1 = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[4]));
+   MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL);
+
+   TMP = _mm_shuffle_epi32(TMP, 0xB1); // CDAB
+   STATE1 = _mm_shuffle_epi32(STATE1, 0x1B); // EFGH
+   STATE0 = _mm_alignr_epi8(TMP, STATE1, 8); // ABEF
+   STATE1 = _mm_blend_epi16(STATE1, TMP, 0xF0); // CDGH
+
+   while (blocks)
+      {
+      // Save current hash
+      ABEF_SAVE = STATE0;
+      CDGH_SAVE = STATE1;
+
+      // Rounds 0-3
+      MSG = _mm_loadu_si128(input_mm);
+      TMSG0 = _mm_shuffle_epi8(MSG, MASK);
+      MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+      // Rounds 4-7
+      TMSG1 = _mm_loadu_si128(input_mm + 1);
+      TMSG1 = _mm_shuffle_epi8(TMSG1, MASK);
+      MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+      // Rounds 8-11
+      TMSG2 = _mm_loadu_si128(input_mm + 2);
+      TMSG2 = _mm_shuffle_epi8(TMSG2, MASK);
+      MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+      // Rounds 12-15
+      TMSG3 = _mm_loadu_si128(input_mm + 3);
+      TMSG3 = _mm_shuffle_epi8(TMSG3, MASK);
+      MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+      TMSG0 = _mm_add_epi32(TMSG0, TMP);
+      TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+      // Rounds 16-19
+      MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+      TMSG1 = _mm_add_epi32(TMSG1, TMP);
+      TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+      // Rounds 20-23
+      MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+      TMSG2 = _mm_add_epi32(TMSG2, TMP);
+      TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+      // Rounds 24-27
+      MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+      TMSG3 = _mm_add_epi32(TMSG3, TMP);
+      TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+      // Rounds 28-31
+      MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0x1429296706CA6351ULL,  0xD5A79147C6E00BF3ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+      TMSG0 = _mm_add_epi32(TMSG0, TMP);
+      TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+      // Rounds 32-35
+      MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+      TMSG1 = _mm_add_epi32(TMSG1, TMP);
+      TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+      // Rounds 36-39
+      MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+      TMSG2 = _mm_add_epi32(TMSG2, TMP);
+      TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+      // Rounds 40-43
+      MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+      TMSG3 = _mm_add_epi32(TMSG3, TMP);
+      TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+      // Rounds 44-47
+      MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0x106AA070F40E3585ULL, 0xD6990624D192E819ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+      TMSG0 = _mm_add_epi32(TMSG0, TMP);
+      TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+      // Rounds 48-51
+      MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+      TMSG1 = _mm_add_epi32(TMSG1, TMP);
+      TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+      TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+      // Rounds 52-55
+      MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+      TMSG2 = _mm_add_epi32(TMSG2, TMP);
+      TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+      // Rounds 56-59
+      MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+      TMSG3 = _mm_add_epi32(TMSG3, TMP);
+      TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+      // Rounds 60-63
+      MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL));
+      STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+      MSG = _mm_shuffle_epi32(MSG, 0x0E);
+      STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+      // Add values back to state
+      STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE);
+      STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE);
+
+      input_mm += 4;
+      blocks--;
+      }
+
+   TMP = _mm_shuffle_epi32(STATE0, 0x1B); // FEBA
+   STATE1 = _mm_shuffle_epi32(STATE1, 0xB1); // DCHG
+   STATE0 = _mm_blend_epi16(TMP, STATE1, 0xF0); // DCBA
+   STATE1 = _mm_alignr_epi8(STATE1, TMP, 8); // ABEF
+
+   // Save state
+   _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[0]), STATE0);
+   _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[4]), STATE1);
+   }
+#endif
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp b/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp
@@ -0,0 +1,281 @@
+/*
+* SHA-{384,512}
+* (C) 1999-2011,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_64.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+std::string sha512_provider()
+   {
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return "bmi2";
+      }
+#endif
+
+   return "base";
+   }
+
+}
+
+std::unique_ptr<HashFunction> SHA_384::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_384(*this));
+   }
+
+std::unique_ptr<HashFunction> SHA_512::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_512(*this));
+   }
+
+std::unique_ptr<HashFunction> SHA_512_256::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_512_256(*this));
+   }
+
+/*
+* SHA-512 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_64_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic)         \
+   do {                                                                  \
+      const uint64_t E_rho = rotr<14>(E) ^ rotr<18>(E) ^ rotr<41>(E);    \
+      const uint64_t A_rho = rotr<28>(A) ^ rotr<34>(A) ^ rotr<39>(A);    \
+      const uint64_t M2_sigma = rotr<19>(M2) ^ rotr<61>(M2) ^ (M2 >> 6); \
+      const uint64_t M4_sigma = rotr<1>(M4) ^ rotr<8>(M4) ^ (M4 >> 7);   \
+      H += magic + E_rho + ((E & F) ^ (~E & G)) + M1;                    \
+      D += H;                                                            \
+      H += A_rho + ((A & B) | ((A | B) & C));                            \
+      M1 += M2_sigma + M3 + M4_sigma;                                    \
+   } while(0);
+
+/*
+* SHA-{384,512} Compression Function
+*/
+//static
+void SHA_512::compress_digest(secure_vector<uint64_t>& digest,
+                              const uint8_t input[], size_t blocks)
+   {
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return compress_digest_bmi2(digest, input, blocks);
+      }
+#endif
+
+   uint64_t A = digest[0], B = digest[1], C = digest[2],
+            D = digest[3], E = digest[4], F = digest[5],
+            G = digest[6], H = digest[7];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint64_t W00 = load_be<uint64_t>(input,  0);
+      uint64_t W01 = load_be<uint64_t>(input,  1);
+      uint64_t W02 = load_be<uint64_t>(input,  2);
+      uint64_t W03 = load_be<uint64_t>(input,  3);
+      uint64_t W04 = load_be<uint64_t>(input,  4);
+      uint64_t W05 = load_be<uint64_t>(input,  5);
+      uint64_t W06 = load_be<uint64_t>(input,  6);
+      uint64_t W07 = load_be<uint64_t>(input,  7);
+      uint64_t W08 = load_be<uint64_t>(input,  8);
+      uint64_t W09 = load_be<uint64_t>(input,  9);
+      uint64_t W10 = load_be<uint64_t>(input, 10);
+      uint64_t W11 = load_be<uint64_t>(input, 11);
+      uint64_t W12 = load_be<uint64_t>(input, 12);
+      uint64_t W13 = load_be<uint64_t>(input, 13);
+      uint64_t W14 = load_be<uint64_t>(input, 14);
+      uint64_t W15 = load_be<uint64_t>(input, 15);
+
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98D728AE22);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x7137449123EF65CD);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCFEC4D3B2F);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA58189DBBC);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25BF348B538);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1B605D019);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4AF194F9B);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5DA6D8118);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98A3030242);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B0145706FBE);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE4EE4B28C);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3D5FFB4E2);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74F27B896F);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE3B1696B1);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A725C71235);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174CF692694);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C19EF14AD2);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786384F25E3);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC68B8CD5B5);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC77AC9C65);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F592B0275);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA6EA6E483);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DCBD41FBD4);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA831153B5);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152EE66DFAB);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D2DB43210);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C898FB213F);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7BEEF0EE4);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF33DA88FC2);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147930AA725);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351E003826F);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x142929670A0E6E70);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A8546D22FFC);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B21385C26C926);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC5AC42AED);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D139D95B3DF);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A73548BAF63DE);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB3C77B2A8);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E47EDAEE6);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C851482353B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A14CF10364);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664BBC423001);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70D0F89791);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A30654BE30);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819D6EF5218);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD69906245565A910);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E35855771202A);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA07032BBD1B8);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116B8D2D0C8);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C085141AB53);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774CDF8EEB99);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5E19B48A8);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3C5C95A63);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4AE3418ACB);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F7763E373);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3D6B2B8A3);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE5DEFB2FC);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F43172F60);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814A1F0AB72);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC702081A6439EC);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA23631E28);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEBDE82BDE9);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7B2C67915);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2E372532B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xCA273ECEEA26619C);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xD186B8C721C0C207);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xEADA7DD6CDE0EB1E);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xF57D4F7FEE6ED178);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x06F067AA72176FBA);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x0A637DC5A2C898A6);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x113F9804BEF90DAE);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x1B710B35131C471B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x28DB77F523047D84);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x32CAAB7B40C72493);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x3C9EBE0A15C9BEBC);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x431D67C49C100D4C);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x4CC5D4BECB3E42B6);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x597F299CFC657E2A);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x5FCB6FAB3AD6FAEC);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x6C44198C4A475817);
+
+      A = (digest[0] += A);
+      B = (digest[1] += B);
+      C = (digest[2] += C);
+      D = (digest[3] += D);
+      E = (digest[4] += E);
+      F = (digest[5] += F);
+      G = (digest[6] += G);
+      H = (digest[7] += H);
+
+      input += 128;
+      }
+   }
+
+#undef SHA2_64_F
+
+std::string SHA_512_256::provider() const
+   {
+   return sha512_provider();
+   }
+
+std::string SHA_384::provider() const
+   {
+   return sha512_provider();
+   }
+
+std::string SHA_512::provider() const
+   {
+   return sha512_provider();
+   }
+
+void SHA_512_256::compress_n(const uint8_t input[], size_t blocks)
+   {
+   SHA_512::compress_digest(m_digest, input, blocks);
+   }
+
+void SHA_384::compress_n(const uint8_t input[], size_t blocks)
+   {
+   SHA_512::compress_digest(m_digest, input, blocks);
+   }
+
+void SHA_512::compress_n(const uint8_t input[], size_t blocks)
+   {
+   SHA_512::compress_digest(m_digest, input, blocks);
+   }
+
+void SHA_512_256::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+void SHA_384::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+void SHA_512::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+void SHA_512_256::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0x22312194FC2BF72C;
+   m_digest[1] = 0x9F555FA3C84C64C2;
+   m_digest[2] = 0x2393B86B6F53B151;
+   m_digest[3] = 0x963877195940EABD;
+   m_digest[4] = 0x96283EE2A88EFFE3;
+   m_digest[5] = 0xBE5E1E2553863992;
+   m_digest[6] = 0x2B0199FC2C85B8AA;
+   m_digest[7] = 0x0EB72DDC81C52CA2;
+   }
+
+void SHA_384::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0xCBBB9D5DC1059ED8;
+   m_digest[1] = 0x629A292A367CD507;
+   m_digest[2] = 0x9159015A3070DD17;
+   m_digest[3] = 0x152FECD8F70E5939;
+   m_digest[4] = 0x67332667FFC00B31;
+   m_digest[5] = 0x8EB44A8768581511;
+   m_digest[6] = 0xDB0C2E0D64F98FA7;
+   m_digest[7] = 0x47B5481DBEFA4FA4;
+   }
+
+void SHA_512::clear()
+   {
+   MDx_HashFunction::clear();
+   m_digest[0] = 0x6A09E667F3BCC908;
+   m_digest[1] = 0xBB67AE8584CAA73B;
+   m_digest[2] = 0x3C6EF372FE94F82B;
+   m_digest[3] = 0xA54FF53A5F1D36F1;
+   m_digest[4] = 0x510E527FADE682D1;
+   m_digest[5] = 0x9B05688C2B3E6C1F;
+   m_digest[6] = 0x1F83D9ABFB41BD6B;
+   m_digest[7] = 0x5BE0CD19137E2179;
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sha2_64/sha2_64.h b/third_party/botan/src/lib/hash/sha2_64/sha2_64.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_64/sha2_64.h
@@ -0,0 +1,102 @@
+/*
+* SHA-{384,512}
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_64BIT_H_
+#define BOTAN_SHA_64BIT_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha2_64.h)
+
+namespace Botan {
+
+/**
+* SHA-384
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_384 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-384"; }
+      size_t output_length() const override { return 48; }
+      HashFunction* clone() const override { return new SHA_384; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string provider() const override;
+
+      void clear() override;
+
+      SHA_384() : MDx_HashFunction(128, true, true, 16), m_digest(8)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      secure_vector<uint64_t> m_digest;
+   };
+
+/**
+* SHA-512
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_512 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-512"; }
+      size_t output_length() const override { return 64; }
+      HashFunction* clone() const override { return new SHA_512; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string provider() const override;
+
+      void clear() override;
+
+      /*
+      * Perform a SHA-512 compression. For internal use
+      */
+      static void compress_digest(secure_vector<uint64_t>& digest,
+                                  const uint8_t input[],
+                                  size_t blocks);
+
+      SHA_512() : MDx_HashFunction(128, true, true, 16), m_digest(8)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      static const uint64_t K[80];
+
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+      static void compress_digest_bmi2(secure_vector<uint64_t>& digest,
+                                       const uint8_t input[],
+                                       size_t blocks);
+#endif
+
+      secure_vector<uint64_t> m_digest;
+   };
+
+/**
+* SHA-512/256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_512_256 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SHA-512-256"; }
+      size_t output_length() const override { return 32; }
+      HashFunction* clone() const override { return new SHA_512_256; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string provider() const override;
+
+      void clear() override;
+
+      SHA_512_256() : MDx_HashFunction(128, true, true, 16), m_digest(8) { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      secure_vector<uint64_t> m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp b/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp
@@ -0,0 +1,153 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_64.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+/*
+* SHA-512 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_64_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic)         \
+   do {                                                                  \
+      const uint64_t E_rho = rotr<14>(E) ^ rotr<18>(E) ^ rotr<41>(E);    \
+      const uint64_t A_rho = rotr<28>(A) ^ rotr<34>(A) ^ rotr<39>(A);    \
+      const uint64_t M2_sigma = rotr<19>(M2) ^ rotr<61>(M2) ^ (M2 >> 6); \
+      const uint64_t M4_sigma = rotr<1>(M4) ^ rotr<8>(M4) ^ (M4 >> 7);   \
+      H += magic + E_rho + ((E & F) ^ (~E & G)) + M1;                    \
+      D += H;                                                            \
+      H += A_rho + ((A & B) | ((A | B) & C));                            \
+      M1 += M2_sigma + M3 + M4_sigma;                                    \
+   } while(0);
+
+void SHA_512::compress_digest_bmi2(secure_vector<uint64_t>& digest,
+                                   const uint8_t input[], size_t blocks)
+   {
+   uint64_t A = digest[0], B = digest[1], C = digest[2],
+          D = digest[3], E = digest[4], F = digest[5],
+          G = digest[6], H = digest[7];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint64_t W00 = load_be<uint64_t>(input,  0);
+      uint64_t W01 = load_be<uint64_t>(input,  1);
+      uint64_t W02 = load_be<uint64_t>(input,  2);
+      uint64_t W03 = load_be<uint64_t>(input,  3);
+      uint64_t W04 = load_be<uint64_t>(input,  4);
+      uint64_t W05 = load_be<uint64_t>(input,  5);
+      uint64_t W06 = load_be<uint64_t>(input,  6);
+      uint64_t W07 = load_be<uint64_t>(input,  7);
+      uint64_t W08 = load_be<uint64_t>(input,  8);
+      uint64_t W09 = load_be<uint64_t>(input,  9);
+      uint64_t W10 = load_be<uint64_t>(input, 10);
+      uint64_t W11 = load_be<uint64_t>(input, 11);
+      uint64_t W12 = load_be<uint64_t>(input, 12);
+      uint64_t W13 = load_be<uint64_t>(input, 13);
+      uint64_t W14 = load_be<uint64_t>(input, 14);
+      uint64_t W15 = load_be<uint64_t>(input, 15);
+
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98D728AE22);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x7137449123EF65CD);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCFEC4D3B2F);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA58189DBBC);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25BF348B538);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1B605D019);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4AF194F9B);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5DA6D8118);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98A3030242);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B0145706FBE);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE4EE4B28C);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3D5FFB4E2);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74F27B896F);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE3B1696B1);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A725C71235);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174CF692694);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C19EF14AD2);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786384F25E3);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC68B8CD5B5);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC77AC9C65);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F592B0275);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA6EA6E483);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DCBD41FBD4);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA831153B5);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152EE66DFAB);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D2DB43210);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C898FB213F);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7BEEF0EE4);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF33DA88FC2);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147930AA725);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351E003826F);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x142929670A0E6E70);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A8546D22FFC);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B21385C26C926);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC5AC42AED);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D139D95B3DF);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A73548BAF63DE);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB3C77B2A8);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E47EDAEE6);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C851482353B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A14CF10364);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664BBC423001);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70D0F89791);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A30654BE30);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819D6EF5218);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD69906245565A910);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E35855771202A);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA07032BBD1B8);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116B8D2D0C8);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C085141AB53);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774CDF8EEB99);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5E19B48A8);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3C5C95A63);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4AE3418ACB);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F7763E373);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3D6B2B8A3);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE5DEFB2FC);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F43172F60);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814A1F0AB72);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC702081A6439EC);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA23631E28);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEBDE82BDE9);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7B2C67915);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2E372532B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xCA273ECEEA26619C);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xD186B8C721C0C207);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xEADA7DD6CDE0EB1E);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xF57D4F7FEE6ED178);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x06F067AA72176FBA);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x0A637DC5A2C898A6);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x113F9804BEF90DAE);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x1B710B35131C471B);
+      SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x28DB77F523047D84);
+      SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x32CAAB7B40C72493);
+      SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x3C9EBE0A15C9BEBC);
+      SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x431D67C49C100D4C);
+      SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x4CC5D4BECB3E42B6);
+      SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x597F299CFC657E2A);
+      SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x5FCB6FAB3AD6FAEC);
+      SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x6C44198C4A475817);
+
+      A = (digest[0] += A);
+      B = (digest[1] += B);
+      C = (digest[2] += C);
+      D = (digest[3] += D);
+      E = (digest[4] += E);
+      F = (digest[5] += F);
+      G = (digest[6] += G);
+      H = (digest[7] += H);
+
+      input += 128;
+      }
+   }
+
+#undef SHA2_64_F
+
+}
diff --git a/third_party/botan/src/lib/hash/sha3/sha3.cpp b/third_party/botan/src/lib/hash/sha3/sha3.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha3/sha3.cpp
@@ -0,0 +1,269 @@
+/*
+* SHA-3
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha3.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/exceptn.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+inline void SHA3_round(uint64_t T[25], const uint64_t A[25], uint64_t RC)
+   {
+   const uint64_t C0 = A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20];
+   const uint64_t C1 = A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21];
+   const uint64_t C2 = A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22];
+   const uint64_t C3 = A[3] ^ A[8] ^ A[13] ^ A[18] ^ A[23];
+   const uint64_t C4 = A[4] ^ A[9] ^ A[14] ^ A[19] ^ A[24];
+
+   const uint64_t D0 = rotl<1>(C0) ^ C3;
+   const uint64_t D1 = rotl<1>(C1) ^ C4;
+   const uint64_t D2 = rotl<1>(C2) ^ C0;
+   const uint64_t D3 = rotl<1>(C3) ^ C1;
+   const uint64_t D4 = rotl<1>(C4) ^ C2;
+
+   const uint64_t B00 =          A[ 0] ^ D1;
+   const uint64_t B01 = rotl<44>(A[ 6] ^ D2);
+   const uint64_t B02 = rotl<43>(A[12] ^ D3);
+   const uint64_t B03 = rotl<21>(A[18] ^ D4);
+   const uint64_t B04 = rotl<14>(A[24] ^ D0);
+   T[ 0] = B00 ^ (~B01 & B02) ^ RC;
+   T[ 1] = B01 ^ (~B02 & B03);
+   T[ 2] = B02 ^ (~B03 & B04);
+   T[ 3] = B03 ^ (~B04 & B00);
+   T[ 4] = B04 ^ (~B00 & B01);
+
+   const uint64_t B05 = rotl<28>(A[ 3] ^ D4);
+   const uint64_t B06 = rotl<20>(A[ 9] ^ D0);
+   const uint64_t B07 = rotl< 3>(A[10] ^ D1);
+   const uint64_t B08 = rotl<45>(A[16] ^ D2);
+   const uint64_t B09 = rotl<61>(A[22] ^ D3);
+   T[ 5] = B05 ^ (~B06 & B07);
+   T[ 6] = B06 ^ (~B07 & B08);
+   T[ 7] = B07 ^ (~B08 & B09);
+   T[ 8] = B08 ^ (~B09 & B05);
+   T[ 9] = B09 ^ (~B05 & B06);
+
+   const uint64_t B10 = rotl< 1>(A[ 1] ^ D2);
+   const uint64_t B11 = rotl< 6>(A[ 7] ^ D3);
+   const uint64_t B12 = rotl<25>(A[13] ^ D4);
+   const uint64_t B13 = rotl< 8>(A[19] ^ D0);
+   const uint64_t B14 = rotl<18>(A[20] ^ D1);
+   T[10] = B10 ^ (~B11 & B12);
+   T[11] = B11 ^ (~B12 & B13);
+   T[12] = B12 ^ (~B13 & B14);
+   T[13] = B13 ^ (~B14 & B10);
+   T[14] = B14 ^ (~B10 & B11);
+
+   const uint64_t B15 = rotl<27>(A[ 4] ^ D0);
+   const uint64_t B16 = rotl<36>(A[ 5] ^ D1);
+   const uint64_t B17 = rotl<10>(A[11] ^ D2);
+   const uint64_t B18 = rotl<15>(A[17] ^ D3);
+   const uint64_t B19 = rotl<56>(A[23] ^ D4);
+   T[15] = B15 ^ (~B16 & B17);
+   T[16] = B16 ^ (~B17 & B18);
+   T[17] = B17 ^ (~B18 & B19);
+   T[18] = B18 ^ (~B19 & B15);
+   T[19] = B19 ^ (~B15 & B16);
+
+   const uint64_t B20 = rotl<62>(A[ 2] ^ D3);
+   const uint64_t B21 = rotl<55>(A[ 8] ^ D4);
+   const uint64_t B22 = rotl<39>(A[14] ^ D0);
+   const uint64_t B23 = rotl<41>(A[15] ^ D1);
+   const uint64_t B24 = rotl< 2>(A[21] ^ D2);
+   T[20] = B20 ^ (~B21 & B22);
+   T[21] = B21 ^ (~B22 & B23);
+   T[22] = B22 ^ (~B23 & B24);
+   T[23] = B23 ^ (~B24 & B20);
+   T[24] = B24 ^ (~B20 & B21);
+   }
+
+}
+
+//static
+void SHA_3::permute(uint64_t A[25])
+   {
+#if defined(BOTAN_HAS_SHA3_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return permute_bmi2(A);
+      }
+#endif
+
+   static const uint64_t RC[24] = {
+      0x0000000000000001, 0x0000000000008082, 0x800000000000808A,
+      0x8000000080008000, 0x000000000000808B, 0x0000000080000001,
+      0x8000000080008081, 0x8000000000008009, 0x000000000000008A,
+      0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
+      0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
+      0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
+      0x000000000000800A, 0x800000008000000A, 0x8000000080008081,
+      0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+   };
+
+   uint64_t T[25];
+
+   for(size_t i = 0; i != 24; i += 2)
+      {
+      SHA3_round(T, A, RC[i+0]);
+      SHA3_round(A, T, RC[i+1]);
+      }
+   }
+
+//static
+size_t SHA_3::absorb(size_t bitrate,
+                     secure_vector<uint64_t>& S, size_t S_pos,
+                     const uint8_t input[], size_t length)
+   {
+   while(length > 0)
+      {
+      size_t to_take = std::min(length, bitrate / 8 - S_pos);
+
+      length -= to_take;
+
+      while(to_take && S_pos % 8)
+         {
+         S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));
+
+         ++S_pos;
+         ++input;
+         --to_take;
+         }
+
+      while(to_take && to_take % 8 == 0)
+         {
+         S[S_pos / 8] ^= load_le<uint64_t>(input, 0);
+         S_pos += 8;
+         input += 8;
+         to_take -= 8;
+         }
+
+      while(to_take)
+         {
+         S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));
+
+         ++S_pos;
+         ++input;
+         --to_take;
+         }
+
+      if(S_pos == bitrate / 8)
+         {
+         SHA_3::permute(S.data());
+         S_pos = 0;
+         }
+      }
+
+   return S_pos;
+   }
+
+//static
+void SHA_3::finish(size_t bitrate,
+                   secure_vector<uint64_t>& S, size_t S_pos,
+                   uint8_t init_pad, uint8_t fini_pad)
+   {
+   BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64");
+
+   S[S_pos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (S_pos % 8));
+   S[(bitrate / 64) - 1] ^= static_cast<uint64_t>(fini_pad) << 56;
+   SHA_3::permute(S.data());
+   }
+
+//static
+void SHA_3::expand(size_t bitrate,
+                   secure_vector<uint64_t>& S,
+                   uint8_t output[], size_t output_length)
+   {
+   BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64");
+
+   const size_t byterate = bitrate / 8;
+
+   while(output_length > 0)
+      {
+      const size_t copying = std::min(byterate, output_length);
+
+      copy_out_vec_le(output, copying, S);
+
+      output += copying;
+      output_length -= copying;
+
+      if(output_length > 0)
+         {
+         SHA_3::permute(S.data());
+         }
+      }
+   }
+
+SHA_3::SHA_3(size_t output_bits) :
+   m_output_bits(output_bits),
+   m_bitrate(1600 - 2*output_bits),
+   m_S(25),
+   m_S_pos(0)
+   {
+   // We only support the parameters for SHA-3 in this constructor
+
+   if(output_bits != 224 && output_bits != 256 &&
+      output_bits != 384 && output_bits != 512)
+      throw Invalid_Argument("SHA_3: Invalid output length " +
+                             std::to_string(output_bits));
+   }
+
+std::string SHA_3::name() const
+   {
+   return "SHA-3(" + std::to_string(m_output_bits) + ")";
+   }
+
+std::string SHA_3::provider() const
+   {
+#if defined(BOTAN_HAS_SHA3_BMI2)
+   if(CPUID::has_bmi2())
+      {
+      return "bmi2";
+      }
+#endif
+
+   return "base";
+   }
+
+std::unique_ptr<HashFunction> SHA_3::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHA_3(*this));
+   }
+
+HashFunction* SHA_3::clone() const
+   {
+   return new SHA_3(m_output_bits);
+   }
+
+void SHA_3::clear()
+   {
+   zeroise(m_S);
+   m_S_pos = 0;
+   }
+
+void SHA_3::add_data(const uint8_t input[], size_t length)
+   {
+   m_S_pos = SHA_3::absorb(m_bitrate, m_S, m_S_pos, input, length);
+   }
+
+void SHA_3::final_result(uint8_t output[])
+   {
+   SHA_3::finish(m_bitrate, m_S, m_S_pos, 0x06, 0x80);
+
+   /*
+   * We never have to run the permutation again because we only support
+   * limited output lengths
+   */
+   copy_out_vec_le(output, m_output_bits/8, m_S);
+
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sha3/sha3.h b/third_party/botan/src/lib/hash/sha3/sha3.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha3/sha3.h
@@ -0,0 +1,136 @@
+/*
+* SHA-3
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA3_H_
+#define BOTAN_SHA3_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha3.h)
+
+namespace Botan {
+
+/**
+* SHA-3
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3 : public HashFunction
+   {
+   public:
+
+      /**
+      * @param output_bits the size of the hash output; must be one of
+      *                    224, 256, 384, or 512
+      */
+      explicit SHA_3(size_t output_bits);
+
+      size_t hash_block_size() const override { return m_bitrate / 8; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string name() const override;
+      void clear() override;
+      std::string provider() const override;
+
+      // Static functions for internal usage
+
+      /**
+      * Absorb data into the provided state
+      * @param bitrate the bitrate to absorb into the sponge
+      * @param S the sponge state
+      * @param S_pos where to begin absorbing into S
+      * @param input the input data
+      * @param length size of input in bytes
+      */
+      static size_t absorb(size_t bitrate,
+                           secure_vector<uint64_t>& S, size_t S_pos,
+                           const uint8_t input[], size_t length);
+
+      /**
+      * Add final padding and permute. The padding is assumed to be
+      * init_pad || 00... || fini_pad
+      *
+      * @param bitrate the bitrate to absorb into the sponge
+      * @param S the sponge state
+      * @param S_pos where to begin absorbing into S
+      * @param init_pad the leading pad bits
+      * @param fini_pad the final pad bits
+      */
+      static void finish(size_t bitrate,
+                         secure_vector<uint64_t>& S, size_t S_pos,
+                         uint8_t init_pad, uint8_t fini_pad);
+
+      /**
+      * Expand from provided state
+      * @param bitrate sponge parameter
+      * @param S the state
+      * @param output the output buffer
+      * @param output_length the size of output in bytes
+      */
+      static void expand(size_t bitrate,
+                         secure_vector<uint64_t>& S,
+                         uint8_t output[], size_t output_length);
+
+      /**
+      * The bare Keccak-1600 permutation
+      */
+      static void permute(uint64_t A[25]);
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+#if defined(BOTAN_HAS_SHA3_BMI2)
+      static void permute_bmi2(uint64_t A[25]);
+#endif
+
+      size_t m_output_bits, m_bitrate;
+      secure_vector<uint64_t> m_S;
+      size_t m_S_pos;
+   };
+
+/**
+* SHA-3-224
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_224 final : public SHA_3
+   {
+   public:
+      SHA_3_224() : SHA_3(224) {}
+   };
+
+/**
+* SHA-3-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_256 final : public SHA_3
+   {
+   public:
+      SHA_3_256() : SHA_3(256) {}
+   };
+
+/**
+* SHA-3-384
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_384 final : public SHA_3
+   {
+   public:
+      SHA_3_384() : SHA_3(384) {}
+   };
+
+/**
+* SHA-3-512
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_512 final : public SHA_3
+   {
+   public:
+      SHA_3_512() : SHA_3(512) {}
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp b/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp
@@ -0,0 +1,109 @@
+/*
+* SHA-3
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha3.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+inline void SHA3_BMI2_round(uint64_t T[25], const uint64_t A[25], uint64_t RC)
+   {
+   const uint64_t C0 = A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20];
+   const uint64_t C1 = A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21];
+   const uint64_t C2 = A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22];
+   const uint64_t C3 = A[3] ^ A[8] ^ A[13] ^ A[18] ^ A[23];
+   const uint64_t C4 = A[4] ^ A[9] ^ A[14] ^ A[19] ^ A[24];
+
+   const uint64_t D0 = rotl<1>(C0) ^ C3;
+   const uint64_t D1 = rotl<1>(C1) ^ C4;
+   const uint64_t D2 = rotl<1>(C2) ^ C0;
+   const uint64_t D3 = rotl<1>(C3) ^ C1;
+   const uint64_t D4 = rotl<1>(C4) ^ C2;
+
+   const uint64_t B00 =          A[ 0] ^ D1;
+   const uint64_t B01 = rotl<44>(A[ 6] ^ D2);
+   const uint64_t B02 = rotl<43>(A[12] ^ D3);
+   const uint64_t B03 = rotl<21>(A[18] ^ D4);
+   const uint64_t B04 = rotl<14>(A[24] ^ D0);
+   T[ 0] = B00 ^ (~B01 & B02) ^ RC;
+   T[ 1] = B01 ^ (~B02 & B03);
+   T[ 2] = B02 ^ (~B03 & B04);
+   T[ 3] = B03 ^ (~B04 & B00);
+   T[ 4] = B04 ^ (~B00 & B01);
+
+   const uint64_t B05 = rotl<28>(A[ 3] ^ D4);
+   const uint64_t B06 = rotl<20>(A[ 9] ^ D0);
+   const uint64_t B07 = rotl< 3>(A[10] ^ D1);
+   const uint64_t B08 = rotl<45>(A[16] ^ D2);
+   const uint64_t B09 = rotl<61>(A[22] ^ D3);
+   T[ 5] = B05 ^ (~B06 & B07);
+   T[ 6] = B06 ^ (~B07 & B08);
+   T[ 7] = B07 ^ (~B08 & B09);
+   T[ 8] = B08 ^ (~B09 & B05);
+   T[ 9] = B09 ^ (~B05 & B06);
+
+   const uint64_t B10 = rotl< 1>(A[ 1] ^ D2);
+   const uint64_t B11 = rotl< 6>(A[ 7] ^ D3);
+   const uint64_t B12 = rotl<25>(A[13] ^ D4);
+   const uint64_t B13 = rotl< 8>(A[19] ^ D0);
+   const uint64_t B14 = rotl<18>(A[20] ^ D1);
+   T[10] = B10 ^ (~B11 & B12);
+   T[11] = B11 ^ (~B12 & B13);
+   T[12] = B12 ^ (~B13 & B14);
+   T[13] = B13 ^ (~B14 & B10);
+   T[14] = B14 ^ (~B10 & B11);
+
+   const uint64_t B15 = rotl<27>(A[ 4] ^ D0);
+   const uint64_t B16 = rotl<36>(A[ 5] ^ D1);
+   const uint64_t B17 = rotl<10>(A[11] ^ D2);
+   const uint64_t B18 = rotl<15>(A[17] ^ D3);
+   const uint64_t B19 = rotl<56>(A[23] ^ D4);
+   T[15] = B15 ^ (~B16 & B17);
+   T[16] = B16 ^ (~B17 & B18);
+   T[17] = B17 ^ (~B18 & B19);
+   T[18] = B18 ^ (~B19 & B15);
+   T[19] = B19 ^ (~B15 & B16);
+
+   const uint64_t B20 = rotl<62>(A[ 2] ^ D3);
+   const uint64_t B21 = rotl<55>(A[ 8] ^ D4);
+   const uint64_t B22 = rotl<39>(A[14] ^ D0);
+   const uint64_t B23 = rotl<41>(A[15] ^ D1);
+   const uint64_t B24 = rotl< 2>(A[21] ^ D2);
+   T[20] = B20 ^ (~B21 & B22);
+   T[21] = B21 ^ (~B22 & B23);
+   T[22] = B22 ^ (~B23 & B24);
+   T[23] = B23 ^ (~B24 & B20);
+   T[24] = B24 ^ (~B20 & B21);
+   }
+
+}
+
+void SHA_3::permute_bmi2(uint64_t A[25])
+   {
+   static const uint64_t RC[24] = {
+      0x0000000000000001, 0x0000000000008082, 0x800000000000808A,
+      0x8000000080008000, 0x000000000000808B, 0x0000000080000001,
+      0x8000000080008081, 0x8000000000008009, 0x000000000000008A,
+      0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
+      0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
+      0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
+      0x000000000000800A, 0x800000008000000A, 0x8000000080008081,
+      0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+   };
+
+   uint64_t T[25];
+
+   for(size_t i = 0; i != 24; i += 2)
+      {
+      SHA3_BMI2_round(T, A, RC[i+0]);
+      SHA3_BMI2_round(A, T, RC[i+1]);
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/shake/shake.cpp b/third_party/botan/src/lib/hash/shake/shake.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/shake/shake.cpp
@@ -0,0 +1,97 @@
+/*
+* SHAKE-128/256 as a hash
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shake.h>
+#include <botan/sha3.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+SHAKE_128::SHAKE_128(size_t output_bits) :
+   m_output_bits(output_bits), m_S(25), m_S_pos(0)
+   {
+   if(output_bits % 8 != 0)
+      throw Invalid_Argument("SHAKE_128: Invalid output length " +
+                             std::to_string(output_bits));
+   }
+
+std::string SHAKE_128::name() const
+   {
+   return "SHAKE-128(" + std::to_string(m_output_bits) + ")";
+   }
+
+HashFunction* SHAKE_128::clone() const
+   {
+   return new SHAKE_128(m_output_bits);
+   }
+
+std::unique_ptr<HashFunction> SHAKE_128::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHAKE_128(*this));
+   }
+
+void SHAKE_128::clear()
+   {
+   zeroise(m_S);
+   m_S_pos = 0;
+   }
+
+void SHAKE_128::add_data(const uint8_t input[], size_t length)
+   {
+   m_S_pos = SHA_3::absorb(SHAKE_128_BITRATE, m_S, m_S_pos, input, length);
+   }
+
+void SHAKE_128::final_result(uint8_t output[])
+   {
+   SHA_3::finish(SHAKE_128_BITRATE, m_S, m_S_pos, 0x1F, 0x80);
+   SHA_3::expand(SHAKE_128_BITRATE, m_S, output, output_length());
+   clear();
+   }
+
+SHAKE_256::SHAKE_256(size_t output_bits) :
+   m_output_bits(output_bits), m_S(25), m_S_pos(0)
+   {
+   if(output_bits % 8 != 0)
+      throw Invalid_Argument("SHAKE_256: Invalid output length " +
+                             std::to_string(output_bits));
+   }
+
+std::string SHAKE_256::name() const
+   {
+   return "SHAKE-256(" + std::to_string(m_output_bits) + ")";
+   }
+
+HashFunction* SHAKE_256::clone() const
+   {
+   return new SHAKE_256(m_output_bits);
+   }
+
+std::unique_ptr<HashFunction> SHAKE_256::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SHAKE_256(*this));
+   }
+
+void SHAKE_256::clear()
+   {
+   zeroise(m_S);
+   m_S_pos = 0;
+   }
+
+void SHAKE_256::add_data(const uint8_t input[], size_t length)
+   {
+   m_S_pos = SHA_3::absorb(SHAKE_256_BITRATE, m_S, m_S_pos, input, length);
+   }
+
+void SHAKE_256::final_result(uint8_t output[])
+   {
+   SHA_3::finish(SHAKE_256_BITRATE, m_S, m_S_pos, 0x1F, 0x80);
+   SHA_3::expand(SHAKE_256_BITRATE, m_S, output, output_length());
+
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/shake/shake.h b/third_party/botan/src/lib/hash/shake/shake.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/shake/shake.h
@@ -0,0 +1,85 @@
+/*
+* SHAKE hash functions
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHAKE_HASH_H_
+#define BOTAN_SHAKE_HASH_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shake.h)
+
+namespace Botan {
+
+/**
+* SHAKE-128
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_128 final : public HashFunction
+   {
+   public:
+
+      /**
+      * @param output_bits the desired output size in bits
+      * must be a multiple of 8
+      */
+      explicit SHAKE_128(size_t output_bits);
+
+      size_t hash_block_size() const override { return SHAKE_128_BITRATE / 8; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string name() const override;
+      void clear() override;
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      static const size_t SHAKE_128_BITRATE = 1600 - 256;
+
+      size_t m_output_bits;
+      secure_vector<uint64_t> m_S;
+      size_t m_S_pos;
+   };
+
+/**
+* SHAKE-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_256 final : public HashFunction
+   {
+   public:
+
+      /**
+      * @param output_bits the desired output size in bits
+      * must be a multiple of 8
+      */
+      explicit SHAKE_256(size_t output_bits);
+
+      size_t hash_block_size() const override { return SHAKE_256_BITRATE / 8; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string name() const override;
+      void clear() override;
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      static const size_t SHAKE_256_BITRATE = 1600 - 512;
+
+      size_t m_output_bits;
+      secure_vector<uint64_t> m_S;
+      size_t m_S_pos;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/skein/skein_512.cpp b/third_party/botan/src/lib/hash/skein/skein_512.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/skein/skein_512.cpp
@@ -0,0 +1,178 @@
+/*
+* The Skein-512 hash function
+* (C) 2009,2010,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/skein_512.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+Skein_512::Skein_512(size_t arg_output_bits,
+                     const std::string& arg_personalization) :
+   m_personalization(arg_personalization),
+   m_output_bits(arg_output_bits),
+   m_threefish(new Threefish_512),
+   m_T(2), m_buffer(64), m_buf_pos(0)
+   {
+   if(m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512)
+      throw Invalid_Argument("Bad output bits size for Skein-512");
+
+   initial_block();
+   }
+
+std::string Skein_512::name() const
+   {
+   if(m_personalization != "")
+      return "Skein-512(" + std::to_string(m_output_bits) + "," +
+                            m_personalization + ")";
+   return "Skein-512(" + std::to_string(m_output_bits) + ")";
+   }
+
+HashFunction* Skein_512::clone() const
+   {
+   return new Skein_512(m_output_bits, m_personalization);
+   }
+
+std::unique_ptr<HashFunction> Skein_512::copy_state() const
+   {
+   std::unique_ptr<Skein_512> copy(new Skein_512(m_output_bits, m_personalization));
+
+   copy->m_threefish->m_K = this->m_threefish->m_K;
+   copy->m_T = this->m_T;
+   copy->m_buffer = this->m_buffer;
+   copy->m_buf_pos = this->m_buf_pos;
+
+   // work around GCC 4.8 bug
+   return std::unique_ptr<HashFunction>(copy.release());
+   }
+
+void Skein_512::clear()
+   {
+   zeroise(m_buffer);
+   m_buf_pos = 0;
+
+   initial_block();
+   }
+
+void Skein_512::reset_tweak(type_code type, bool is_final)
+   {
+   m_T[0] = 0;
+
+   m_T[1] = (static_cast<uint64_t>(type) << 56) |
+          (static_cast<uint64_t>(1) << 62) |
+          (static_cast<uint64_t>(is_final) << 63);
+   }
+
+void Skein_512::initial_block()
+   {
+   const uint8_t zeros[64] = { 0 };
+
+   m_threefish->set_key(zeros, sizeof(zeros));
+
+   // ASCII("SHA3") followed by version (0x0001) code
+   uint8_t config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 };
+   store_le(uint32_t(m_output_bits), config_str + 8);
+
+   reset_tweak(SKEIN_CONFIG, true);
+   ubi_512(config_str, sizeof(config_str));
+
+   if(m_personalization != "")
+      {
+      /*
+        This is a limitation of this implementation, and not of the
+        algorithm specification. Could be fixed relatively easily, but
+        doesn't seem worth the trouble.
+      */
+      if(m_personalization.length() > 64)
+         throw Invalid_Argument("Skein personalization must be less than 64 bytes");
+
+      const uint8_t* bits = cast_char_ptr_to_uint8(m_personalization.data());
+      reset_tweak(SKEIN_PERSONALIZATION, true);
+      ubi_512(bits, m_personalization.length());
+      }
+
+   reset_tweak(SKEIN_MSG, false);
+   }
+
+void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len)
+   {
+   secure_vector<uint64_t> M(8);
+
+   do
+      {
+      const size_t to_proc = std::min<size_t>(msg_len, 64);
+      m_T[0] += to_proc;
+
+      load_le(M.data(), msg, to_proc / 8);
+
+      if(to_proc % 8)
+         {
+         for(size_t j = 0; j != to_proc % 8; ++j)
+           M[to_proc/8] |= static_cast<uint64_t>(msg[8*(to_proc/8)+j]) << (8*j);
+         }
+
+      m_threefish->skein_feedfwd(M, m_T);
+
+      // clear first flag if set
+      m_T[1] &= ~(static_cast<uint64_t>(1) << 62);
+
+      msg_len -= to_proc;
+      msg += to_proc;
+      } while(msg_len);
+   }
+
+void Skein_512::add_data(const uint8_t input[], size_t length)
+   {
+   if(length == 0)
+      return;
+
+   if(m_buf_pos)
+      {
+      buffer_insert(m_buffer, m_buf_pos, input, length);
+      if(m_buf_pos + length > 64)
+         {
+         ubi_512(m_buffer.data(), m_buffer.size());
+
+         input += (64 - m_buf_pos);
+         length -= (64 - m_buf_pos);
+         m_buf_pos = 0;
+         }
+      }
+
+   const size_t full_blocks = (length - 1) / 64;
+
+   if(full_blocks)
+      ubi_512(input, 64*full_blocks);
+
+   length -= full_blocks * 64;
+
+   buffer_insert(m_buffer, m_buf_pos, input + full_blocks * 64, length);
+   m_buf_pos += length;
+   }
+
+void Skein_512::final_result(uint8_t out[])
+   {
+   m_T[1] |= (static_cast<uint64_t>(1) << 63); // final block flag
+
+   for(size_t i = m_buf_pos; i != m_buffer.size(); ++i)
+      m_buffer[i] = 0;
+
+   ubi_512(m_buffer.data(), m_buf_pos);
+
+   const uint8_t counter[8] = { 0 };
+
+   reset_tweak(SKEIN_OUTPUT, true);
+   ubi_512(counter, sizeof(counter));
+
+   copy_out_vec_le(out, m_output_bits / 8, m_threefish->m_K);
+
+   m_buf_pos = 0;
+   initial_block();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/skein/skein_512.h b/third_party/botan/src/lib/hash/skein/skein_512.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/skein/skein_512.h
@@ -0,0 +1,72 @@
+/*
+* The Skein-512 hash function
+* (C) 2009,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SKEIN_512_H_
+#define BOTAN_SKEIN_512_H_
+
+#include <botan/hash.h>
+#include <botan/threefish_512.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(skin_512.h)
+
+namespace Botan {
+
+/**
+* Skein-512, a SHA-3 candidate
+*/
+class BOTAN_PUBLIC_API(2,0) Skein_512 final : public HashFunction
+   {
+   public:
+      /**
+      * @param output_bits the output size of Skein in bits
+      * @param personalization is a string that will parameterize the
+      * hash output
+      */
+      Skein_512(size_t output_bits = 512,
+                const std::string& personalization = "");
+
+      size_t hash_block_size() const override { return 64; }
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override;
+      std::unique_ptr<HashFunction> copy_state() const override;
+      std::string name() const override;
+      void clear() override;
+   private:
+      enum type_code {
+         SKEIN_KEY = 0,
+         SKEIN_CONFIG = 4,
+         SKEIN_PERSONALIZATION = 8,
+         SKEIN_PUBLIC_KEY = 12,
+         SKEIN_KEY_IDENTIFIER = 16,
+         SKEIN_NONCE = 20,
+         SKEIN_MSG = 48,
+         SKEIN_OUTPUT = 63
+      };
+
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      void ubi_512(const uint8_t msg[], size_t msg_len);
+
+      void initial_block();
+      void reset_tweak(type_code type, bool is_final);
+
+      std::string m_personalization;
+      size_t m_output_bits;
+
+      std::unique_ptr<Threefish_512> m_threefish;
+      secure_vector<uint64_t> m_T;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_buf_pos;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/sm3/sm3.cpp b/third_party/botan/src/lib/hash/sm3/sm3.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sm3/sm3.cpp
@@ -0,0 +1,259 @@
+/*
+* SM3
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm3.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> SM3::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new SM3(*this));
+   }
+
+namespace {
+
+const uint32_t SM3_IV[] = {
+   0x7380166fUL, 0x4914b2b9UL, 0x172442d7UL, 0xda8a0600UL,
+   0xa96f30bcUL, 0x163138aaUL, 0xe38dee4dUL, 0xb0fb0e4eUL
+};
+
+inline uint32_t P0(uint32_t X)
+   {
+   return X ^ rotl<9>(X) ^ rotl<17>(X);
+   }
+
+inline uint32_t FF1(uint32_t X, uint32_t Y, uint32_t Z)
+   {
+   return (X & Y) | ((X | Y) & Z);
+   //return (X & Y) | (X & Z) | (Y & Z);
+   }
+
+inline uint32_t GG1(uint32_t X, uint32_t Y, uint32_t Z)
+   {
+   //return (X & Y) | (~X & Z);
+   return ((Z ^ (X & (Y ^ Z))));
+   }
+
+inline void R1(uint32_t A, uint32_t& B, uint32_t C, uint32_t& D,
+               uint32_t E, uint32_t& F, uint32_t G, uint32_t& H,
+               uint32_t TJ, uint32_t Wi, uint32_t Wj)
+   {
+   const uint32_t A12 = rotl<12>(A);
+   const uint32_t SS1 = rotl<7>(A12 + E + TJ);
+   const uint32_t TT1 = (A ^ B ^ C) + D + (SS1 ^ A12) + Wj;
+   const uint32_t TT2 = (E ^ F ^ G) + H + SS1 + Wi;
+
+   B = rotl<9>(B);
+   D = TT1;
+   F = rotl<19>(F);
+   H = P0(TT2);
+   }
+
+inline void R2(uint32_t A, uint32_t& B, uint32_t C, uint32_t& D,
+               uint32_t E, uint32_t& F, uint32_t G, uint32_t& H,
+               uint32_t TJ, uint32_t Wi, uint32_t Wj)
+   {
+   const uint32_t A12 = rotl<12>(A);
+   const uint32_t SS1 = rotl<7>(A12 + E + TJ);
+   const uint32_t TT1 = FF1(A, B, C) + D + (SS1 ^ A12) + Wj;
+   const uint32_t TT2 = GG1(E, F, G) + H + SS1 + Wi;
+
+   B = rotl<9>(B);
+   D = TT1;
+   F = rotl<19>(F);
+   H = P0(TT2);
+   }
+
+inline uint32_t P1(uint32_t X)
+   {
+   return X ^ rotl<15>(X) ^ rotl<23>(X);
+   }
+
+inline uint32_t SM3_E(uint32_t W0, uint32_t W7, uint32_t W13, uint32_t W3, uint32_t W10)
+   {
+   return P1(W0 ^ W7 ^ rotl<15>(W13)) ^ rotl<7>(W3) ^ W10;
+   }
+
+}
+
+/*
+* SM3 Compression Function
+*/
+void SM3::compress_n(const uint8_t input[], size_t blocks)
+   {
+   uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3],
+            E = m_digest[4], F = m_digest[5], G = m_digest[6], H = m_digest[7];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      uint32_t W00 = load_be<uint32_t>(input, 0);
+      uint32_t W01 = load_be<uint32_t>(input, 1);
+      uint32_t W02 = load_be<uint32_t>(input, 2);
+      uint32_t W03 = load_be<uint32_t>(input, 3);
+      uint32_t W04 = load_be<uint32_t>(input, 4);
+      uint32_t W05 = load_be<uint32_t>(input, 5);
+      uint32_t W06 = load_be<uint32_t>(input, 6);
+      uint32_t W07 = load_be<uint32_t>(input, 7);
+      uint32_t W08 = load_be<uint32_t>(input, 8);
+      uint32_t W09 = load_be<uint32_t>(input, 9);
+      uint32_t W10 = load_be<uint32_t>(input, 10);
+      uint32_t W11 = load_be<uint32_t>(input, 11);
+      uint32_t W12 = load_be<uint32_t>(input, 12);
+      uint32_t W13 = load_be<uint32_t>(input, 13);
+      uint32_t W14 = load_be<uint32_t>(input, 14);
+      uint32_t W15 = load_be<uint32_t>(input, 15);
+
+      R1(A, B, C, D, E, F, G, H, 0x79CC4519, W00, W00 ^ W04);
+      W00 = SM3_E(W00, W07, W13, W03, W10);
+      R1(D, A, B, C, H, E, F, G, 0xF3988A32, W01, W01 ^ W05);
+      W01 = SM3_E(W01, W08, W14, W04, W11);
+      R1(C, D, A, B, G, H, E, F, 0xE7311465, W02, W02 ^ W06);
+      W02 = SM3_E(W02, W09, W15, W05, W12);
+      R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W03, W03 ^ W07);
+      W03 = SM3_E(W03, W10, W00, W06, W13);
+      R1(A, B, C, D, E, F, G, H, 0x9CC45197, W04, W04 ^ W08);
+      W04 = SM3_E(W04, W11, W01, W07, W14);
+      R1(D, A, B, C, H, E, F, G, 0x3988A32F, W05, W05 ^ W09);
+      W05 = SM3_E(W05, W12, W02, W08, W15);
+      R1(C, D, A, B, G, H, E, F, 0x7311465E, W06, W06 ^ W10);
+      W06 = SM3_E(W06, W13, W03, W09, W00);
+      R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W07, W07 ^ W11);
+      W07 = SM3_E(W07, W14, W04, W10, W01);
+      R1(A, B, C, D, E, F, G, H, 0xCC451979, W08, W08 ^ W12);
+      W08 = SM3_E(W08, W15, W05, W11, W02);
+      R1(D, A, B, C, H, E, F, G, 0x988A32F3, W09, W09 ^ W13);
+      W09 = SM3_E(W09, W00, W06, W12, W03);
+      R1(C, D, A, B, G, H, E, F, 0x311465E7, W10, W10 ^ W14);
+      W10 = SM3_E(W10, W01, W07, W13, W04);
+      R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W11, W11 ^ W15);
+      W11 = SM3_E(W11, W02, W08, W14, W05);
+      R1(A, B, C, D, E, F, G, H, 0xC451979C, W12, W12 ^ W00);
+      W12 = SM3_E(W12, W03, W09, W15, W06);
+      R1(D, A, B, C, H, E, F, G, 0x88A32F39, W13, W13 ^ W01);
+      W13 = SM3_E(W13, W04, W10, W00, W07);
+      R1(C, D, A, B, G, H, E, F, 0x11465E73, W14, W14 ^ W02);
+      W14 = SM3_E(W14, W05, W11, W01, W08);
+      R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W15, W15 ^ W03);
+      W15 = SM3_E(W15, W06, W12, W02, W09);
+      R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W00, W00 ^ W04);
+      W00 = SM3_E(W00, W07, W13, W03, W10);
+      R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W01, W01 ^ W05);
+      W01 = SM3_E(W01, W08, W14, W04, W11);
+      R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W02, W02 ^ W06);
+      W02 = SM3_E(W02, W09, W15, W05, W12);
+      R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W03, W03 ^ W07);
+      W03 = SM3_E(W03, W10, W00, W06, W13);
+      R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W04, W04 ^ W08);
+      W04 = SM3_E(W04, W11, W01, W07, W14);
+      R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W05, W05 ^ W09);
+      W05 = SM3_E(W05, W12, W02, W08, W15);
+      R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W06, W06 ^ W10);
+      W06 = SM3_E(W06, W13, W03, W09, W00);
+      R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W07, W07 ^ W11);
+      W07 = SM3_E(W07, W14, W04, W10, W01);
+      R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W08, W08 ^ W12);
+      W08 = SM3_E(W08, W15, W05, W11, W02);
+      R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W09, W09 ^ W13);
+      W09 = SM3_E(W09, W00, W06, W12, W03);
+      R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W10, W10 ^ W14);
+      W10 = SM3_E(W10, W01, W07, W13, W04);
+      R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W11, W11 ^ W15);
+      W11 = SM3_E(W11, W02, W08, W14, W05);
+      R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W12, W12 ^ W00);
+      W12 = SM3_E(W12, W03, W09, W15, W06);
+      R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W13, W13 ^ W01);
+      W13 = SM3_E(W13, W04, W10, W00, W07);
+      R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W14, W14 ^ W02);
+      W14 = SM3_E(W14, W05, W11, W01, W08);
+      R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W15, W15 ^ W03);
+      W15 = SM3_E(W15, W06, W12, W02, W09);
+      R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W00, W00 ^ W04);
+      W00 = SM3_E(W00, W07, W13, W03, W10);
+      R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W01, W01 ^ W05);
+      W01 = SM3_E(W01, W08, W14, W04, W11);
+      R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W02, W02 ^ W06);
+      W02 = SM3_E(W02, W09, W15, W05, W12);
+      R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W03, W03 ^ W07);
+      W03 = SM3_E(W03, W10, W00, W06, W13);
+      R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W04, W04 ^ W08);
+      W04 = SM3_E(W04, W11, W01, W07, W14);
+      R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W05, W05 ^ W09);
+      W05 = SM3_E(W05, W12, W02, W08, W15);
+      R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W06, W06 ^ W10);
+      W06 = SM3_E(W06, W13, W03, W09, W00);
+      R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W07, W07 ^ W11);
+      W07 = SM3_E(W07, W14, W04, W10, W01);
+      R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W08, W08 ^ W12);
+      W08 = SM3_E(W08, W15, W05, W11, W02);
+      R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W09, W09 ^ W13);
+      W09 = SM3_E(W09, W00, W06, W12, W03);
+      R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W10, W10 ^ W14);
+      W10 = SM3_E(W10, W01, W07, W13, W04);
+      R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W11, W11 ^ W15);
+      W11 = SM3_E(W11, W02, W08, W14, W05);
+      R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W12, W12 ^ W00);
+      W12 = SM3_E(W12, W03, W09, W15, W06);
+      R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W13, W13 ^ W01);
+      W13 = SM3_E(W13, W04, W10, W00, W07);
+      R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W14, W14 ^ W02);
+      W14 = SM3_E(W14, W05, W11, W01, W08);
+      R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W15, W15 ^ W03);
+      W15 = SM3_E(W15, W06, W12, W02, W09);
+      R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W00, W00 ^ W04);
+      W00 = SM3_E(W00, W07, W13, W03, W10);
+      R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W01, W01 ^ W05);
+      W01 = SM3_E(W01, W08, W14, W04, W11);
+      R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W02, W02 ^ W06);
+      W02 = SM3_E(W02, W09, W15, W05, W12);
+      R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W03, W03 ^ W07);
+      W03 = SM3_E(W03, W10, W00, W06, W13);
+      R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W04, W04 ^ W08);
+      R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W05, W05 ^ W09);
+      R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W06, W06 ^ W10);
+      R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W07, W07 ^ W11);
+      R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W08, W08 ^ W12);
+      R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W09, W09 ^ W13);
+      R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W10, W10 ^ W14);
+      R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W11, W11 ^ W15);
+      R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W12, W12 ^ W00);
+      R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W13, W13 ^ W01);
+      R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W14, W14 ^ W02);
+      R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W15, W15 ^ W03);
+
+      A = (m_digest[0] ^= A);
+      B = (m_digest[1] ^= B);
+      C = (m_digest[2] ^= C);
+      D = (m_digest[3] ^= D);
+      E = (m_digest[4] ^= E);
+      F = (m_digest[5] ^= F);
+      G = (m_digest[6] ^= G);
+      H = (m_digest[7] ^= H);
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void SM3::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void SM3::clear()
+   {
+   MDx_HashFunction::clear();
+   std::copy(std::begin(SM3_IV), std::end(SM3_IV), m_digest.begin());
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/sm3/sm3.h b/third_party/botan/src/lib/hash/sm3/sm3.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/sm3/sm3.h
@@ -0,0 +1,49 @@
+/*
+* SM3
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM3_H_
+#define BOTAN_SM3_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sm3.h)
+
+namespace Botan {
+
+enum {
+  SM3_BLOCK_BYTES = 64,
+  SM3_DIGEST_BYTES = 32
+};
+
+/**
+* SM3
+*/
+class BOTAN_PUBLIC_API(2,2) SM3 final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "SM3"; }
+      size_t output_length() const override { return SM3_DIGEST_BYTES; }
+      HashFunction* clone() const override { return new SM3; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      SM3() : MDx_HashFunction(SM3_BLOCK_BYTES, true, true), m_digest(SM3_DIGEST_BYTES)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      /**
+      * The digest value
+      */
+      secure_vector<uint32_t> m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/streebog/streebog.cpp b/third_party/botan/src/lib/hash/streebog/streebog.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/streebog/streebog.cpp
@@ -0,0 +1,207 @@
+/*
+* Streebog
+* (C) 2017 Ribose Inc.
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/streebog.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+extern const uint64_t STREEBOG_Ax[8][256];
+extern const uint64_t STREEBOG_C[12][8];
+
+std::unique_ptr<HashFunction> Streebog::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new Streebog(*this));
+   }
+
+Streebog::Streebog(size_t output_bits) :
+   m_output_bits(output_bits),
+   m_count(0),
+   m_position(0),
+   m_buffer(64),
+   m_h(8),
+   m_S(8)
+   {
+   if(output_bits != 256 && output_bits != 512)
+      throw Invalid_Argument("Streebog: Invalid output length " +
+                             std::to_string(output_bits));
+
+   clear();
+   }
+
+std::string Streebog::name() const
+   {
+   return "Streebog-" + std::to_string(m_output_bits);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Streebog::clear()
+   {
+   m_count = 0;
+   m_position = 0;
+   zeroise(m_buffer);
+   zeroise(m_S);
+
+   const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101;
+   std::fill(m_h.begin(), m_h.end(), fill);
+   }
+
+/*
+* Update the hash
+*/
+void Streebog::add_data(const uint8_t input[], size_t length)
+   {
+   const size_t block_size = m_buffer.size();
+
+   if(m_position)
+      {
+      buffer_insert(m_buffer, m_position, input, length);
+
+      if(m_position + length >= block_size)
+         {
+         compress(m_buffer.data());
+         m_count += 512;
+         input += (block_size - m_position);
+         length -= (block_size - m_position);
+         m_position = 0;
+         }
+      }
+
+   const size_t full_blocks = length / block_size;
+   const size_t remaining   = length % block_size;
+
+   for(size_t i = 0; i != full_blocks; ++i)
+      {
+      compress(input + block_size * i);
+      m_count += 512;
+      }
+
+   buffer_insert(m_buffer, m_position, input + full_blocks * block_size, remaining);
+   m_position += remaining;
+   }
+
+/*
+* Finalize a hash
+*/
+void Streebog::final_result(uint8_t output[])
+   {
+   m_buffer[m_position++] = 0x01;
+
+   if(m_position != m_buffer.size())
+      clear_mem(&m_buffer[m_position], m_buffer.size() - m_position);
+
+   compress(m_buffer.data());
+   m_count += (m_position - 1) * 8;
+
+   zeroise(m_buffer);
+   store_le(m_count, m_buffer.data());
+   compress(m_buffer.data(), true);
+
+   compress_64(m_S.data(), true);
+   // FIXME
+   std::memcpy(output, &m_h[8 - output_length() / 8], output_length());
+   clear();
+   }
+
+namespace {
+
+inline uint64_t force_le(uint64_t x)
+   {
+#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+   return x;
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+   return reverse_bytes(x);
+#else
+   store_le(x, reinterpret_cast<uint8_t*>(&x));
+   return x;
+#endif
+   }
+
+inline void lps(uint64_t block[8])
+   {
+   uint8_t r[64];
+   // FIXME
+   std::memcpy(r, block, 64);
+
+   for(int i = 0; i < 8; ++i)
+      {
+      block[i] = force_le(STREEBOG_Ax[0][r[i + 0*8]]) ^
+                 force_le(STREEBOG_Ax[1][r[i + 1*8]]) ^
+                 force_le(STREEBOG_Ax[2][r[i + 2*8]]) ^
+                 force_le(STREEBOG_Ax[3][r[i + 3*8]]) ^
+                 force_le(STREEBOG_Ax[4][r[i + 4*8]]) ^
+                 force_le(STREEBOG_Ax[5][r[i + 5*8]]) ^
+                 force_le(STREEBOG_Ax[6][r[i + 6*8]]) ^
+                 force_le(STREEBOG_Ax[7][r[i + 7*8]]);
+      }
+   }
+
+} //namespace
+
+void Streebog::compress(const uint8_t input[], bool last_block)
+   {
+   uint64_t M[8];
+   std::memcpy(M, input, 64);
+
+   compress_64(M, last_block);
+   }
+
+void Streebog::compress_64(const uint64_t M[], bool last_block)
+   {
+   uint64_t N = force_le(last_block ? 0ULL : m_count);
+
+   uint64_t hN[8];
+   uint64_t A[8];
+
+   copy_mem(hN, m_h.data(), 8);
+   hN[0] ^= N;
+   lps(hN);
+
+   copy_mem(A, hN, 8);
+
+   for(size_t i = 0; i != 8; ++i)
+      {
+      hN[i] ^= M[i];
+      }
+
+   for(size_t i = 0; i < 12; ++i)
+      {
+      for(size_t j = 0; j != 8; ++j)
+         A[j] ^= force_le(STREEBOG_C[i][j]);
+      lps(A);
+
+      lps(hN);
+      for(size_t j = 0; j != 8; ++j)
+         hN[j] ^= A[j];
+      }
+
+   for(size_t i = 0; i != 8; ++i)
+      {
+      m_h[i] ^= hN[i] ^ M[i];
+      }
+
+   if(!last_block)
+      {
+      uint64_t carry = 0;
+      for(int i = 0; i < 8; i++)
+         {
+         const uint64_t m = force_le(M[i]);
+         const uint64_t hi = force_le(m_S[i]);
+         const uint64_t t = hi + m + carry;
+
+         m_S[i] = force_le(t);
+         if(t != m)
+            carry = (t < m);
+         }
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/streebog/streebog.h b/third_party/botan/src/lib/hash/streebog/streebog.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/streebog/streebog.h
@@ -0,0 +1,72 @@
+/*
+* Streebog
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREEBOG_H_
+#define BOTAN_STREEBOG_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(streebog.h)
+
+namespace Botan {
+
+/**
+* Streebog (GOST R 34.11-2012)
+* RFC 6986
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog : public HashFunction
+   {
+   public:
+      size_t output_length() const override { return m_output_bits / 8; }
+
+      HashFunction* clone() const override { return new Streebog(m_output_bits); }
+      void clear() override;
+      std::string name() const override;
+      size_t hash_block_size() const override { return 64; }
+
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      explicit Streebog(size_t output_bits);
+   protected:
+      void add_data(const uint8_t input[], size_t length) override;
+      void final_result(uint8_t out[]) override;
+
+      void compress(const uint8_t input[], bool lastblock = false);
+
+      void compress_64(const uint64_t input[], bool lastblock = false);
+
+   private:
+      const size_t m_output_bits;
+      uint64_t m_count;
+      size_t m_position;
+      secure_vector<uint8_t> m_buffer;
+      secure_vector<uint64_t> m_h;
+      secure_vector<uint64_t> m_S;
+   };
+
+
+/**
+* Streebog-256
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog_256 final : public Streebog
+   {
+   public:
+      Streebog_256() : Streebog(256) {}
+   };
+
+/**
+* Streebog-512
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog_512 final : public Streebog
+   {
+   public:
+      Streebog_512() : Streebog(512) {}
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp b/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp
@@ -0,0 +1,866 @@
+/*
+ * Derived from:
+ * https://github.com/degtyarevalexey/streebog
+ *
+ * Copyright (c) 2013, Alexey Degtyarev <alexey@renatasystems.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <botan/streebog.h>
+
+namespace Botan {
+
+extern const uint64_t STREEBOG_Ax[8][256] =
+   {
+      {
+      0xd01f715b5c7ef8e6ULL, 0x16fa240980778325ULL, 0xa8a42e857ee049c8ULL,
+      0x6ac1068fa186465bULL, 0x6e417bd7a2e9320bULL, 0x665c8167a437daabULL,
+      0x7666681aa89617f6ULL, 0x4b959163700bdcf5ULL, 0xf14be6b78df36248ULL,
+      0xc585bd689a625cffULL, 0x9557d7fca67d82cbULL, 0x89f0b969af6dd366ULL,
+      0xb0833d48749f6c35ULL, 0xa1998c23b1ecbc7cULL, 0x8d70c431ac02a736ULL,
+      0xd6dfbc2fd0a8b69eULL, 0x37aeb3e551fa198bULL, 0x0b7d128a40b5cf9cULL,
+      0x5a8f2008b5780cbcULL, 0xedec882284e333e5ULL, 0xd25fc177d3c7c2ceULL,
+      0x5e0f5d50b61778ecULL, 0x1d873683c0c24cb9ULL, 0xad040bcbb45d208cULL,
+      0x2f89a0285b853c76ULL, 0x5732fff6791b8d58ULL, 0x3e9311439ef6ec3fULL,
+      0xc9183a809fd3c00fULL, 0x83adf3f5260a01eeULL, 0xa6791941f4e8ef10ULL,
+      0x103ae97d0ca1cd5dULL, 0x2ce948121dee1b4aULL, 0x39738421dbf2bf53ULL,
+      0x093da2a6cf0cf5b4ULL, 0xcd9847d89cbcb45fULL, 0xf9561c078b2d8ae8ULL,
+      0x9c6a755a6971777fULL, 0xbc1ebaa0712ef0c5ULL, 0x72e61542abf963a6ULL,
+      0x78bb5fde229eb12eULL, 0x14ba94250fceb90dULL, 0x844d6697630e5282ULL,
+      0x98ea08026a1e032fULL, 0xf06bbea144217f5cULL, 0xdb6263d11ccb377aULL,
+      0x641c314b2b8ee083ULL, 0x320e96ab9b4770cfULL, 0x1ee7deb986a96b85ULL,
+      0xe96cf57a878c47b5ULL, 0xfdd6615f8842feb8ULL, 0xc83862965601dd1bULL,
+      0x2ea9f83e92572162ULL, 0xf876441142ff97fcULL, 0xeb2c455608357d9dULL,
+      0x5612a7e0b0c9904cULL, 0x6c01cbfb2d500823ULL, 0x4548a6a7fa037a2dULL,
+      0xabc4c6bf388b6ef4ULL, 0xbade77d4fdf8bebdULL, 0x799b07c8eb4cac3aULL,
+      0x0c9d87e805b19cf0ULL, 0xcb588aac106afa27ULL, 0xea0c1d40c1e76089ULL,
+      0x2869354a1e816f1aULL, 0xff96d17307fbc490ULL, 0x9f0a9d602f1a5043ULL,
+      0x96373fc6e016a5f7ULL, 0x5292dab8b3a6e41cULL, 0x9b8ae0382c752413ULL,
+      0x4f15ec3b7364a8a5ULL, 0x3fb349555724f12bULL, 0xc7c50d4415db66d7ULL,
+      0x92b7429ee379d1a7ULL, 0xd37f99611a15dfdaULL, 0x231427c05e34a086ULL,
+      0xa439a96d7b51d538ULL, 0xb403401077f01865ULL, 0xdda2aea5901d7902ULL,
+      0x0a5d4a9c8967d288ULL, 0xc265280adf660f93ULL, 0x8bb0094520d4e94eULL,
+      0x2a29856691385532ULL, 0x42a833c5bf072941ULL, 0x73c64d54622b7eb2ULL,
+      0x07e095624504536cULL, 0x8a905153e906f45aULL, 0x6f6123c16b3b2f1fULL,
+      0xc6e55552dc097bc3ULL, 0x4468feb133d16739ULL, 0xe211e7f0c7398829ULL,
+      0xa2f96419f7879b40ULL, 0x19074bdbc3ad38e9ULL, 0xf4ebc3f9474e0b0cULL,
+      0x43886bd376d53455ULL, 0xd8028beb5aa01046ULL, 0x51f23282f5cdc320ULL,
+      0xe7b1c2be0d84e16dULL, 0x081dfab006dee8a0ULL, 0x3b33340d544b857bULL,
+      0x7f5bcabc679ae242ULL, 0x0edd37c48a08a6d8ULL, 0x81ed43d9a9b33bc6ULL,
+      0xb1a3655ebd4d7121ULL, 0x69a1eeb5e7ed6167ULL, 0xf6ab73d5c8f73124ULL,
+      0x1a67a3e185c61fd5ULL, 0x2dc91004d43c065eULL, 0x0240b02c8fb93a28ULL,
+      0x90f7f2b26cc0eb8fULL, 0x3cd3a16f114fd617ULL, 0xaae49ea9f15973e0ULL,
+      0x06c0cd748cd64e78ULL, 0xda423bc7d5192a6eULL, 0xc345701c16b41287ULL,
+      0x6d2193ede4821537ULL, 0xfcf639494190e3acULL, 0x7c3b228621f1c57eULL,
+      0xfb16ac2b0494b0c0ULL, 0xbf7e529a3745d7f9ULL, 0x6881b6a32e3f7c73ULL,
+      0xca78d2bad9b8e733ULL, 0xbbfe2fc2342aa3a9ULL, 0x0dbddffecc6381e4ULL,
+      0x70a6a56e2440598eULL, 0xe4d12a844befc651ULL, 0x8c509c2765d0ba22ULL,
+      0xee8c6018c28814d9ULL, 0x17da7c1f49a59e31ULL, 0x609c4c1328e194d3ULL,
+      0xb3e3d57232f44b09ULL, 0x91d7aaa4a512f69bULL, 0x0ffd6fd243dabbccULL,
+      0x50d26a943c1fde34ULL, 0x6be15e9968545b4fULL, 0x94778fea6faf9fdfULL,
+      0x2b09dd7058ea4826ULL, 0x677cd9716de5c7bfULL, 0x49d5214fffb2e6ddULL,
+      0x0360e83a466b273cULL, 0x1fc786af4f7b7691ULL, 0xa0b9d435783ea168ULL,
+      0xd49f0c035f118cb6ULL, 0x01205816c9d21d14ULL, 0xac2453dd7d8f3d98ULL,
+      0x545217cc3f70aa64ULL, 0x26b4028e9489c9c2ULL, 0xdec2469fd6765e3eULL,
+      0x04807d58036f7450ULL, 0xe5f17292823ddb45ULL, 0xf30b569b024a5860ULL,
+      0x62dcfc3fa758aefbULL, 0xe84cad6c4e5e5aa1ULL, 0xccb81fce556ea94bULL,
+      0x53b282ae7a74f908ULL, 0x1b47fbf74c1402c1ULL, 0x368eebf39828049fULL,
+      0x7afbeff2ad278b06ULL, 0xbe5e0a8cfe97caedULL, 0xcfd8f7f413058e77ULL,
+      0xf78b2bc301252c30ULL, 0x4d555c17fcdd928dULL, 0x5f2f05467fc565f8ULL,
+      0x24f4b2a21b30f3eaULL, 0x860dd6bbecb768aaULL, 0x4c750401350f8f99ULL,
+      0x0000000000000000ULL, 0xecccd0344d312ef1ULL, 0xb5231806be220571ULL,
+      0xc105c030990d28afULL, 0x653c695de25cfd97ULL, 0x159acc33c61ca419ULL,
+      0xb89ec7f872418495ULL, 0xa9847693b73254dcULL, 0x58cf90243ac13694ULL,
+      0x59efc832f3132b80ULL, 0x5c4fed7c39ae42c4ULL, 0x828dabe3efd81cfaULL,
+      0xd13f294d95ace5f2ULL, 0x7d1b7a90e823d86aULL, 0xb643f03cf849224dULL,
+      0x3df3f979d89dcb03ULL, 0x7426d836272f2ddeULL, 0xdfe21e891fa4432aULL,
+      0x3a136c1b9d99986fULL, 0xfa36f43dcd46add4ULL, 0xc025982650df35bbULL,
+      0x856d3e81aadc4f96ULL, 0xc4a5e57e53b041ebULL, 0x4708168b75ba4005ULL,
+      0xaf44bbe73be41aa4ULL, 0x971767d029c4b8e3ULL, 0xb9be9feebb939981ULL,
+      0x215497ecd18d9aaeULL, 0x316e7e91dd2c57f3ULL, 0xcef8afe2dad79363ULL,
+      0x3853dc371220a247ULL, 0x35ee03c9de4323a3ULL, 0xe6919aa8c456fc79ULL,
+      0xe05157dc4880b201ULL, 0x7bdbb7e464f59612ULL, 0x127a59518318f775ULL,
+      0x332ecebd52956ddbULL, 0x8f30741d23bb9d1eULL, 0xd922d3fd93720d52ULL,
+      0x7746300c61440ae2ULL, 0x25d4eab4d2e2eefeULL, 0x75068020eefd30caULL,
+      0x135a01474acaea61ULL, 0x304e268714fe4ae7ULL, 0xa519f17bb283c82cULL,
+      0xdc82f6b359cf6416ULL, 0x5baf781e7caa11a8ULL, 0xb2c38d64fb26561dULL,
+      0x34ce5bdf17913eb7ULL, 0x5d6fb56af07c5fd0ULL, 0x182713cd0a7f25fdULL,
+      0x9e2ac576e6c84d57ULL, 0x9aaab82ee5a73907ULL, 0xa3d93c0f3e558654ULL,
+      0x7e7b92aaae48ff56ULL, 0x872d8ead256575beULL, 0x41c8dbfff96c0e7dULL,
+      0x99ca5014a3cc1e3bULL, 0x40e883e930be1369ULL, 0x1ca76e95091051adULL,
+      0x4e35b42dbab6b5b1ULL, 0x05a0254ecabd6944ULL, 0xe1710fca8152af15ULL,
+      0xf22b0e8dcb984574ULL, 0xb763a82a319b3f59ULL, 0x63fca4296e8ab3efULL,
+      0x9d4a2d4ca0a36a6bULL, 0xe331bfe60eeb953dULL, 0xd5bf541596c391a2ULL,
+      0xf5cb9bef8e9c1618ULL, 0x46284e9dbc685d11ULL, 0x2074cffa185f87baULL,
+      0xbd3ee2b6b8fcedd1ULL, 0xae64e3f1f23607b0ULL, 0xfeb68965ce29d984ULL,
+      0x55724fdaf6a2b770ULL, 0x29496d5cd753720eULL, 0xa75941573d3af204ULL,
+      0x8e102c0bea69800aULL, 0x111ab16bc573d049ULL, 0xd7ffe439197aab8aULL,
+      0xefac380e0b5a09cdULL, 0x48f579593660fbc9ULL, 0x22347fd697e6bd92ULL,
+      0x61bc1405e13389c7ULL, 0x4ab5c975b9d9c1e1ULL, 0x80cd1bcf606126d2ULL,
+      0x7186fd78ed92449aULL, 0x93971a882aabccb3ULL, 0x88d0e17f66bfce72ULL,
+      0x27945a985d5bd4d6ULL
+      },
+      {
+      0xde553f8c05a811c8ULL, 0x1906b59631b4f565ULL, 0x436e70d6b1964ff7ULL,
+      0x36d343cb8b1e9d85ULL, 0x843dfacc858aab5aULL, 0xfdfc95c299bfc7f9ULL,
+      0x0f634bdea1d51fa2ULL, 0x6d458b3b76efb3cdULL, 0x85c3f77cf8593f80ULL,
+      0x3c91315fbe737cb2ULL, 0x2148b03366ace398ULL, 0x18f8b8264c6761bfULL,
+      0xc830c1c495c9fb0fULL, 0x981a76102086a0aaULL, 0xaa16012142f35760ULL,
+      0x35cc54060c763cf6ULL, 0x42907d66cc45db2dULL, 0x8203d44b965af4bcULL,
+      0x3d6f3cefc3a0e868ULL, 0xbc73ff69d292bda7ULL, 0x8722ed0102e20a29ULL,
+      0x8f8185e8cd34deb7ULL, 0x9b0561dda7ee01d9ULL, 0x5335a0193227fad6ULL,
+      0xc9cecc74e81a6fd5ULL, 0x54f5832e5c2431eaULL, 0x99e47ba05d553470ULL,
+      0xf7bee756acd226ceULL, 0x384e05a5571816fdULL, 0xd1367452a47d0e6aULL,
+      0xf29fde1c386ad85bULL, 0x320c77316275f7caULL, 0xd0c879e2d9ae9ab0ULL,
+      0xdb7406c69110ef5dULL, 0x45505e51a2461011ULL, 0xfc029872e46c5323ULL,
+      0xfa3cb6f5f7bc0cc5ULL, 0x031f17cd8768a173ULL, 0xbd8df2d9af41297dULL,
+      0x9d3b4f5ab43e5e3fULL, 0x4071671b36feee84ULL, 0x716207e7d3e3b83dULL,
+      0x48d20ff2f9283a1aULL, 0x27769eb4757cbc7eULL, 0x5c56ebc793f2e574ULL,
+      0xa48b474f9ef5dc18ULL, 0x52cbada94ff46e0cULL, 0x60c7da982d8199c6ULL,
+      0x0e9d466edc068b78ULL, 0x4eec2175eaf865fcULL, 0x550b8e9e21f7a530ULL,
+      0x6b7ba5bc653fec2bULL, 0x5eb7f1ba6949d0ddULL, 0x57ea94e3db4c9099ULL,
+      0xf640eae6d101b214ULL, 0xdd4a284182c0b0bbULL, 0xff1d8fbf6304f250ULL,
+      0xb8accb933bf9d7e8ULL, 0xe8867c478eb68c4dULL, 0x3f8e2692391bddc1ULL,
+      0xcb2fd60912a15a7cULL, 0xaec935dbab983d2fULL, 0xf55ffd2b56691367ULL,
+      0x80e2ce366ce1c115ULL, 0x179bf3f8edb27e1dULL, 0x01fe0db07dd394daULL,
+      0xda8a0b76ecc37b87ULL, 0x44ae53e1df9584cbULL, 0xb310b4b77347a205ULL,
+      0xdfab323c787b8512ULL, 0x3b511268d070b78eULL, 0x65e6e3d2b9396753ULL,
+      0x6864b271e2574d58ULL, 0x259784c98fc789d7ULL, 0x02e11a7dfabb35a9ULL,
+      0x8841a6dfa337158bULL, 0x7ade78c39b5dcdd0ULL, 0xb7cf804d9a2cc84aULL,
+      0x20b6bd831b7f7742ULL, 0x75bd331d3a88d272ULL, 0x418f6aab4b2d7a5eULL,
+      0xd9951cbb6babdaf4ULL, 0xb6318dfde7ff5c90ULL, 0x1f389b112264aa83ULL,
+      0x492c024284fbaec0ULL, 0xe33a0363c608f9a0ULL, 0x2688930408af28a4ULL,
+      0xc7538a1a341ce4adULL, 0x5da8e677ee2171aeULL, 0x8c9e92254a5c7fc4ULL,
+      0x63d8cd55aae938b5ULL, 0x29ebd8daa97a3706ULL, 0x959827b37be88aa1ULL,
+      0x1484e4356adadf6eULL, 0xa7945082199d7d6bULL, 0xbf6ce8a455fa1cd4ULL,
+      0x9cc542eac9edcae5ULL, 0x79c16f0e1c356ca3ULL, 0x89bfab6fdee48151ULL,
+      0xd4174d1830c5f0ffULL, 0x9258048415eb419dULL, 0x6139d72850520d1cULL,
+      0x6a85a80c18ec78f1ULL, 0xcd11f88e0171059aULL, 0xcceff53e7ca29140ULL,
+      0xd229639f2315af19ULL, 0x90b91ef9ef507434ULL, 0x5977d28d074a1be1ULL,
+      0x311360fce51d56b9ULL, 0xc093a92d5a1f2f91ULL, 0x1a19a25bb6dc5416ULL,
+      0xeb996b8a09de2d3eULL, 0xfee3820f1ed7668aULL, 0xd7085ad5b7ad518cULL,
+      0x7fff41890fe53345ULL, 0xec5948bd67dde602ULL, 0x2fd5f65dbaaa68e0ULL,
+      0xa5754affe32648c2ULL, 0xf8ddac880d07396cULL, 0x6fa491468c548664ULL,
+      0x0c7c5c1326bdbed1ULL, 0x4a33158f03930fb3ULL, 0x699abfc19f84d982ULL,
+      0xe4fa2054a80b329cULL, 0x6707f9af438252faULL, 0x08a368e9cfd6d49eULL,
+      0x47b1442c58fd25b8ULL, 0xbbb3dc5ebc91769bULL, 0x1665fe489061eac7ULL,
+      0x33f27a811fa66310ULL, 0x93a609346838d547ULL, 0x30ed6d4c98cec263ULL,
+      0x1dd9816cd8df9f2aULL, 0x94662a03063b1e7bULL, 0x83fdd9fbeb896066ULL,
+      0x7b207573e68e590aULL, 0x5f49fc0a149a4407ULL, 0x343259b671a5a82cULL,
+      0xfbc2bb458a6f981fULL, 0xc272b350a0a41a38ULL, 0x3aaf1fd8ada32354ULL,
+      0x6cbb868b0b3c2717ULL, 0xa2b569c88d2583feULL, 0xf180c9d1bf027928ULL,
+      0xaf37386bd64ba9f5ULL, 0x12bacab2790a8088ULL, 0x4c0d3b0810435055ULL,
+      0xb2eeb9070e9436dfULL, 0xc5b29067cea7d104ULL, 0xdcb425f1ff132461ULL,
+      0x4f122cc5972bf126ULL, 0xac282fa651230886ULL, 0xe7e537992f6393efULL,
+      0xe61b3a2952b00735ULL, 0x709c0a57ae302ce7ULL, 0xe02514ae416058d3ULL,
+      0xc44c9dd7b37445deULL, 0x5a68c5408022ba92ULL, 0x1c278cdca50c0bf0ULL,
+      0x6e5a9cf6f18712beULL, 0x86dce0b17f319ef3ULL, 0x2d34ec2040115d49ULL,
+      0x4bcd183f7e409b69ULL, 0x2815d56ad4a9a3dcULL, 0x24698979f2141d0dULL,
+      0x0000000000000000ULL, 0x1ec696a15fb73e59ULL, 0xd86b110b16784e2eULL,
+      0x8e7f8858b0e74a6dULL, 0x063e2e8713d05fe6ULL, 0xe2c40ed3bbdb6d7aULL,
+      0xb1f1aeca89fc97acULL, 0xe1db191e3cb3cc09ULL, 0x6418ee62c4eaf389ULL,
+      0xc6ad87aa49cf7077ULL, 0xd6f65765ca7ec556ULL, 0x9afb6c6dda3d9503ULL,
+      0x7ce05644888d9236ULL, 0x8d609f95378feb1eULL, 0x23a9aa4e9c17d631ULL,
+      0x6226c0e5d73aac6fULL, 0x56149953a69f0443ULL, 0xeeb852c09d66d3abULL,
+      0x2b0ac2a753c102afULL, 0x07c023376e03cb3cULL, 0x2ccae1903dc2c993ULL,
+      0xd3d76e2f5ec63bc3ULL, 0x9e2458973356ff4cULL, 0xa66a5d32644ee9b1ULL,
+      0x0a427294356de137ULL, 0x783f62be61e6f879ULL, 0x1344c70204d91452ULL,
+      0x5b96c8f0fdf12e48ULL, 0xa90916ecc59bf613ULL, 0xbe92e5142829880eULL,
+      0x727d102a548b194eULL, 0x1be7afebcb0fc0ccULL, 0x3e702b2244c8491bULL,
+      0xd5e940a84d166425ULL, 0x66f9f41f3e51c620ULL, 0xabe80c913f20c3baULL,
+      0xf07ec461c2d1edf2ULL, 0xf361d3ac45b94c81ULL, 0x0521394a94b8fe95ULL,
+      0xadd622162cf09c5cULL, 0xe97871f7f3651897ULL, 0xf4a1f09b2bba87bdULL,
+      0x095d6559b2054044ULL, 0x0bbc7f2448be75edULL, 0x2af4cf172e129675ULL,
+      0x157ae98517094bb4ULL, 0x9fda55274e856b96ULL, 0x914713499283e0eeULL,
+      0xb952c623462a4332ULL, 0x74433ead475b46a8ULL, 0x8b5eb112245fb4f8ULL,
+      0xa34b6478f0f61724ULL, 0x11a5dd7ffe6221fbULL, 0xc16da49d27ccbb4bULL,
+      0x76a224d0bde07301ULL, 0x8aa0bca2598c2022ULL, 0x4df336b86d90c48fULL,
+      0xea67663a740db9e4ULL, 0xef465f70e0b54771ULL, 0x39b008152acb8227ULL,
+      0x7d1e5bf4f55e06ecULL, 0x105bd0cf83b1b521ULL, 0x775c2960c033e7dbULL,
+      0x7e014c397236a79fULL, 0x811cc386113255cfULL, 0xeda7450d1a0e72d8ULL,
+      0x5889df3d7a998f3bULL, 0x2e2bfbedc779fc3aULL, 0xce0eef438619a4e9ULL,
+      0x372d4e7bf6cd095fULL, 0x04df34fae96b6a4fULL, 0xf923a13870d4adb6ULL,
+      0xa1aa7e050a4d228dULL, 0xa8f71b5cb84862c9ULL, 0xb52e9a306097fde3ULL,
+      0x0d8251a35b6e2a0bULL, 0x2257a7fee1c442ebULL, 0x73831d9a29588d94ULL,
+      0x51d4ba64c89ccf7fULL, 0x502ab7d4b54f5ba5ULL, 0x97793dce8153bf08ULL,
+      0xe5042de4d5d8a646ULL, 0x9687307efc802bd2ULL, 0xa05473b5779eb657ULL,
+      0xb4d097801d446939ULL, 0xcff0e2f3fbca3033ULL, 0xc38cbee0dd778ee2ULL,
+      0x464f499c252eb162ULL, 0xcad1dbb96f72cea6ULL, 0xba4dd1eec142e241ULL,
+      0xb00fa37af42f0376ULL
+      },
+      {
+      0xcce4cd3aa968b245ULL, 0x089d5484e80b7fafULL, 0x638246c1b3548304ULL,
+      0xd2fe0ec8c2355492ULL, 0xa7fbdf7ff2374eeeULL, 0x4df1600c92337a16ULL,
+      0x84e503ea523b12fbULL, 0x0790bbfd53ab0c4aULL, 0x198a780f38f6ea9dULL,
+      0x2ab30c8f55ec48cbULL, 0xe0f7fed6b2c49db5ULL, 0xb6ecf3f422cadbdcULL,
+      0x409c9a541358df11ULL, 0xd3ce8a56dfde3fe3ULL, 0xc3e9224312c8c1a0ULL,
+      0x0d6dfa58816ba507ULL, 0xddf3e1b179952777ULL, 0x04c02a42748bb1d9ULL,
+      0x94c2abff9f2decb8ULL, 0x4f91752da8f8acf4ULL, 0x78682befb169bf7bULL,
+      0xe1c77a48af2ff6c4ULL, 0x0c5d7ec69c80ce76ULL, 0x4cc1e4928fd81167ULL,
+      0xfeed3d24d9997b62ULL, 0x518bb6dfc3a54a23ULL, 0x6dbf2d26151f9b90ULL,
+      0xb5bc624b05ea664fULL, 0xe86aaa525acfe21aULL, 0x4801ced0fb53a0beULL,
+      0xc91463e6c00868edULL, 0x1027a815cd16fe43ULL, 0xf67069a0319204cdULL,
+      0xb04ccc976c8abce7ULL, 0xc0b9b3fc35e87c33ULL, 0xf380c77c58f2de65ULL,
+      0x50bb3241de4e2152ULL, 0xdf93f490435ef195ULL, 0xf1e0d25d62390887ULL,
+      0xaf668bfb1a3c3141ULL, 0xbc11b251f00a7291ULL, 0x73a5eed47e427d47ULL,
+      0x25bee3f6ee4c3b2eULL, 0x43cc0beb34786282ULL, 0xc824e778dde3039cULL,
+      0xf97d86d98a327728ULL, 0xf2b043e24519b514ULL, 0xe297ebf7880f4b57ULL,
+      0x3a94a49a98fab688ULL, 0x868516cb68f0c419ULL, 0xeffa11af0964ee50ULL,
+      0xa4ab4ec0d517f37dULL, 0xa9c6b498547c567aULL, 0x8e18424f80fbbbb6ULL,
+      0x0bcdc53bcf2bc23cULL, 0x137739aaea3643d0ULL, 0x2c1333ec1bac2ff0ULL,
+      0x8d48d3f0a7db0625ULL, 0x1e1ac3f26b5de6d7ULL, 0xf520f81f16b2b95eULL,
+      0x9f0f6ec450062e84ULL, 0x0130849e1deb6b71ULL, 0xd45e31ab8c7533a9ULL,
+      0x652279a2fd14e43fULL, 0x3209f01e70f1c927ULL, 0xbe71a770cac1a473ULL,
+      0x0e3d6be7a64b1894ULL, 0x7ec8148cff29d840ULL, 0xcb7476c7fac3be0fULL,
+      0x72956a4a63a91636ULL, 0x37f95ec21991138fULL, 0x9e3fea5a4ded45f5ULL,
+      0x7b38ba50964902e8ULL, 0x222e580bbde73764ULL, 0x61e253e0899f55e6ULL,
+      0xfc8d2805e352ad80ULL, 0x35994be3235ac56dULL, 0x09add01af5e014deULL,
+      0x5e8659a6780539c6ULL, 0xb17c48097161d796ULL, 0x026015213acbd6e2ULL,
+      0xd1ae9f77e515e901ULL, 0xb7dc776a3f21b0adULL, 0xaba6a1b96eb78098ULL,
+      0x9bcf4486248d9f5dULL, 0x582666c536455efdULL, 0xfdbdac9bfeb9c6f1ULL,
+      0xc47999be4163cdeaULL, 0x765540081722a7efULL, 0x3e548ed8ec710751ULL,
+      0x3d041f67cb51bac2ULL, 0x7958af71ac82d40aULL, 0x36c9da5c047a78feULL,
+      0xed9a048e33af38b2ULL, 0x26ee7249c96c86bdULL, 0x900281bdeba65d61ULL,
+      0x11172c8bd0fd9532ULL, 0xea0abf73600434f8ULL, 0x42fc8f75299309f3ULL,
+      0x34a9cf7d3eb1ae1cULL, 0x2b838811480723baULL, 0x5ce64c8742ceef24ULL,
+      0x1adae9b01fd6570eULL, 0x3c349bf9d6bad1b3ULL, 0x82453c891c7b75c0ULL,
+      0x97923a40b80d512bULL, 0x4a61dbf1c198765cULL, 0xb48ce6d518010d3eULL,
+      0xcfb45c858e480fd6ULL, 0xd933cbf30d1e96aeULL, 0xd70ea014ab558e3aULL,
+      0xc189376228031742ULL, 0x9262949cd16d8b83ULL, 0xeb3a3bed7def5f89ULL,
+      0x49314a4ee6b8cbcfULL, 0xdcc3652f647e4c06ULL, 0xda635a4c2a3e2b3dULL,
+      0x470c21a940f3d35bULL, 0x315961a157d174b4ULL, 0x6672e81dda3459acULL,
+      0x5b76f77a1165e36eULL, 0x445cb01667d36ec8ULL, 0xc5491d205c88a69bULL,
+      0x456c34887a3805b9ULL, 0xffddb9bac4721013ULL, 0x99af51a71e4649bfULL,
+      0xa15be01cbc7729d5ULL, 0x52db2760e485f7b0ULL, 0x8c78576eba306d54ULL,
+      0xae560f6507d75a30ULL, 0x95f22f6182c687c9ULL, 0x71c5fbf54489aba5ULL,
+      0xca44f259e728d57eULL, 0x88b87d2ccebbdc8dULL, 0xbab18d32be4a15aaULL,
+      0x8be8ec93e99b611eULL, 0x17b713e89ebdf209ULL, 0xb31c5d284baa0174ULL,
+      0xeeca9531148f8521ULL, 0xb8d198138481c348ULL, 0x8988f9b2d350b7fcULL,
+      0xb9e11c8d996aa839ULL, 0x5a4673e40c8e881fULL, 0x1687977683569978ULL,
+      0xbf4123eed72acf02ULL, 0x4ea1f1b3b513c785ULL, 0xe767452be16f91ffULL,
+      0x7505d1b730021a7cULL, 0xa59bca5ec8fc980cULL, 0xad069eda20f7e7a3ULL,
+      0x38f4b1bba231606aULL, 0x60d2d77e94743e97ULL, 0x9affc0183966f42cULL,
+      0x248e6768f3a7505fULL, 0xcdd449a4b483d934ULL, 0x87b59255751baf68ULL,
+      0x1bea6d2e023d3c7fULL, 0x6b1f12455b5ffcabULL, 0x743555292de9710dULL,
+      0xd8034f6d10f5fddfULL, 0xc6198c9f7ba81b08ULL, 0xbb8109aca3a17edbULL,
+      0xfa2d1766ad12cabbULL, 0xc729080166437079ULL, 0x9c5fff7b77269317ULL,
+      0x0000000000000000ULL, 0x15d706c9a47624ebULL, 0x6fdf38072fd44d72ULL,
+      0x5fb6dd3865ee52b7ULL, 0xa33bf53d86bcff37ULL, 0xe657c1b5fc84fa8eULL,
+      0xaa962527735cebe9ULL, 0x39c43525bfda0b1bULL, 0x204e4d2a872ce186ULL,
+      0x7a083ece8ba26999ULL, 0x554b9c9db72efbfaULL, 0xb22cd9b656416a05ULL,
+      0x96a2bedea5e63a5aULL, 0x802529a826b0a322ULL, 0x8115ad363b5bc853ULL,
+      0x8375b81701901eb1ULL, 0x3069e53f4a3a1fc5ULL, 0xbd2136cfede119e0ULL,
+      0x18bafc91251d81ecULL, 0x1d4a524d4c7d5b44ULL, 0x05f0aedc6960daa8ULL,
+      0x29e39d3072ccf558ULL, 0x70f57f6b5962c0d4ULL, 0x989fd53903ad22ceULL,
+      0xf84d024797d91c59ULL, 0x547b1803aac5908bULL, 0xf0d056c37fd263f6ULL,
+      0xd56eb535919e58d8ULL, 0x1c7ad6d351963035ULL, 0x2e7326cd2167f912ULL,
+      0xac361a443d1c8cd2ULL, 0x697f076461942a49ULL, 0x4b515f6fdc731d2dULL,
+      0x8ad8680df4700a6fULL, 0x41ac1eca0eb3b460ULL, 0x7d988533d80965d3ULL,
+      0xa8f6300649973d0bULL, 0x7765c4960ac9cc9eULL, 0x7ca801adc5e20ea2ULL,
+      0xdea3700e5eb59ae4ULL, 0xa06b6482a19c42a4ULL, 0x6a2f96db46b497daULL,
+      0x27def6d7d487edccULL, 0x463ca5375d18b82aULL, 0xa6cb5be1efdc259fULL,
+      0x53eba3fef96e9cc1ULL, 0xce84d81b93a364a7ULL, 0xf4107c810b59d22fULL,
+      0x333974806d1aa256ULL, 0x0f0def79bba073e5ULL, 0x231edc95a00c5c15ULL,
+      0xe437d494c64f2c6cULL, 0x91320523f64d3610ULL, 0x67426c83c7df32ddULL,
+      0x6eefbc99323f2603ULL, 0x9d6f7be56acdf866ULL, 0x5916e25b2bae358cULL,
+      0x7ff89012e2c2b331ULL, 0x035091bf2720bd93ULL, 0x561b0d22900e4669ULL,
+      0x28d319ae6f279e29ULL, 0x2f43a2533c8c9263ULL, 0xd09e1be9f8fe8270ULL,
+      0xf740ed3e2c796fbcULL, 0xdb53ded237d5404cULL, 0x62b2c25faebfe875ULL,
+      0x0afd41a5d2c0a94dULL, 0x6412fd3ce0ff8f4eULL, 0xe3a76f6995e42026ULL,
+      0x6c8fa9b808f4f0e1ULL, 0xc2d9a6dd0f23aad1ULL, 0x8f28c6d19d10d0c7ULL,
+      0x85d587744fd0798aULL, 0xa20b71a39b579446ULL, 0x684f83fa7c7f4138ULL,
+      0xe507500adba4471dULL, 0x3f640a46f19a6c20ULL, 0x1247bd34f7dd28a1ULL,
+      0x2d23b77206474481ULL, 0x93521002cc86e0f2ULL, 0x572b89bc8de52d18ULL,
+      0xfb1d93f8b0f9a1caULL, 0xe95a2ecc4724896bULL, 0x3ba420048511ddf9ULL,
+      0xd63e248ab6bee54bULL, 0x5dd6c8195f258455ULL, 0x06a03f634e40673bULL,
+      0x1f2a476c76b68da6ULL, 0x217ec9b49ac78af7ULL, 0xecaa80102e4453c3ULL,
+      0x14e78257b99d4f9aULL
+      },
+      {
+      0x20329b2cc87bba05ULL, 0x4f5eb6f86546a531ULL, 0xd4f44775f751b6b1ULL,
+      0x8266a47b850dfa8bULL, 0xbb986aa15a6ca985ULL, 0xc979eb08f9ae0f99ULL,
+      0x2da6f447a2375ea1ULL, 0x1e74275dcd7d8576ULL, 0xbc20180a800bc5f8ULL,
+      0xb4a2f701b2dc65beULL, 0xe726946f981b6d66ULL, 0x48e6c453bf21c94cULL,
+      0x42cad9930f0a4195ULL, 0xefa47b64aacccd20ULL, 0x71180a8960409a42ULL,
+      0x8bb3329bf6a44e0cULL, 0xd34c35de2d36daccULL, 0xa92f5b7cbc23dc96ULL,
+      0xb31a85aa68bb09c3ULL, 0x13e04836a73161d2ULL, 0xb24dfc4129c51d02ULL,
+      0x8ae44b70b7da5acdULL, 0xe671ed84d96579a7ULL, 0xa4bb3417d66f3832ULL,
+      0x4572ab38d56d2de8ULL, 0xb1b47761ea47215cULL, 0xe81c09cf70aba15dULL,
+      0xffbdb872ce7f90acULL, 0xa8782297fd5dc857ULL, 0x0d946f6b6a4ce4a4ULL,
+      0xe4df1f4f5b995138ULL, 0x9ebc71edca8c5762ULL, 0x0a2c1dc0b02b88d9ULL,
+      0x3b503c115d9d7b91ULL, 0xc64376a8111ec3a2ULL, 0xcec199a323c963e4ULL,
+      0xdc76a87ec58616f7ULL, 0x09d596e073a9b487ULL, 0x14583a9d7d560dafULL,
+      0xf4c6dc593f2a0cb4ULL, 0xdd21d19584f80236ULL, 0x4a4836983ddde1d3ULL,
+      0xe58866a41ae745f9ULL, 0xf591a5b27e541875ULL, 0x891dc05074586693ULL,
+      0x5b068c651810a89eULL, 0xa30346bc0c08544fULL, 0x3dbf3751c684032dULL,
+      0x2a1e86ec785032dcULL, 0xf73f5779fca830eaULL, 0xb60c05ca30204d21ULL,
+      0x0cc316802b32f065ULL, 0x8770241bdd96be69ULL, 0xb861e18199ee95dbULL,
+      0xf805cad91418fcd1ULL, 0x29e70dccbbd20e82ULL, 0xc7140f435060d763ULL,
+      0x0f3a9da0e8b0cc3bULL, 0xa2543f574d76408eULL, 0xbd7761e1c175d139ULL,
+      0x4b1f4f737ca3f512ULL, 0x6dc2df1f2fc137abULL, 0xf1d05c3967b14856ULL,
+      0xa742bf3715ed046cULL, 0x654030141d1697edULL, 0x07b872abda676c7dULL,
+      0x3ce84eba87fa17ecULL, 0xc1fb0403cb79afdfULL, 0x3e46bc7105063f73ULL,
+      0x278ae987121cd678ULL, 0xa1adb4778ef47cd0ULL, 0x26dd906c5362c2b9ULL,
+      0x05168060589b44e2ULL, 0xfbfc41f9d79ac08fULL, 0x0e6de44ba9ced8faULL,
+      0x9feb08068bf243a3ULL, 0x7b341749d06b129bULL, 0x229c69e74a87929aULL,
+      0xe09ee6c4427c011bULL, 0x5692e30e725c4c3aULL, 0xda99a33e5e9f6e4bULL,
+      0x353dd85af453a36bULL, 0x25241b4c90e0fee7ULL, 0x5de987258309d022ULL,
+      0xe230140fc0802984ULL, 0x93281e86a0c0b3c6ULL, 0xf229d719a4337408ULL,
+      0x6f6c2dd4ad3d1f34ULL, 0x8ea5b2fbae3f0aeeULL, 0x8331dd90c473ee4aULL,
+      0x346aa1b1b52db7aaULL, 0xdf8f235e06042aa9ULL, 0xcc6f6b68a1354b7bULL,
+      0x6c95a6f46ebf236aULL, 0x52d31a856bb91c19ULL, 0x1a35ded6d498d555ULL,
+      0xf37eaef2e54d60c9ULL, 0x72e181a9a3c2a61cULL, 0x98537aad51952fdeULL,
+      0x16f6c856ffaa2530ULL, 0xd960281e9d1d5215ULL, 0x3a0745fa1ce36f50ULL,
+      0x0b7b642bf1559c18ULL, 0x59a87eae9aec8001ULL, 0x5e100c05408bec7cULL,
+      0x0441f98b19e55023ULL, 0xd70dcc5534d38aefULL, 0x927f676de1bea707ULL,
+      0x9769e70db925e3e5ULL, 0x7a636ea29115065aULL, 0x468b201816ef11b6ULL,
+      0xab81a9b73edff409ULL, 0xc0ac7de88a07bb1eULL, 0x1f235eb68c0391b7ULL,
+      0x6056b074458dd30fULL, 0xbe8eeac102f7ed67ULL, 0xcd381283e04b5fbaULL,
+      0x5cbefecec277c4e3ULL, 0xd21b4c356c48ce0dULL, 0x1019c31664b35d8cULL,
+      0x247362a7d19eea26ULL, 0xebe582efb3299d03ULL, 0x02aef2cb82fc289fULL,
+      0x86275df09ce8aaa8ULL, 0x28b07427faac1a43ULL, 0x38a9b7319e1f47cfULL,
+      0xc82e92e3b8d01b58ULL, 0x06ef0b409b1978bcULL, 0x62f842bfc771fb90ULL,
+      0x9904034610eb3b1fULL, 0xded85ab5477a3e68ULL, 0x90d195a663428f98ULL,
+      0x5384636e2ac708d8ULL, 0xcbd719c37b522706ULL, 0xae9729d76644b0ebULL,
+      0x7c8c65e20a0c7ee6ULL, 0x80c856b007f1d214ULL, 0x8c0b40302cc32271ULL,
+      0xdbcedad51fe17a8aULL, 0x740e8ae938dbdea0ULL, 0xa615c6dc549310adULL,
+      0x19cc55f6171ae90bULL, 0x49b1bdb8fe5fdd8dULL, 0xed0a89af2830e5bfULL,
+      0x6a7aadb4f5a65bd6ULL, 0x7e22972988f05679ULL, 0xf952b3325566e810ULL,
+      0x39fecedadf61530eULL, 0x6101c99f04f3c7ceULL, 0x2e5f7f6761b562ffULL,
+      0xf08725d226cf5c97ULL, 0x63af3b54860fef51ULL, 0x8ff2cb10ef411e2fULL,
+      0x884ab9bb35267252ULL, 0x4df04433e7ba8daeULL, 0x9afd8866d3690741ULL,
+      0x66b9bb34de94abb3ULL, 0x9baaf18d92171380ULL, 0x543c11c5f0a064a5ULL,
+      0x17a1b1bdbed431f1ULL, 0xb5f58eeaf3a2717fULL, 0xc355f6c849858740ULL,
+      0xec5df044694ef17eULL, 0xd83751f5dc6346d4ULL, 0xfc4433520dfdacf2ULL,
+      0x0000000000000000ULL, 0x5a51f58e596ebc5fULL, 0x3285aaf12e34cf16ULL,
+      0x8d5c39db6dbd36b0ULL, 0x12b731dde64f7513ULL, 0x94906c2d7aa7dfbbULL,
+      0x302b583aacc8e789ULL, 0x9d45facd090e6b3cULL, 0x2165e2c78905aec4ULL,
+      0x68d45f7f775a7349ULL, 0x189b2c1d5664fdcaULL, 0xe1c99f2f030215daULL,
+      0x6983269436246788ULL, 0x8489af3b1e148237ULL, 0xe94b702431d5b59cULL,
+      0x33d2d31a6f4adbd7ULL, 0xbfd9932a4389f9a6ULL, 0xb0e30e8aab39359dULL,
+      0xd1e2c715afcaf253ULL, 0x150f43763c28196eULL, 0xc4ed846393e2eb3dULL,
+      0x03f98b20c3823c5eULL, 0xfd134ab94c83b833ULL, 0x556b682eb1de7064ULL,
+      0x36c4537a37d19f35ULL, 0x7559f30279a5ca61ULL, 0x799ae58252973a04ULL,
+      0x9c12832648707ffdULL, 0x78cd9c6913e92ec5ULL, 0x1d8dac7d0effb928ULL,
+      0x439da0784e745554ULL, 0x413352b3cc887dcbULL, 0xbacf134a1b12bd44ULL,
+      0x114ebafd25cd494dULL, 0x2f08068c20cb763eULL, 0x76a07822ba27f63fULL,
+      0xeab2fb04f25789c2ULL, 0xe3676de481fe3d45ULL, 0x1b62a73d95e6c194ULL,
+      0x641749ff5c68832cULL, 0xa5ec4dfc97112cf3ULL, 0xf6682e92bdd6242bULL,
+      0x3f11c59a44782bb2ULL, 0x317c21d1edb6f348ULL, 0xd65ab5be75ad9e2eULL,
+      0x6b2dd45fb4d84f17ULL, 0xfaab381296e4d44eULL, 0xd0b5befeeeb4e692ULL,
+      0x0882ef0b32d7a046ULL, 0x512a91a5a83b2047ULL, 0x963e9ee6f85bf724ULL,
+      0x4e09cf132438b1f0ULL, 0x77f701c9fb59e2feULL, 0x7ddb1c094b726a27ULL,
+      0x5f4775ee01f5f8bdULL, 0x9186ec4d223c9b59ULL, 0xfeeac1998f01846dULL,
+      0xac39db1ce4b89874ULL, 0xb75b7c21715e59e0ULL, 0xafc0503c273aa42aULL,
+      0x6e3b543fec430bf5ULL, 0x704f7362213e8e83ULL, 0x58ff0745db9294c0ULL,
+      0x67eec2df9feabf72ULL, 0xa0facd9ccf8a6811ULL, 0xb936986ad890811aULL,
+      0x95c715c63bd9cb7aULL, 0xca8060283a2c33c7ULL, 0x507de84ee9453486ULL,
+      0x85ded6d05f6a96f6ULL, 0x1cdad5964f81ade9ULL, 0xd5a33e9eb62fa270ULL,
+      0x40642b588df6690aULL, 0x7f75eec2c98e42b8ULL, 0x2cf18dace3494a60ULL,
+      0x23cb100c0bf9865bULL, 0xeef3028febb2d9e1ULL, 0x4425d2d394133929ULL,
+      0xaad6d05c7fa1e0c8ULL, 0xad6ea2f7a5c68cb5ULL, 0xc2028f2308fb9381ULL,
+      0x819f2f5b468fc6d5ULL, 0xc5bafd88d29cfffcULL, 0x47dc59f357910577ULL,
+      0x2b49ff07392e261dULL, 0x57c59ae5332258fbULL, 0x73b6f842e2bcb2ddULL,
+      0xcf96e04862b77725ULL, 0x4ca73dd8a6c4996fULL, 0x015779eb417e14c1ULL,
+      0x37932a9176af8bf4ULL
+      },
+      {
+      0x190a2c9b249df23eULL, 0x2f62f8b62263e1e9ULL, 0x7a7f754740993655ULL,
+      0x330b7ba4d5564d9fULL, 0x4c17a16a46672582ULL, 0xb22f08eb7d05f5b8ULL,
+      0x535f47f40bc148ccULL, 0x3aec5d27d4883037ULL, 0x10ed0a1825438f96ULL,
+      0x516101f72c233d17ULL, 0x13cc6f949fd04eaeULL, 0x739853c441474bfdULL,
+      0x653793d90d3f5b1bULL, 0x5240647b96b0fc2fULL, 0x0c84890ad27623e0ULL,
+      0xd7189b32703aaea3ULL, 0x2685de3523bd9c41ULL, 0x99317c5b11bffefaULL,
+      0x0d9baa854f079703ULL, 0x70b93648fbd48ac5ULL, 0xa80441fce30bc6beULL,
+      0x7287704bdc36ff1eULL, 0xb65384ed33dc1f13ULL, 0xd36417343ee34408ULL,
+      0x39cd38ab6e1bf10fULL, 0x5ab861770a1f3564ULL, 0x0ebacf09f594563bULL,
+      0xd04572b884708530ULL, 0x3cae9722bdb3af47ULL, 0x4a556b6f2f5cbaf2ULL,
+      0xe1704f1f76c4bd74ULL, 0x5ec4ed7144c6dfcfULL, 0x16afc01d4c7810e6ULL,
+      0x283f113cd629ca7aULL, 0xaf59a8761741ed2dULL, 0xeed5a3991e215facULL,
+      0x3bf37ea849f984d4ULL, 0xe413e096a56ce33cULL, 0x2c439d3a98f020d1ULL,
+      0x637559dc6404c46bULL, 0x9e6c95d1e5f5d569ULL, 0x24bb9836045fe99aULL,
+      0x44efa466dac8ecc9ULL, 0xc6eab2a5c80895d6ULL, 0x803b50c035220cc4ULL,
+      0x0321658cba93c138ULL, 0x8f9ebc465dc7ee1cULL, 0xd15a5137190131d3ULL,
+      0x0fa5ec8668e5e2d8ULL, 0x91c979578d1037b1ULL, 0x0642ca05693b9f70ULL,
+      0xefca80168350eb4fULL, 0x38d21b24f36a45ecULL, 0xbeab81e1af73d658ULL,
+      0x8cbfd9cae7542f24ULL, 0xfd19cc0d81f11102ULL, 0x0ac6430fbb4dbc90ULL,
+      0x1d76a09d6a441895ULL, 0x2a01573ff1cbbfa1ULL, 0xb572e161894fde2bULL,
+      0x8124734fa853b827ULL, 0x614b1fdf43e6b1b0ULL, 0x68ac395c4238cc18ULL,
+      0x21d837bfd7f7b7d2ULL, 0x20c714304a860331ULL, 0x5cfaab726324aa14ULL,
+      0x74c5ba4eb50d606eULL, 0xf3a3030474654739ULL, 0x23e671bcf015c209ULL,
+      0x45f087e947b9582aULL, 0xd8bd77b418df4c7bULL, 0xe06f6c90ebb50997ULL,
+      0x0bd96080263c0873ULL, 0x7e03f9410e40dcfeULL, 0xb8e94be4c6484928ULL,
+      0xfb5b0608e8ca8e72ULL, 0x1a2b49179e0e3306ULL, 0x4e29e76961855059ULL,
+      0x4f36c4e6fcf4e4baULL, 0x49740ee395cf7bcaULL, 0xc2963ea386d17f7dULL,
+      0x90d65ad810618352ULL, 0x12d34c1b02a1fa4dULL, 0xfa44258775bb3a91ULL,
+      0x18150f14b9ec46ddULL, 0x1491861e6b9a653dULL, 0x9a1019d7ab2c3fc2ULL,
+      0x3668d42d06fe13d7ULL, 0xdcc1fbb25606a6d0ULL, 0x969490dd795a1c22ULL,
+      0x3549b1a1bc6dd2efULL, 0xc94f5e23a0ed770eULL, 0xb9f6686b5b39fdcbULL,
+      0xc4d4f4a6efeae00dULL, 0xe732851a1fff2204ULL, 0x94aad6de5eb869f9ULL,
+      0x3f8ff2ae07206e7fULL, 0xfe38a9813b62d03aULL, 0xa7a1ad7a8bee2466ULL,
+      0x7b6056c8dde882b6ULL, 0x302a1e286fc58ca7ULL, 0x8da0fa457a259bc7ULL,
+      0xb3302b64e074415bULL, 0x5402ae7eff8b635fULL, 0x08f8050c9cafc94bULL,
+      0xae468bf98a3059ceULL, 0x88c355cca98dc58fULL, 0xb10e6d67c7963480ULL,
+      0xbad70de7e1aa3cf3ULL, 0xbfb4a26e320262bbULL, 0xcb711820870f02d5ULL,
+      0xce12b7a954a75c9dULL, 0x563ce87dd8691684ULL, 0x9f73b65e7884618aULL,
+      0x2b1e74b06cba0b42ULL, 0x47cec1ea605b2df1ULL, 0x1c698312f735ac76ULL,
+      0x5fdbcefed9b76b2cULL, 0x831a354c8fb1cdfcULL, 0x820516c312c0791fULL,
+      0xb74ca762aeadabf0ULL, 0xfc06ef821c80a5e1ULL, 0x5723cbf24518a267ULL,
+      0x9d4df05d5f661451ULL, 0x588627742dfd40bfULL, 0xda8331b73f3d39a0ULL,
+      0x17b0e392d109a405ULL, 0xf965400bcf28fba9ULL, 0x7c3dbf4229a2a925ULL,
+      0x023e460327e275dbULL, 0x6cd0b55a0ce126b3ULL, 0xe62da695828e96e7ULL,
+      0x42ad6e63b3f373b9ULL, 0xe50cc319381d57dfULL, 0xc5cbd729729b54eeULL,
+      0x46d1e265fd2a9912ULL, 0x6428b056904eeff8ULL, 0x8be23040131e04b7ULL,
+      0x6709d5da2add2ec0ULL, 0x075de98af44a2b93ULL, 0x8447dcc67bfbe66fULL,
+      0x6616f655b7ac9a23ULL, 0xd607b8bded4b1a40ULL, 0x0563af89d3a85e48ULL,
+      0x3db1b4ad20c21ba4ULL, 0x11f22997b8323b75ULL, 0x292032b34b587e99ULL,
+      0x7f1cdace9331681dULL, 0x8e819fc9c0b65affULL, 0xa1e3677fe2d5bb16ULL,
+      0xcd33d225ee349da5ULL, 0xd9a2543b85aef898ULL, 0x795e10cbfa0af76dULL,
+      0x25a4bbb9992e5d79ULL, 0x78413344677b438eULL, 0xf0826688cef68601ULL,
+      0xd27b34bba392f0ebULL, 0x551d8df162fad7bcULL, 0x1e57c511d0d7d9adULL,
+      0xdeffbdb171e4d30bULL, 0xf4feea8e802f6caaULL, 0xa480c8f6317de55eULL,
+      0xa0fc44f07fa40ff5ULL, 0x95b5f551c3c9dd1aULL, 0x22f952336d6476eaULL,
+      0x0000000000000000ULL, 0xa6be8ef5169f9085ULL, 0xcc2cf1aa73452946ULL,
+      0x2e7ddb39bf12550aULL, 0xd526dd3157d8db78ULL, 0x486b2d6c08becf29ULL,
+      0x9b0f3a58365d8b21ULL, 0xac78cdfaadd22c15ULL, 0xbc95c7e28891a383ULL,
+      0x6a927f5f65dab9c3ULL, 0xc3891d2c1ba0cb9eULL, 0xeaa92f9f50f8b507ULL,
+      0xcf0d9426c9d6e87eULL, 0xca6e3baf1a7eb636ULL, 0xab25247059980786ULL,
+      0x69b31ad3df4978fbULL, 0xe2512a93cc577c4cULL, 0xff278a0ea61364d9ULL,
+      0x71a615c766a53e26ULL, 0x89dc764334fc716cULL, 0xf87a638452594f4aULL,
+      0xf2bc208be914f3daULL, 0x8766b94ac1682757ULL, 0xbbc82e687cdb8810ULL,
+      0x626a7a53f9757088ULL, 0xa2c202f358467a2eULL, 0x4d0882e5db169161ULL,
+      0x09e7268301de7da8ULL, 0xe897699c771ac0dcULL, 0xc8507dac3d9cc3edULL,
+      0xc0a878a0a1330aa6ULL, 0x978bb352e42ba8c1ULL, 0xe9884a13ea6b743fULL,
+      0x279afdbabecc28a2ULL, 0x047c8c064ed9eaabULL, 0x507e2278b15289f4ULL,
+      0x599904fbb08cf45cULL, 0xbd8ae46d15e01760ULL, 0x31353da7f2b43844ULL,
+      0x8558ff49e68a528cULL, 0x76fbfc4d92ef15b5ULL, 0x3456922e211c660cULL,
+      0x86799ac55c1993b4ULL, 0x3e90d1219a51da9cULL, 0x2d5cbeb505819432ULL,
+      0x982e5fd48cce4a19ULL, 0xdb9c1238a24c8d43ULL, 0xd439febecaa96f9bULL,
+      0x418c0bef0960b281ULL, 0x158ea591f6ebd1deULL, 0x1f48e69e4da66d4eULL,
+      0x8afd13cf8e6fb054ULL, 0xf5e1c9011d5ed849ULL, 0xe34e091c5126c8afULL,
+      0xad67ee7530a398f6ULL, 0x43b24dec2e82c75aULL, 0x75da99c1287cd48dULL,
+      0x92e81cdb3783f689ULL, 0xa3dd217cc537cecdULL, 0x60543c50de970553ULL,
+      0x93f73f54aaf2426aULL, 0xa91b62737e7a725dULL, 0xf19d4507538732e2ULL,
+      0x77e4dfc20f9ea156ULL, 0x7d229ccdb4d31dc6ULL, 0x1b346a98037f87e5ULL,
+      0xedf4c615a4b29e94ULL, 0x4093286094110662ULL, 0xb0114ee85ae78063ULL,
+      0x6ff1d0d6b672e78bULL, 0x6dcf96d591909250ULL, 0xdfe09e3eec9567e8ULL,
+      0x3214582b4827f97cULL, 0xb46dc2ee143e6ac8ULL, 0xf6c0ac8da7cd1971ULL,
+      0xebb60c10cd8901e4ULL, 0xf7df8f023abcad92ULL, 0x9c52d3d2c217a0b2ULL,
+      0x6b8d5cd0f8ab0d20ULL, 0x3777f7a29b8fa734ULL, 0x011f238f9d71b4e3ULL,
+      0xc1b75b2f3c42be45ULL, 0x5de588fdfe551ef7ULL, 0x6eeef3592b035368ULL,
+      0xaa3a07ffc4e9b365ULL, 0xecebe59a39c32a77ULL, 0x5ba742f8976e8187ULL,
+      0x4b4a48e0b22d0e11ULL, 0xddded83dcb771233ULL, 0xa59feb79ac0c51bdULL,
+      0xc7f5912a55792135ULL
+      },
+      {
+      0x6d6ae04668a9b08aULL, 0x3ab3f04b0be8c743ULL, 0xe51e166b54b3c908ULL,
+      0xbe90a9eb35c2f139ULL, 0xb2c7066637f2bec1ULL, 0xaa6945613392202cULL,
+      0x9a28c36f3b5201ebULL, 0xddce5a93ab536994ULL, 0x0e34133ef6382827ULL,
+      0x52a02ba1ec55048bULL, 0xa2f88f97c4b2a177ULL, 0x8640e513ca2251a5ULL,
+      0xcdf1d36258137622ULL, 0xfe6cb708dedf8ddbULL, 0x8a174a9ec8121e5dULL,
+      0x679896036b81560eULL, 0x59ed033395795feeULL, 0x1dd778ab8b74edafULL,
+      0xee533ef92d9f926dULL, 0x2a8c79baf8a8d8f5ULL, 0x6bcf398e69b119f6ULL,
+      0xe20491742fafdd95ULL, 0x276488e0809c2aecULL, 0xea955b82d88f5cceULL,
+      0x7102c63a99d9e0c4ULL, 0xf9763017a5c39946ULL, 0x429fa2501f151b3dULL,
+      0x4659c72bea05d59eULL, 0x984b7fdccf5a6634ULL, 0xf742232953fbb161ULL,
+      0x3041860e08c021c7ULL, 0x747bfd9616cd9386ULL, 0x4bb1367192312787ULL,
+      0x1b72a1638a6c44d3ULL, 0x4a0e68a6e8359a66ULL, 0x169a5039f258b6caULL,
+      0xb98a2ef44edee5a4ULL, 0xd9083fe85e43a737ULL, 0x967f6ce239624e13ULL,
+      0x8874f62d3c1a7982ULL, 0x3c1629830af06e3fULL, 0x9165ebfd427e5a8eULL,
+      0xb5dd81794ceeaa5cULL, 0x0de8f15a7834f219ULL, 0x70bd98ede3dd5d25ULL,
+      0xaccc9ca9328a8950ULL, 0x56664eda1945ca28ULL, 0x221db34c0f8859aeULL,
+      0x26dbd637fa98970dULL, 0x1acdffb4f068f932ULL, 0x4585254f64090fa0ULL,
+      0x72de245e17d53afaULL, 0x1546b25d7c546cf4ULL, 0x207e0ffffb803e71ULL,
+      0xfaaad2732bcf4378ULL, 0xb462dfae36ea17bdULL, 0xcf926fd1ac1b11fdULL,
+      0xe0672dc7dba7ba4aULL, 0xd3fa49ad5d6b41b3ULL, 0x8ba81449b216a3bcULL,
+      0x14f9ec8a0650d115ULL, 0x40fc1ee3eb1d7ce2ULL, 0x23a2ed9b758ce44fULL,
+      0x782c521b14fddc7eULL, 0x1c68267cf170504eULL, 0xbcf31558c1ca96e6ULL,
+      0xa781b43b4ba6d235ULL, 0xf6fd7dfe29ff0c80ULL, 0xb0a4bad5c3fad91eULL,
+      0xd199f51ea963266cULL, 0x414340349119c103ULL, 0x5405f269ed4dadf7ULL,
+      0xabd61bb649969dcdULL, 0x6813dbeae7bdc3c8ULL, 0x65fb2ab09f8931d1ULL,
+      0xf1e7fae152e3181dULL, 0xc1a67cef5a2339daULL, 0x7a4feea8e0f5bba1ULL,
+      0x1e0b9acf05783791ULL, 0x5b8ebf8061713831ULL, 0x80e53cdbcb3af8d9ULL,
+      0x7e898bd315e57502ULL, 0xc6bcfbf0213f2d47ULL, 0x95a38e86b76e942dULL,
+      0x092e94218d243cbaULL, 0x8339debf453622e7ULL, 0xb11be402b9fe64ffULL,
+      0x57d9100d634177c9ULL, 0xcc4e8db52217cbc3ULL, 0x3b0cae9c71ec7aa2ULL,
+      0xfb158ca451cbfe99ULL, 0x2b33276d82ac6514ULL, 0x01bf5ed77a04bde1ULL,
+      0xc5601994af33f779ULL, 0x75c4a3416cc92e67ULL, 0xf3844652a6eb7fc2ULL,
+      0x3487e375fdd0ef64ULL, 0x18ae430704609eedULL, 0x4d14efb993298efbULL,
+      0x815a620cb13e4538ULL, 0x125c354207487869ULL, 0x9eeea614ce42cf48ULL,
+      0xce2d3106d61fac1cULL, 0xbbe99247bad6827bULL, 0x071a871f7b1c149dULL,
+      0x2e4a1cc10db81656ULL, 0x77a71ff298c149b8ULL, 0x06a5d9c80118a97cULL,
+      0xad73c27e488e34b1ULL, 0x443a7b981e0db241ULL, 0xe3bbcfa355ab6074ULL,
+      0x0af276450328e684ULL, 0x73617a896dd1871bULL, 0x58525de4ef7de20fULL,
+      0xb7be3dcab8e6cd83ULL, 0x19111dd07e64230cULL, 0x842359a03e2a367aULL,
+      0x103f89f1f3401fb6ULL, 0xdc710444d157d475ULL, 0xb835702334da5845ULL,
+      0x4320fc876511a6dcULL, 0xd026abc9d3679b8dULL, 0x17250eee885c0b2bULL,
+      0x90dab52a387ae76fULL, 0x31fed8d972c49c26ULL, 0x89cba8fa461ec463ULL,
+      0x2ff5421677bcabb7ULL, 0x396f122f85e41d7dULL, 0xa09b332430bac6a8ULL,
+      0xc888e8ced7070560ULL, 0xaeaf201ac682ee8fULL, 0x1180d7268944a257ULL,
+      0xf058a43628e7a5fcULL, 0xbd4c4b8fbbce2b07ULL, 0xa1246df34abe7b49ULL,
+      0x7d5569b79be9af3cULL, 0xa9b5a705bd9efa12ULL, 0xdb6b835baa4bc0e8ULL,
+      0x05793bac8f147342ULL, 0x21c1512881848390ULL, 0xfdb0556c50d357e5ULL,
+      0x613d4fcb6a99ff72ULL, 0x03dce2648e0cda3eULL, 0xe949b9e6568386f0ULL,
+      0xfc0f0bbb2ad7ea04ULL, 0x6a70675913b5a417ULL, 0x7f36d5046fe1c8e3ULL,
+      0x0c57af8d02304ff8ULL, 0x32223abdfcc84618ULL, 0x0891caf6f720815bULL,
+      0xa63eeaec31a26fd4ULL, 0x2507345374944d33ULL, 0x49d28ac266394058ULL,
+      0xf5219f9aa7f3d6beULL, 0x2d96fea583b4cc68ULL, 0x5a31e1571b7585d0ULL,
+      0x8ed12fe53d02d0feULL, 0xdfade6205f5b0e4bULL, 0x4cabb16ee92d331aULL,
+      0x04c6657bf510cea3ULL, 0xd73c2cd6a87b8f10ULL, 0xe1d87310a1a307abULL,
+      0x6cd5be9112ad0d6bULL, 0x97c032354366f3f2ULL, 0xd4e0ceb22677552eULL,
+      0x0000000000000000ULL, 0x29509bde76a402cbULL, 0xc27a9e8bd42fe3e4ULL,
+      0x5ef7842cee654b73ULL, 0xaf107ecdbc86536eULL, 0x3fcacbe784fcb401ULL,
+      0xd55f90655c73e8cfULL, 0xe6c2f40fdabf1336ULL, 0xe8f6e7312c873b11ULL,
+      0xeb2a0555a28be12fULL, 0xe4a148bc2eb774e9ULL, 0x9b979db84156bc0aULL,
+      0x6eb60222e6a56ab4ULL, 0x87ffbbc4b026ec44ULL, 0xc703a5275b3b90a6ULL,
+      0x47e699fc9001687fULL, 0x9c8d1aa73a4aa897ULL, 0x7cea3760e1ed12ddULL,
+      0x4ec80ddd1d2554c5ULL, 0x13e36b957d4cc588ULL, 0x5d2b66486069914dULL,
+      0x92b90999cc7280b0ULL, 0x517cc9c56259deb5ULL, 0xc937b619ad03b881ULL,
+      0xec30824ad997f5b2ULL, 0xa45d565fc5aa080bULL, 0xd6837201d27f32f1ULL,
+      0x635ef3789e9198adULL, 0x531f75769651b96aULL, 0x4f77530a6721e924ULL,
+      0x486dd4151c3dfdb9ULL, 0x5f48dafb9461f692ULL, 0x375b011173dc355aULL,
+      0x3da9775470f4d3deULL, 0x8d0dcd81b30e0ac0ULL, 0x36e45fc609d888bbULL,
+      0x55baacbe97491016ULL, 0x8cb29356c90ab721ULL, 0x76184125e2c5f459ULL,
+      0x99f4210bb55edbd5ULL, 0x6f095cf59ca1d755ULL, 0x9f51f8c3b44672a9ULL,
+      0x3538bda287d45285ULL, 0x50c39712185d6354ULL, 0xf23b1885dcefc223ULL,
+      0x79930ccc6ef9619fULL, 0xed8fdc9da3934853ULL, 0xcb540aaa590bdf5eULL,
+      0x5c94389f1a6d2cacULL, 0xe77daad8a0bbaed7ULL, 0x28efc5090ca0bf2aULL,
+      0xbf2ff73c4fc64cd8ULL, 0xb37858b14df60320ULL, 0xf8c96ec0dfc724a7ULL,
+      0x828680683f329f06ULL, 0x941cd051cd6a29ccULL, 0xc3c5c05cae2b5e05ULL,
+      0xb601631dc2e27062ULL, 0xc01922382027843bULL, 0x24b86a840e90f0d2ULL,
+      0xd245177a276ffc52ULL, 0x0f8b4de98c3c95c6ULL, 0x3e759530fef809e0ULL,
+      0x0b4d2892792c5b65ULL, 0xc4df4743d5374a98ULL, 0xa5e20888bfaeb5eaULL,
+      0xba56cc90c0d23f9aULL, 0x38d04cf8ffe0a09cULL, 0x62e1adafe495254cULL,
+      0x0263bcb3f40867dfULL, 0xcaeb547d230f62bfULL, 0x6082111c109d4293ULL,
+      0xdad4dd8cd04f7d09ULL, 0xefec602e579b2f8cULL, 0x1fb4c4187f7c8a70ULL,
+      0xffd3e9dfa4db303aULL, 0x7bf0b07f9af10640ULL, 0xf49ec14dddf76b5fULL,
+      0x8f6e713247066d1fULL, 0x339d646a86ccfbf9ULL, 0x64447467e58d8c30ULL,
+      0x2c29a072f9b07189ULL, 0xd8b7613f24471ad6ULL, 0x6627c8d41185ebefULL,
+      0xa347d140beb61c96ULL, 0xde12b8f7255fb3aaULL, 0x9d324470404e1576ULL,
+      0x9306574eb6763d51ULL, 0xa80af9d2c79a47f3ULL, 0x859c0777442e8b9bULL,
+      0x69ac853d9db97e29ULL
+      },
+      {
+      0xc3407dfc2de6377eULL, 0x5b9e93eea4256f77ULL, 0xadb58fdd50c845e0ULL,
+      0x5219ff11a75bed86ULL, 0x356b61cfd90b1de9ULL, 0xfb8f406e25abe037ULL,
+      0x7a5a0231c0f60796ULL, 0x9d3cd216e1f5020bULL, 0x0c6550fb6b48d8f3ULL,
+      0xf57508c427ff1c62ULL, 0x4ad35ffa71cb407dULL, 0x6290a2da1666aa6dULL,
+      0xe284ec2349355f9fULL, 0xb3c307c53d7c84ecULL, 0x05e23c0468365a02ULL,
+      0x190bac4d6c9ebfa8ULL, 0x94bbbee9e28b80faULL, 0xa34fc777529cb9b5ULL,
+      0xcc7b39f095bcd978ULL, 0x2426addb0ce532e3ULL, 0x7e79329312ce4fc7ULL,
+      0xab09a72eebec2917ULL, 0xf8d15499f6b9d6c2ULL, 0x1a55b8babf8c895dULL,
+      0xdb8add17fb769a85ULL, 0xb57f2f368658e81bULL, 0x8acd36f18f3f41f6ULL,
+      0x5ce3b7bba50f11d3ULL, 0x114dcc14d5ee2f0aULL, 0xb91a7fcded1030e8ULL,
+      0x81d5425fe55de7a1ULL, 0xb6213bc1554adeeeULL, 0x80144ef95f53f5f2ULL,
+      0x1e7688186db4c10cULL, 0x3b912965db5fe1bcULL, 0xc281715a97e8252dULL,
+      0x54a5d7e21c7f8171ULL, 0x4b12535ccbc5522eULL, 0x1d289cefbea6f7f9ULL,
+      0x6ef5f2217d2e729eULL, 0xe6a7dc819b0d17ceULL, 0x1b94b41c05829b0eULL,
+      0x33d7493c622f711eULL, 0xdcf7f942fa5ce421ULL, 0x600fba8b7f7a8ecbULL,
+      0x46b60f011a83988eULL, 0x235b898e0dcf4c47ULL, 0x957ab24f588592a9ULL,
+      0x4354330572b5c28cULL, 0xa5f3ef84e9b8d542ULL, 0x8c711e02341b2d01ULL,
+      0x0b1874ae6a62a657ULL, 0x1213d8e306fc19ffULL, 0xfe6d7c6a4d9dba35ULL,
+      0x65ed868f174cd4c9ULL, 0x88522ea0e6236550ULL, 0x899322065c2d7703ULL,
+      0xc01e690bfef4018bULL, 0x915982ed8abddaf8ULL, 0xbe675b98ec3a4e4cULL,
+      0xa996bf7f82f00db1ULL, 0xe1daf8d49a27696aULL, 0x2effd5d3dc8986e7ULL,
+      0xd153a51f2b1a2e81ULL, 0x18caa0ebd690adfbULL, 0x390e3134b243c51aULL,
+      0x2778b92cdff70416ULL, 0x029f1851691c24a6ULL, 0x5e7cafeacc133575ULL,
+      0xfa4e4cc89fa5f264ULL, 0x5a5f9f481e2b7d24ULL, 0x484c47ab18d764dbULL,
+      0x400a27f2a1a7f479ULL, 0xaeeb9b2a83da7315ULL, 0x721c626879869734ULL,
+      0x042330a2d2384851ULL, 0x85f672fd3765aff0ULL, 0xba446b3a3e02061dULL,
+      0x73dd6ecec3888567ULL, 0xffac70ccf793a866ULL, 0xdfa9edb5294ed2d4ULL,
+      0x6c6aea7014325638ULL, 0x834a5a0e8c41c307ULL, 0xcdba35562fb2cb2bULL,
+      0x0ad97808d06cb404ULL, 0x0f3b440cb85aee06ULL, 0xe5f9c876481f213bULL,
+      0x98deee1289c35809ULL, 0x59018bbfcd394bd1ULL, 0xe01bf47220297b39ULL,
+      0xde68e1139340c087ULL, 0x9fa3ca4788e926adULL, 0xbb85679c840c144eULL,
+      0x53d8f3b71d55ffd5ULL, 0x0da45c5dd146caa0ULL, 0x6f34fe87c72060cdULL,
+      0x57fbc315cf6db784ULL, 0xcee421a1fca0fddeULL, 0x3d2d0196607b8d4bULL,
+      0x642c8a29ad42c69aULL, 0x14aff010bdd87508ULL, 0xac74837beac657b3ULL,
+      0x3216459ad821634dULL, 0x3fb219c70967a9edULL, 0x06bc28f3bb246cf7ULL,
+      0xf2082c9126d562c6ULL, 0x66b39278c45ee23cULL, 0xbd394f6f3f2878b9ULL,
+      0xfd33689d9e8f8cc0ULL, 0x37f4799eb017394fULL, 0x108cc0b26fe03d59ULL,
+      0xda4bd1b1417888d6ULL, 0xb09d1332ee6eb219ULL, 0x2f3ed975668794b4ULL,
+      0x58c0871977375982ULL, 0x7561463d78ace990ULL, 0x09876cff037e82f1ULL,
+      0x7fb83e35a8c05d94ULL, 0x26b9b58a65f91645ULL, 0xef20b07e9873953fULL,
+      0x3148516d0b3355b8ULL, 0x41cb2b541ba9e62aULL, 0x790416c613e43163ULL,
+      0xa011d380818e8f40ULL, 0x3a5025c36151f3efULL, 0xd57095bdf92266d0ULL,
+      0x498d4b0da2d97688ULL, 0x8b0c3a57353153a5ULL, 0x21c491df64d368e1ULL,
+      0x8f2f0af5e7091bf4ULL, 0x2da1c1240f9bb012ULL, 0xc43d59a92ccc49daULL,
+      0xbfa6573e56345c1fULL, 0x828b56a8364fd154ULL, 0x9a41f643e0df7cafULL,
+      0xbcf843c985266aeaULL, 0x2b1de9d7b4bfdce5ULL, 0x20059d79dedd7ab2ULL,
+      0x6dabe6d6ae3c446bULL, 0x45e81bf6c991ae7bULL, 0x6351ae7cac68b83eULL,
+      0xa432e32253b6c711ULL, 0xd092a9b991143cd2ULL, 0xcac711032e98b58fULL,
+      0xd8d4c9e02864ac70ULL, 0xc5fc550f96c25b89ULL, 0xd7ef8dec903e4276ULL,
+      0x67729ede7e50f06fULL, 0xeac28c7af045cf3dULL, 0xb15c1f945460a04aULL,
+      0x9cfddeb05bfb1058ULL, 0x93c69abce3a1fe5eULL, 0xeb0380dc4a4bdd6eULL,
+      0xd20db1e8f8081874ULL, 0x229a8528b7c15e14ULL, 0x44291750739fbc28ULL,
+      0xd3ccbd4e42060a27ULL, 0xf62b1c33f4ed2a97ULL, 0x86a8660ae4779905ULL,
+      0xd62e814a2a305025ULL, 0x477703a7a08d8addULL, 0x7b9b0e977af815c5ULL,
+      0x78c51a60a9ea2330ULL, 0xa6adfb733aaae3b7ULL, 0x97e5aa1e3199b60fULL,
+      0x0000000000000000ULL, 0xf4b404629df10e31ULL, 0x5564db44a6719322ULL,
+      0x9207961a59afec0dULL, 0x9624a6b88b97a45cULL, 0x363575380a192b1cULL,
+      0x2c60cd82b595a241ULL, 0x7d272664c1dc7932ULL, 0x7142769faa94a1c1ULL,
+      0xa1d0df263b809d13ULL, 0x1630e841d4c451aeULL, 0xc1df65ad44fa13d8ULL,
+      0x13d2d445bcf20bacULL, 0xd915c546926abe23ULL, 0x38cf3d92084dd749ULL,
+      0xe766d0272103059dULL, 0xc7634d5effde7f2fULL, 0x077d2455012a7ea4ULL,
+      0xedbfa82ff16fb199ULL, 0xaf2a978c39d46146ULL, 0x42953fa3c8bbd0dfULL,
+      0xcb061da59496a7dcULL, 0x25e7a17db6eb20b0ULL, 0x34aa6d6963050fbaULL,
+      0xa76cf7d580a4f1e4ULL, 0xf7ea10954ee338c4ULL, 0xfcf2643b24819e93ULL,
+      0xcf252d0746aeef8dULL, 0x4ef06f58a3f3082cULL, 0x563acfb37563a5d7ULL,
+      0x5086e740ce47c920ULL, 0x2982f186dda3f843ULL, 0x87696aac5e798b56ULL,
+      0x5d22bb1d1f010380ULL, 0x035e14f7d31236f5ULL, 0x3cec0d30da759f18ULL,
+      0xf3c920379cdb7095ULL, 0xb8db736b571e22bbULL, 0xdd36f5e44052f672ULL,
+      0xaac8ab8851e23b44ULL, 0xa857b3d938fe1fe2ULL, 0x17f1e4e76eca43fdULL,
+      0xec7ea4894b61a3caULL, 0x9e62c6e132e734feULL, 0xd4b1991b432c7483ULL,
+      0x6ad6c283af163acfULL, 0x1ce9904904a8e5aaULL, 0x5fbda34c761d2726ULL,
+      0xf910583f4cb7c491ULL, 0xc6a241f845d06d7cULL, 0x4f3163fe19fd1a7fULL,
+      0xe99c988d2357f9c8ULL, 0x8eee06535d0709a7ULL, 0x0efa48aa0254fc55ULL,
+      0xb4be23903c56fa48ULL, 0x763f52caabbedf65ULL, 0xeee1bcd8227d876cULL,
+      0xe345e085f33b4dccULL, 0x3e731561b369bbbeULL, 0x2843fd2067adea10ULL,
+      0x2adce5710eb1ceb6ULL, 0xb7e03767ef44ccbdULL, 0x8db012a48e153f52ULL,
+      0x61ceb62dc5749c98ULL, 0xe85d942b9959eb9bULL, 0x4c6f7709caef2c8aULL,
+      0x84377e5b8d6bbda3ULL, 0x30895dcbb13d47ebULL, 0x74a04a9bc2a2fbc3ULL,
+      0x6b17ce251518289cULL, 0xe438c4d0f2113368ULL, 0x1fb784bed7bad35fULL,
+      0x9b80fae55ad16efcULL, 0x77fe5e6c11b0cd36ULL, 0xc858095247849129ULL,
+      0x08466059b97090a2ULL, 0x01c10ca6ba0e1253ULL, 0x6988d6747c040c3aULL,
+      0x6849dad2c60a1e69ULL, 0x5147ebe67449db73ULL, 0xc99905f4fd8a837aULL,
+      0x991fe2b433cd4a5aULL, 0xf09734c04fc94660ULL, 0xa28ecbd1e892abe6ULL,
+      0xf1563866f5c75433ULL, 0x4dae7baf70e13ed9ULL, 0x7ce62ac27bd26b61ULL,
+      0x70837a39109ab392ULL, 0x90988e4b30b3c8abULL, 0xb2020b63877296bfULL,
+      0x156efcb607d6675bULL
+      },
+      {
+      0xe63f55ce97c331d0ULL, 0x25b506b0015bba16ULL, 0xc8706e29e6ad9ba8ULL,
+      0x5b43d3775d521f6aULL, 0x0bfa3d577035106eULL, 0xab95fc172afb0e66ULL,
+      0xf64b63979e7a3276ULL, 0xf58b4562649dad4bULL, 0x48f7c3dbae0c83f1ULL,
+      0xff31916642f5c8c5ULL, 0xcbb048dc1c4a0495ULL, 0x66b8f83cdf622989ULL,
+      0x35c130e908e2b9b0ULL, 0x7c761a61f0b34fa1ULL, 0x3601161cf205268dULL,
+      0x9e54ccfe2219b7d6ULL, 0x8b7d90a538940837ULL, 0x9cd403588ea35d0bULL,
+      0xbc3c6fea9ccc5b5aULL, 0xe5ff733b6d24aeedULL, 0xceed22de0f7eb8d2ULL,
+      0xec8581cab1ab545eULL, 0xb96105e88ff8e71dULL, 0x8ca03501871a5eadULL,
+      0x76ccce65d6db2a2fULL, 0x5883f582a7b58057ULL, 0x3f7be4ed2e8adc3eULL,
+      0x0fe7be06355cd9c9ULL, 0xee054e6c1d11be83ULL, 0x1074365909b903a6ULL,
+      0x5dde9f80b4813c10ULL, 0x4a770c7d02b6692cULL, 0x5379c8d5d7809039ULL,
+      0xb4067448161ed409ULL, 0x5f5e5026183bd6cdULL, 0xe898029bf4c29df9ULL,
+      0x7fb63c940a54d09cULL, 0xc5171f897f4ba8bcULL, 0xa6f28db7b31d3d72ULL,
+      0x2e4f3be7716eaa78ULL, 0x0d6771a099e63314ULL, 0x82076254e41bf284ULL,
+      0x2f0fd2b42733df98ULL, 0x5c9e76d3e2dc49f0ULL, 0x7aeb569619606cdbULL,
+      0x83478b07b2468764ULL, 0xcfadcb8d5923cd32ULL, 0x85dac7f05b95a41eULL,
+      0xb5469d1b4043a1e9ULL, 0xb821ecbbd9a592fdULL, 0x1b8e0b0e798c13c8ULL,
+      0x62a57b6d9a0be02eULL, 0xfcf1b793b81257f8ULL, 0x9d94ea0bd8fe28ebULL,
+      0x4cea408aeb654a56ULL, 0x23284a47e888996cULL, 0x2d8f1d128b893545ULL,
+      0xf4cbac3132c0d8abULL, 0xbd7c86b9ca912ebaULL, 0x3a268eef3dbe6079ULL,
+      0xf0d62f6077a9110cULL, 0x2735c916ade150cbULL, 0x89fd5f03942ee2eaULL,
+      0x1acee25d2fd16628ULL, 0x90f39bab41181bffULL, 0x430dfe8cde39939fULL,
+      0xf70b8ac4c8274796ULL, 0x1c53aeaac6024552ULL, 0x13b410acf35e9c9bULL,
+      0xa532ab4249faa24fULL, 0x2b1251e5625a163fULL, 0xd7e3e676da4841c7ULL,
+      0xa7b264e4e5404892ULL, 0xda8497d643ae72d3ULL, 0x861ae105a1723b23ULL,
+      0x38a6414991048aa4ULL, 0x6578dec92585b6b4ULL, 0x0280cfa6acbaeaddULL,
+      0x88bdb650c273970aULL, 0x9333bd5ebbff84c2ULL, 0x4e6a8f2c47dfa08bULL,
+      0x321c954db76cef2aULL, 0x418d312a72837942ULL, 0xb29b38bfffcdf773ULL,
+      0x6c022c38f90a4c07ULL, 0x5a033a240b0f6a8aULL, 0x1f93885f3ce5da6fULL,
+      0xc38a537e96988bc6ULL, 0x39e6a81ac759ff44ULL, 0x29929e43cee0fce2ULL,
+      0x40cdd87924de0ca2ULL, 0xe9d8ebc8a29fe819ULL, 0x0c2798f3cfbb46f4ULL,
+      0x55e484223e53b343ULL, 0x4650948ecd0d2fd8ULL, 0x20e86cb2126f0651ULL,
+      0x6d42c56baf5739e7ULL, 0xa06fc1405ace1e08ULL, 0x7babbfc54f3d193bULL,
+      0x424d17df8864e67fULL, 0xd8045870ef14980eULL, 0xc6d7397c85ac3781ULL,
+      0x21a885e1443273b1ULL, 0x67f8116f893f5c69ULL, 0x24f5efe35706cff6ULL,
+      0xd56329d076f2ab1aULL, 0x5e1eb9754e66a32dULL, 0x28d2771098bd8902ULL,
+      0x8f6013f47dfdc190ULL, 0x17a993fdb637553cULL, 0xe0a219397e1012aaULL,
+      0x786b9930b5da8606ULL, 0x6e82e39e55b0a6daULL, 0x875a0856f72f4ec3ULL,
+      0x3741ff4fa458536dULL, 0xac4859b3957558fcULL, 0x7ef6d5c75c09a57cULL,
+      0xc04a758b6c7f14fbULL, 0xf9acdd91ab26ebbfULL, 0x7391a467c5ef9668ULL,
+      0x335c7c1ee1319acaULL, 0xa91533b18641e4bbULL, 0xe4bf9a683b79db0dULL,
+      0x8e20faa72ba0b470ULL, 0x51f907737b3a7ae4ULL, 0x2268a314bed5ec8cULL,
+      0xd944b123b949edeeULL, 0x31dcb3b84d8b7017ULL, 0xd3fe65279f218860ULL,
+      0x097af2f1dc8ffab3ULL, 0x9b09a6fc312d0b91ULL, 0xcc6ded78a3c4520fULL,
+      0x3481d9ba5ebfcc50ULL, 0x4f2a667f1182d56bULL, 0xdfd9fdd4509ace94ULL,
+      0x26752045fbbc252bULL, 0xbffc491f662bc467ULL, 0xdd593272fc202449ULL,
+      0x3cbbc218d46d4303ULL, 0x91b372f817456e1fULL, 0x681faf69bc6385a0ULL,
+      0xb686bbeebaa43ed4ULL, 0x1469b5084cd0ca01ULL, 0x98c98009cbca94acULL,
+      0x6438379a73d8c354ULL, 0xc2caba2dc0c5fe26ULL, 0x3e3b0dbe78d7a9deULL,
+      0x50b9ee202d670f04ULL, 0x4590b27b37eab0e5ULL, 0x6025b4cb36b10af3ULL,
+      0xfb2c1237079c0162ULL, 0xa12f28130c936be8ULL, 0x4b37e52e54eb1cccULL,
+      0x083a1ba28ad28f53ULL, 0xc10a9cd83a22611bULL, 0x9f1425ad7444c236ULL,
+      0x069d4cf7e9d3237aULL, 0xedc56899e7f621beULL, 0x778c273680865fcfULL,
+      0x309c5aeb1bd605f7ULL, 0x8de0dc52d1472b4dULL, 0xf8ec34c2fd7b9e5fULL,
+      0xea18cd3d58787724ULL, 0xaad515447ca67b86ULL, 0x9989695a9d97e14cULL,
+      0x0000000000000000ULL, 0xf196c63321f464ecULL, 0x71116bc169557cb5ULL,
+      0xaf887f466f92c7c1ULL, 0x972e3e0ffe964d65ULL, 0x190ec4a8d536f915ULL,
+      0x95aef1a9522ca7b8ULL, 0xdc19db21aa7d51a9ULL, 0x94ee18fa0471d258ULL,
+      0x8087adf248a11859ULL, 0xc457f6da2916dd5cULL, 0xfa6cfb6451c17482ULL,
+      0xf256e0c6db13fbd1ULL, 0x6a9f60cf10d96f7dULL, 0x4daaa9d9bd383fb6ULL,
+      0x03c026f5fae79f3dULL, 0xde99148706c7bb74ULL, 0x2a52b8b6340763dfULL,
+      0x6fc20acd03edd33aULL, 0xd423c08320afdefaULL, 0xbbe1ca4e23420dc0ULL,
+      0x966ed75ca8cb3885ULL, 0xeb58246e0e2502c4ULL, 0x055d6a021334bc47ULL,
+      0xa47242111fa7d7afULL, 0xe3623fcc84f78d97ULL, 0x81c744a11efc6db9ULL,
+      0xaec8961539cfb221ULL, 0xf31609958d4e8e31ULL, 0x63e5923ecc5695ceULL,
+      0x47107ddd9b505a38ULL, 0xa3afe7b5a0298135ULL, 0x792b7063e387f3e6ULL,
+      0x0140e953565d75e0ULL, 0x12f4f9ffa503e97bULL, 0x750ce8902c3cb512ULL,
+      0xdbc47e8515f30733ULL, 0x1ed3610c6ab8af8fULL, 0x5239218681dde5d9ULL,
+      0xe222d69fd2aaf877ULL, 0xfe71783514a8bd25ULL, 0xcaf0a18f4a177175ULL,
+      0x61655d9860ec7f13ULL, 0xe77fbc9dc19e4430ULL, 0x2ccff441ddd440a5ULL,
+      0x16e97aaee06a20dcULL, 0xa855dae2d01c915bULL, 0x1d1347f9905f30b2ULL,
+      0xb7c652bdecf94b34ULL, 0xd03e43d265c6175dULL, 0xfdb15ec0ee4f2218ULL,
+      0x57644b8492e9599eULL, 0x07dda5a4bf8e569aULL, 0x54a46d71680ec6a3ULL,
+      0x5624a2d7c4b42c7eULL, 0xbebca04c3076b187ULL, 0x7d36f332a6ee3a41ULL,
+      0x3b6667bc6be31599ULL, 0x695f463aea3ef040ULL, 0xad08b0e0c3282d1cULL,
+      0xb15b1e4a052a684eULL, 0x44d05b2861b7c505ULL, 0x15295c5b1a8dbfe1ULL,
+      0x744c01c37a61c0f2ULL, 0x59c31cd1f1e8f5b7ULL, 0xef45a73f4b4ccb63ULL,
+      0x6bdf899c46841a9dULL, 0x3dfb2b4b823036e3ULL, 0xa2ef0ee6f674f4d5ULL,
+      0x184e2dfb836b8cf5ULL, 0x1134df0a5fe47646ULL, 0xbaa1231d751f7820ULL,
+      0xd17eaa81339b62bdULL, 0xb01bf71953771daeULL, 0x849a2ea30dc8d1feULL,
+      0x705182923f080955ULL, 0x0ea757556301ac29ULL, 0x041d83514569c9a7ULL,
+      0x0abad4042668658eULL, 0x49b72a88f851f611ULL, 0x8a3d79f66ec97dd7ULL,
+      0xcd2d042bf59927efULL, 0xc930877ab0f0ee48ULL, 0x9273540deda2f122ULL,
+      0xc797d02fd3f14261ULL, 0xe1e2f06a284d674aULL, 0xd2be8c74c97cfd80ULL,
+      0x9a494faf67707e71ULL, 0xb3dbd1eca9908293ULL, 0x72d14d3493b2e388ULL,
+      0xd6a30f258c153427ULL
+      }
+   };
+
+extern const uint64_t STREEBOG_C[12][8] =
+   {
+      {
+      0xdd806559f2a64507ULL,
+      0x05767436cc744d23ULL,
+      0xa2422a08a460d315ULL,
+      0x4b7ce09192676901ULL,
+      0x714eb88d7585c4fcULL,
+      0x2f6a76432e45d016ULL,
+      0xebcb2f81c0657c1fULL,
+      0xb1085bda1ecadae9ULL
+      },
+      {
+      0xe679047021b19bb7ULL,
+      0x55dda21bd7cbcd56ULL,
+      0x5cb561c2db0aa7caULL,
+      0x9ab5176b12d69958ULL,
+      0x61d55e0f16b50131ULL,
+      0xf3feea720a232b98ULL,
+      0x4fe39d460f70b5d7ULL,
+      0x6fa3b58aa99d2f1aULL
+      },
+      {
+      0x991e96f50aba0ab2ULL,
+      0xc2b6f443867adb31ULL,
+      0xc1c93a376062db09ULL,
+      0xd3e20fe490359eb1ULL,
+      0xf2ea7514b1297b7bULL,
+      0x06f15e5f529c1f8bULL,
+      0x0a39fc286a3d8435ULL,
+      0xf574dcac2bce2fc7ULL
+      },
+      {
+      0x220cbebc84e3d12eULL,
+      0x3453eaa193e837f1ULL,
+      0xd8b71333935203beULL,
+      0xa9d72c82ed03d675ULL,
+      0x9d721cad685e353fULL,
+      0x488e857e335c3c7dULL,
+      0xf948e1a05d71e4ddULL,
+      0xef1fdfb3e81566d2ULL
+      },
+      {
+      0x601758fd7c6cfe57ULL,
+      0x7a56a27ea9ea63f5ULL,
+      0xdfff00b723271a16ULL,
+      0xbfcd1747253af5a3ULL,
+      0x359e35d7800fffbdULL,
+      0x7f151c1f1686104aULL,
+      0x9a3f410c6ca92363ULL,
+      0x4bea6bacad474799ULL
+      },
+      {
+      0xfa68407a46647d6eULL,
+      0xbf71c57236904f35ULL,
+      0x0af21f66c2bec6b6ULL,
+      0xcffaa6b71c9ab7b4ULL,
+      0x187f9ab49af08ec6ULL,
+      0x2d66c4f95142a46cULL,
+      0x6fa4c33b7a3039c0ULL,
+      0xae4faeae1d3ad3d9ULL
+      },
+      {
+      0x8886564d3a14d493ULL,
+      0x3517454ca23c4af3ULL,
+      0x06476983284a0504ULL,
+      0x0992abc52d822c37ULL,
+      0xd3473e33197a93c9ULL,
+      0x399ec6c7e6bf87c9ULL,
+      0x51ac86febf240954ULL,
+      0xf4c70e16eeaac5ecULL
+      },
+      {
+      0xa47f0dd4bf02e71eULL,
+      0x36acc2355951a8d9ULL,
+      0x69d18d2bd1a5c42fULL,
+      0xf4892bcb929b0690ULL,
+      0x89b4443b4ddbc49aULL,
+      0x4eb7f8719c36de1eULL,
+      0x03e7aa020c6e4141ULL,
+      0x9b1f5b424d93c9a7ULL
+      },
+      {
+      0x7261445183235adbULL,
+      0x0e38dc92cb1f2a60ULL,
+      0x7b2b8a9aa6079c54ULL,
+      0x800a440bdbb2ceb1ULL,
+      0x3cd955b7e00d0984ULL,
+      0x3a7d3a1b25894224ULL,
+      0x944c9ad8ec165fdeULL,
+      0x378f5a541631229bULL
+      },
+      {
+      0x74b4c7fb98459cedULL,
+      0x3698fad1153bb6c3ULL,
+      0x7a1e6c303b7652f4ULL,
+      0x9fe76702af69334bULL,
+      0x1fffe18a1b336103ULL,
+      0x8941e71cff8a78dbULL,
+      0x382ae548b2e4f3f3ULL,
+      0xabbedea680056f52ULL
+      },
+      {
+      0x6bcaa4cd81f32d1bULL,
+      0xdea2594ac06fd85dULL,
+      0xefbacd1d7d476e98ULL,
+      0x8a1d71efea48b9caULL,
+      0x2001802114846679ULL,
+      0xd8fa6bbbebab0761ULL,
+      0x3002c6cd635afe94ULL,
+      0x7bcd9ed0efc889fbULL
+      },
+      {
+      0x48bc924af11bd720ULL,
+      0xfaf417d5d9b21b99ULL,
+      0xe71da4aa88e12852ULL,
+      0x5d80ef9d1891cc86ULL,
+      0xf82012d430219f9bULL,
+      0xcda43c32bcdf1d77ULL,
+      0xd21380b00449b17aULL,
+      0x378ee767f11631baULL
+      }
+   };
+
+}
diff --git a/third_party/botan/src/lib/hash/tiger/tig_tab.cpp b/third_party/botan/src/lib/hash/tiger/tig_tab.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/tiger/tig_tab.cpp
@@ -0,0 +1,364 @@
+/*
+* S-Box Tables for Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tiger.h>
+
+namespace Botan {
+
+const uint64_t Tiger::SBOX1[256] = {
+   0x02AAB17CF7E90C5E, 0xAC424B03E243A8EC, 0x72CD5BE30DD5FCD3,
+   0x6D019B93F6F97F3A, 0xCD9978FFD21F9193, 0x7573A1C9708029E2,
+   0xB164326B922A83C3, 0x46883EEE04915870, 0xEAACE3057103ECE6,
+   0xC54169B808A3535C, 0x4CE754918DDEC47C, 0x0AA2F4DFDC0DF40C,
+   0x10B76F18A74DBEFA, 0xC6CCB6235AD1AB6A, 0x13726121572FE2FF,
+   0x1A488C6F199D921E, 0x4BC9F9F4DA0007CA, 0x26F5E6F6E85241C7,
+   0x859079DBEA5947B6, 0x4F1885C5C99E8C92, 0xD78E761EA96F864B,
+   0x8E36428C52B5C17D, 0x69CF6827373063C1, 0xB607C93D9BB4C56E,
+   0x7D820E760E76B5EA, 0x645C9CC6F07FDC42, 0xBF38A078243342E0,
+   0x5F6B343C9D2E7D04, 0xF2C28AEB600B0EC6, 0x6C0ED85F7254BCAC,
+   0x71592281A4DB4FE5, 0x1967FA69CE0FED9F, 0xFD5293F8B96545DB,
+   0xC879E9D7F2A7600B, 0x860248920193194E, 0xA4F9533B2D9CC0B3,
+   0x9053836C15957613, 0xDB6DCF8AFC357BF1, 0x18BEEA7A7A370F57,
+   0x037117CA50B99066, 0x6AB30A9774424A35, 0xF4E92F02E325249B,
+   0x7739DB07061CCAE1, 0xD8F3B49CECA42A05, 0xBD56BE3F51382F73,
+   0x45FAED5843B0BB28, 0x1C813D5C11BF1F83, 0x8AF0E4B6D75FA169,
+   0x33EE18A487AD9999, 0x3C26E8EAB1C94410, 0xB510102BC0A822F9,
+   0x141EEF310CE6123B, 0xFC65B90059DDB154, 0xE0158640C5E0E607,
+   0x884E079826C3A3CF, 0x930D0D9523C535FD, 0x35638D754E9A2B00,
+   0x4085FCCF40469DD5, 0xC4B17AD28BE23A4C, 0xCAB2F0FC6A3E6A2E,
+   0x2860971A6B943FCD, 0x3DDE6EE212E30446, 0x6222F32AE01765AE,
+   0x5D550BB5478308FE, 0xA9EFA98DA0EDA22A, 0xC351A71686C40DA7,
+   0x1105586D9C867C84, 0xDCFFEE85FDA22853, 0xCCFBD0262C5EEF76,
+   0xBAF294CB8990D201, 0xE69464F52AFAD975, 0x94B013AFDF133E14,
+   0x06A7D1A32823C958, 0x6F95FE5130F61119, 0xD92AB34E462C06C0,
+   0xED7BDE33887C71D2, 0x79746D6E6518393E, 0x5BA419385D713329,
+   0x7C1BA6B948A97564, 0x31987C197BFDAC67, 0xDE6C23C44B053D02,
+   0x581C49FED002D64D, 0xDD474D6338261571, 0xAA4546C3E473D062,
+   0x928FCE349455F860, 0x48161BBACAAB94D9, 0x63912430770E6F68,
+   0x6EC8A5E602C6641C, 0x87282515337DDD2B, 0x2CDA6B42034B701B,
+   0xB03D37C181CB096D, 0xE108438266C71C6F, 0x2B3180C7EB51B255,
+   0xDF92B82F96C08BBC, 0x5C68C8C0A632F3BA, 0x5504CC861C3D0556,
+   0xABBFA4E55FB26B8F, 0x41848B0AB3BACEB4, 0xB334A273AA445D32,
+   0xBCA696F0A85AD881, 0x24F6EC65B528D56C, 0x0CE1512E90F4524A,
+   0x4E9DD79D5506D35A, 0x258905FAC6CE9779, 0x2019295B3E109B33,
+   0xF8A9478B73A054CC, 0x2924F2F934417EB0, 0x3993357D536D1BC4,
+   0x38A81AC21DB6FF8B, 0x47C4FBF17D6016BF, 0x1E0FAADD7667E3F5,
+   0x7ABCFF62938BEB96, 0xA78DAD948FC179C9, 0x8F1F98B72911E50D,
+   0x61E48EAE27121A91, 0x4D62F7AD31859808, 0xECEBA345EF5CEAEB,
+   0xF5CEB25EBC9684CE, 0xF633E20CB7F76221, 0xA32CDF06AB8293E4,
+   0x985A202CA5EE2CA4, 0xCF0B8447CC8A8FB1, 0x9F765244979859A3,
+   0xA8D516B1A1240017, 0x0BD7BA3EBB5DC726, 0xE54BCA55B86ADB39,
+   0x1D7A3AFD6C478063, 0x519EC608E7669EDD, 0x0E5715A2D149AA23,
+   0x177D4571848FF194, 0xEEB55F3241014C22, 0x0F5E5CA13A6E2EC2,
+   0x8029927B75F5C361, 0xAD139FABC3D6E436, 0x0D5DF1A94CCF402F,
+   0x3E8BD948BEA5DFC8, 0xA5A0D357BD3FF77E, 0xA2D12E251F74F645,
+   0x66FD9E525E81A082, 0x2E0C90CE7F687A49, 0xC2E8BCBEBA973BC5,
+   0x000001BCE509745F, 0x423777BBE6DAB3D6, 0xD1661C7EAEF06EB5,
+   0xA1781F354DAACFD8, 0x2D11284A2B16AFFC, 0xF1FC4F67FA891D1F,
+   0x73ECC25DCB920ADA, 0xAE610C22C2A12651, 0x96E0A810D356B78A,
+   0x5A9A381F2FE7870F, 0xD5AD62EDE94E5530, 0xD225E5E8368D1427,
+   0x65977B70C7AF4631, 0x99F889B2DE39D74F, 0x233F30BF54E1D143,
+   0x9A9675D3D9A63C97, 0x5470554FF334F9A8, 0x166ACB744A4F5688,
+   0x70C74CAAB2E4AEAD, 0xF0D091646F294D12, 0x57B82A89684031D1,
+   0xEFD95A5A61BE0B6B, 0x2FBD12E969F2F29A, 0x9BD37013FEFF9FE8,
+   0x3F9B0404D6085A06, 0x4940C1F3166CFE15, 0x09542C4DCDF3DEFB,
+   0xB4C5218385CD5CE3, 0xC935B7DC4462A641, 0x3417F8A68ED3B63F,
+   0xB80959295B215B40, 0xF99CDAEF3B8C8572, 0x018C0614F8FCB95D,
+   0x1B14ACCD1A3ACDF3, 0x84D471F200BB732D, 0xC1A3110E95E8DA16,
+   0x430A7220BF1A82B8, 0xB77E090D39DF210E, 0x5EF4BD9F3CD05E9D,
+   0x9D4FF6DA7E57A444, 0xDA1D60E183D4A5F8, 0xB287C38417998E47,
+   0xFE3EDC121BB31886, 0xC7FE3CCC980CCBEF, 0xE46FB590189BFD03,
+   0x3732FD469A4C57DC, 0x7EF700A07CF1AD65, 0x59C64468A31D8859,
+   0x762FB0B4D45B61F6, 0x155BAED099047718, 0x68755E4C3D50BAA6,
+   0xE9214E7F22D8B4DF, 0x2ADDBF532EAC95F4, 0x32AE3909B4BD0109,
+   0x834DF537B08E3450, 0xFA209DA84220728D, 0x9E691D9B9EFE23F7,
+   0x0446D288C4AE8D7F, 0x7B4CC524E169785B, 0x21D87F0135CA1385,
+   0xCEBB400F137B8AA5, 0x272E2B66580796BE, 0x3612264125C2B0DE,
+   0x057702BDAD1EFBB2, 0xD4BABB8EACF84BE9, 0x91583139641BC67B,
+   0x8BDC2DE08036E024, 0x603C8156F49F68ED, 0xF7D236F7DBEF5111,
+   0x9727C4598AD21E80, 0xA08A0896670A5FD7, 0xCB4A8F4309EBA9CB,
+   0x81AF564B0F7036A1, 0xC0B99AA778199ABD, 0x959F1EC83FC8E952,
+   0x8C505077794A81B9, 0x3ACAAF8F056338F0, 0x07B43F50627A6778,
+   0x4A44AB49F5ECCC77, 0x3BC3D6E4B679EE98, 0x9CC0D4D1CF14108C,
+   0x4406C00B206BC8A0, 0x82A18854C8D72D89, 0x67E366B35C3C432C,
+   0xB923DD61102B37F2, 0x56AB2779D884271D, 0xBE83E1B0FF1525AF,
+   0xFB7C65D4217E49A9, 0x6BDBE0E76D48E7D4, 0x08DF828745D9179E,
+   0x22EA6A9ADD53BD34, 0xE36E141C5622200A, 0x7F805D1B8CB750EE,
+   0xAFE5C7A59F58E837, 0xE27F996A4FB1C23C, 0xD3867DFB0775F0D0,
+   0xD0E673DE6E88891A, 0x123AEB9EAFB86C25, 0x30F1D5D5C145B895,
+   0xBB434A2DEE7269E7, 0x78CB67ECF931FA38, 0xF33B0372323BBF9C,
+   0x52D66336FB279C74, 0x505F33AC0AFB4EAA, 0xE8A5CD99A2CCE187,
+   0x534974801E2D30BB, 0x8D2D5711D5876D90, 0x1F1A412891BC038E,
+   0xD6E2E71D82E56648, 0x74036C3A497732B7, 0x89B67ED96361F5AB,
+   0xFFED95D8F1EA02A2, 0xE72B3BD61464D43D, 0xA6300F170BDC4820,
+   0xEBC18760ED78A77A };
+
+const uint64_t Tiger::SBOX2[256] = {
+   0xE6A6BE5A05A12138, 0xB5A122A5B4F87C98, 0x563C6089140B6990,
+   0x4C46CB2E391F5DD5, 0xD932ADDBC9B79434, 0x08EA70E42015AFF5,
+   0xD765A6673E478CF1, 0xC4FB757EAB278D99, 0xDF11C6862D6E0692,
+   0xDDEB84F10D7F3B16, 0x6F2EF604A665EA04, 0x4A8E0F0FF0E0DFB3,
+   0xA5EDEEF83DBCBA51, 0xFC4F0A2A0EA4371E, 0xE83E1DA85CB38429,
+   0xDC8FF882BA1B1CE2, 0xCD45505E8353E80D, 0x18D19A00D4DB0717,
+   0x34A0CFEDA5F38101, 0x0BE77E518887CAF2, 0x1E341438B3C45136,
+   0xE05797F49089CCF9, 0xFFD23F9DF2591D14, 0x543DDA228595C5CD,
+   0x661F81FD99052A33, 0x8736E641DB0F7B76, 0x15227725418E5307,
+   0xE25F7F46162EB2FA, 0x48A8B2126C13D9FE, 0xAFDC541792E76EEA,
+   0x03D912BFC6D1898F, 0x31B1AAFA1B83F51B, 0xF1AC2796E42AB7D9,
+   0x40A3A7D7FCD2EBAC, 0x1056136D0AFBBCC5, 0x7889E1DD9A6D0C85,
+   0xD33525782A7974AA, 0xA7E25D09078AC09B, 0xBD4138B3EAC6EDD0,
+   0x920ABFBE71EB9E70, 0xA2A5D0F54FC2625C, 0xC054E36B0B1290A3,
+   0xF6DD59FF62FE932B, 0x3537354511A8AC7D, 0xCA845E9172FADCD4,
+   0x84F82B60329D20DC, 0x79C62CE1CD672F18, 0x8B09A2ADD124642C,
+   0xD0C1E96A19D9E726, 0x5A786A9B4BA9500C, 0x0E020336634C43F3,
+   0xC17B474AEB66D822, 0x6A731AE3EC9BAAC2, 0x8226667AE0840258,
+   0x67D4567691CAECA5, 0x1D94155C4875ADB5, 0x6D00FD985B813FDF,
+   0x51286EFCB774CD06, 0x5E8834471FA744AF, 0xF72CA0AEE761AE2E,
+   0xBE40E4CDAEE8E09A, 0xE9970BBB5118F665, 0x726E4BEB33DF1964,
+   0x703B000729199762, 0x4631D816F5EF30A7, 0xB880B5B51504A6BE,
+   0x641793C37ED84B6C, 0x7B21ED77F6E97D96, 0x776306312EF96B73,
+   0xAE528948E86FF3F4, 0x53DBD7F286A3F8F8, 0x16CADCE74CFC1063,
+   0x005C19BDFA52C6DD, 0x68868F5D64D46AD3, 0x3A9D512CCF1E186A,
+   0x367E62C2385660AE, 0xE359E7EA77DCB1D7, 0x526C0773749ABE6E,
+   0x735AE5F9D09F734B, 0x493FC7CC8A558BA8, 0xB0B9C1533041AB45,
+   0x321958BA470A59BD, 0x852DB00B5F46C393, 0x91209B2BD336B0E5,
+   0x6E604F7D659EF19F, 0xB99A8AE2782CCB24, 0xCCF52AB6C814C4C7,
+   0x4727D9AFBE11727B, 0x7E950D0C0121B34D, 0x756F435670AD471F,
+   0xF5ADD442615A6849, 0x4E87E09980B9957A, 0x2ACFA1DF50AEE355,
+   0xD898263AFD2FD556, 0xC8F4924DD80C8FD6, 0xCF99CA3D754A173A,
+   0xFE477BACAF91BF3C, 0xED5371F6D690C12D, 0x831A5C285E687094,
+   0xC5D3C90A3708A0A4, 0x0F7F903717D06580, 0x19F9BB13B8FDF27F,
+   0xB1BD6F1B4D502843, 0x1C761BA38FFF4012, 0x0D1530C4E2E21F3B,
+   0x8943CE69A7372C8A, 0xE5184E11FEB5CE66, 0x618BDB80BD736621,
+   0x7D29BAD68B574D0B, 0x81BB613E25E6FE5B, 0x071C9C10BC07913F,
+   0xC7BEEB7909AC2D97, 0xC3E58D353BC5D757, 0xEB017892F38F61E8,
+   0xD4EFFB9C9B1CC21A, 0x99727D26F494F7AB, 0xA3E063A2956B3E03,
+   0x9D4A8B9A4AA09C30, 0x3F6AB7D500090FB4, 0x9CC0F2A057268AC0,
+   0x3DEE9D2DEDBF42D1, 0x330F49C87960A972, 0xC6B2720287421B41,
+   0x0AC59EC07C00369C, 0xEF4EAC49CB353425, 0xF450244EEF0129D8,
+   0x8ACC46E5CAF4DEB6, 0x2FFEAB63989263F7, 0x8F7CB9FE5D7A4578,
+   0x5BD8F7644E634635, 0x427A7315BF2DC900, 0x17D0C4AA2125261C,
+   0x3992486C93518E50, 0xB4CBFEE0A2D7D4C3, 0x7C75D6202C5DDD8D,
+   0xDBC295D8E35B6C61, 0x60B369D302032B19, 0xCE42685FDCE44132,
+   0x06F3DDB9DDF65610, 0x8EA4D21DB5E148F0, 0x20B0FCE62FCD496F,
+   0x2C1B912358B0EE31, 0xB28317B818F5A308, 0xA89C1E189CA6D2CF,
+   0x0C6B18576AAADBC8, 0xB65DEAA91299FAE3, 0xFB2B794B7F1027E7,
+   0x04E4317F443B5BEB, 0x4B852D325939D0A6, 0xD5AE6BEEFB207FFC,
+   0x309682B281C7D374, 0xBAE309A194C3B475, 0x8CC3F97B13B49F05,
+   0x98A9422FF8293967, 0x244B16B01076FF7C, 0xF8BF571C663D67EE,
+   0x1F0D6758EEE30DA1, 0xC9B611D97ADEB9B7, 0xB7AFD5887B6C57A2,
+   0x6290AE846B984FE1, 0x94DF4CDEACC1A5FD, 0x058A5BD1C5483AFF,
+   0x63166CC142BA3C37, 0x8DB8526EB2F76F40, 0xE10880036F0D6D4E,
+   0x9E0523C9971D311D, 0x45EC2824CC7CD691, 0x575B8359E62382C9,
+   0xFA9E400DC4889995, 0xD1823ECB45721568, 0xDAFD983B8206082F,
+   0xAA7D29082386A8CB, 0x269FCD4403B87588, 0x1B91F5F728BDD1E0,
+   0xE4669F39040201F6, 0x7A1D7C218CF04ADE, 0x65623C29D79CE5CE,
+   0x2368449096C00BB1, 0xAB9BF1879DA503BA, 0xBC23ECB1A458058E,
+   0x9A58DF01BB401ECC, 0xA070E868A85F143D, 0x4FF188307DF2239E,
+   0x14D565B41A641183, 0xEE13337452701602, 0x950E3DCF3F285E09,
+   0x59930254B9C80953, 0x3BF299408930DA6D, 0xA955943F53691387,
+   0xA15EDECAA9CB8784, 0x29142127352BE9A0, 0x76F0371FFF4E7AFB,
+   0x0239F450274F2228, 0xBB073AF01D5E868B, 0xBFC80571C10E96C1,
+   0xD267088568222E23, 0x9671A3D48E80B5B0, 0x55B5D38AE193BB81,
+   0x693AE2D0A18B04B8, 0x5C48B4ECADD5335F, 0xFD743B194916A1CA,
+   0x2577018134BE98C4, 0xE77987E83C54A4AD, 0x28E11014DA33E1B9,
+   0x270CC59E226AA213, 0x71495F756D1A5F60, 0x9BE853FB60AFEF77,
+   0xADC786A7F7443DBF, 0x0904456173B29A82, 0x58BC7A66C232BD5E,
+   0xF306558C673AC8B2, 0x41F639C6B6C9772A, 0x216DEFE99FDA35DA,
+   0x11640CC71C7BE615, 0x93C43694565C5527, 0xEA038E6246777839,
+   0xF9ABF3CE5A3E2469, 0x741E768D0FD312D2, 0x0144B883CED652C6,
+   0xC20B5A5BA33F8552, 0x1AE69633C3435A9D, 0x97A28CA4088CFDEC,
+   0x8824A43C1E96F420, 0x37612FA66EEEA746, 0x6B4CB165F9CF0E5A,
+   0x43AA1C06A0ABFB4A, 0x7F4DC26FF162796B, 0x6CBACC8E54ED9B0F,
+   0xA6B7FFEFD2BB253E, 0x2E25BC95B0A29D4F, 0x86D6A58BDEF1388C,
+   0xDED74AC576B6F054, 0x8030BDBC2B45805D, 0x3C81AF70E94D9289,
+   0x3EFF6DDA9E3100DB, 0xB38DC39FDFCC8847, 0x123885528D17B87E,
+   0xF2DA0ED240B1B642, 0x44CEFADCD54BF9A9, 0x1312200E433C7EE6,
+   0x9FFCC84F3A78C748, 0xF0CD1F72248576BB, 0xEC6974053638CFE4,
+   0x2BA7B67C0CEC4E4C, 0xAC2F4DF3E5CE32ED, 0xCB33D14326EA4C11,
+   0xA4E9044CC77E58BC, 0x5F513293D934FCEF, 0x5DC9645506E55444,
+   0x50DE418F317DE40A, 0x388CB31A69DDE259, 0x2DB4A83455820A86,
+   0x9010A91E84711AE9, 0x4DF7F0B7B1498371, 0xD62A2EABC0977179,
+   0x22FAC097AA8D5C0E };
+
+const uint64_t Tiger::SBOX3[256] = {
+   0xF49FCC2FF1DAF39B, 0x487FD5C66FF29281, 0xE8A30667FCDCA83F,
+   0x2C9B4BE3D2FCCE63, 0xDA3FF74B93FBBBC2, 0x2FA165D2FE70BA66,
+   0xA103E279970E93D4, 0xBECDEC77B0E45E71, 0xCFB41E723985E497,
+   0xB70AAA025EF75017, 0xD42309F03840B8E0, 0x8EFC1AD035898579,
+   0x96C6920BE2B2ABC5, 0x66AF4163375A9172, 0x2174ABDCCA7127FB,
+   0xB33CCEA64A72FF41, 0xF04A4933083066A5, 0x8D970ACDD7289AF5,
+   0x8F96E8E031C8C25E, 0xF3FEC02276875D47, 0xEC7BF310056190DD,
+   0xF5ADB0AEBB0F1491, 0x9B50F8850FD58892, 0x4975488358B74DE8,
+   0xA3354FF691531C61, 0x0702BBE481D2C6EE, 0x89FB24057DEDED98,
+   0xAC3075138596E902, 0x1D2D3580172772ED, 0xEB738FC28E6BC30D,
+   0x5854EF8F63044326, 0x9E5C52325ADD3BBE, 0x90AA53CF325C4623,
+   0xC1D24D51349DD067, 0x2051CFEEA69EA624, 0x13220F0A862E7E4F,
+   0xCE39399404E04864, 0xD9C42CA47086FCB7, 0x685AD2238A03E7CC,
+   0x066484B2AB2FF1DB, 0xFE9D5D70EFBF79EC, 0x5B13B9DD9C481854,
+   0x15F0D475ED1509AD, 0x0BEBCD060EC79851, 0xD58C6791183AB7F8,
+   0xD1187C5052F3EEE4, 0xC95D1192E54E82FF, 0x86EEA14CB9AC6CA2,
+   0x3485BEB153677D5D, 0xDD191D781F8C492A, 0xF60866BAA784EBF9,
+   0x518F643BA2D08C74, 0x8852E956E1087C22, 0xA768CB8DC410AE8D,
+   0x38047726BFEC8E1A, 0xA67738B4CD3B45AA, 0xAD16691CEC0DDE19,
+   0xC6D4319380462E07, 0xC5A5876D0BA61938, 0x16B9FA1FA58FD840,
+   0x188AB1173CA74F18, 0xABDA2F98C99C021F, 0x3E0580AB134AE816,
+   0x5F3B05B773645ABB, 0x2501A2BE5575F2F6, 0x1B2F74004E7E8BA9,
+   0x1CD7580371E8D953, 0x7F6ED89562764E30, 0xB15926FF596F003D,
+   0x9F65293DA8C5D6B9, 0x6ECEF04DD690F84C, 0x4782275FFF33AF88,
+   0xE41433083F820801, 0xFD0DFE409A1AF9B5, 0x4325A3342CDB396B,
+   0x8AE77E62B301B252, 0xC36F9E9F6655615A, 0x85455A2D92D32C09,
+   0xF2C7DEA949477485, 0x63CFB4C133A39EBA, 0x83B040CC6EBC5462,
+   0x3B9454C8FDB326B0, 0x56F56A9E87FFD78C, 0x2DC2940D99F42BC6,
+   0x98F7DF096B096E2D, 0x19A6E01E3AD852BF, 0x42A99CCBDBD4B40B,
+   0xA59998AF45E9C559, 0x366295E807D93186, 0x6B48181BFAA1F773,
+   0x1FEC57E2157A0A1D, 0x4667446AF6201AD5, 0xE615EBCACFB0F075,
+   0xB8F31F4F68290778, 0x22713ED6CE22D11E, 0x3057C1A72EC3C93B,
+   0xCB46ACC37C3F1F2F, 0xDBB893FD02AAF50E, 0x331FD92E600B9FCF,
+   0xA498F96148EA3AD6, 0xA8D8426E8B6A83EA, 0xA089B274B7735CDC,
+   0x87F6B3731E524A11, 0x118808E5CBC96749, 0x9906E4C7B19BD394,
+   0xAFED7F7E9B24A20C, 0x6509EADEEB3644A7, 0x6C1EF1D3E8EF0EDE,
+   0xB9C97D43E9798FB4, 0xA2F2D784740C28A3, 0x7B8496476197566F,
+   0x7A5BE3E6B65F069D, 0xF96330ED78BE6F10, 0xEEE60DE77A076A15,
+   0x2B4BEE4AA08B9BD0, 0x6A56A63EC7B8894E, 0x02121359BA34FEF4,
+   0x4CBF99F8283703FC, 0x398071350CAF30C8, 0xD0A77A89F017687A,
+   0xF1C1A9EB9E423569, 0x8C7976282DEE8199, 0x5D1737A5DD1F7ABD,
+   0x4F53433C09A9FA80, 0xFA8B0C53DF7CA1D9, 0x3FD9DCBC886CCB77,
+   0xC040917CA91B4720, 0x7DD00142F9D1DCDF, 0x8476FC1D4F387B58,
+   0x23F8E7C5F3316503, 0x032A2244E7E37339, 0x5C87A5D750F5A74B,
+   0x082B4CC43698992E, 0xDF917BECB858F63C, 0x3270B8FC5BF86DDA,
+   0x10AE72BB29B5DD76, 0x576AC94E7700362B, 0x1AD112DAC61EFB8F,
+   0x691BC30EC5FAA427, 0xFF246311CC327143, 0x3142368E30E53206,
+   0x71380E31E02CA396, 0x958D5C960AAD76F1, 0xF8D6F430C16DA536,
+   0xC8FFD13F1BE7E1D2, 0x7578AE66004DDBE1, 0x05833F01067BE646,
+   0xBB34B5AD3BFE586D, 0x095F34C9A12B97F0, 0x247AB64525D60CA8,
+   0xDCDBC6F3017477D1, 0x4A2E14D4DECAD24D, 0xBDB5E6D9BE0A1EEB,
+   0x2A7E70F7794301AB, 0xDEF42D8A270540FD, 0x01078EC0A34C22C1,
+   0xE5DE511AF4C16387, 0x7EBB3A52BD9A330A, 0x77697857AA7D6435,
+   0x004E831603AE4C32, 0xE7A21020AD78E312, 0x9D41A70C6AB420F2,
+   0x28E06C18EA1141E6, 0xD2B28CBD984F6B28, 0x26B75F6C446E9D83,
+   0xBA47568C4D418D7F, 0xD80BADBFE6183D8E, 0x0E206D7F5F166044,
+   0xE258A43911CBCA3E, 0x723A1746B21DC0BC, 0xC7CAA854F5D7CDD3,
+   0x7CAC32883D261D9C, 0x7690C26423BA942C, 0x17E55524478042B8,
+   0xE0BE477656A2389F, 0x4D289B5E67AB2DA0, 0x44862B9C8FBBFD31,
+   0xB47CC8049D141365, 0x822C1B362B91C793, 0x4EB14655FB13DFD8,
+   0x1ECBBA0714E2A97B, 0x6143459D5CDE5F14, 0x53A8FBF1D5F0AC89,
+   0x97EA04D81C5E5B00, 0x622181A8D4FDB3F3, 0xE9BCD341572A1208,
+   0x1411258643CCE58A, 0x9144C5FEA4C6E0A4, 0x0D33D06565CF620F,
+   0x54A48D489F219CA1, 0xC43E5EAC6D63C821, 0xA9728B3A72770DAF,
+   0xD7934E7B20DF87EF, 0xE35503B61A3E86E5, 0xCAE321FBC819D504,
+   0x129A50B3AC60BFA6, 0xCD5E68EA7E9FB6C3, 0xB01C90199483B1C7,
+   0x3DE93CD5C295376C, 0xAED52EDF2AB9AD13, 0x2E60F512C0A07884,
+   0xBC3D86A3E36210C9, 0x35269D9B163951CE, 0x0C7D6E2AD0CDB5FA,
+   0x59E86297D87F5733, 0x298EF221898DB0E7, 0x55000029D1A5AA7E,
+   0x8BC08AE1B5061B45, 0xC2C31C2B6C92703A, 0x94CC596BAF25EF42,
+   0x0A1D73DB22540456, 0x04B6A0F9D9C4179A, 0xEFFDAFA2AE3D3C60,
+   0xF7C8075BB49496C4, 0x9CC5C7141D1CD4E3, 0x78BD1638218E5534,
+   0xB2F11568F850246A, 0xEDFABCFA9502BC29, 0x796CE5F2DA23051B,
+   0xAAE128B0DC93537C, 0x3A493DA0EE4B29AE, 0xB5DF6B2C416895D7,
+   0xFCABBD25122D7F37, 0x70810B58105DC4B1, 0xE10FDD37F7882A90,
+   0x524DCAB5518A3F5C, 0x3C9E85878451255B, 0x4029828119BD34E2,
+   0x74A05B6F5D3CECCB, 0xB610021542E13ECA, 0x0FF979D12F59E2AC,
+   0x6037DA27E4F9CC50, 0x5E92975A0DF1847D, 0xD66DE190D3E623FE,
+   0x5032D6B87B568048, 0x9A36B7CE8235216E, 0x80272A7A24F64B4A,
+   0x93EFED8B8C6916F7, 0x37DDBFF44CCE1555, 0x4B95DB5D4B99BD25,
+   0x92D3FDA169812FC0, 0xFB1A4A9A90660BB6, 0x730C196946A4B9B2,
+   0x81E289AA7F49DA68, 0x64669A0F83B1A05F, 0x27B3FF7D9644F48B,
+   0xCC6B615C8DB675B3, 0x674F20B9BCEBBE95, 0x6F31238275655982,
+   0x5AE488713E45CF05, 0xBF619F9954C21157, 0xEABAC46040A8EAE9,
+   0x454C6FE9F2C0C1CD, 0x419CF6496412691C, 0xD3DC3BEF265B0F70,
+   0x6D0E60F5C3578A9E };
+
+const uint64_t Tiger::SBOX4[256] = {
+   0x5B0E608526323C55, 0x1A46C1A9FA1B59F5, 0xA9E245A17C4C8FFA,
+   0x65CA5159DB2955D7, 0x05DB0A76CE35AFC2, 0x81EAC77EA9113D45,
+   0x528EF88AB6AC0A0D, 0xA09EA253597BE3FF, 0x430DDFB3AC48CD56,
+   0xC4B3A67AF45CE46F, 0x4ECECFD8FBE2D05E, 0x3EF56F10B39935F0,
+   0x0B22D6829CD619C6, 0x17FD460A74DF2069, 0x6CF8CC8E8510ED40,
+   0xD6C824BF3A6ECAA7, 0x61243D581A817049, 0x048BACB6BBC163A2,
+   0xD9A38AC27D44CC32, 0x7FDDFF5BAAF410AB, 0xAD6D495AA804824B,
+   0xE1A6A74F2D8C9F94, 0xD4F7851235DEE8E3, 0xFD4B7F886540D893,
+   0x247C20042AA4BFDA, 0x096EA1C517D1327C, 0xD56966B4361A6685,
+   0x277DA5C31221057D, 0x94D59893A43ACFF7, 0x64F0C51CCDC02281,
+   0x3D33BCC4FF6189DB, 0xE005CB184CE66AF1, 0xFF5CCD1D1DB99BEA,
+   0xB0B854A7FE42980F, 0x7BD46A6A718D4B9F, 0xD10FA8CC22A5FD8C,
+   0xD31484952BE4BD31, 0xC7FA975FCB243847, 0x4886ED1E5846C407,
+   0x28CDDB791EB70B04, 0xC2B00BE2F573417F, 0x5C9590452180F877,
+   0x7A6BDDFFF370EB00, 0xCE509E38D6D9D6A4, 0xEBEB0F00647FA702,
+   0x1DCC06CF76606F06, 0xE4D9F28BA286FF0A, 0xD85A305DC918C262,
+   0x475B1D8732225F54, 0x2D4FB51668CCB5FE, 0xA679B9D9D72BBA20,
+   0x53841C0D912D43A5, 0x3B7EAA48BF12A4E8, 0x781E0E47F22F1DDF,
+   0xEFF20CE60AB50973, 0x20D261D19DFFB742, 0x16A12B03062A2E39,
+   0x1960EB2239650495, 0x251C16FED50EB8B8, 0x9AC0C330F826016E,
+   0xED152665953E7671, 0x02D63194A6369570, 0x5074F08394B1C987,
+   0x70BA598C90B25CE1, 0x794A15810B9742F6, 0x0D5925E9FCAF8C6C,
+   0x3067716CD868744E, 0x910AB077E8D7731B, 0x6A61BBDB5AC42F61,
+   0x93513EFBF0851567, 0xF494724B9E83E9D5, 0xE887E1985C09648D,
+   0x34B1D3C675370CFD, 0xDC35E433BC0D255D, 0xD0AAB84234131BE0,
+   0x08042A50B48B7EAF, 0x9997C4EE44A3AB35, 0x829A7B49201799D0,
+   0x263B8307B7C54441, 0x752F95F4FD6A6CA6, 0x927217402C08C6E5,
+   0x2A8AB754A795D9EE, 0xA442F7552F72943D, 0x2C31334E19781208,
+   0x4FA98D7CEAEE6291, 0x55C3862F665DB309, 0xBD0610175D53B1F3,
+   0x46FE6CB840413F27, 0x3FE03792DF0CFA59, 0xCFE700372EB85E8F,
+   0xA7BE29E7ADBCE118, 0xE544EE5CDE8431DD, 0x8A781B1B41F1873E,
+   0xA5C94C78A0D2F0E7, 0x39412E2877B60728, 0xA1265EF3AFC9A62C,
+   0xBCC2770C6A2506C5, 0x3AB66DD5DCE1CE12, 0xE65499D04A675B37,
+   0x7D8F523481BFD216, 0x0F6F64FCEC15F389, 0x74EFBE618B5B13C8,
+   0xACDC82B714273E1D, 0xDD40BFE003199D17, 0x37E99257E7E061F8,
+   0xFA52626904775AAA, 0x8BBBF63A463D56F9, 0xF0013F1543A26E64,
+   0xA8307E9F879EC898, 0xCC4C27A4150177CC, 0x1B432F2CCA1D3348,
+   0xDE1D1F8F9F6FA013, 0x606602A047A7DDD6, 0xD237AB64CC1CB2C7,
+   0x9B938E7225FCD1D3, 0xEC4E03708E0FF476, 0xFEB2FBDA3D03C12D,
+   0xAE0BCED2EE43889A, 0x22CB8923EBFB4F43, 0x69360D013CF7396D,
+   0x855E3602D2D4E022, 0x073805BAD01F784C, 0x33E17A133852F546,
+   0xDF4874058AC7B638, 0xBA92B29C678AA14A, 0x0CE89FC76CFAADCD,
+   0x5F9D4E0908339E34, 0xF1AFE9291F5923B9, 0x6E3480F60F4A265F,
+   0xEEBF3A2AB29B841C, 0xE21938A88F91B4AD, 0x57DFEFF845C6D3C3,
+   0x2F006B0BF62CAAF2, 0x62F479EF6F75EE78, 0x11A55AD41C8916A9,
+   0xF229D29084FED453, 0x42F1C27B16B000E6, 0x2B1F76749823C074,
+   0x4B76ECA3C2745360, 0x8C98F463B91691BD, 0x14BCC93CF1ADE66A,
+   0x8885213E6D458397, 0x8E177DF0274D4711, 0xB49B73B5503F2951,
+   0x10168168C3F96B6B, 0x0E3D963B63CAB0AE, 0x8DFC4B5655A1DB14,
+   0xF789F1356E14DE5C, 0x683E68AF4E51DAC1, 0xC9A84F9D8D4B0FD9,
+   0x3691E03F52A0F9D1, 0x5ED86E46E1878E80, 0x3C711A0E99D07150,
+   0x5A0865B20C4E9310, 0x56FBFC1FE4F0682E, 0xEA8D5DE3105EDF9B,
+   0x71ABFDB12379187A, 0x2EB99DE1BEE77B9C, 0x21ECC0EA33CF4523,
+   0x59A4D7521805C7A1, 0x3896F5EB56AE7C72, 0xAA638F3DB18F75DC,
+   0x9F39358DABE9808E, 0xB7DEFA91C00B72AC, 0x6B5541FD62492D92,
+   0x6DC6DEE8F92E4D5B, 0x353F57ABC4BEEA7E, 0x735769D6DA5690CE,
+   0x0A234AA642391484, 0xF6F9508028F80D9D, 0xB8E319A27AB3F215,
+   0x31AD9C1151341A4D, 0x773C22A57BEF5805, 0x45C7561A07968633,
+   0xF913DA9E249DBE36, 0xDA652D9B78A64C68, 0x4C27A97F3BC334EF,
+   0x76621220E66B17F4, 0x967743899ACD7D0B, 0xF3EE5BCAE0ED6782,
+   0x409F753600C879FC, 0x06D09A39B5926DB6, 0x6F83AEB0317AC588,
+   0x01E6CA4A86381F21, 0x66FF3462D19F3025, 0x72207C24DDFD3BFB,
+   0x4AF6B6D3E2ECE2EB, 0x9C994DBEC7EA08DE, 0x49ACE597B09A8BC4,
+   0xB38C4766CF0797BA, 0x131B9373C57C2A75, 0xB1822CCE61931E58,
+   0x9D7555B909BA1C0C, 0x127FAFDD937D11D2, 0x29DA3BADC66D92E4,
+   0xA2C1D57154C2ECBC, 0x58C5134D82F6FE24, 0x1C3AE3515B62274F,
+   0xE907C82E01CB8126, 0xF8ED091913E37FCB, 0x3249D8F9C80046C9,
+   0x80CF9BEDE388FB63, 0x1881539A116CF19E, 0x5103F3F76BD52457,
+   0x15B7E6F5AE47F7A8, 0xDBD7C6DED47E9CCF, 0x44E55C410228BB1A,
+   0xB647D4255EDB4E99, 0x5D11882BB8AAFC30, 0xF5098BBB29D3212A,
+   0x8FB5EA14E90296B3, 0x677B942157DD025A, 0xFB58E7C0A390ACB5,
+   0x89D3674C83BD4A01, 0x9E2DA4DF4BF3B93B, 0xFCC41E328CAB4829,
+   0x03F38C96BA582C52, 0xCAD1BDBD7FD85DB2, 0xBBB442C16082AE83,
+   0xB95FE86BA5DA9AB0, 0xB22E04673771A93F, 0x845358C9493152D8,
+   0xBE2A488697B4541E, 0x95A2DC2DD38E6966, 0xC02C11AC923C852B,
+   0x2388B1990DF2A87B, 0x7C8008FA1B4F37BE, 0x1F70D0C84D54E503,
+   0x5490ADEC7ECE57D4, 0x002B3C27D9063A3A, 0x7EAEA3848030A2BF,
+   0xC602326DED2003C0, 0x83A7287D69A94086, 0xC57A5FCB30F57A8A,
+   0xB56844E479EBE779, 0xA373B40F05DCBCE9, 0xD71A786E88570EE2,
+   0x879CBACDBDE8F6A0, 0x976AD1BCC164A32F, 0xAB21E25E9666D78B,
+   0x901063AAE5E5C33C, 0x9818B34448698D90, 0xE36487AE3E1E8ABB,
+   0xAFBDF931893BDCB4, 0x6345A0DC5FBBD519, 0x8628FE269B9465CA,
+   0x1E5D01603F9C51EC, 0x4DE44006A15049B7, 0xBF6C70E5F776CBB1,
+   0x411218F2EF552BED, 0xCB0C0708705A36A3, 0xE74D14754F986044,
+   0xCD56D9430EA8280E, 0xC12591D7535F5065, 0xC83223F1720AEF96,
+   0xC3A0396F7363A51F };
+
+}
diff --git a/third_party/botan/src/lib/hash/tiger/tiger.cpp b/third_party/botan/src/lib/hash/tiger/tiger.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/tiger/tiger.cpp
@@ -0,0 +1,190 @@
+/*
+* Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tiger.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Tiger::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new Tiger(*this));
+   }
+
+namespace {
+
+/*
+* Tiger Mixing Function
+*/
+inline void mix(secure_vector<uint64_t>& X)
+   {
+   X[0] -= X[7] ^ 0xA5A5A5A5A5A5A5A5;
+   X[1] ^= X[0];
+   X[2] += X[1];
+   X[3] -= X[2] ^ ((~X[1]) << 19);
+   X[4] ^= X[3];
+   X[5] += X[4];
+   X[6] -= X[5] ^ ((~X[4]) >> 23);
+   X[7] ^= X[6];
+
+   X[0] += X[7];
+   X[1] -= X[0] ^ ((~X[7]) << 19);
+   X[2] ^= X[1];
+   X[3] += X[2];
+   X[4] -= X[3] ^ ((~X[2]) >> 23);
+   X[5] ^= X[4];
+   X[6] += X[5];
+   X[7] -= X[6] ^ 0x0123456789ABCDEF;
+   }
+
+}
+
+/*
+* Tiger Compression Function
+*/
+void Tiger::compress_n(const uint8_t input[], size_t blocks)
+   {
+   uint64_t A = m_digest[0], B = m_digest[1], C = m_digest[2];
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      load_le(m_X.data(), input, m_X.size());
+
+      pass(A, B, C, m_X, 5); mix(m_X);
+      pass(C, A, B, m_X, 7); mix(m_X);
+      pass(B, C, A, m_X, 9);
+
+      for(size_t j = 3; j != m_passes; ++j)
+         {
+         mix(m_X);
+         pass(A, B, C, m_X, 9);
+         uint64_t T = A; A = C; C = B; B = T;
+         }
+
+      A = (m_digest[0] ^= A);
+      B = m_digest[1] = B - m_digest[1];
+      C = (m_digest[2] += C);
+
+      input += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void Tiger::copy_out(uint8_t output[])
+   {
+   copy_out_vec_le(output, output_length(), m_digest);
+   }
+
+/*
+* Tiger Pass
+*/
+void Tiger::pass(uint64_t& A, uint64_t& B, uint64_t& C,
+                 const secure_vector<uint64_t>& X,
+                 uint8_t mul)
+   {
+   C ^= X[0];
+   A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+        SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+   B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+        SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+   B *= mul;
+
+   A ^= X[1];
+   B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+        SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+   C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+        SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+   C *= mul;
+
+   B ^= X[2];
+   C -= SBOX1[get_byte(7, B)] ^ SBOX2[get_byte(5, B)] ^
+        SBOX3[get_byte(3, B)] ^ SBOX4[get_byte(1, B)];
+   A += SBOX1[get_byte(0, B)] ^ SBOX2[get_byte(2, B)] ^
+        SBOX3[get_byte(4, B)] ^ SBOX4[get_byte(6, B)];
+   A *= mul;
+
+   C ^= X[3];
+   A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+        SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+   B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+        SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+   B *= mul;
+
+   A ^= X[4];
+   B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+        SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+   C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+        SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+   C *= mul;
+
+   B ^= X[5];
+   C -= SBOX1[get_byte(7, B)] ^ SBOX2[get_byte(5, B)] ^
+        SBOX3[get_byte(3, B)] ^ SBOX4[get_byte(1, B)];
+   A += SBOX1[get_byte(0, B)] ^ SBOX2[get_byte(2, B)] ^
+        SBOX3[get_byte(4, B)] ^ SBOX4[get_byte(6, B)];
+   A *= mul;
+
+   C ^= X[6];
+   A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+        SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+   B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+        SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+   B *= mul;
+
+   A ^= X[7];
+   B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+        SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+   C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+        SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+   C *= mul;
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Tiger::clear()
+   {
+   MDx_HashFunction::clear();
+   zeroise(m_X);
+   m_digest[0] = 0x0123456789ABCDEF;
+   m_digest[1] = 0xFEDCBA9876543210;
+   m_digest[2] = 0xF096A5B4C3B2E187;
+   }
+
+/*
+* Return the name of this type
+*/
+std::string Tiger::name() const
+   {
+   return "Tiger(" + std::to_string(output_length()) + "," +
+                     std::to_string(m_passes) + ")";
+   }
+
+/*
+* Tiger Constructor
+*/
+Tiger::Tiger(size_t hash_len, size_t passes) :
+   MDx_HashFunction(64, false, false),
+   m_X(8),
+   m_digest(3),
+   m_hash_len(hash_len),
+   m_passes(passes)
+   {
+   if(output_length() != 16 && output_length() != 20 && output_length() != 24)
+      throw Invalid_Argument("Tiger: Illegal hash output size: " +
+                             std::to_string(output_length()));
+
+   if(passes < 3)
+      throw Invalid_Argument("Tiger: Invalid number of passes: "
+                             + std::to_string(passes));
+   clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/tiger/tiger.h b/third_party/botan/src/lib/hash/tiger/tiger.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/tiger/tiger.h
@@ -0,0 +1,59 @@
+/*
+* Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TIGER_H_
+#define BOTAN_TIGER_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(tiger.h)
+
+namespace Botan {
+
+/**
+* Tiger
+*/
+class BOTAN_PUBLIC_API(2,0) Tiger final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override;
+      size_t output_length() const override { return m_hash_len; }
+
+      HashFunction* clone() const override
+         {
+         return new Tiger(output_length(), m_passes);
+         }
+
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      /**
+      * @param out_size specifies the output length; can be 16, 20, or 24
+      * @param passes to make in the algorithm
+      */
+      Tiger(size_t out_size = 24, size_t passes = 3);
+   private:
+      void compress_n(const uint8_t[], size_t block) override;
+      void copy_out(uint8_t[]) override;
+
+      static void pass(uint64_t& A, uint64_t& B, uint64_t& C,
+                       const secure_vector<uint64_t>& M,
+                       uint8_t mul);
+
+      static const uint64_t SBOX1[256];
+      static const uint64_t SBOX2[256];
+      static const uint64_t SBOX3[256];
+      static const uint64_t SBOX4[256];
+
+      secure_vector<uint64_t> m_X, m_digest;
+      const size_t m_hash_len, m_passes;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp b/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp
@@ -0,0 +1,150 @@
+/*
+* Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/whrlpool.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Whirlpool::copy_state() const
+   {
+   return std::unique_ptr<HashFunction>(new Whirlpool(*this));
+   }
+
+/*
+* Whirlpool Compression Function
+*/
+void Whirlpool::compress_n(const uint8_t in[], size_t blocks)
+   {
+   static const uint64_t RC[10] = {
+      0x1823C6E887B8014F, 0x36A6D2F5796F9152,
+      0x60BC9B8EA30C7B35, 0x1DE0D7C22E4BFE57,
+      0x157737E59FF04ADA, 0x58C9290AB1A06B85,
+      0xBD5D10F4CB3E0567, 0xE427418BA77D95D8,
+      0xFBEE7C66DD17479E, 0xCA2DBF07AD5A8333
+   };
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      load_be(m_M.data(), in, m_M.size());
+
+      uint64_t K0, K1, K2, K3, K4, K5, K6, K7;
+      K0 = m_digest[0]; K1 = m_digest[1]; K2 = m_digest[2]; K3 = m_digest[3];
+      K4 = m_digest[4]; K5 = m_digest[5]; K6 = m_digest[6]; K7 = m_digest[7];
+
+      uint64_t B0, B1, B2, B3, B4, B5, B6, B7;
+      B0 = K0 ^ m_M[0]; B1 = K1 ^ m_M[1]; B2 = K2 ^ m_M[2]; B3 = K3 ^ m_M[3];
+      B4 = K4 ^ m_M[4]; B5 = K5 ^ m_M[5]; B6 = K6 ^ m_M[6]; B7 = K7 ^ m_M[7];
+
+      for(size_t j = 0; j != 10; ++j)
+         {
+         uint64_t T0, T1, T2, T3, T4, T5, T6, T7;
+         T0 = C0[get_byte(0, K0)] ^ C1[get_byte(1, K7)] ^
+              C2[get_byte(2, K6)] ^ C3[get_byte(3, K5)] ^
+              C4[get_byte(4, K4)] ^ C5[get_byte(5, K3)] ^
+              C6[get_byte(6, K2)] ^ C7[get_byte(7, K1)] ^ RC[j];
+         T1 = C0[get_byte(0, K1)] ^ C1[get_byte(1, K0)] ^
+              C2[get_byte(2, K7)] ^ C3[get_byte(3, K6)] ^
+              C4[get_byte(4, K5)] ^ C5[get_byte(5, K4)] ^
+              C6[get_byte(6, K3)] ^ C7[get_byte(7, K2)];
+         T2 = C0[get_byte(0, K2)] ^ C1[get_byte(1, K1)] ^
+              C2[get_byte(2, K0)] ^ C3[get_byte(3, K7)] ^
+              C4[get_byte(4, K6)] ^ C5[get_byte(5, K5)] ^
+              C6[get_byte(6, K4)] ^ C7[get_byte(7, K3)];
+         T3 = C0[get_byte(0, K3)] ^ C1[get_byte(1, K2)] ^
+              C2[get_byte(2, K1)] ^ C3[get_byte(3, K0)] ^
+              C4[get_byte(4, K7)] ^ C5[get_byte(5, K6)] ^
+              C6[get_byte(6, K5)] ^ C7[get_byte(7, K4)];
+         T4 = C0[get_byte(0, K4)] ^ C1[get_byte(1, K3)] ^
+              C2[get_byte(2, K2)] ^ C3[get_byte(3, K1)] ^
+              C4[get_byte(4, K0)] ^ C5[get_byte(5, K7)] ^
+              C6[get_byte(6, K6)] ^ C7[get_byte(7, K5)];
+         T5 = C0[get_byte(0, K5)] ^ C1[get_byte(1, K4)] ^
+              C2[get_byte(2, K3)] ^ C3[get_byte(3, K2)] ^
+              C4[get_byte(4, K1)] ^ C5[get_byte(5, K0)] ^
+              C6[get_byte(6, K7)] ^ C7[get_byte(7, K6)];
+         T6 = C0[get_byte(0, K6)] ^ C1[get_byte(1, K5)] ^
+              C2[get_byte(2, K4)] ^ C3[get_byte(3, K3)] ^
+              C4[get_byte(4, K2)] ^ C5[get_byte(5, K1)] ^
+              C6[get_byte(6, K0)] ^ C7[get_byte(7, K7)];
+         T7 = C0[get_byte(0, K7)] ^ C1[get_byte(1, K6)] ^
+              C2[get_byte(2, K5)] ^ C3[get_byte(3, K4)] ^
+              C4[get_byte(4, K3)] ^ C5[get_byte(5, K2)] ^
+              C6[get_byte(6, K1)] ^ C7[get_byte(7, K0)];
+
+         K0 = T0; K1 = T1; K2 = T2; K3 = T3;
+         K4 = T4; K5 = T5; K6 = T6; K7 = T7;
+
+         T0 = C0[get_byte(0, B0)] ^ C1[get_byte(1, B7)] ^
+              C2[get_byte(2, B6)] ^ C3[get_byte(3, B5)] ^
+              C4[get_byte(4, B4)] ^ C5[get_byte(5, B3)] ^
+              C6[get_byte(6, B2)] ^ C7[get_byte(7, B1)] ^ K0;
+         T1 = C0[get_byte(0, B1)] ^ C1[get_byte(1, B0)] ^
+              C2[get_byte(2, B7)] ^ C3[get_byte(3, B6)] ^
+              C4[get_byte(4, B5)] ^ C5[get_byte(5, B4)] ^
+              C6[get_byte(6, B3)] ^ C7[get_byte(7, B2)] ^ K1;
+         T2 = C0[get_byte(0, B2)] ^ C1[get_byte(1, B1)] ^
+              C2[get_byte(2, B0)] ^ C3[get_byte(3, B7)] ^
+              C4[get_byte(4, B6)] ^ C5[get_byte(5, B5)] ^
+              C6[get_byte(6, B4)] ^ C7[get_byte(7, B3)] ^ K2;
+         T3 = C0[get_byte(0, B3)] ^ C1[get_byte(1, B2)] ^
+              C2[get_byte(2, B1)] ^ C3[get_byte(3, B0)] ^
+              C4[get_byte(4, B7)] ^ C5[get_byte(5, B6)] ^
+              C6[get_byte(6, B5)] ^ C7[get_byte(7, B4)] ^ K3;
+         T4 = C0[get_byte(0, B4)] ^ C1[get_byte(1, B3)] ^
+              C2[get_byte(2, B2)] ^ C3[get_byte(3, B1)] ^
+              C4[get_byte(4, B0)] ^ C5[get_byte(5, B7)] ^
+              C6[get_byte(6, B6)] ^ C7[get_byte(7, B5)] ^ K4;
+         T5 = C0[get_byte(0, B5)] ^ C1[get_byte(1, B4)] ^
+              C2[get_byte(2, B3)] ^ C3[get_byte(3, B2)] ^
+              C4[get_byte(4, B1)] ^ C5[get_byte(5, B0)] ^
+              C6[get_byte(6, B7)] ^ C7[get_byte(7, B6)] ^ K5;
+         T6 = C0[get_byte(0, B6)] ^ C1[get_byte(1, B5)] ^
+              C2[get_byte(2, B4)] ^ C3[get_byte(3, B3)] ^
+              C4[get_byte(4, B2)] ^ C5[get_byte(5, B1)] ^
+              C6[get_byte(6, B0)] ^ C7[get_byte(7, B7)] ^ K6;
+         T7 = C0[get_byte(0, B7)] ^ C1[get_byte(1, B6)] ^
+              C2[get_byte(2, B5)] ^ C3[get_byte(3, B4)] ^
+              C4[get_byte(4, B3)] ^ C5[get_byte(5, B2)] ^
+              C6[get_byte(6, B1)] ^ C7[get_byte(7, B0)] ^ K7;
+
+         B0 = T0; B1 = T1; B2 = T2; B3 = T3;
+         B4 = T4; B5 = T5; B6 = T6; B7 = T7;
+         }
+
+      m_digest[0] ^= B0 ^ m_M[0];
+      m_digest[1] ^= B1 ^ m_M[1];
+      m_digest[2] ^= B2 ^ m_M[2];
+      m_digest[3] ^= B3 ^ m_M[3];
+      m_digest[4] ^= B4 ^ m_M[4];
+      m_digest[5] ^= B5 ^ m_M[5];
+      m_digest[6] ^= B6 ^ m_M[6];
+      m_digest[7] ^= B7 ^ m_M[7];
+
+      in += hash_block_size();
+      }
+   }
+
+/*
+* Copy out the digest
+*/
+void Whirlpool::copy_out(uint8_t output[])
+   {
+   copy_out_vec_be(output, output_length(), m_digest);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Whirlpool::clear()
+   {
+   MDx_HashFunction::clear();
+   zeroise(m_M);
+   zeroise(m_digest);
+   }
+
+}
diff --git a/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp b/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp
@@ -0,0 +1,540 @@
+/*
+* Diffusion Tables for Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/whrlpool.h>
+
+namespace Botan {
+
+const uint64_t Whirlpool::C0[256] = {
+0x18186018C07830D8, 0x23238C2305AF4626, 0xC6C63FC67EF991B8, 0xE8E887E8136FCDFB,
+0x878726874CA113CB, 0xB8B8DAB8A9626D11, 0x0101040108050209, 0x4F4F214F426E9E0D,
+0x3636D836ADEE6C9B, 0xA6A6A2A6590451FF, 0xD2D26FD2DEBDB90C, 0xF5F5F3F5FB06F70E,
+0x7979F979EF80F296, 0x6F6FA16F5FCEDE30, 0x91917E91FCEF3F6D, 0x52525552AA07A4F8,
+0x60609D6027FDC047, 0xBCBCCABC89766535, 0x9B9B569BACCD2B37, 0x8E8E028E048C018A,
+0xA3A3B6A371155BD2, 0x0C0C300C603C186C, 0x7B7BF17BFF8AF684, 0x3535D435B5E16A80,
+0x1D1D741DE8693AF5, 0xE0E0A7E05347DDB3, 0xD7D77BD7F6ACB321, 0xC2C22FC25EED999C,
+0x2E2EB82E6D965C43, 0x4B4B314B627A9629, 0xFEFEDFFEA321E15D, 0x575741578216AED5,
+0x15155415A8412ABD, 0x7777C1779FB6EEE8, 0x3737DC37A5EB6E92, 0xE5E5B3E57B56D79E,
+0x9F9F469F8CD92313, 0xF0F0E7F0D317FD23, 0x4A4A354A6A7F9420, 0xDADA4FDA9E95A944,
+0x58587D58FA25B0A2, 0xC9C903C906CA8FCF, 0x2929A429558D527C, 0x0A0A280A5022145A,
+0xB1B1FEB1E14F7F50, 0xA0A0BAA0691A5DC9, 0x6B6BB16B7FDAD614, 0x85852E855CAB17D9,
+0xBDBDCEBD8173673C, 0x5D5D695DD234BA8F, 0x1010401080502090, 0xF4F4F7F4F303F507,
+0xCBCB0BCB16C08BDD, 0x3E3EF83EEDC67CD3, 0x0505140528110A2D, 0x676781671FE6CE78,
+0xE4E4B7E47353D597, 0x27279C2725BB4E02, 0x4141194132588273, 0x8B8B168B2C9D0BA7,
+0xA7A7A6A7510153F6, 0x7D7DE97DCF94FAB2, 0x95956E95DCFB3749, 0xD8D847D88E9FAD56,
+0xFBFBCBFB8B30EB70, 0xEEEE9FEE2371C1CD, 0x7C7CED7CC791F8BB, 0x6666856617E3CC71,
+0xDDDD53DDA68EA77B, 0x17175C17B84B2EAF, 0x4747014702468E45, 0x9E9E429E84DC211A,
+0xCACA0FCA1EC589D4, 0x2D2DB42D75995A58, 0xBFBFC6BF9179632E, 0x07071C07381B0E3F,
+0xADAD8EAD012347AC, 0x5A5A755AEA2FB4B0, 0x838336836CB51BEF, 0x3333CC3385FF66B6,
+0x636391633FF2C65C, 0x02020802100A0412, 0xAAAA92AA39384993, 0x7171D971AFA8E2DE,
+0xC8C807C80ECF8DC6, 0x19196419C87D32D1, 0x494939497270923B, 0xD9D943D9869AAF5F,
+0xF2F2EFF2C31DF931, 0xE3E3ABE34B48DBA8, 0x5B5B715BE22AB6B9, 0x88881A8834920DBC,
+0x9A9A529AA4C8293E, 0x262698262DBE4C0B, 0x3232C8328DFA64BF, 0xB0B0FAB0E94A7D59,
+0xE9E983E91B6ACFF2, 0x0F0F3C0F78331E77, 0xD5D573D5E6A6B733, 0x80803A8074BA1DF4,
+0xBEBEC2BE997C6127, 0xCDCD13CD26DE87EB, 0x3434D034BDE46889, 0x48483D487A759032,
+0xFFFFDBFFAB24E354, 0x7A7AF57AF78FF48D, 0x90907A90F4EA3D64, 0x5F5F615FC23EBE9D,
+0x202080201DA0403D, 0x6868BD6867D5D00F, 0x1A1A681AD07234CA, 0xAEAE82AE192C41B7,
+0xB4B4EAB4C95E757D, 0x54544D549A19A8CE, 0x93937693ECE53B7F, 0x222288220DAA442F,
+0x64648D6407E9C863, 0xF1F1E3F1DB12FF2A, 0x7373D173BFA2E6CC, 0x12124812905A2482,
+0x40401D403A5D807A, 0x0808200840281048, 0xC3C32BC356E89B95, 0xECEC97EC337BC5DF,
+0xDBDB4BDB9690AB4D, 0xA1A1BEA1611F5FC0, 0x8D8D0E8D1C830791, 0x3D3DF43DF5C97AC8,
+0x97976697CCF1335B, 0x0000000000000000, 0xCFCF1BCF36D483F9, 0x2B2BAC2B4587566E,
+0x7676C57697B3ECE1, 0x8282328264B019E6, 0xD6D67FD6FEA9B128, 0x1B1B6C1BD87736C3,
+0xB5B5EEB5C15B7774, 0xAFAF86AF112943BE, 0x6A6AB56A77DFD41D, 0x50505D50BA0DA0EA,
+0x45450945124C8A57, 0xF3F3EBF3CB18FB38, 0x3030C0309DF060AD, 0xEFEF9BEF2B74C3C4,
+0x3F3FFC3FE5C37EDA, 0x55554955921CAAC7, 0xA2A2B2A2791059DB, 0xEAEA8FEA0365C9E9,
+0x656589650FECCA6A, 0xBABAD2BAB9686903, 0x2F2FBC2F65935E4A, 0xC0C027C04EE79D8E,
+0xDEDE5FDEBE81A160, 0x1C1C701CE06C38FC, 0xFDFDD3FDBB2EE746, 0x4D4D294D52649A1F,
+0x92927292E4E03976, 0x7575C9758FBCEAFA, 0x06061806301E0C36, 0x8A8A128A249809AE,
+0xB2B2F2B2F940794B, 0xE6E6BFE66359D185, 0x0E0E380E70361C7E, 0x1F1F7C1FF8633EE7,
+0x6262956237F7C455, 0xD4D477D4EEA3B53A, 0xA8A89AA829324D81, 0x96966296C4F43152,
+0xF9F9C3F99B3AEF62, 0xC5C533C566F697A3, 0x2525942535B14A10, 0x59597959F220B2AB,
+0x84842A8454AE15D0, 0x7272D572B7A7E4C5, 0x3939E439D5DD72EC, 0x4C4C2D4C5A619816,
+0x5E5E655ECA3BBC94, 0x7878FD78E785F09F, 0x3838E038DDD870E5, 0x8C8C0A8C14860598,
+0xD1D163D1C6B2BF17, 0xA5A5AEA5410B57E4, 0xE2E2AFE2434DD9A1, 0x616199612FF8C24E,
+0xB3B3F6B3F1457B42, 0x2121842115A54234, 0x9C9C4A9C94D62508, 0x1E1E781EF0663CEE,
+0x4343114322528661, 0xC7C73BC776FC93B1, 0xFCFCD7FCB32BE54F, 0x0404100420140824,
+0x51515951B208A2E3, 0x99995E99BCC72F25, 0x6D6DA96D4FC4DA22, 0x0D0D340D68391A65,
+0xFAFACFFA8335E979, 0xDFDF5BDFB684A369, 0x7E7EE57ED79BFCA9, 0x242490243DB44819,
+0x3B3BEC3BC5D776FE, 0xABAB96AB313D4B9A, 0xCECE1FCE3ED181F0, 0x1111441188552299,
+0x8F8F068F0C890383, 0x4E4E254E4A6B9C04, 0xB7B7E6B7D1517366, 0xEBEB8BEB0B60CBE0,
+0x3C3CF03CFDCC78C1, 0x81813E817CBF1FFD, 0x94946A94D4FE3540, 0xF7F7FBF7EB0CF31C,
+0xB9B9DEB9A1676F18, 0x13134C13985F268B, 0x2C2CB02C7D9C5851, 0xD3D36BD3D6B8BB05,
+0xE7E7BBE76B5CD38C, 0x6E6EA56E57CBDC39, 0xC4C437C46EF395AA, 0x03030C03180F061B,
+0x565645568A13ACDC, 0x44440D441A49885E, 0x7F7FE17FDF9EFEA0, 0xA9A99EA921374F88,
+0x2A2AA82A4D825467, 0xBBBBD6BBB16D6B0A, 0xC1C123C146E29F87, 0x53535153A202A6F1,
+0xDCDC57DCAE8BA572, 0x0B0B2C0B58271653, 0x9D9D4E9D9CD32701, 0x6C6CAD6C47C1D82B,
+0x3131C43195F562A4, 0x7474CD7487B9E8F3, 0xF6F6FFF6E309F115, 0x464605460A438C4C,
+0xACAC8AAC092645A5, 0x89891E893C970FB5, 0x14145014A04428B4, 0xE1E1A3E15B42DFBA,
+0x16165816B04E2CA6, 0x3A3AE83ACDD274F7, 0x6969B9696FD0D206, 0x09092409482D1241,
+0x7070DD70A7ADE0D7, 0xB6B6E2B6D954716F, 0xD0D067D0CEB7BD1E, 0xEDED93ED3B7EC7D6,
+0xCCCC17CC2EDB85E2, 0x424215422A578468, 0x98985A98B4C22D2C, 0xA4A4AAA4490E55ED,
+0x2828A0285D885075, 0x5C5C6D5CDA31B886, 0xF8F8C7F8933FED6B, 0x8686228644A411C2 };
+
+const uint64_t Whirlpool::C1[256] = {
+0xD818186018C07830, 0x2623238C2305AF46, 0xB8C6C63FC67EF991, 0xFBE8E887E8136FCD,
+0xCB878726874CA113, 0x11B8B8DAB8A9626D, 0x0901010401080502, 0x0D4F4F214F426E9E,
+0x9B3636D836ADEE6C, 0xFFA6A6A2A6590451, 0x0CD2D26FD2DEBDB9, 0x0EF5F5F3F5FB06F7,
+0x967979F979EF80F2, 0x306F6FA16F5FCEDE, 0x6D91917E91FCEF3F, 0xF852525552AA07A4,
+0x4760609D6027FDC0, 0x35BCBCCABC897665, 0x379B9B569BACCD2B, 0x8A8E8E028E048C01,
+0xD2A3A3B6A371155B, 0x6C0C0C300C603C18, 0x847B7BF17BFF8AF6, 0x803535D435B5E16A,
+0xF51D1D741DE8693A, 0xB3E0E0A7E05347DD, 0x21D7D77BD7F6ACB3, 0x9CC2C22FC25EED99,
+0x432E2EB82E6D965C, 0x294B4B314B627A96, 0x5DFEFEDFFEA321E1, 0xD5575741578216AE,
+0xBD15155415A8412A, 0xE87777C1779FB6EE, 0x923737DC37A5EB6E, 0x9EE5E5B3E57B56D7,
+0x139F9F469F8CD923, 0x23F0F0E7F0D317FD, 0x204A4A354A6A7F94, 0x44DADA4FDA9E95A9,
+0xA258587D58FA25B0, 0xCFC9C903C906CA8F, 0x7C2929A429558D52, 0x5A0A0A280A502214,
+0x50B1B1FEB1E14F7F, 0xC9A0A0BAA0691A5D, 0x146B6BB16B7FDAD6, 0xD985852E855CAB17,
+0x3CBDBDCEBD817367, 0x8F5D5D695DD234BA, 0x9010104010805020, 0x07F4F4F7F4F303F5,
+0xDDCBCB0BCB16C08B, 0xD33E3EF83EEDC67C, 0x2D0505140528110A, 0x78676781671FE6CE,
+0x97E4E4B7E47353D5, 0x0227279C2725BB4E, 0x7341411941325882, 0xA78B8B168B2C9D0B,
+0xF6A7A7A6A7510153, 0xB27D7DE97DCF94FA, 0x4995956E95DCFB37, 0x56D8D847D88E9FAD,
+0x70FBFBCBFB8B30EB, 0xCDEEEE9FEE2371C1, 0xBB7C7CED7CC791F8, 0x716666856617E3CC,
+0x7BDDDD53DDA68EA7, 0xAF17175C17B84B2E, 0x454747014702468E, 0x1A9E9E429E84DC21,
+0xD4CACA0FCA1EC589, 0x582D2DB42D75995A, 0x2EBFBFC6BF917963, 0x3F07071C07381B0E,
+0xACADAD8EAD012347, 0xB05A5A755AEA2FB4, 0xEF838336836CB51B, 0xB63333CC3385FF66,
+0x5C636391633FF2C6, 0x1202020802100A04, 0x93AAAA92AA393849, 0xDE7171D971AFA8E2,
+0xC6C8C807C80ECF8D, 0xD119196419C87D32, 0x3B49493949727092, 0x5FD9D943D9869AAF,
+0x31F2F2EFF2C31DF9, 0xA8E3E3ABE34B48DB, 0xB95B5B715BE22AB6, 0xBC88881A8834920D,
+0x3E9A9A529AA4C829, 0x0B262698262DBE4C, 0xBF3232C8328DFA64, 0x59B0B0FAB0E94A7D,
+0xF2E9E983E91B6ACF, 0x770F0F3C0F78331E, 0x33D5D573D5E6A6B7, 0xF480803A8074BA1D,
+0x27BEBEC2BE997C61, 0xEBCDCD13CD26DE87, 0x893434D034BDE468, 0x3248483D487A7590,
+0x54FFFFDBFFAB24E3, 0x8D7A7AF57AF78FF4, 0x6490907A90F4EA3D, 0x9D5F5F615FC23EBE,
+0x3D202080201DA040, 0x0F6868BD6867D5D0, 0xCA1A1A681AD07234, 0xB7AEAE82AE192C41,
+0x7DB4B4EAB4C95E75, 0xCE54544D549A19A8, 0x7F93937693ECE53B, 0x2F222288220DAA44,
+0x6364648D6407E9C8, 0x2AF1F1E3F1DB12FF, 0xCC7373D173BFA2E6, 0x8212124812905A24,
+0x7A40401D403A5D80, 0x4808082008402810, 0x95C3C32BC356E89B, 0xDFECEC97EC337BC5,
+0x4DDBDB4BDB9690AB, 0xC0A1A1BEA1611F5F, 0x918D8D0E8D1C8307, 0xC83D3DF43DF5C97A,
+0x5B97976697CCF133, 0x0000000000000000, 0xF9CFCF1BCF36D483, 0x6E2B2BAC2B458756,
+0xE17676C57697B3EC, 0xE68282328264B019, 0x28D6D67FD6FEA9B1, 0xC31B1B6C1BD87736,
+0x74B5B5EEB5C15B77, 0xBEAFAF86AF112943, 0x1D6A6AB56A77DFD4, 0xEA50505D50BA0DA0,
+0x5745450945124C8A, 0x38F3F3EBF3CB18FB, 0xAD3030C0309DF060, 0xC4EFEF9BEF2B74C3,
+0xDA3F3FFC3FE5C37E, 0xC755554955921CAA, 0xDBA2A2B2A2791059, 0xE9EAEA8FEA0365C9,
+0x6A656589650FECCA, 0x03BABAD2BAB96869, 0x4A2F2FBC2F65935E, 0x8EC0C027C04EE79D,
+0x60DEDE5FDEBE81A1, 0xFC1C1C701CE06C38, 0x46FDFDD3FDBB2EE7, 0x1F4D4D294D52649A,
+0x7692927292E4E039, 0xFA7575C9758FBCEA, 0x3606061806301E0C, 0xAE8A8A128A249809,
+0x4BB2B2F2B2F94079, 0x85E6E6BFE66359D1, 0x7E0E0E380E70361C, 0xE71F1F7C1FF8633E,
+0x556262956237F7C4, 0x3AD4D477D4EEA3B5, 0x81A8A89AA829324D, 0x5296966296C4F431,
+0x62F9F9C3F99B3AEF, 0xA3C5C533C566F697, 0x102525942535B14A, 0xAB59597959F220B2,
+0xD084842A8454AE15, 0xC57272D572B7A7E4, 0xEC3939E439D5DD72, 0x164C4C2D4C5A6198,
+0x945E5E655ECA3BBC, 0x9F7878FD78E785F0, 0xE53838E038DDD870, 0x988C8C0A8C148605,
+0x17D1D163D1C6B2BF, 0xE4A5A5AEA5410B57, 0xA1E2E2AFE2434DD9, 0x4E616199612FF8C2,
+0x42B3B3F6B3F1457B, 0x342121842115A542, 0x089C9C4A9C94D625, 0xEE1E1E781EF0663C,
+0x6143431143225286, 0xB1C7C73BC776FC93, 0x4FFCFCD7FCB32BE5, 0x2404041004201408,
+0xE351515951B208A2, 0x2599995E99BCC72F, 0x226D6DA96D4FC4DA, 0x650D0D340D68391A,
+0x79FAFACFFA8335E9, 0x69DFDF5BDFB684A3, 0xA97E7EE57ED79BFC, 0x19242490243DB448,
+0xFE3B3BEC3BC5D776, 0x9AABAB96AB313D4B, 0xF0CECE1FCE3ED181, 0x9911114411885522,
+0x838F8F068F0C8903, 0x044E4E254E4A6B9C, 0x66B7B7E6B7D15173, 0xE0EBEB8BEB0B60CB,
+0xC13C3CF03CFDCC78, 0xFD81813E817CBF1F, 0x4094946A94D4FE35, 0x1CF7F7FBF7EB0CF3,
+0x18B9B9DEB9A1676F, 0x8B13134C13985F26, 0x512C2CB02C7D9C58, 0x05D3D36BD3D6B8BB,
+0x8CE7E7BBE76B5CD3, 0x396E6EA56E57CBDC, 0xAAC4C437C46EF395, 0x1B03030C03180F06,
+0xDC565645568A13AC, 0x5E44440D441A4988, 0xA07F7FE17FDF9EFE, 0x88A9A99EA921374F,
+0x672A2AA82A4D8254, 0x0ABBBBD6BBB16D6B, 0x87C1C123C146E29F, 0xF153535153A202A6,
+0x72DCDC57DCAE8BA5, 0x530B0B2C0B582716, 0x019D9D4E9D9CD327, 0x2B6C6CAD6C47C1D8,
+0xA43131C43195F562, 0xF37474CD7487B9E8, 0x15F6F6FFF6E309F1, 0x4C464605460A438C,
+0xA5ACAC8AAC092645, 0xB589891E893C970F, 0xB414145014A04428, 0xBAE1E1A3E15B42DF,
+0xA616165816B04E2C, 0xF73A3AE83ACDD274, 0x066969B9696FD0D2, 0x4109092409482D12,
+0xD77070DD70A7ADE0, 0x6FB6B6E2B6D95471, 0x1ED0D067D0CEB7BD, 0xD6EDED93ED3B7EC7,
+0xE2CCCC17CC2EDB85, 0x68424215422A5784, 0x2C98985A98B4C22D, 0xEDA4A4AAA4490E55,
+0x752828A0285D8850, 0x865C5C6D5CDA31B8, 0x6BF8F8C7F8933FED, 0xC28686228644A411 };
+
+const uint64_t Whirlpool::C2[256] = {
+0x30D818186018C078, 0x462623238C2305AF, 0x91B8C6C63FC67EF9, 0xCDFBE8E887E8136F,
+0x13CB878726874CA1, 0x6D11B8B8DAB8A962, 0x0209010104010805, 0x9E0D4F4F214F426E,
+0x6C9B3636D836ADEE, 0x51FFA6A6A2A65904, 0xB90CD2D26FD2DEBD, 0xF70EF5F5F3F5FB06,
+0xF2967979F979EF80, 0xDE306F6FA16F5FCE, 0x3F6D91917E91FCEF, 0xA4F852525552AA07,
+0xC04760609D6027FD, 0x6535BCBCCABC8976, 0x2B379B9B569BACCD, 0x018A8E8E028E048C,
+0x5BD2A3A3B6A37115, 0x186C0C0C300C603C, 0xF6847B7BF17BFF8A, 0x6A803535D435B5E1,
+0x3AF51D1D741DE869, 0xDDB3E0E0A7E05347, 0xB321D7D77BD7F6AC, 0x999CC2C22FC25EED,
+0x5C432E2EB82E6D96, 0x96294B4B314B627A, 0xE15DFEFEDFFEA321, 0xAED5575741578216,
+0x2ABD15155415A841, 0xEEE87777C1779FB6, 0x6E923737DC37A5EB, 0xD79EE5E5B3E57B56,
+0x23139F9F469F8CD9, 0xFD23F0F0E7F0D317, 0x94204A4A354A6A7F, 0xA944DADA4FDA9E95,
+0xB0A258587D58FA25, 0x8FCFC9C903C906CA, 0x527C2929A429558D, 0x145A0A0A280A5022,
+0x7F50B1B1FEB1E14F, 0x5DC9A0A0BAA0691A, 0xD6146B6BB16B7FDA, 0x17D985852E855CAB,
+0x673CBDBDCEBD8173, 0xBA8F5D5D695DD234, 0x2090101040108050, 0xF507F4F4F7F4F303,
+0x8BDDCBCB0BCB16C0, 0x7CD33E3EF83EEDC6, 0x0A2D050514052811, 0xCE78676781671FE6,
+0xD597E4E4B7E47353, 0x4E0227279C2725BB, 0x8273414119413258, 0x0BA78B8B168B2C9D,
+0x53F6A7A7A6A75101, 0xFAB27D7DE97DCF94, 0x374995956E95DCFB, 0xAD56D8D847D88E9F,
+0xEB70FBFBCBFB8B30, 0xC1CDEEEE9FEE2371, 0xF8BB7C7CED7CC791, 0xCC716666856617E3,
+0xA77BDDDD53DDA68E, 0x2EAF17175C17B84B, 0x8E45474701470246, 0x211A9E9E429E84DC,
+0x89D4CACA0FCA1EC5, 0x5A582D2DB42D7599, 0x632EBFBFC6BF9179, 0x0E3F07071C07381B,
+0x47ACADAD8EAD0123, 0xB4B05A5A755AEA2F, 0x1BEF838336836CB5, 0x66B63333CC3385FF,
+0xC65C636391633FF2, 0x041202020802100A, 0x4993AAAA92AA3938, 0xE2DE7171D971AFA8,
+0x8DC6C8C807C80ECF, 0x32D119196419C87D, 0x923B494939497270, 0xAF5FD9D943D9869A,
+0xF931F2F2EFF2C31D, 0xDBA8E3E3ABE34B48, 0xB6B95B5B715BE22A, 0x0DBC88881A883492,
+0x293E9A9A529AA4C8, 0x4C0B262698262DBE, 0x64BF3232C8328DFA, 0x7D59B0B0FAB0E94A,
+0xCFF2E9E983E91B6A, 0x1E770F0F3C0F7833, 0xB733D5D573D5E6A6, 0x1DF480803A8074BA,
+0x6127BEBEC2BE997C, 0x87EBCDCD13CD26DE, 0x68893434D034BDE4, 0x903248483D487A75,
+0xE354FFFFDBFFAB24, 0xF48D7A7AF57AF78F, 0x3D6490907A90F4EA, 0xBE9D5F5F615FC23E,
+0x403D202080201DA0, 0xD00F6868BD6867D5, 0x34CA1A1A681AD072, 0x41B7AEAE82AE192C,
+0x757DB4B4EAB4C95E, 0xA8CE54544D549A19, 0x3B7F93937693ECE5, 0x442F222288220DAA,
+0xC86364648D6407E9, 0xFF2AF1F1E3F1DB12, 0xE6CC7373D173BFA2, 0x248212124812905A,
+0x807A40401D403A5D, 0x1048080820084028, 0x9B95C3C32BC356E8, 0xC5DFECEC97EC337B,
+0xAB4DDBDB4BDB9690, 0x5FC0A1A1BEA1611F, 0x07918D8D0E8D1C83, 0x7AC83D3DF43DF5C9,
+0x335B97976697CCF1, 0x0000000000000000, 0x83F9CFCF1BCF36D4, 0x566E2B2BAC2B4587,
+0xECE17676C57697B3, 0x19E68282328264B0, 0xB128D6D67FD6FEA9, 0x36C31B1B6C1BD877,
+0x7774B5B5EEB5C15B, 0x43BEAFAF86AF1129, 0xD41D6A6AB56A77DF, 0xA0EA50505D50BA0D,
+0x8A5745450945124C, 0xFB38F3F3EBF3CB18, 0x60AD3030C0309DF0, 0xC3C4EFEF9BEF2B74,
+0x7EDA3F3FFC3FE5C3, 0xAAC755554955921C, 0x59DBA2A2B2A27910, 0xC9E9EAEA8FEA0365,
+0xCA6A656589650FEC, 0x6903BABAD2BAB968, 0x5E4A2F2FBC2F6593, 0x9D8EC0C027C04EE7,
+0xA160DEDE5FDEBE81, 0x38FC1C1C701CE06C, 0xE746FDFDD3FDBB2E, 0x9A1F4D4D294D5264,
+0x397692927292E4E0, 0xEAFA7575C9758FBC, 0x0C3606061806301E, 0x09AE8A8A128A2498,
+0x794BB2B2F2B2F940, 0xD185E6E6BFE66359, 0x1C7E0E0E380E7036, 0x3EE71F1F7C1FF863,
+0xC4556262956237F7, 0xB53AD4D477D4EEA3, 0x4D81A8A89AA82932, 0x315296966296C4F4,
+0xEF62F9F9C3F99B3A, 0x97A3C5C533C566F6, 0x4A102525942535B1, 0xB2AB59597959F220,
+0x15D084842A8454AE, 0xE4C57272D572B7A7, 0x72EC3939E439D5DD, 0x98164C4C2D4C5A61,
+0xBC945E5E655ECA3B, 0xF09F7878FD78E785, 0x70E53838E038DDD8, 0x05988C8C0A8C1486,
+0xBF17D1D163D1C6B2, 0x57E4A5A5AEA5410B, 0xD9A1E2E2AFE2434D, 0xC24E616199612FF8,
+0x7B42B3B3F6B3F145, 0x42342121842115A5, 0x25089C9C4A9C94D6, 0x3CEE1E1E781EF066,
+0x8661434311432252, 0x93B1C7C73BC776FC, 0xE54FFCFCD7FCB32B, 0x0824040410042014,
+0xA2E351515951B208, 0x2F2599995E99BCC7, 0xDA226D6DA96D4FC4, 0x1A650D0D340D6839,
+0xE979FAFACFFA8335, 0xA369DFDF5BDFB684, 0xFCA97E7EE57ED79B, 0x4819242490243DB4,
+0x76FE3B3BEC3BC5D7, 0x4B9AABAB96AB313D, 0x81F0CECE1FCE3ED1, 0x2299111144118855,
+0x03838F8F068F0C89, 0x9C044E4E254E4A6B, 0x7366B7B7E6B7D151, 0xCBE0EBEB8BEB0B60,
+0x78C13C3CF03CFDCC, 0x1FFD81813E817CBF, 0x354094946A94D4FE, 0xF31CF7F7FBF7EB0C,
+0x6F18B9B9DEB9A167, 0x268B13134C13985F, 0x58512C2CB02C7D9C, 0xBB05D3D36BD3D6B8,
+0xD38CE7E7BBE76B5C, 0xDC396E6EA56E57CB, 0x95AAC4C437C46EF3, 0x061B03030C03180F,
+0xACDC565645568A13, 0x885E44440D441A49, 0xFEA07F7FE17FDF9E, 0x4F88A9A99EA92137,
+0x54672A2AA82A4D82, 0x6B0ABBBBD6BBB16D, 0x9F87C1C123C146E2, 0xA6F153535153A202,
+0xA572DCDC57DCAE8B, 0x16530B0B2C0B5827, 0x27019D9D4E9D9CD3, 0xD82B6C6CAD6C47C1,
+0x62A43131C43195F5, 0xE8F37474CD7487B9, 0xF115F6F6FFF6E309, 0x8C4C464605460A43,
+0x45A5ACAC8AAC0926, 0x0FB589891E893C97, 0x28B414145014A044, 0xDFBAE1E1A3E15B42,
+0x2CA616165816B04E, 0x74F73A3AE83ACDD2, 0xD2066969B9696FD0, 0x124109092409482D,
+0xE0D77070DD70A7AD, 0x716FB6B6E2B6D954, 0xBD1ED0D067D0CEB7, 0xC7D6EDED93ED3B7E,
+0x85E2CCCC17CC2EDB, 0x8468424215422A57, 0x2D2C98985A98B4C2, 0x55EDA4A4AAA4490E,
+0x50752828A0285D88, 0xB8865C5C6D5CDA31, 0xED6BF8F8C7F8933F, 0x11C28686228644A4 };
+
+const uint64_t Whirlpool::C3[256] = {
+0x7830D818186018C0, 0xAF462623238C2305, 0xF991B8C6C63FC67E, 0x6FCDFBE8E887E813,
+0xA113CB878726874C, 0x626D11B8B8DAB8A9, 0x0502090101040108, 0x6E9E0D4F4F214F42,
+0xEE6C9B3636D836AD, 0x0451FFA6A6A2A659, 0xBDB90CD2D26FD2DE, 0x06F70EF5F5F3F5FB,
+0x80F2967979F979EF, 0xCEDE306F6FA16F5F, 0xEF3F6D91917E91FC, 0x07A4F852525552AA,
+0xFDC04760609D6027, 0x766535BCBCCABC89, 0xCD2B379B9B569BAC, 0x8C018A8E8E028E04,
+0x155BD2A3A3B6A371, 0x3C186C0C0C300C60, 0x8AF6847B7BF17BFF, 0xE16A803535D435B5,
+0x693AF51D1D741DE8, 0x47DDB3E0E0A7E053, 0xACB321D7D77BD7F6, 0xED999CC2C22FC25E,
+0x965C432E2EB82E6D, 0x7A96294B4B314B62, 0x21E15DFEFEDFFEA3, 0x16AED55757415782,
+0x412ABD15155415A8, 0xB6EEE87777C1779F, 0xEB6E923737DC37A5, 0x56D79EE5E5B3E57B,
+0xD923139F9F469F8C, 0x17FD23F0F0E7F0D3, 0x7F94204A4A354A6A, 0x95A944DADA4FDA9E,
+0x25B0A258587D58FA, 0xCA8FCFC9C903C906, 0x8D527C2929A42955, 0x22145A0A0A280A50,
+0x4F7F50B1B1FEB1E1, 0x1A5DC9A0A0BAA069, 0xDAD6146B6BB16B7F, 0xAB17D985852E855C,
+0x73673CBDBDCEBD81, 0x34BA8F5D5D695DD2, 0x5020901010401080, 0x03F507F4F4F7F4F3,
+0xC08BDDCBCB0BCB16, 0xC67CD33E3EF83EED, 0x110A2D0505140528, 0xE6CE78676781671F,
+0x53D597E4E4B7E473, 0xBB4E0227279C2725, 0x5882734141194132, 0x9D0BA78B8B168B2C,
+0x0153F6A7A7A6A751, 0x94FAB27D7DE97DCF, 0xFB374995956E95DC, 0x9FAD56D8D847D88E,
+0x30EB70FBFBCBFB8B, 0x71C1CDEEEE9FEE23, 0x91F8BB7C7CED7CC7, 0xE3CC716666856617,
+0x8EA77BDDDD53DDA6, 0x4B2EAF17175C17B8, 0x468E454747014702, 0xDC211A9E9E429E84,
+0xC589D4CACA0FCA1E, 0x995A582D2DB42D75, 0x79632EBFBFC6BF91, 0x1B0E3F07071C0738,
+0x2347ACADAD8EAD01, 0x2FB4B05A5A755AEA, 0xB51BEF838336836C, 0xFF66B63333CC3385,
+0xF2C65C636391633F, 0x0A04120202080210, 0x384993AAAA92AA39, 0xA8E2DE7171D971AF,
+0xCF8DC6C8C807C80E, 0x7D32D119196419C8, 0x70923B4949394972, 0x9AAF5FD9D943D986,
+0x1DF931F2F2EFF2C3, 0x48DBA8E3E3ABE34B, 0x2AB6B95B5B715BE2, 0x920DBC88881A8834,
+0xC8293E9A9A529AA4, 0xBE4C0B262698262D, 0xFA64BF3232C8328D, 0x4A7D59B0B0FAB0E9,
+0x6ACFF2E9E983E91B, 0x331E770F0F3C0F78, 0xA6B733D5D573D5E6, 0xBA1DF480803A8074,
+0x7C6127BEBEC2BE99, 0xDE87EBCDCD13CD26, 0xE468893434D034BD, 0x75903248483D487A,
+0x24E354FFFFDBFFAB, 0x8FF48D7A7AF57AF7, 0xEA3D6490907A90F4, 0x3EBE9D5F5F615FC2,
+0xA0403D202080201D, 0xD5D00F6868BD6867, 0x7234CA1A1A681AD0, 0x2C41B7AEAE82AE19,
+0x5E757DB4B4EAB4C9, 0x19A8CE54544D549A, 0xE53B7F93937693EC, 0xAA442F222288220D,
+0xE9C86364648D6407, 0x12FF2AF1F1E3F1DB, 0xA2E6CC7373D173BF, 0x5A24821212481290,
+0x5D807A40401D403A, 0x2810480808200840, 0xE89B95C3C32BC356, 0x7BC5DFECEC97EC33,
+0x90AB4DDBDB4BDB96, 0x1F5FC0A1A1BEA161, 0x8307918D8D0E8D1C, 0xC97AC83D3DF43DF5,
+0xF1335B97976697CC, 0x0000000000000000, 0xD483F9CFCF1BCF36, 0x87566E2B2BAC2B45,
+0xB3ECE17676C57697, 0xB019E68282328264, 0xA9B128D6D67FD6FE, 0x7736C31B1B6C1BD8,
+0x5B7774B5B5EEB5C1, 0x2943BEAFAF86AF11, 0xDFD41D6A6AB56A77, 0x0DA0EA50505D50BA,
+0x4C8A574545094512, 0x18FB38F3F3EBF3CB, 0xF060AD3030C0309D, 0x74C3C4EFEF9BEF2B,
+0xC37EDA3F3FFC3FE5, 0x1CAAC75555495592, 0x1059DBA2A2B2A279, 0x65C9E9EAEA8FEA03,
+0xECCA6A656589650F, 0x686903BABAD2BAB9, 0x935E4A2F2FBC2F65, 0xE79D8EC0C027C04E,
+0x81A160DEDE5FDEBE, 0x6C38FC1C1C701CE0, 0x2EE746FDFDD3FDBB, 0x649A1F4D4D294D52,
+0xE0397692927292E4, 0xBCEAFA7575C9758F, 0x1E0C360606180630, 0x9809AE8A8A128A24,
+0x40794BB2B2F2B2F9, 0x59D185E6E6BFE663, 0x361C7E0E0E380E70, 0x633EE71F1F7C1FF8,
+0xF7C4556262956237, 0xA3B53AD4D477D4EE, 0x324D81A8A89AA829, 0xF4315296966296C4,
+0x3AEF62F9F9C3F99B, 0xF697A3C5C533C566, 0xB14A102525942535, 0x20B2AB59597959F2,
+0xAE15D084842A8454, 0xA7E4C57272D572B7, 0xDD72EC3939E439D5, 0x6198164C4C2D4C5A,
+0x3BBC945E5E655ECA, 0x85F09F7878FD78E7, 0xD870E53838E038DD, 0x8605988C8C0A8C14,
+0xB2BF17D1D163D1C6, 0x0B57E4A5A5AEA541, 0x4DD9A1E2E2AFE243, 0xF8C24E616199612F,
+0x457B42B3B3F6B3F1, 0xA542342121842115, 0xD625089C9C4A9C94, 0x663CEE1E1E781EF0,
+0x5286614343114322, 0xFC93B1C7C73BC776, 0x2BE54FFCFCD7FCB3, 0x1408240404100420,
+0x08A2E351515951B2, 0xC72F2599995E99BC, 0xC4DA226D6DA96D4F, 0x391A650D0D340D68,
+0x35E979FAFACFFA83, 0x84A369DFDF5BDFB6, 0x9BFCA97E7EE57ED7, 0xB44819242490243D,
+0xD776FE3B3BEC3BC5, 0x3D4B9AABAB96AB31, 0xD181F0CECE1FCE3E, 0x5522991111441188,
+0x8903838F8F068F0C, 0x6B9C044E4E254E4A, 0x517366B7B7E6B7D1, 0x60CBE0EBEB8BEB0B,
+0xCC78C13C3CF03CFD, 0xBF1FFD81813E817C, 0xFE354094946A94D4, 0x0CF31CF7F7FBF7EB,
+0x676F18B9B9DEB9A1, 0x5F268B13134C1398, 0x9C58512C2CB02C7D, 0xB8BB05D3D36BD3D6,
+0x5CD38CE7E7BBE76B, 0xCBDC396E6EA56E57, 0xF395AAC4C437C46E, 0x0F061B03030C0318,
+0x13ACDC565645568A, 0x49885E44440D441A, 0x9EFEA07F7FE17FDF, 0x374F88A9A99EA921,
+0x8254672A2AA82A4D, 0x6D6B0ABBBBD6BBB1, 0xE29F87C1C123C146, 0x02A6F153535153A2,
+0x8BA572DCDC57DCAE, 0x2716530B0B2C0B58, 0xD327019D9D4E9D9C, 0xC1D82B6C6CAD6C47,
+0xF562A43131C43195, 0xB9E8F37474CD7487, 0x09F115F6F6FFF6E3, 0x438C4C464605460A,
+0x2645A5ACAC8AAC09, 0x970FB589891E893C, 0x4428B414145014A0, 0x42DFBAE1E1A3E15B,
+0x4E2CA616165816B0, 0xD274F73A3AE83ACD, 0xD0D2066969B9696F, 0x2D12410909240948,
+0xADE0D77070DD70A7, 0x54716FB6B6E2B6D9, 0xB7BD1ED0D067D0CE, 0x7EC7D6EDED93ED3B,
+0xDB85E2CCCC17CC2E, 0x578468424215422A, 0xC22D2C98985A98B4, 0x0E55EDA4A4AAA449,
+0x8850752828A0285D, 0x31B8865C5C6D5CDA, 0x3FED6BF8F8C7F893, 0xA411C28686228644 };
+
+const uint64_t Whirlpool::C4[256] = {
+0xC07830D818186018, 0x05AF462623238C23, 0x7EF991B8C6C63FC6, 0x136FCDFBE8E887E8,
+0x4CA113CB87872687, 0xA9626D11B8B8DAB8, 0x0805020901010401, 0x426E9E0D4F4F214F,
+0xADEE6C9B3636D836, 0x590451FFA6A6A2A6, 0xDEBDB90CD2D26FD2, 0xFB06F70EF5F5F3F5,
+0xEF80F2967979F979, 0x5FCEDE306F6FA16F, 0xFCEF3F6D91917E91, 0xAA07A4F852525552,
+0x27FDC04760609D60, 0x89766535BCBCCABC, 0xACCD2B379B9B569B, 0x048C018A8E8E028E,
+0x71155BD2A3A3B6A3, 0x603C186C0C0C300C, 0xFF8AF6847B7BF17B, 0xB5E16A803535D435,
+0xE8693AF51D1D741D, 0x5347DDB3E0E0A7E0, 0xF6ACB321D7D77BD7, 0x5EED999CC2C22FC2,
+0x6D965C432E2EB82E, 0x627A96294B4B314B, 0xA321E15DFEFEDFFE, 0x8216AED557574157,
+0xA8412ABD15155415, 0x9FB6EEE87777C177, 0xA5EB6E923737DC37, 0x7B56D79EE5E5B3E5,
+0x8CD923139F9F469F, 0xD317FD23F0F0E7F0, 0x6A7F94204A4A354A, 0x9E95A944DADA4FDA,
+0xFA25B0A258587D58, 0x06CA8FCFC9C903C9, 0x558D527C2929A429, 0x5022145A0A0A280A,
+0xE14F7F50B1B1FEB1, 0x691A5DC9A0A0BAA0, 0x7FDAD6146B6BB16B, 0x5CAB17D985852E85,
+0x8173673CBDBDCEBD, 0xD234BA8F5D5D695D, 0x8050209010104010, 0xF303F507F4F4F7F4,
+0x16C08BDDCBCB0BCB, 0xEDC67CD33E3EF83E, 0x28110A2D05051405, 0x1FE6CE7867678167,
+0x7353D597E4E4B7E4, 0x25BB4E0227279C27, 0x3258827341411941, 0x2C9D0BA78B8B168B,
+0x510153F6A7A7A6A7, 0xCF94FAB27D7DE97D, 0xDCFB374995956E95, 0x8E9FAD56D8D847D8,
+0x8B30EB70FBFBCBFB, 0x2371C1CDEEEE9FEE, 0xC791F8BB7C7CED7C, 0x17E3CC7166668566,
+0xA68EA77BDDDD53DD, 0xB84B2EAF17175C17, 0x02468E4547470147, 0x84DC211A9E9E429E,
+0x1EC589D4CACA0FCA, 0x75995A582D2DB42D, 0x9179632EBFBFC6BF, 0x381B0E3F07071C07,
+0x012347ACADAD8EAD, 0xEA2FB4B05A5A755A, 0x6CB51BEF83833683, 0x85FF66B63333CC33,
+0x3FF2C65C63639163, 0x100A041202020802, 0x39384993AAAA92AA, 0xAFA8E2DE7171D971,
+0x0ECF8DC6C8C807C8, 0xC87D32D119196419, 0x7270923B49493949, 0x869AAF5FD9D943D9,
+0xC31DF931F2F2EFF2, 0x4B48DBA8E3E3ABE3, 0xE22AB6B95B5B715B, 0x34920DBC88881A88,
+0xA4C8293E9A9A529A, 0x2DBE4C0B26269826, 0x8DFA64BF3232C832, 0xE94A7D59B0B0FAB0,
+0x1B6ACFF2E9E983E9, 0x78331E770F0F3C0F, 0xE6A6B733D5D573D5, 0x74BA1DF480803A80,
+0x997C6127BEBEC2BE, 0x26DE87EBCDCD13CD, 0xBDE468893434D034, 0x7A75903248483D48,
+0xAB24E354FFFFDBFF, 0xF78FF48D7A7AF57A, 0xF4EA3D6490907A90, 0xC23EBE9D5F5F615F,
+0x1DA0403D20208020, 0x67D5D00F6868BD68, 0xD07234CA1A1A681A, 0x192C41B7AEAE82AE,
+0xC95E757DB4B4EAB4, 0x9A19A8CE54544D54, 0xECE53B7F93937693, 0x0DAA442F22228822,
+0x07E9C86364648D64, 0xDB12FF2AF1F1E3F1, 0xBFA2E6CC7373D173, 0x905A248212124812,
+0x3A5D807A40401D40, 0x4028104808082008, 0x56E89B95C3C32BC3, 0x337BC5DFECEC97EC,
+0x9690AB4DDBDB4BDB, 0x611F5FC0A1A1BEA1, 0x1C8307918D8D0E8D, 0xF5C97AC83D3DF43D,
+0xCCF1335B97976697, 0x0000000000000000, 0x36D483F9CFCF1BCF, 0x4587566E2B2BAC2B,
+0x97B3ECE17676C576, 0x64B019E682823282, 0xFEA9B128D6D67FD6, 0xD87736C31B1B6C1B,
+0xC15B7774B5B5EEB5, 0x112943BEAFAF86AF, 0x77DFD41D6A6AB56A, 0xBA0DA0EA50505D50,
+0x124C8A5745450945, 0xCB18FB38F3F3EBF3, 0x9DF060AD3030C030, 0x2B74C3C4EFEF9BEF,
+0xE5C37EDA3F3FFC3F, 0x921CAAC755554955, 0x791059DBA2A2B2A2, 0x0365C9E9EAEA8FEA,
+0x0FECCA6A65658965, 0xB9686903BABAD2BA, 0x65935E4A2F2FBC2F, 0x4EE79D8EC0C027C0,
+0xBE81A160DEDE5FDE, 0xE06C38FC1C1C701C, 0xBB2EE746FDFDD3FD, 0x52649A1F4D4D294D,
+0xE4E0397692927292, 0x8FBCEAFA7575C975, 0x301E0C3606061806, 0x249809AE8A8A128A,
+0xF940794BB2B2F2B2, 0x6359D185E6E6BFE6, 0x70361C7E0E0E380E, 0xF8633EE71F1F7C1F,
+0x37F7C45562629562, 0xEEA3B53AD4D477D4, 0x29324D81A8A89AA8, 0xC4F4315296966296,
+0x9B3AEF62F9F9C3F9, 0x66F697A3C5C533C5, 0x35B14A1025259425, 0xF220B2AB59597959,
+0x54AE15D084842A84, 0xB7A7E4C57272D572, 0xD5DD72EC3939E439, 0x5A6198164C4C2D4C,
+0xCA3BBC945E5E655E, 0xE785F09F7878FD78, 0xDDD870E53838E038, 0x148605988C8C0A8C,
+0xC6B2BF17D1D163D1, 0x410B57E4A5A5AEA5, 0x434DD9A1E2E2AFE2, 0x2FF8C24E61619961,
+0xF1457B42B3B3F6B3, 0x15A5423421218421, 0x94D625089C9C4A9C, 0xF0663CEE1E1E781E,
+0x2252866143431143, 0x76FC93B1C7C73BC7, 0xB32BE54FFCFCD7FC, 0x2014082404041004,
+0xB208A2E351515951, 0xBCC72F2599995E99, 0x4FC4DA226D6DA96D, 0x68391A650D0D340D,
+0x8335E979FAFACFFA, 0xB684A369DFDF5BDF, 0xD79BFCA97E7EE57E, 0x3DB4481924249024,
+0xC5D776FE3B3BEC3B, 0x313D4B9AABAB96AB, 0x3ED181F0CECE1FCE, 0x8855229911114411,
+0x0C8903838F8F068F, 0x4A6B9C044E4E254E, 0xD1517366B7B7E6B7, 0x0B60CBE0EBEB8BEB,
+0xFDCC78C13C3CF03C, 0x7CBF1FFD81813E81, 0xD4FE354094946A94, 0xEB0CF31CF7F7FBF7,
+0xA1676F18B9B9DEB9, 0x985F268B13134C13, 0x7D9C58512C2CB02C, 0xD6B8BB05D3D36BD3,
+0x6B5CD38CE7E7BBE7, 0x57CBDC396E6EA56E, 0x6EF395AAC4C437C4, 0x180F061B03030C03,
+0x8A13ACDC56564556, 0x1A49885E44440D44, 0xDF9EFEA07F7FE17F, 0x21374F88A9A99EA9,
+0x4D8254672A2AA82A, 0xB16D6B0ABBBBD6BB, 0x46E29F87C1C123C1, 0xA202A6F153535153,
+0xAE8BA572DCDC57DC, 0x582716530B0B2C0B, 0x9CD327019D9D4E9D, 0x47C1D82B6C6CAD6C,
+0x95F562A43131C431, 0x87B9E8F37474CD74, 0xE309F115F6F6FFF6, 0x0A438C4C46460546,
+0x092645A5ACAC8AAC, 0x3C970FB589891E89, 0xA04428B414145014, 0x5B42DFBAE1E1A3E1,
+0xB04E2CA616165816, 0xCDD274F73A3AE83A, 0x6FD0D2066969B969, 0x482D124109092409,
+0xA7ADE0D77070DD70, 0xD954716FB6B6E2B6, 0xCEB7BD1ED0D067D0, 0x3B7EC7D6EDED93ED,
+0x2EDB85E2CCCC17CC, 0x2A57846842421542, 0xB4C22D2C98985A98, 0x490E55EDA4A4AAA4,
+0x5D8850752828A028, 0xDA31B8865C5C6D5C, 0x933FED6BF8F8C7F8, 0x44A411C286862286 };
+
+const uint64_t Whirlpool::C5[256] = {
+0x18C07830D8181860, 0x2305AF462623238C, 0xC67EF991B8C6C63F, 0xE8136FCDFBE8E887,
+0x874CA113CB878726, 0xB8A9626D11B8B8DA, 0x0108050209010104, 0x4F426E9E0D4F4F21,
+0x36ADEE6C9B3636D8, 0xA6590451FFA6A6A2, 0xD2DEBDB90CD2D26F, 0xF5FB06F70EF5F5F3,
+0x79EF80F2967979F9, 0x6F5FCEDE306F6FA1, 0x91FCEF3F6D91917E, 0x52AA07A4F8525255,
+0x6027FDC04760609D, 0xBC89766535BCBCCA, 0x9BACCD2B379B9B56, 0x8E048C018A8E8E02,
+0xA371155BD2A3A3B6, 0x0C603C186C0C0C30, 0x7BFF8AF6847B7BF1, 0x35B5E16A803535D4,
+0x1DE8693AF51D1D74, 0xE05347DDB3E0E0A7, 0xD7F6ACB321D7D77B, 0xC25EED999CC2C22F,
+0x2E6D965C432E2EB8, 0x4B627A96294B4B31, 0xFEA321E15DFEFEDF, 0x578216AED5575741,
+0x15A8412ABD151554, 0x779FB6EEE87777C1, 0x37A5EB6E923737DC, 0xE57B56D79EE5E5B3,
+0x9F8CD923139F9F46, 0xF0D317FD23F0F0E7, 0x4A6A7F94204A4A35, 0xDA9E95A944DADA4F,
+0x58FA25B0A258587D, 0xC906CA8FCFC9C903, 0x29558D527C2929A4, 0x0A5022145A0A0A28,
+0xB1E14F7F50B1B1FE, 0xA0691A5DC9A0A0BA, 0x6B7FDAD6146B6BB1, 0x855CAB17D985852E,
+0xBD8173673CBDBDCE, 0x5DD234BA8F5D5D69, 0x1080502090101040, 0xF4F303F507F4F4F7,
+0xCB16C08BDDCBCB0B, 0x3EEDC67CD33E3EF8, 0x0528110A2D050514, 0x671FE6CE78676781,
+0xE47353D597E4E4B7, 0x2725BB4E0227279C, 0x4132588273414119, 0x8B2C9D0BA78B8B16,
+0xA7510153F6A7A7A6, 0x7DCF94FAB27D7DE9, 0x95DCFB374995956E, 0xD88E9FAD56D8D847,
+0xFB8B30EB70FBFBCB, 0xEE2371C1CDEEEE9F, 0x7CC791F8BB7C7CED, 0x6617E3CC71666685,
+0xDDA68EA77BDDDD53, 0x17B84B2EAF17175C, 0x4702468E45474701, 0x9E84DC211A9E9E42,
+0xCA1EC589D4CACA0F, 0x2D75995A582D2DB4, 0xBF9179632EBFBFC6, 0x07381B0E3F07071C,
+0xAD012347ACADAD8E, 0x5AEA2FB4B05A5A75, 0x836CB51BEF838336, 0x3385FF66B63333CC,
+0x633FF2C65C636391, 0x02100A0412020208, 0xAA39384993AAAA92, 0x71AFA8E2DE7171D9,
+0xC80ECF8DC6C8C807, 0x19C87D32D1191964, 0x497270923B494939, 0xD9869AAF5FD9D943,
+0xF2C31DF931F2F2EF, 0xE34B48DBA8E3E3AB, 0x5BE22AB6B95B5B71, 0x8834920DBC88881A,
+0x9AA4C8293E9A9A52, 0x262DBE4C0B262698, 0x328DFA64BF3232C8, 0xB0E94A7D59B0B0FA,
+0xE91B6ACFF2E9E983, 0x0F78331E770F0F3C, 0xD5E6A6B733D5D573, 0x8074BA1DF480803A,
+0xBE997C6127BEBEC2, 0xCD26DE87EBCDCD13, 0x34BDE468893434D0, 0x487A75903248483D,
+0xFFAB24E354FFFFDB, 0x7AF78FF48D7A7AF5, 0x90F4EA3D6490907A, 0x5FC23EBE9D5F5F61,
+0x201DA0403D202080, 0x6867D5D00F6868BD, 0x1AD07234CA1A1A68, 0xAE192C41B7AEAE82,
+0xB4C95E757DB4B4EA, 0x549A19A8CE54544D, 0x93ECE53B7F939376, 0x220DAA442F222288,
+0x6407E9C86364648D, 0xF1DB12FF2AF1F1E3, 0x73BFA2E6CC7373D1, 0x12905A2482121248,
+0x403A5D807A40401D, 0x0840281048080820, 0xC356E89B95C3C32B, 0xEC337BC5DFECEC97,
+0xDB9690AB4DDBDB4B, 0xA1611F5FC0A1A1BE, 0x8D1C8307918D8D0E, 0x3DF5C97AC83D3DF4,
+0x97CCF1335B979766, 0x0000000000000000, 0xCF36D483F9CFCF1B, 0x2B4587566E2B2BAC,
+0x7697B3ECE17676C5, 0x8264B019E6828232, 0xD6FEA9B128D6D67F, 0x1BD87736C31B1B6C,
+0xB5C15B7774B5B5EE, 0xAF112943BEAFAF86, 0x6A77DFD41D6A6AB5, 0x50BA0DA0EA50505D,
+0x45124C8A57454509, 0xF3CB18FB38F3F3EB, 0x309DF060AD3030C0, 0xEF2B74C3C4EFEF9B,
+0x3FE5C37EDA3F3FFC, 0x55921CAAC7555549, 0xA2791059DBA2A2B2, 0xEA0365C9E9EAEA8F,
+0x650FECCA6A656589, 0xBAB9686903BABAD2, 0x2F65935E4A2F2FBC, 0xC04EE79D8EC0C027,
+0xDEBE81A160DEDE5F, 0x1CE06C38FC1C1C70, 0xFDBB2EE746FDFDD3, 0x4D52649A1F4D4D29,
+0x92E4E03976929272, 0x758FBCEAFA7575C9, 0x06301E0C36060618, 0x8A249809AE8A8A12,
+0xB2F940794BB2B2F2, 0xE66359D185E6E6BF, 0x0E70361C7E0E0E38, 0x1FF8633EE71F1F7C,
+0x6237F7C455626295, 0xD4EEA3B53AD4D477, 0xA829324D81A8A89A, 0x96C4F43152969662,
+0xF99B3AEF62F9F9C3, 0xC566F697A3C5C533, 0x2535B14A10252594, 0x59F220B2AB595979,
+0x8454AE15D084842A, 0x72B7A7E4C57272D5, 0x39D5DD72EC3939E4, 0x4C5A6198164C4C2D,
+0x5ECA3BBC945E5E65, 0x78E785F09F7878FD, 0x38DDD870E53838E0, 0x8C148605988C8C0A,
+0xD1C6B2BF17D1D163, 0xA5410B57E4A5A5AE, 0xE2434DD9A1E2E2AF, 0x612FF8C24E616199,
+0xB3F1457B42B3B3F6, 0x2115A54234212184, 0x9C94D625089C9C4A, 0x1EF0663CEE1E1E78,
+0x4322528661434311, 0xC776FC93B1C7C73B, 0xFCB32BE54FFCFCD7, 0x0420140824040410,
+0x51B208A2E3515159, 0x99BCC72F2599995E, 0x6D4FC4DA226D6DA9, 0x0D68391A650D0D34,
+0xFA8335E979FAFACF, 0xDFB684A369DFDF5B, 0x7ED79BFCA97E7EE5, 0x243DB44819242490,
+0x3BC5D776FE3B3BEC, 0xAB313D4B9AABAB96, 0xCE3ED181F0CECE1F, 0x1188552299111144,
+0x8F0C8903838F8F06, 0x4E4A6B9C044E4E25, 0xB7D1517366B7B7E6, 0xEB0B60CBE0EBEB8B,
+0x3CFDCC78C13C3CF0, 0x817CBF1FFD81813E, 0x94D4FE354094946A, 0xF7EB0CF31CF7F7FB,
+0xB9A1676F18B9B9DE, 0x13985F268B13134C, 0x2C7D9C58512C2CB0, 0xD3D6B8BB05D3D36B,
+0xE76B5CD38CE7E7BB, 0x6E57CBDC396E6EA5, 0xC46EF395AAC4C437, 0x03180F061B03030C,
+0x568A13ACDC565645, 0x441A49885E44440D, 0x7FDF9EFEA07F7FE1, 0xA921374F88A9A99E,
+0x2A4D8254672A2AA8, 0xBBB16D6B0ABBBBD6, 0xC146E29F87C1C123, 0x53A202A6F1535351,
+0xDCAE8BA572DCDC57, 0x0B582716530B0B2C, 0x9D9CD327019D9D4E, 0x6C47C1D82B6C6CAD,
+0x3195F562A43131C4, 0x7487B9E8F37474CD, 0xF6E309F115F6F6FF, 0x460A438C4C464605,
+0xAC092645A5ACAC8A, 0x893C970FB589891E, 0x14A04428B4141450, 0xE15B42DFBAE1E1A3,
+0x16B04E2CA6161658, 0x3ACDD274F73A3AE8, 0x696FD0D2066969B9, 0x09482D1241090924,
+0x70A7ADE0D77070DD, 0xB6D954716FB6B6E2, 0xD0CEB7BD1ED0D067, 0xED3B7EC7D6EDED93,
+0xCC2EDB85E2CCCC17, 0x422A578468424215, 0x98B4C22D2C98985A, 0xA4490E55EDA4A4AA,
+0x285D8850752828A0, 0x5CDA31B8865C5C6D, 0xF8933FED6BF8F8C7, 0x8644A411C2868622 };
+
+const uint64_t Whirlpool::C6[256] = {
+0x6018C07830D81818, 0x8C2305AF46262323, 0x3FC67EF991B8C6C6, 0x87E8136FCDFBE8E8,
+0x26874CA113CB8787, 0xDAB8A9626D11B8B8, 0x0401080502090101, 0x214F426E9E0D4F4F,
+0xD836ADEE6C9B3636, 0xA2A6590451FFA6A6, 0x6FD2DEBDB90CD2D2, 0xF3F5FB06F70EF5F5,
+0xF979EF80F2967979, 0xA16F5FCEDE306F6F, 0x7E91FCEF3F6D9191, 0x5552AA07A4F85252,
+0x9D6027FDC0476060, 0xCABC89766535BCBC, 0x569BACCD2B379B9B, 0x028E048C018A8E8E,
+0xB6A371155BD2A3A3, 0x300C603C186C0C0C, 0xF17BFF8AF6847B7B, 0xD435B5E16A803535,
+0x741DE8693AF51D1D, 0xA7E05347DDB3E0E0, 0x7BD7F6ACB321D7D7, 0x2FC25EED999CC2C2,
+0xB82E6D965C432E2E, 0x314B627A96294B4B, 0xDFFEA321E15DFEFE, 0x41578216AED55757,
+0x5415A8412ABD1515, 0xC1779FB6EEE87777, 0xDC37A5EB6E923737, 0xB3E57B56D79EE5E5,
+0x469F8CD923139F9F, 0xE7F0D317FD23F0F0, 0x354A6A7F94204A4A, 0x4FDA9E95A944DADA,
+0x7D58FA25B0A25858, 0x03C906CA8FCFC9C9, 0xA429558D527C2929, 0x280A5022145A0A0A,
+0xFEB1E14F7F50B1B1, 0xBAA0691A5DC9A0A0, 0xB16B7FDAD6146B6B, 0x2E855CAB17D98585,
+0xCEBD8173673CBDBD, 0x695DD234BA8F5D5D, 0x4010805020901010, 0xF7F4F303F507F4F4,
+0x0BCB16C08BDDCBCB, 0xF83EEDC67CD33E3E, 0x140528110A2D0505, 0x81671FE6CE786767,
+0xB7E47353D597E4E4, 0x9C2725BB4E022727, 0x1941325882734141, 0x168B2C9D0BA78B8B,
+0xA6A7510153F6A7A7, 0xE97DCF94FAB27D7D, 0x6E95DCFB37499595, 0x47D88E9FAD56D8D8,
+0xCBFB8B30EB70FBFB, 0x9FEE2371C1CDEEEE, 0xED7CC791F8BB7C7C, 0x856617E3CC716666,
+0x53DDA68EA77BDDDD, 0x5C17B84B2EAF1717, 0x014702468E454747, 0x429E84DC211A9E9E,
+0x0FCA1EC589D4CACA, 0xB42D75995A582D2D, 0xC6BF9179632EBFBF, 0x1C07381B0E3F0707,
+0x8EAD012347ACADAD, 0x755AEA2FB4B05A5A, 0x36836CB51BEF8383, 0xCC3385FF66B63333,
+0x91633FF2C65C6363, 0x0802100A04120202, 0x92AA39384993AAAA, 0xD971AFA8E2DE7171,
+0x07C80ECF8DC6C8C8, 0x6419C87D32D11919, 0x39497270923B4949, 0x43D9869AAF5FD9D9,
+0xEFF2C31DF931F2F2, 0xABE34B48DBA8E3E3, 0x715BE22AB6B95B5B, 0x1A8834920DBC8888,
+0x529AA4C8293E9A9A, 0x98262DBE4C0B2626, 0xC8328DFA64BF3232, 0xFAB0E94A7D59B0B0,
+0x83E91B6ACFF2E9E9, 0x3C0F78331E770F0F, 0x73D5E6A6B733D5D5, 0x3A8074BA1DF48080,
+0xC2BE997C6127BEBE, 0x13CD26DE87EBCDCD, 0xD034BDE468893434, 0x3D487A7590324848,
+0xDBFFAB24E354FFFF, 0xF57AF78FF48D7A7A, 0x7A90F4EA3D649090, 0x615FC23EBE9D5F5F,
+0x80201DA0403D2020, 0xBD6867D5D00F6868, 0x681AD07234CA1A1A, 0x82AE192C41B7AEAE,
+0xEAB4C95E757DB4B4, 0x4D549A19A8CE5454, 0x7693ECE53B7F9393, 0x88220DAA442F2222,
+0x8D6407E9C8636464, 0xE3F1DB12FF2AF1F1, 0xD173BFA2E6CC7373, 0x4812905A24821212,
+0x1D403A5D807A4040, 0x2008402810480808, 0x2BC356E89B95C3C3, 0x97EC337BC5DFECEC,
+0x4BDB9690AB4DDBDB, 0xBEA1611F5FC0A1A1, 0x0E8D1C8307918D8D, 0xF43DF5C97AC83D3D,
+0x6697CCF1335B9797, 0x0000000000000000, 0x1BCF36D483F9CFCF, 0xAC2B4587566E2B2B,
+0xC57697B3ECE17676, 0x328264B019E68282, 0x7FD6FEA9B128D6D6, 0x6C1BD87736C31B1B,
+0xEEB5C15B7774B5B5, 0x86AF112943BEAFAF, 0xB56A77DFD41D6A6A, 0x5D50BA0DA0EA5050,
+0x0945124C8A574545, 0xEBF3CB18FB38F3F3, 0xC0309DF060AD3030, 0x9BEF2B74C3C4EFEF,
+0xFC3FE5C37EDA3F3F, 0x4955921CAAC75555, 0xB2A2791059DBA2A2, 0x8FEA0365C9E9EAEA,
+0x89650FECCA6A6565, 0xD2BAB9686903BABA, 0xBC2F65935E4A2F2F, 0x27C04EE79D8EC0C0,
+0x5FDEBE81A160DEDE, 0x701CE06C38FC1C1C, 0xD3FDBB2EE746FDFD, 0x294D52649A1F4D4D,
+0x7292E4E039769292, 0xC9758FBCEAFA7575, 0x1806301E0C360606, 0x128A249809AE8A8A,
+0xF2B2F940794BB2B2, 0xBFE66359D185E6E6, 0x380E70361C7E0E0E, 0x7C1FF8633EE71F1F,
+0x956237F7C4556262, 0x77D4EEA3B53AD4D4, 0x9AA829324D81A8A8, 0x6296C4F431529696,
+0xC3F99B3AEF62F9F9, 0x33C566F697A3C5C5, 0x942535B14A102525, 0x7959F220B2AB5959,
+0x2A8454AE15D08484, 0xD572B7A7E4C57272, 0xE439D5DD72EC3939, 0x2D4C5A6198164C4C,
+0x655ECA3BBC945E5E, 0xFD78E785F09F7878, 0xE038DDD870E53838, 0x0A8C148605988C8C,
+0x63D1C6B2BF17D1D1, 0xAEA5410B57E4A5A5, 0xAFE2434DD9A1E2E2, 0x99612FF8C24E6161,
+0xF6B3F1457B42B3B3, 0x842115A542342121, 0x4A9C94D625089C9C, 0x781EF0663CEE1E1E,
+0x1143225286614343, 0x3BC776FC93B1C7C7, 0xD7FCB32BE54FFCFC, 0x1004201408240404,
+0x5951B208A2E35151, 0x5E99BCC72F259999, 0xA96D4FC4DA226D6D, 0x340D68391A650D0D,
+0xCFFA8335E979FAFA, 0x5BDFB684A369DFDF, 0xE57ED79BFCA97E7E, 0x90243DB448192424,
+0xEC3BC5D776FE3B3B, 0x96AB313D4B9AABAB, 0x1FCE3ED181F0CECE, 0x4411885522991111,
+0x068F0C8903838F8F, 0x254E4A6B9C044E4E, 0xE6B7D1517366B7B7, 0x8BEB0B60CBE0EBEB,
+0xF03CFDCC78C13C3C, 0x3E817CBF1FFD8181, 0x6A94D4FE35409494, 0xFBF7EB0CF31CF7F7,
+0xDEB9A1676F18B9B9, 0x4C13985F268B1313, 0xB02C7D9C58512C2C, 0x6BD3D6B8BB05D3D3,
+0xBBE76B5CD38CE7E7, 0xA56E57CBDC396E6E, 0x37C46EF395AAC4C4, 0x0C03180F061B0303,
+0x45568A13ACDC5656, 0x0D441A49885E4444, 0xE17FDF9EFEA07F7F, 0x9EA921374F88A9A9,
+0xA82A4D8254672A2A, 0xD6BBB16D6B0ABBBB, 0x23C146E29F87C1C1, 0x5153A202A6F15353,
+0x57DCAE8BA572DCDC, 0x2C0B582716530B0B, 0x4E9D9CD327019D9D, 0xAD6C47C1D82B6C6C,
+0xC43195F562A43131, 0xCD7487B9E8F37474, 0xFFF6E309F115F6F6, 0x05460A438C4C4646,
+0x8AAC092645A5ACAC, 0x1E893C970FB58989, 0x5014A04428B41414, 0xA3E15B42DFBAE1E1,
+0x5816B04E2CA61616, 0xE83ACDD274F73A3A, 0xB9696FD0D2066969, 0x2409482D12410909,
+0xDD70A7ADE0D77070, 0xE2B6D954716FB6B6, 0x67D0CEB7BD1ED0D0, 0x93ED3B7EC7D6EDED,
+0x17CC2EDB85E2CCCC, 0x15422A5784684242, 0x5A98B4C22D2C9898, 0xAAA4490E55EDA4A4,
+0xA0285D8850752828, 0x6D5CDA31B8865C5C, 0xC7F8933FED6BF8F8, 0x228644A411C28686 };
+
+const uint64_t Whirlpool::C7[256] = {
+0x186018C07830D818, 0x238C2305AF462623, 0xC63FC67EF991B8C6, 0xE887E8136FCDFBE8,
+0x8726874CA113CB87, 0xB8DAB8A9626D11B8, 0x0104010805020901, 0x4F214F426E9E0D4F,
+0x36D836ADEE6C9B36, 0xA6A2A6590451FFA6, 0xD26FD2DEBDB90CD2, 0xF5F3F5FB06F70EF5,
+0x79F979EF80F29679, 0x6FA16F5FCEDE306F, 0x917E91FCEF3F6D91, 0x525552AA07A4F852,
+0x609D6027FDC04760, 0xBCCABC89766535BC, 0x9B569BACCD2B379B, 0x8E028E048C018A8E,
+0xA3B6A371155BD2A3, 0x0C300C603C186C0C, 0x7BF17BFF8AF6847B, 0x35D435B5E16A8035,
+0x1D741DE8693AF51D, 0xE0A7E05347DDB3E0, 0xD77BD7F6ACB321D7, 0xC22FC25EED999CC2,
+0x2EB82E6D965C432E, 0x4B314B627A96294B, 0xFEDFFEA321E15DFE, 0x5741578216AED557,
+0x155415A8412ABD15, 0x77C1779FB6EEE877, 0x37DC37A5EB6E9237, 0xE5B3E57B56D79EE5,
+0x9F469F8CD923139F, 0xF0E7F0D317FD23F0, 0x4A354A6A7F94204A, 0xDA4FDA9E95A944DA,
+0x587D58FA25B0A258, 0xC903C906CA8FCFC9, 0x29A429558D527C29, 0x0A280A5022145A0A,
+0xB1FEB1E14F7F50B1, 0xA0BAA0691A5DC9A0, 0x6BB16B7FDAD6146B, 0x852E855CAB17D985,
+0xBDCEBD8173673CBD, 0x5D695DD234BA8F5D, 0x1040108050209010, 0xF4F7F4F303F507F4,
+0xCB0BCB16C08BDDCB, 0x3EF83EEDC67CD33E, 0x05140528110A2D05, 0x6781671FE6CE7867,
+0xE4B7E47353D597E4, 0x279C2725BB4E0227, 0x4119413258827341, 0x8B168B2C9D0BA78B,
+0xA7A6A7510153F6A7, 0x7DE97DCF94FAB27D, 0x956E95DCFB374995, 0xD847D88E9FAD56D8,
+0xFBCBFB8B30EB70FB, 0xEE9FEE2371C1CDEE, 0x7CED7CC791F8BB7C, 0x66856617E3CC7166,
+0xDD53DDA68EA77BDD, 0x175C17B84B2EAF17, 0x47014702468E4547, 0x9E429E84DC211A9E,
+0xCA0FCA1EC589D4CA, 0x2DB42D75995A582D, 0xBFC6BF9179632EBF, 0x071C07381B0E3F07,
+0xAD8EAD012347ACAD, 0x5A755AEA2FB4B05A, 0x8336836CB51BEF83, 0x33CC3385FF66B633,
+0x6391633FF2C65C63, 0x020802100A041202, 0xAA92AA39384993AA, 0x71D971AFA8E2DE71,
+0xC807C80ECF8DC6C8, 0x196419C87D32D119, 0x4939497270923B49, 0xD943D9869AAF5FD9,
+0xF2EFF2C31DF931F2, 0xE3ABE34B48DBA8E3, 0x5B715BE22AB6B95B, 0x881A8834920DBC88,
+0x9A529AA4C8293E9A, 0x2698262DBE4C0B26, 0x32C8328DFA64BF32, 0xB0FAB0E94A7D59B0,
+0xE983E91B6ACFF2E9, 0x0F3C0F78331E770F, 0xD573D5E6A6B733D5, 0x803A8074BA1DF480,
+0xBEC2BE997C6127BE, 0xCD13CD26DE87EBCD, 0x34D034BDE4688934, 0x483D487A75903248,
+0xFFDBFFAB24E354FF, 0x7AF57AF78FF48D7A, 0x907A90F4EA3D6490, 0x5F615FC23EBE9D5F,
+0x2080201DA0403D20, 0x68BD6867D5D00F68, 0x1A681AD07234CA1A, 0xAE82AE192C41B7AE,
+0xB4EAB4C95E757DB4, 0x544D549A19A8CE54, 0x937693ECE53B7F93, 0x2288220DAA442F22,
+0x648D6407E9C86364, 0xF1E3F1DB12FF2AF1, 0x73D173BFA2E6CC73, 0x124812905A248212,
+0x401D403A5D807A40, 0x0820084028104808, 0xC32BC356E89B95C3, 0xEC97EC337BC5DFEC,
+0xDB4BDB9690AB4DDB, 0xA1BEA1611F5FC0A1, 0x8D0E8D1C8307918D, 0x3DF43DF5C97AC83D,
+0x976697CCF1335B97, 0x0000000000000000, 0xCF1BCF36D483F9CF, 0x2BAC2B4587566E2B,
+0x76C57697B3ECE176, 0x82328264B019E682, 0xD67FD6FEA9B128D6, 0x1B6C1BD87736C31B,
+0xB5EEB5C15B7774B5, 0xAF86AF112943BEAF, 0x6AB56A77DFD41D6A, 0x505D50BA0DA0EA50,
+0x450945124C8A5745, 0xF3EBF3CB18FB38F3, 0x30C0309DF060AD30, 0xEF9BEF2B74C3C4EF,
+0x3FFC3FE5C37EDA3F, 0x554955921CAAC755, 0xA2B2A2791059DBA2, 0xEA8FEA0365C9E9EA,
+0x6589650FECCA6A65, 0xBAD2BAB9686903BA, 0x2FBC2F65935E4A2F, 0xC027C04EE79D8EC0,
+0xDE5FDEBE81A160DE, 0x1C701CE06C38FC1C, 0xFDD3FDBB2EE746FD, 0x4D294D52649A1F4D,
+0x927292E4E0397692, 0x75C9758FBCEAFA75, 0x061806301E0C3606, 0x8A128A249809AE8A,
+0xB2F2B2F940794BB2, 0xE6BFE66359D185E6, 0x0E380E70361C7E0E, 0x1F7C1FF8633EE71F,
+0x62956237F7C45562, 0xD477D4EEA3B53AD4, 0xA89AA829324D81A8, 0x966296C4F4315296,
+0xF9C3F99B3AEF62F9, 0xC533C566F697A3C5, 0x25942535B14A1025, 0x597959F220B2AB59,
+0x842A8454AE15D084, 0x72D572B7A7E4C572, 0x39E439D5DD72EC39, 0x4C2D4C5A6198164C,
+0x5E655ECA3BBC945E, 0x78FD78E785F09F78, 0x38E038DDD870E538, 0x8C0A8C148605988C,
+0xD163D1C6B2BF17D1, 0xA5AEA5410B57E4A5, 0xE2AFE2434DD9A1E2, 0x6199612FF8C24E61,
+0xB3F6B3F1457B42B3, 0x21842115A5423421, 0x9C4A9C94D625089C, 0x1E781EF0663CEE1E,
+0x4311432252866143, 0xC73BC776FC93B1C7, 0xFCD7FCB32BE54FFC, 0x0410042014082404,
+0x515951B208A2E351, 0x995E99BCC72F2599, 0x6DA96D4FC4DA226D, 0x0D340D68391A650D,
+0xFACFFA8335E979FA, 0xDF5BDFB684A369DF, 0x7EE57ED79BFCA97E, 0x2490243DB4481924,
+0x3BEC3BC5D776FE3B, 0xAB96AB313D4B9AAB, 0xCE1FCE3ED181F0CE, 0x1144118855229911,
+0x8F068F0C8903838F, 0x4E254E4A6B9C044E, 0xB7E6B7D1517366B7, 0xEB8BEB0B60CBE0EB,
+0x3CF03CFDCC78C13C, 0x813E817CBF1FFD81, 0x946A94D4FE354094, 0xF7FBF7EB0CF31CF7,
+0xB9DEB9A1676F18B9, 0x134C13985F268B13, 0x2CB02C7D9C58512C, 0xD36BD3D6B8BB05D3,
+0xE7BBE76B5CD38CE7, 0x6EA56E57CBDC396E, 0xC437C46EF395AAC4, 0x030C03180F061B03,
+0x5645568A13ACDC56, 0x440D441A49885E44, 0x7FE17FDF9EFEA07F, 0xA99EA921374F88A9,
+0x2AA82A4D8254672A, 0xBBD6BBB16D6B0ABB, 0xC123C146E29F87C1, 0x535153A202A6F153,
+0xDC57DCAE8BA572DC, 0x0B2C0B582716530B, 0x9D4E9D9CD327019D, 0x6CAD6C47C1D82B6C,
+0x31C43195F562A431, 0x74CD7487B9E8F374, 0xF6FFF6E309F115F6, 0x4605460A438C4C46,
+0xAC8AAC092645A5AC, 0x891E893C970FB589, 0x145014A04428B414, 0xE1A3E15B42DFBAE1,
+0x165816B04E2CA616, 0x3AE83ACDD274F73A, 0x69B9696FD0D20669, 0x092409482D124109,
+0x70DD70A7ADE0D770, 0xB6E2B6D954716FB6, 0xD067D0CEB7BD1ED0, 0xED93ED3B7EC7D6ED,
+0xCC17CC2EDB85E2CC, 0x4215422A57846842, 0x985A98B4C22D2C98, 0xA4AAA4490E55EDA4,
+0x28A0285D88507528, 0x5C6D5CDA31B8865C, 0xF8C7F8933FED6BF8, 0x86228644A411C286 };
+
+}
diff --git a/third_party/botan/src/lib/hash/whirlpool/whrlpool.h b/third_party/botan/src/lib/hash/whirlpool/whrlpool.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/hash/whirlpool/whrlpool.h
@@ -0,0 +1,50 @@
+/*
+* Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WHIRLPOOL_H_
+#define BOTAN_WHIRLPOOL_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(whrlpool.h)
+
+namespace Botan {
+
+/**
+* Whirlpool
+*/
+class BOTAN_PUBLIC_API(2,0) Whirlpool final : public MDx_HashFunction
+   {
+   public:
+      std::string name() const override { return "Whirlpool"; }
+      size_t output_length() const override { return 64; }
+      HashFunction* clone() const override { return new Whirlpool; }
+      std::unique_ptr<HashFunction> copy_state() const override;
+
+      void clear() override;
+
+      Whirlpool() : MDx_HashFunction(64, true, true, 32), m_M(8), m_digest(8)
+         { clear(); }
+   private:
+      void compress_n(const uint8_t[], size_t blocks) override;
+      void copy_out(uint8_t[]) override;
+
+      static const uint64_t C0[256];
+      static const uint64_t C1[256];
+      static const uint64_t C2[256];
+      static const uint64_t C3[256];
+      static const uint64_t C4[256];
+      static const uint64_t C5[256];
+      static const uint64_t C6[256];
+      static const uint64_t C7[256];
+
+      secure_vector<uint64_t> m_M, m_digest;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp b/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp
@@ -0,0 +1,122 @@
+/*
+* HKDF
+* (C) 2013,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hkdf.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+size_t HKDF::kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const
+   {
+   HKDF_Extract extract(m_prf->clone());
+   HKDF_Expand expand(m_prf->clone());
+   secure_vector<uint8_t> prk(m_prf->output_length());
+
+   extract.kdf(prk.data(), prk.size(), secret, secret_len, salt, salt_len, nullptr, 0);
+   return expand.kdf(key, key_len, prk.data(), prk.size(), nullptr, 0, label, label_len);
+   }
+
+size_t HKDF_Extract::kdf(uint8_t key[], size_t key_len,
+                         const uint8_t secret[], size_t secret_len,
+                         const uint8_t salt[], size_t salt_len,
+                         const uint8_t[], size_t) const
+   {
+   secure_vector<uint8_t> prk;
+   if(salt_len == 0)
+      {
+      m_prf->set_key(std::vector<uint8_t>(m_prf->output_length()));
+      }
+   else
+      {
+      m_prf->set_key(salt, salt_len);
+      }
+
+   m_prf->update(secret, secret_len);
+   m_prf->final(prk);
+
+   const size_t written = std::min(prk.size(), key_len);
+   copy_mem(&key[0], prk.data(), written);
+   // FIXME: returns truncated output
+   return written;
+   }
+
+size_t HKDF_Expand::kdf(uint8_t key[], size_t key_len,
+                        const uint8_t secret[], size_t secret_len,
+                        const uint8_t salt[], size_t salt_len,
+                        const uint8_t label[], size_t label_len) const
+   {
+   m_prf->set_key(secret, secret_len);
+
+   uint8_t counter = 1;
+   secure_vector<uint8_t> h;
+   size_t offset = 0;
+
+   while(offset != key_len && counter != 0)
+      {
+      m_prf->update(h);
+      m_prf->update(label, label_len);
+      m_prf->update(salt, salt_len);
+      m_prf->update(counter++);
+      m_prf->final(h);
+
+      const size_t written = std::min(h.size(), key_len - offset);
+      copy_mem(&key[offset], h.data(), written);
+      offset += written;
+      }
+
+   // FIXME: returns truncated output
+   return offset;
+   }
+
+secure_vector<uint8_t>
+hkdf_expand_label(const std::string& hash_fn,
+                  const uint8_t secret[], size_t secret_len,
+                  const std::string& label,
+                  const uint8_t hash_val[], size_t hash_val_len,
+                  size_t length)
+   {
+   BOTAN_ARG_CHECK(length <= 0xFFFF, "HKDF-Expand-Label requested output too large");
+   BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long");
+   BOTAN_ARG_CHECK(hash_val_len <= 0xFF, "HKDF-Expand-Label hash too long");
+
+   const uint16_t length16 = static_cast<uint16_t>(length);
+
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(" + hash_fn + ")");
+
+   HKDF_Expand hkdf(mac.release());
+
+   secure_vector<uint8_t> output(length16);
+   std::vector<uint8_t> prefix(3 + label.size() + 1);
+
+   prefix[0] = get_byte(0, length16);
+   prefix[1] = get_byte(1, length16);
+   prefix[2] = static_cast<uint8_t>(label.size());
+
+   copy_mem(prefix.data() + 3,
+            cast_char_ptr_to_uint8(label.data()),
+            label.size());
+
+   prefix[3 + label.size()] = static_cast<uint8_t>(hash_val_len);
+
+   /*
+   * We do something a little dirty here to avoid copying the hash_val,
+   * making use of the fact that Botan's KDF interface supports label+salt,
+   * and knowing that our HKDF hashes first param label then param salt.
+   */
+   hkdf.kdf(output.data(), output.size(),
+            secret, secret_len,
+            hash_val, hash_val_len,
+            prefix.data(), prefix.size());
+
+   return output;
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/hkdf/hkdf.h b/third_party/botan/src/lib/kdf/hkdf/hkdf.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/hkdf/hkdf.h
@@ -0,0 +1,117 @@
+/*
+* HKDF
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HKDF_H_
+#define BOTAN_HKDF_H_
+
+#include <botan/mac.h>
+#include <botan/kdf.h>
+
+/*
+* The definitions of HKDF, HKDF_Extract, HKDF_Expand will be made internal
+* in the future. However the function hkdf_expand_label will still be defined.
+*/
+//BOTAN_FUTURE_INTERNAL_HEADER(hkdf.h)
+
+namespace Botan {
+
+/**
+* HKDF from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF final : public KDF
+   {
+   public:
+      /**
+      * @param prf MAC algorithm to use
+      */
+      explicit HKDF(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+      KDF* clone() const override { return new HKDF(m_prf->clone()); }
+
+      std::string name() const override { return "HKDF(" + m_prf->name() + ")"; }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+* HKDF Extraction Step from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF_Extract final : public KDF
+   {
+   public:
+      /**
+      * @param prf MAC algorithm to use
+      */
+      explicit HKDF_Extract(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+      KDF* clone() const override { return new HKDF_Extract(m_prf->clone()); }
+
+      std::string name() const override { return "HKDF-Extract(" + m_prf->name() + ")"; }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+* HKDF Expansion Step from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF_Expand final : public KDF
+   {
+   public:
+      /**
+      * @param prf MAC algorithm to use
+      */
+      explicit HKDF_Expand(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+      KDF* clone() const override { return new HKDF_Expand(m_prf->clone()); }
+
+      std::string name() const override { return "HKDF-Expand(" + m_prf->name() + ")"; }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+* HKDF-Expand-Label from TLS 1.3/QUIC
+* @param hash_fn the hash to use
+* @param secret the secret bits
+* @param secret_len the length of secret
+* @param label the full label (no "TLS 1.3, " or "tls13 " prefix
+*  is applied)
+* @param hash_val the previous hash value (used for chaining, may be empty)
+* @param hash_val_len the length of hash_val
+* @param length the desired output length
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,3) hkdf_expand_label(
+   const std::string& hash_fn,
+   const uint8_t secret[], size_t secret_len,
+   const std::string& label,
+   const uint8_t hash_val[], size_t hash_val_len,
+   size_t length);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/kdf.cpp b/third_party/botan/src/lib/kdf/kdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf.cpp
@@ -0,0 +1,255 @@
+/*
+* KDF Retrieval
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_HKDF)
+#include <botan/hkdf.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF1)
+#include <botan/kdf1.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF2)
+#include <botan/kdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF1_18033)
+#include <botan/kdf1_iso18033.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS_V10_PRF) || defined(BOTAN_HAS_TLS_V12_PRF)
+#include <botan/prf_tls.h>
+#endif
+
+#if defined(BOTAN_HAS_X942_PRF)
+#include <botan/prf_x942.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_108)
+#include <botan/sp800_108.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_56A)
+#include <botan/sp800_56a.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_56C)
+#include <botan/sp800_56c.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+template<typename KDF_Type>
+std::unique_ptr<KDF>
+kdf_create_mac_or_hash(const std::string& nm)
+   {
+   if(auto mac = MessageAuthenticationCode::create(nm))
+      return std::unique_ptr<KDF>(new KDF_Type(mac.release()));
+
+   if(auto mac = MessageAuthenticationCode::create("HMAC(" + nm + ")"))
+      return std::unique_ptr<KDF>(new KDF_Type(mac.release()));
+
+   return nullptr;
+   }
+
+}
+
+std::unique_ptr<KDF> KDF::create(const std::string& algo_spec,
+                                 const std::string& provider)
+   {
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_HKDF)
+   if(req.algo_name() == "HKDF" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<HKDF>(req.arg(0));
+         }
+      }
+
+   if(req.algo_name() == "HKDF-Extract" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<HKDF_Extract>(req.arg(0));
+         }
+      }
+
+   if(req.algo_name() == "HKDF-Expand" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<HKDF_Expand>(req.arg(0));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_KDF2)
+   if(req.algo_name() == "KDF2" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto hash = HashFunction::create(req.arg(0)))
+            return std::unique_ptr<KDF>(new KDF2(hash.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_KDF1_18033)
+   if(req.algo_name() == "KDF1-18033" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto hash = HashFunction::create(req.arg(0)))
+            return std::unique_ptr<KDF>(new KDF1_18033(hash.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_KDF1)
+   if(req.algo_name() == "KDF1" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto hash = HashFunction::create(req.arg(0)))
+            return std::unique_ptr<KDF>(new KDF1(hash.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_TLS_V10_PRF)
+   if(req.algo_name() == "TLS-PRF" && req.arg_count() == 0)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         auto hmac_md5 = MessageAuthenticationCode::create("HMAC(MD5)");
+         auto hmac_sha1 = MessageAuthenticationCode::create("HMAC(SHA-1)");
+
+         if(hmac_md5 && hmac_sha1)
+            return std::unique_ptr<KDF>(new TLS_PRF(std::move(hmac_md5), std::move(hmac_sha1)));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_TLS_V12_PRF)
+   if(req.algo_name() == "TLS-12-PRF" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<TLS_12_PRF>(req.arg(0));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_X942_PRF)
+   if(req.algo_name() == "X9.42-PRF" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return std::unique_ptr<KDF>(new X942_PRF(req.arg(0)));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SP800_108)
+   if(req.algo_name() == "SP800-108-Counter" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<SP800_108_Counter>(req.arg(0));
+         }
+      }
+
+   if(req.algo_name() == "SP800-108-Feedback" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0));
+         }
+      }
+
+   if(req.algo_name() == "SP800-108-Pipeline" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return kdf_create_mac_or_hash<SP800_108_Pipeline>(req.arg(0));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_SP800_56A)
+   if(req.algo_name() == "SP800-56A" && req.arg_count() == 1)
+      {
+      if(auto hash = HashFunction::create(req.arg(0)))
+         return std::unique_ptr<KDF>(new SP800_56A_Hash(hash.release()));
+      if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+         return std::unique_ptr<KDF>(new SP800_56A_HMAC(mac.release()));
+      }
+#endif
+
+#if defined(BOTAN_HAS_SP800_56C)
+   if(req.algo_name() == "SP800-56C" && req.arg_count() == 1)
+      {
+      std::unique_ptr<KDF> exp(kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0)));
+      if(exp)
+         {
+         if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+            return std::unique_ptr<KDF>(new SP800_56C(mac.release(), exp.release()));
+
+         if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+            return std::unique_ptr<KDF>(new SP800_56C(mac.release(), exp.release()));
+         }
+      }
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<KDF>
+KDF::create_or_throw(const std::string& algo,
+                             const std::string& provider)
+   {
+   if(auto kdf = KDF::create(algo, provider))
+      {
+      return kdf;
+      }
+   throw Lookup_Error("KDF", algo, provider);
+   }
+
+std::vector<std::string> KDF::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<KDF>(algo_spec, { "base" });
+   }
+
+KDF* get_kdf(const std::string& algo_spec)
+   {
+   SCAN_Name request(algo_spec);
+
+   if(request.algo_name() == "Raw")
+      return nullptr; // No KDF
+
+   //return KDF::create_or_throw(algo_spec).release();
+   auto kdf = KDF::create(algo_spec);
+   if(!kdf)
+      throw Algorithm_Not_Found(algo_spec);
+   return kdf.release();
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/kdf.h b/third_party/botan/src/lib/kdf/kdf.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf.h
@@ -0,0 +1,196 @@
+/*
+* Key Derivation Function interfaces
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF_BASE_H_
+#define BOTAN_KDF_BASE_H_
+
+#include <botan/secmem.h>
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Key Derivation Function
+*/
+class BOTAN_PUBLIC_API(2,0) KDF
+   {
+   public:
+      virtual ~KDF() = default;
+
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to choose
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<KDF>
+         create(const std::string& algo_spec,
+                const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name, or throw if the
+      * algo/provider combination cannot be found. If provider is
+      * empty then best available is chosen.
+      */
+      static std::unique_ptr<KDF>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * @return KDF name
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Derive a key
+      * @param key buffer holding the derived key, must be of length key_len
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param secret_len size of secret in bytes
+      * @param salt a diversifier
+      * @param salt_len size of salt in bytes
+      * @param label purpose for the derived keying material
+      * @param label_len size of label in bytes
+      * @return the derived key
+      */
+      virtual size_t kdf(uint8_t key[], size_t key_len,
+                         const uint8_t secret[], size_t secret_len,
+                         const uint8_t salt[], size_t salt_len,
+                         const uint8_t label[], size_t label_len) const = 0;
+
+      /**
+      * Derive a key
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param secret_len size of secret in bytes
+      * @param salt a diversifier
+      * @param salt_len size of salt in bytes
+      * @param label purpose for the derived keying material
+      * @param label_len size of label in bytes
+      * @return the derived key
+      */
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const uint8_t secret[],
+                                    size_t secret_len,
+                                    const uint8_t salt[],
+                                    size_t salt_len,
+                                    const uint8_t label[] = nullptr,
+                                    size_t label_len = 0) const
+         {
+         secure_vector<uint8_t> key(key_len);
+         key.resize(kdf(key.data(), key.size(), secret, secret_len, salt, salt_len, label, label_len));
+         return key;
+         }
+
+      /**
+      * Derive a key
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param salt a diversifier
+      * @param label purpose for the derived keying material
+      * @return the derived key
+      */
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const secure_vector<uint8_t>& secret,
+                                    const std::string& salt = "",
+                                    const std::string& label = "") const
+         {
+         return derive_key(key_len, secret.data(), secret.size(),
+                           cast_char_ptr_to_uint8(salt.data()),
+                           salt.length(),
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.length());
+
+         }
+
+      /**
+      * Derive a key
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param salt a diversifier
+      * @param label purpose for the derived keying material
+      * @return the derived key
+      */
+      template<typename Alloc, typename Alloc2, typename Alloc3>
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                     const std::vector<uint8_t, Alloc>& secret,
+                                     const std::vector<uint8_t, Alloc2>& salt,
+                                     const std::vector<uint8_t, Alloc3>& label) const
+         {
+         return derive_key(key_len,
+                           secret.data(), secret.size(),
+                           salt.data(), salt.size(),
+                           label.data(), label.size());
+         }
+
+      /**
+      * Derive a key
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param salt a diversifier
+      * @param salt_len size of salt in bytes
+      * @param label purpose for the derived keying material
+      * @return the derived key
+      */
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const secure_vector<uint8_t>& secret,
+                                    const uint8_t salt[],
+                                    size_t salt_len,
+                                    const std::string& label = "") const
+         {
+         return derive_key(key_len,
+                           secret.data(), secret.size(),
+                           salt, salt_len,
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.size());
+         }
+
+      /**
+      * Derive a key
+      * @param key_len the desired output length in bytes
+      * @param secret the secret input
+      * @param secret_len size of secret in bytes
+      * @param salt a diversifier
+      * @param label purpose for the derived keying material
+      * @return the derived key
+      */
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const uint8_t secret[],
+                                    size_t secret_len,
+                                    const std::string& salt = "",
+                                    const std::string& label = "") const
+         {
+         return derive_key(key_len, secret, secret_len,
+                           cast_char_ptr_to_uint8(salt.data()),
+                           salt.length(),
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.length());
+         }
+
+      /**
+      * @return new object representing the same algorithm as *this
+      */
+      virtual KDF* clone() const = 0;
+   };
+
+/**
+* Factory method for KDF (key derivation function)
+* @param algo_spec the name of the KDF to create
+* @return pointer to newly allocated object of that type
+*/
+BOTAN_PUBLIC_API(2,0) KDF* get_kdf(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp b/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp
@@ -0,0 +1,33 @@
+/*
+* KDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf1.h>
+
+namespace Botan {
+
+size_t KDF1::kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const
+   {
+   m_hash->update(secret, secret_len);
+   m_hash->update(label, label_len);
+   m_hash->update(salt, salt_len);
+
+   if(key_len < m_hash->output_length())
+      {
+      secure_vector<uint8_t> v = m_hash->final();
+      copy_mem(key, v.data(), key_len);
+      return key_len;
+      }
+
+   m_hash->final(key);
+   // FIXME: returns truncated output
+   return m_hash->output_length();
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/kdf1/kdf1.h b/third_party/botan/src/lib/kdf/kdf1/kdf1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf1/kdf1.h
@@ -0,0 +1,43 @@
+/*
+* KDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF1_H_
+#define BOTAN_KDF1_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf1.h)
+
+namespace Botan {
+
+/**
+* KDF1, from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) KDF1 final : public KDF
+   {
+   public:
+      std::string name() const override { return "KDF1(" + m_hash->name() + ")"; }
+
+      KDF* clone() const override { return new KDF1(m_hash->clone()); }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param h hash function to use
+      */
+      explicit KDF1(HashFunction* h) : m_hash(h) {}
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp b/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp
@@ -0,0 +1,38 @@
+/*
+* KDF1 from ISO 18033-2
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf1_iso18033.h>
+
+namespace Botan {
+
+size_t KDF1_18033::kdf(uint8_t key[], size_t key_len,
+                       const uint8_t secret[], size_t secret_len,
+                       const uint8_t salt[], size_t salt_len,
+                       const uint8_t label[], size_t label_len) const
+   {
+   uint32_t counter = 0;
+   secure_vector<uint8_t> h;
+
+   size_t offset = 0;
+   while(offset != key_len && counter != 0xFFFFFFFF)
+      {
+      m_hash->update(secret, secret_len);
+      m_hash->update_be(counter++);
+      m_hash->update(label, label_len);
+      m_hash->update(salt, salt_len);
+      m_hash->final(h);
+
+      const size_t added = std::min(h.size(), key_len - offset);
+      copy_mem(&key[offset], h.data(), added);
+      offset += added;
+      }
+
+   // FIXME: returns truncated output
+   return offset;
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h b/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h
@@ -0,0 +1,43 @@
+/*
+* KDF1 from ISO 18033-2
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF1_18033_H_
+#define BOTAN_KDF1_18033_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf1_iso18033.h)
+
+namespace Botan {
+
+/**
+* KDF1, from ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) KDF1_18033 final : public KDF
+   {
+   public:
+      std::string name() const override { return "KDF1-18033(" + m_hash->name() + ")"; }
+
+      KDF* clone() const override { return new KDF1_18033(m_hash->clone()); }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param h hash function to use
+      */
+      explicit KDF1_18033(HashFunction* h) : m_hash(h) {}
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp b/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp
@@ -0,0 +1,38 @@
+/*
+* KDF2
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf2.h>
+
+namespace Botan {
+
+size_t KDF2::kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const
+   {
+   uint32_t counter = 1;
+   secure_vector<uint8_t> h;
+
+   size_t offset = 0;
+   while(offset != key_len && counter != 0)
+      {
+      m_hash->update(secret, secret_len);
+      m_hash->update_be(counter++);
+      m_hash->update(label, label_len);
+      m_hash->update(salt, salt_len);
+      m_hash->final(h);
+
+      const size_t added = std::min(h.size(), key_len - offset);
+      copy_mem(&key[offset], h.data(), added);
+      offset += added;
+      }
+
+   // FIXME: returns truncated output
+   return offset;
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/kdf2/kdf2.h b/third_party/botan/src/lib/kdf/kdf2/kdf2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/kdf2/kdf2.h
@@ -0,0 +1,43 @@
+/*
+* KDF2
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF2_H_
+#define BOTAN_KDF2_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf2.h)
+
+namespace Botan {
+
+/**
+* KDF2, from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) KDF2 final : public KDF
+   {
+   public:
+      std::string name() const override { return "KDF2(" + m_hash->name() + ")"; }
+
+      KDF* clone() const override { return new KDF2(m_hash->clone()); }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param h hash function to use
+      */
+      explicit KDF2(HashFunction* h) : m_hash(h) {}
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp b/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp
@@ -0,0 +1,96 @@
+/*
+* TLS v1.0 and v1.2 PRFs
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/prf_tls.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+TLS_PRF::TLS_PRF() :
+   TLS_PRF(MessageAuthenticationCode::create_or_throw("HMAC(MD5)"),
+           MessageAuthenticationCode::create_or_throw("HMAC(SHA-1)"))
+   {
+   }
+
+namespace {
+
+/*
+* TLS PRF P_hash function
+*/
+void P_hash(uint8_t out[], size_t out_len,
+            MessageAuthenticationCode& mac,
+            const uint8_t secret[], size_t secret_len,
+            const uint8_t salt[], size_t salt_len)
+   {
+   try
+      {
+      mac.set_key(secret, secret_len);
+      }
+   catch(Invalid_Key_Length&)
+      {
+      throw Internal_Error("The premaster secret of " +
+                           std::to_string(secret_len) +
+                           " bytes is too long for the PRF");
+      }
+
+   secure_vector<uint8_t> A(salt, salt + salt_len);
+   secure_vector<uint8_t> h;
+
+   size_t offset = 0;
+
+   while(offset != out_len)
+      {
+      A = mac.process(A);
+
+      mac.update(A);
+      mac.update(salt, salt_len);
+      mac.final(h);
+
+      const size_t writing = std::min(h.size(), out_len - offset);
+      xor_buf(&out[offset], h.data(), writing);
+      offset += writing;
+      }
+   }
+
+}
+
+size_t TLS_PRF::kdf(uint8_t key[], size_t key_len,
+                    const uint8_t secret[], size_t secret_len,
+                    const uint8_t salt[], size_t salt_len,
+                    const uint8_t label[], size_t label_len) const
+   {
+   const size_t S1_len = (secret_len + 1) / 2,
+                S2_len = (secret_len + 1) / 2;
+   const uint8_t* S1 = secret;
+   const uint8_t* S2 = secret + (secret_len - S2_len);
+   secure_vector<uint8_t> msg;
+
+   msg.reserve(label_len + salt_len);
+   msg += std::make_pair(label, label_len);
+   msg += std::make_pair(salt, salt_len);
+
+   P_hash(key, key_len, *m_hmac_md5,  S1, S1_len, msg.data(), msg.size());
+   P_hash(key, key_len, *m_hmac_sha1, S2, S2_len, msg.data(), msg.size());
+   return key_len;
+   }
+
+size_t TLS_12_PRF::kdf(uint8_t key[], size_t key_len,
+                       const uint8_t secret[], size_t secret_len,
+                       const uint8_t salt[], size_t salt_len,
+                       const uint8_t label[], size_t label_len) const
+   {
+   secure_vector<uint8_t> msg;
+
+   msg.reserve(label_len + salt_len);
+   msg += std::make_pair(label, label_len);
+   msg += std::make_pair(salt, salt_len);
+
+   P_hash(key, key_len, *m_mac, secret, secret_len, msg.data(), msg.size());
+   return key_len;
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h b/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h
@@ -0,0 +1,70 @@
+/*
+* TLS v1.0 and v1.2 PRFs
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PRF_H_
+#define BOTAN_TLS_PRF_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(prf_tls.h)
+
+namespace Botan {
+
+/**
+* PRF used in TLS 1.0/1.1
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_PRF final : public KDF
+   {
+   public:
+      std::string name() const override { return "TLS-PRF"; }
+
+      KDF* clone() const override { return new TLS_PRF; }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      TLS_PRF(std::unique_ptr<MessageAuthenticationCode> hmac_md5,
+              std::unique_ptr<MessageAuthenticationCode> hmac_sha1) :
+         m_hmac_md5(std::move(hmac_md5)),
+         m_hmac_sha1(std::move(hmac_sha1))
+         {}
+
+      TLS_PRF();
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_hmac_md5;
+      std::unique_ptr<MessageAuthenticationCode> m_hmac_sha1;
+   };
+
+/**
+* PRF used in TLS 1.2
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_12_PRF final : public KDF
+   {
+   public:
+      std::string name() const override { return "TLS-12-PRF(" + m_mac->name() + ")"; }
+
+      KDF* clone() const override { return new TLS_12_PRF(m_mac->clone()); }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param mac MAC algorithm to use
+      */
+      explicit TLS_12_PRF(MessageAuthenticationCode* mac) : m_mac(mac) {}
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp b/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp
@@ -0,0 +1,92 @@
+/*
+* X9.42 PRF
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/prf_x942.h>
+#include <botan/der_enc.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Encode an integer as an OCTET STRING
+*/
+std::vector<uint8_t> encode_x942_int(uint32_t n)
+   {
+   uint8_t n_buf[4] = { 0 };
+   store_be(n, n_buf);
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(n_buf, 4, OCTET_STRING);
+   return output;
+   }
+
+}
+
+size_t X942_PRF::kdf(uint8_t key[], size_t key_len,
+                     const uint8_t secret[], size_t secret_len,
+                     const uint8_t salt[], size_t salt_len,
+                     const uint8_t label[], size_t label_len) const
+   {
+   std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-160"));
+
+   secure_vector<uint8_t> h;
+   secure_vector<uint8_t> in;
+   size_t offset = 0;
+   uint32_t counter = 1;
+
+   in.reserve(salt_len + label_len);
+   in += std::make_pair(label,label_len);
+   in += std::make_pair(salt,salt_len);
+
+   while(offset != key_len && counter)
+      {
+      hash->update(secret, secret_len);
+
+      hash->update(
+         DER_Encoder().start_cons(SEQUENCE)
+
+            .start_cons(SEQUENCE)
+               .encode(m_key_wrap_oid)
+               .raw_bytes(encode_x942_int(counter))
+            .end_cons()
+
+            .encode_if(salt_len != 0,
+               DER_Encoder()
+                  .start_explicit(0)
+                     .encode(in, OCTET_STRING)
+                  .end_explicit()
+               )
+
+            .start_explicit(2)
+               .raw_bytes(encode_x942_int(static_cast<uint32_t>(8 * key_len)))
+            .end_explicit()
+
+         .end_cons().get_contents()
+         );
+
+      hash->final(h);
+      const size_t copied = std::min(h.size(), key_len - offset);
+      copy_mem(&key[offset], h.data(), copied);
+      offset += copied;
+
+      ++counter;
+      }
+
+   // FIXME: returns truncated output
+   return offset;
+   }
+
+std::string X942_PRF::name() const
+   {
+   return "X9.42-PRF(" + m_key_wrap_oid.to_formatted_string() + ")";
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h b/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h
@@ -0,0 +1,42 @@
+/*
+* X9.42 PRF
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ANSI_X942_PRF_H_
+#define BOTAN_ANSI_X942_PRF_H_
+
+#include <botan/kdf.h>
+#include <botan/asn1_oid.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(prf_x942.h)
+
+namespace Botan {
+
+/**
+* PRF from ANSI X9.42
+*/
+class BOTAN_PUBLIC_API(2,0) X942_PRF final : public KDF
+   {
+   public:
+      std::string name() const override;
+
+      KDF* clone() const override { return new X942_PRF(m_key_wrap_oid); }
+
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      explicit X942_PRF(const std::string& oid) : m_key_wrap_oid(OID::from_string(oid)) {}
+
+      explicit X942_PRF(const OID& oid) : m_key_wrap_oid(oid) {}
+   private:
+      OID m_key_wrap_oid;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp b/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp
@@ -0,0 +1,170 @@
+/*
+* KDFs defined in NIST SP 800-108
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_108.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <iterator>
+
+namespace Botan {
+
+size_t SP800_108_Counter::kdf(uint8_t key[], size_t key_len,
+                              const uint8_t secret[], size_t secret_len,
+                              const uint8_t salt[], size_t salt_len,
+                              const uint8_t label[], size_t label_len) const
+   {
+   const std::size_t prf_len =  m_prf->output_length();
+
+   const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+   if(blocks_required > 0xFFFFFFFF)
+      throw Invalid_Argument("SP800_108_Counter output size too large");
+
+   const uint8_t delim = 0;
+   const uint32_t length = static_cast<uint32_t>(key_len * 8);
+
+   uint8_t *p = key;
+   uint32_t counter = 1;
+   uint8_t be_len[4] = { 0 };
+   secure_vector<uint8_t> tmp;
+
+   store_be(length, be_len);
+   m_prf->set_key(secret, secret_len);
+
+   while(p < key + key_len)
+      {
+      const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+      uint8_t be_cnt[4] = { 0 };
+
+      store_be(counter, be_cnt);
+
+      m_prf->update(be_cnt,4);
+      m_prf->update(label,label_len);
+      m_prf->update(delim);
+      m_prf->update(salt,salt_len);
+      m_prf->update(be_len,4);
+      m_prf->final(tmp);
+
+      copy_mem(p, tmp.data(), to_copy);
+      p += to_copy;
+
+      ++counter;
+      BOTAN_ASSERT(counter != 0, "No counter overflow");
+      }
+
+   return key_len;
+   }
+
+size_t SP800_108_Feedback::kdf(uint8_t key[], size_t key_len,
+                               const uint8_t secret[], size_t secret_len,
+                               const uint8_t salt[], size_t salt_len,
+                               const uint8_t label[], size_t label_len) const
+   {
+   const uint32_t length = static_cast<uint32_t>(key_len * 8);
+   const std::size_t prf_len =  m_prf->output_length();
+   const std::size_t iv_len = (salt_len >= prf_len ? prf_len : 0);
+   const uint8_t delim = 0;
+
+   const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+   if(blocks_required > 0xFFFFFFFF)
+      throw Invalid_Argument("SP800_108_Feedback output size too large");
+
+   uint8_t *p = key;
+   uint32_t counter = 1;
+   uint8_t be_len[4] = { 0 };
+   secure_vector< uint8_t > prev(salt, salt + iv_len);
+   secure_vector< uint8_t > ctx(salt + iv_len, salt + salt_len);
+
+   store_be(length, be_len);
+   m_prf->set_key(secret, secret_len);
+
+   while(p < key + key_len)
+      {
+      const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+      uint8_t be_cnt[4] = { 0 };
+
+      store_be(counter, be_cnt);
+
+      m_prf->update(prev);
+      m_prf->update(be_cnt,4);
+      m_prf->update(label,label_len);
+      m_prf->update(delim);
+      m_prf->update(ctx);
+      m_prf->update(be_len,4);
+      m_prf->final(prev);
+
+      copy_mem(p, prev.data(), to_copy);
+      p += to_copy;
+
+      ++counter;
+
+      BOTAN_ASSERT(counter != 0, "No overflow");
+      }
+
+   return key_len;
+   }
+
+size_t SP800_108_Pipeline::kdf(uint8_t key[], size_t key_len,
+                               const uint8_t secret[], size_t secret_len,
+                               const uint8_t salt[], size_t salt_len,
+                               const uint8_t label[], size_t label_len) const
+   {
+   const uint32_t length = static_cast<uint32_t>(key_len * 8);
+   const std::size_t prf_len =  m_prf->output_length();
+   const uint8_t delim = 0;
+
+   const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+   if(blocks_required > 0xFFFFFFFF)
+      throw Invalid_Argument("SP800_108_Feedback output size too large");
+
+   uint8_t *p = key;
+   uint32_t counter = 1;
+   uint8_t be_len[4] = { 0 };
+   secure_vector<uint8_t> ai, ki;
+
+   store_be(length, be_len);
+   m_prf->set_key(secret,secret_len);
+
+   // A(0)
+   std::copy(label,label + label_len,std::back_inserter(ai));
+   ai.emplace_back(delim);
+   std::copy(salt,salt + salt_len,std::back_inserter(ai));
+   std::copy(be_len,be_len + 4,std::back_inserter(ai));
+
+   while(p < key + key_len)
+      {
+      // A(i)
+      m_prf->update(ai);
+      m_prf->final(ai);
+
+      // K(i)
+      const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+      uint8_t be_cnt[4] = { 0 };
+
+      store_be(counter, be_cnt);
+
+      m_prf->update(ai);
+      m_prf->update(be_cnt,4);
+      m_prf->update(label, label_len);
+      m_prf->update(delim);
+      m_prf->update(salt, salt_len);
+      m_prf->update(be_len,4);
+      m_prf->final(ki);
+
+      copy_mem(p, ki.data(), to_copy);
+      p += to_copy;
+
+      ++counter;
+
+      BOTAN_ASSERT(counter != 0, "No overflow");
+      }
+
+   return key_len;
+   }
+}
diff --git a/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h b/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h
@@ -0,0 +1,135 @@
+/*
+* KDFs defined in NIST SP 800-108
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_108_H_
+#define BOTAN_SP800_108_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_108.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-108 KDF in Counter Mode (5.1)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Counter final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-108-Counter(" + m_prf->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_108_Counter(m_prf->clone()); }
+
+      /**
+      * Derive a key using the SP800-108 KDF in Counter mode.
+      *
+      * The implementation hard codes the length of [L]_2
+      * and [i]_2 (the value r) to 32 bits.
+      *
+      * @param key resulting keying material
+      * @param key_len the desired output length in bytes
+      * @param secret K_I
+      * @param secret_len size of K_I in bytes
+      * @param salt Context
+      * @param salt_len size of Context in bytes
+      * @param label Label
+      * @param label_len size of Label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param mac MAC algorithm to use
+      */
+      explicit SP800_108_Counter(MessageAuthenticationCode* mac) : m_prf(mac) {}
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+ * NIST SP 800-108 KDF in Feedback Mode (5.2)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Feedback final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-108-Feedback(" + m_prf->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_108_Feedback(m_prf->clone()); }
+
+      /**
+      * Derive a key using the SP800-108 KDF in Feedback mode.
+      *
+      * The implementation uses the optional counter i and hard
+      * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits.
+      *
+      * @param key resulting keying material
+      * @param key_len the desired output length in bytes
+      * @param secret K_I
+      * @param secret_len size of K_I in bytes
+      * @param salt IV || Context
+      * @param salt_len size of Context plus IV in bytes
+      * @param label Label
+      * @param label_len size of Label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      explicit SP800_108_Feedback(MessageAuthenticationCode* mac) : m_prf(mac) {}
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+ * NIST SP 800-108 KDF in Double Pipeline Mode (5.3)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Pipeline final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-108-Pipeline(" + m_prf->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_108_Pipeline(m_prf->clone()); }
+
+      /**
+      * Derive a key using the SP800-108 KDF in Double Pipeline mode.
+      *
+      * The implementation uses the optional counter i and hard
+      * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits.
+      *
+      * @param key resulting keying material
+      * @param key_len the desired output length in bytes
+      * @param secret K_I
+      * @param secret_len size of K_I in bytes
+      * @param salt Context
+      * @param salt_len size of Context in bytes
+      * @param label Label
+      * @param label_len size of Label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      explicit SP800_108_Pipeline(MessageAuthenticationCode* mac) : m_prf(mac) {}
+
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp b/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp
@@ -0,0 +1,98 @@
+/*
+* KDF defined in NIST SP 800-56a (Approved Alternative 1)
+*
+* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_56a.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+template<class AuxiliaryFunction_t>
+size_t SP800_56A_kdf(
+   AuxiliaryFunction_t& auxfunc,
+   uint8_t key[], size_t key_len,
+   const uint8_t secret[], size_t secret_len,
+   const uint8_t label[], size_t label_len)
+   {
+   const uint64_t kRepsUpperBound = (1ULL << 32);
+
+   const size_t digest_len = auxfunc.output_length();
+
+   const size_t reps = key_len / digest_len + ((key_len % digest_len) ? 1 : 0);
+
+   if (reps >= kRepsUpperBound)
+      {
+      // See SP-800-56A, point 5.8.1
+      throw Invalid_Argument("SP800-56A KDF requested output too large");
+      }
+
+   uint32_t counter = 1;
+   secure_vector<uint8_t> result;
+   for(size_t i = 0; i < reps; i++)
+      {
+      auxfunc.update_be(counter++);
+      auxfunc.update(secret, secret_len);
+      auxfunc.update(label, label_len);
+      auxfunc.final(result);
+
+      const size_t offset = digest_len * i;
+      const size_t len = std::min(result.size(), key_len - offset);
+      copy_mem(&key[offset], result.data(), len);
+      }
+
+   return key_len;
+   }
+
+}
+
+size_t SP800_56A_Hash::kdf(uint8_t key[], size_t key_len,
+                           const uint8_t secret[], size_t secret_len,
+                           const uint8_t salt[], size_t salt_len,
+                           const uint8_t label[], size_t label_len) const
+   {
+   /*
+   * TODO: should we reject a non-empty salt with an exception?
+   * Ignoring the salt seems quite dangerous to applications which
+   * don't expect it.
+   */
+   BOTAN_UNUSED(salt, salt_len);
+
+   return SP800_56A_kdf(*m_hash, key, key_len, secret, secret_len, label, label_len);
+   }
+
+SP800_56A_HMAC::SP800_56A_HMAC(MessageAuthenticationCode* mac) : m_mac(mac)
+   {
+   // TODO: we need a MessageAuthenticationCode::is_hmac
+   const SCAN_Name req(m_mac->name());
+   if(req.algo_name() != "HMAC")
+      {
+      throw Algorithm_Not_Found("Only HMAC can be used with KDF SP800-56A");
+      }
+   }
+
+size_t SP800_56A_HMAC::kdf(uint8_t key[], size_t key_len,
+                           const uint8_t secret[], size_t secret_len,
+                           const uint8_t salt[], size_t salt_len,
+                           const uint8_t label[], size_t label_len) const
+   {
+   /*
+   * SP 800-56A specifies if the salt is empty then a block of zeros
+   * equal to the hash's underlying block size are used. However this
+   * is equivalent to setting a zero-length key, so the same call
+   * works for either case.
+   */
+   m_mac->set_key(salt, salt_len);
+
+   return SP800_56A_kdf(*m_mac, key, key_len, secret, secret_len, label, label_len);
+   }
+
+
+
+}
diff --git a/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h b/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h
@@ -0,0 +1,103 @@
+/*
+* KDF defined in NIST SP 800-56a revision 2 (Single-step key-derivation function)
+*
+* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_56A_H_
+#define BOTAN_SP800_56A_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_56a.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-56A KDF using hash function
+ * @warning This KDF ignores the provided salt value
+ */
+class BOTAN_PUBLIC_API(2,2) SP800_56A_Hash final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-56A(" + m_hash->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_56A_Hash(m_hash->clone()); }
+
+      /**
+      * Derive a key using the SP800-56A KDF.
+      *
+      * The implementation hard codes the context value for the
+      * expansion step to the empty string.
+      *
+      * @param key derived keying material K_M
+      * @param key_len the desired output length in bytes
+      * @param secret shared secret Z
+      * @param secret_len size of Z in bytes
+      * @param salt ignored
+      * @param salt_len ignored
+      * @param label label for the expansion step
+      * @param label_len size of label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param hash the hash function to use as the auxiliary function
+      */
+      explicit SP800_56A_Hash(HashFunction* hash) : m_hash(hash) {}
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+/**
+ * NIST SP 800-56A KDF using HMAC
+ */
+class BOTAN_PUBLIC_API(2,2) SP800_56A_HMAC final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-56A(" + m_mac->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_56A_HMAC(m_mac->clone()); }
+
+      /**
+      * Derive a key using the SP800-56A KDF.
+      *
+      * The implementation hard codes the context value for the
+      * expansion step to the empty string.
+      *
+      * @param key derived keying material K_M
+      * @param key_len the desired output length in bytes
+      * @param secret shared secret Z
+      * @param secret_len size of Z in bytes
+      * @param salt ignored
+      * @param salt_len ignored
+      * @param label label for the expansion step
+      * @param label_len size of label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32 or MAC is not a HMAC
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param mac the HMAC to use as the auxiliary function
+      */
+      explicit SP800_56A_HMAC(MessageAuthenticationCode* mac);
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp b/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp
@@ -0,0 +1,28 @@
+/*
+* KDF defined in NIST SP 800-56c
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_56c.h>
+
+namespace Botan {
+
+size_t SP800_56C::kdf(uint8_t key[], size_t key_len,
+                      const uint8_t secret[], size_t secret_len,
+                      const uint8_t salt[], size_t salt_len,
+                      const uint8_t label[], size_t label_len) const
+   {
+   // Randomness Extraction
+   secure_vector<uint8_t> k_dk;
+
+   m_prf->set_key(salt, salt_len);
+   m_prf->update(secret, secret_len);
+   m_prf->final(k_dk);
+
+   // Key Expansion
+   return m_exp->kdf(key, key_len, k_dk.data(), k_dk.size(), nullptr, 0, label, label_len);
+   }
+
+}
diff --git a/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h b/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h
@@ -0,0 +1,61 @@
+/*
+* KDF defined in NIST SP 800-56c
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_56C_H_
+#define BOTAN_SP800_56C_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_56c.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-56C KDF
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_56C final : public KDF
+   {
+   public:
+      std::string name() const override { return "SP800-56C(" + m_prf->name() + ")"; }
+
+      KDF* clone() const override { return new SP800_56C(m_prf->clone(), m_exp->clone()); }
+
+      /**
+      * Derive a key using the SP800-56C KDF.
+      *
+      * The implementation hard codes the context value for the
+      * expansion step to the empty string.
+      *
+      * @param key derived keying material K_M
+      * @param key_len the desired output length in bytes
+      * @param secret shared secret Z
+      * @param secret_len size of Z in bytes
+      * @param salt salt s of the extraction step
+      * @param salt_len size of s in bytes
+      * @param label label for the expansion step
+      * @param label_len size of label in bytes
+      *
+      * @throws Invalid_Argument key_len > 2^32
+      */
+      size_t kdf(uint8_t key[], size_t key_len,
+                 const uint8_t secret[], size_t secret_len,
+                 const uint8_t salt[], size_t salt_len,
+                 const uint8_t label[], size_t label_len) const override;
+
+      /**
+      * @param mac MAC algorithm used for randomness extraction
+      * @param exp KDF used for key expansion
+      */
+      SP800_56C(MessageAuthenticationCode* mac, KDF* exp) : m_prf(mac), m_exp(exp) {}
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+      std::unique_ptr<KDF> m_exp;
+   };
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp b/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp
@@ -0,0 +1,99 @@
+/*
+* CBC-MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cbc_mac.h>
+
+namespace Botan {
+
+/*
+* Update an CBC-MAC Calculation
+*/
+void CBC_MAC::add_data(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   size_t xored = std::min(output_length() - m_position, length);
+   xor_buf(&m_state[m_position], input, xored);
+   m_position += xored;
+
+   if(m_position < output_length())
+      return;
+
+   m_cipher->encrypt(m_state);
+   input += xored;
+   length -= xored;
+   while(length >= output_length())
+      {
+      xor_buf(m_state, input, output_length());
+      m_cipher->encrypt(m_state);
+      input += output_length();
+      length -= output_length();
+      }
+
+   xor_buf(m_state, input, length);
+   m_position = length;
+   }
+
+/*
+* Finalize an CBC-MAC Calculation
+*/
+void CBC_MAC::final_result(uint8_t mac[])
+   {
+   verify_key_set(m_state.empty() == false);
+
+   if(m_position)
+      m_cipher->encrypt(m_state);
+
+   copy_mem(mac, m_state.data(), m_state.size());
+   zeroise(m_state);
+   m_position = 0;
+   }
+
+/*
+* CBC-MAC Key Schedule
+*/
+void CBC_MAC::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_state.resize(m_cipher->block_size());
+   m_cipher->set_key(key, length);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void CBC_MAC::clear()
+   {
+   m_cipher->clear();
+   zap(m_state);
+   m_position = 0;
+   }
+
+/*
+* Return the name of this type
+*/
+std::string CBC_MAC::name() const
+   {
+   return "CBC-MAC(" + m_cipher->name() + ")";
+   }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* CBC_MAC::clone() const
+   {
+   return new CBC_MAC(m_cipher->clone());
+   }
+
+/*
+* CBC-MAC Constructor
+*/
+CBC_MAC::CBC_MAC(BlockCipher* cipher) :
+   m_cipher(cipher)
+   {
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h b/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h
@@ -0,0 +1,50 @@
+/*
+* CBC-MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CBC_MAC_H_
+#define BOTAN_CBC_MAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cbc_mac.h)
+
+namespace Botan {
+
+/**
+* CBC-MAC
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_MAC final : public MessageAuthenticationCode
+   {
+   public:
+      std::string name() const override;
+      MessageAuthenticationCode* clone() const override;
+      size_t output_length() const override { return m_cipher->block_size(); }
+      void clear() override;
+
+      Key_Length_Specification key_spec() const override
+         {
+         return m_cipher->key_spec();
+         }
+
+      /**
+      * @param cipher the block cipher to use
+      */
+      explicit CBC_MAC(BlockCipher* cipher);
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      secure_vector<uint8_t> m_state;
+      size_t m_position = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/cmac/cmac.cpp b/third_party/botan/src/lib/mac/cmac/cmac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/cmac/cmac.cpp
@@ -0,0 +1,139 @@
+/*
+* CMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cmac.h>
+#include <botan/exceptn.h>
+#include <botan/internal/poly_dbl.h>
+
+namespace Botan {
+
+/*
+* Perform CMAC's multiplication in GF(2^n)
+*/
+secure_vector<uint8_t> CMAC::poly_double(const secure_vector<uint8_t>& in)
+   {
+   secure_vector<uint8_t> out(in.size());
+   poly_double_n(out.data(), in.data(), out.size());
+   return out;
+   }
+
+/*
+* Update an CMAC Calculation
+*/
+void CMAC::add_data(const uint8_t input[], size_t length)
+   {
+   const size_t bs = output_length();
+
+   buffer_insert(m_buffer, m_position, input, length);
+   if(m_position + length > bs)
+      {
+      xor_buf(m_state, m_buffer, bs);
+      m_cipher->encrypt(m_state);
+      input += (bs - m_position);
+      length -= (bs - m_position);
+      while(length > bs)
+         {
+         xor_buf(m_state, input, bs);
+         m_cipher->encrypt(m_state);
+         input += bs;
+         length -= bs;
+         }
+      copy_mem(m_buffer.data(), input, length);
+      m_position = 0;
+      }
+   m_position += length;
+   }
+
+/*
+* Finalize an CMAC Calculation
+*/
+void CMAC::final_result(uint8_t mac[])
+   {
+   xor_buf(m_state, m_buffer, m_position);
+
+   if(m_position == output_length())
+      {
+      xor_buf(m_state, m_B, output_length());
+      }
+   else
+      {
+      m_state[m_position] ^= 0x80;
+      xor_buf(m_state, m_P, output_length());
+      }
+
+   m_cipher->encrypt(m_state);
+
+   copy_mem(mac, m_state.data(), output_length());
+
+   zeroise(m_state);
+   zeroise(m_buffer);
+   m_position = 0;
+   }
+
+/*
+* CMAC Key Schedule
+*/
+void CMAC::key_schedule(const uint8_t key[], size_t length)
+   {
+   clear();
+   m_cipher->set_key(key, length);
+   m_cipher->encrypt(m_B);
+   poly_double_n(m_B.data(), m_B.size());
+   poly_double_n(m_P.data(), m_B.data(), m_P.size());
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void CMAC::clear()
+   {
+   m_cipher->clear();
+   zeroise(m_state);
+   zeroise(m_buffer);
+   zeroise(m_B);
+   zeroise(m_P);
+   m_position = 0;
+   }
+
+/*
+* Return the name of this type
+*/
+std::string CMAC::name() const
+   {
+   return "CMAC(" + m_cipher->name() + ")";
+   }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* CMAC::clone() const
+   {
+   return new CMAC(m_cipher->clone());
+   }
+
+/*
+* CMAC Constructor
+*/
+CMAC::CMAC(BlockCipher* cipher) :
+   m_cipher(cipher),
+   m_block_size(m_cipher->block_size())
+   {
+   if(poly_double_supported_size(m_block_size) == false)
+      {
+      throw Invalid_Argument("CMAC cannot use the " +
+                             std::to_string(m_block_size * 8) +
+                             " bit cipher " + m_cipher->name());
+      }
+
+   m_state.resize(output_length());
+   m_buffer.resize(output_length());
+   m_B.resize(output_length());
+   m_P.resize(output_length());
+   m_position = 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/cmac/cmac.h b/third_party/botan/src/lib/mac/cmac/cmac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/cmac/cmac.h
@@ -0,0 +1,67 @@
+/*
+* CMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CMAC_H_
+#define BOTAN_CMAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cmac.h)
+
+namespace Botan {
+
+/**
+* CMAC, also known as OMAC1
+*/
+class BOTAN_PUBLIC_API(2,0) CMAC final : public MessageAuthenticationCode
+   {
+   public:
+      std::string name() const override;
+      size_t output_length() const override { return m_block_size; }
+      MessageAuthenticationCode* clone() const override;
+
+      void clear() override;
+
+      Key_Length_Specification key_spec() const override
+         {
+         return m_cipher->key_spec();
+         }
+
+      /**
+      * CMAC's polynomial doubling operation
+      *
+      * This function was only exposed for use elsewhere in the library, but it is not
+      * longer used. This function will be removed in a future release.
+      *
+      * @param in the input
+      */
+      static secure_vector<uint8_t>
+         BOTAN_DEPRECATED("This was only for internal use and is no longer used")
+         poly_double(const secure_vector<uint8_t>& in);
+
+      /**
+      * @param cipher the block cipher to use
+      */
+      explicit CMAC(BlockCipher* cipher);
+
+      CMAC(const CMAC&) = delete;
+      CMAC& operator=(const CMAC&) = delete;
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      secure_vector<uint8_t> m_buffer, m_state, m_B, m_P;
+      const size_t m_block_size;
+      size_t m_position;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/gmac/gmac.cpp b/third_party/botan/src/lib/mac/gmac/gmac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/gmac/gmac.cpp
@@ -0,0 +1,134 @@
+/*
+ * GMAC
+ * (C) 2016 Matthias Gierlings, René Korthaus
+ * (C) 2017 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#include <botan/gmac.h>
+#include <botan/ghash.h>
+#include <botan/exceptn.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+GMAC::GMAC(BlockCipher* cipher) :
+   m_cipher(cipher),
+   m_ghash(new GHASH),
+   m_aad_buf(GCM_BS),
+   m_aad_buf_pos(0),
+   m_initialized(false)
+   {
+   }
+
+void GMAC::clear()
+   {
+   m_cipher->clear();
+   m_ghash->clear();
+   zeroise(m_aad_buf);
+   m_aad_buf_pos = 0;
+   m_initialized = false;
+   }
+
+GMAC::~GMAC() { /* for unique_ptr */ }
+
+Key_Length_Specification GMAC::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+std::string GMAC::name() const
+   {
+   return "GMAC(" + m_cipher->name() + ")";
+   }
+
+size_t GMAC::output_length() const
+   {
+   return GCM_BS;
+   }
+
+void GMAC::add_data(const uint8_t input[], size_t size)
+   {
+   if(m_aad_buf_pos > 0)
+      {
+      const size_t taking = std::min(GCM_BS - m_aad_buf_pos, size);
+      copy_mem(&m_aad_buf[m_aad_buf_pos], input, taking);
+      m_aad_buf_pos += taking;
+      input += taking;
+      size -= taking;
+
+      if(m_aad_buf_pos == GCM_BS)
+         {
+         m_ghash->update_associated_data(m_aad_buf.data(), GCM_BS);
+         m_aad_buf_pos = 0;
+         }
+      }
+
+   const size_t left_over = size % GCM_BS;
+   const size_t full_blocks = size - left_over;
+   m_ghash->update_associated_data(input, full_blocks);
+   input += full_blocks;
+
+   if(left_over > 0)
+      {
+      copy_mem(&m_aad_buf[m_aad_buf_pos], input, left_over);
+      m_aad_buf_pos += left_over;
+      }
+   }
+
+void GMAC::key_schedule(const uint8_t key[], size_t size)
+   {
+   clear();
+   m_cipher->set_key(key, size);
+
+   secure_vector<uint8_t> H(GCM_BS);
+   m_cipher->encrypt(H);
+   m_ghash->set_key(H);
+   }
+
+void GMAC::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   secure_vector<uint8_t> y0(GCM_BS);
+
+   if(nonce_len == 12)
+      {
+      copy_mem(y0.data(), nonce, nonce_len);
+      y0[GCM_BS - 1] = 1;
+      }
+   else
+      {
+      m_ghash->ghash_update(y0, nonce, nonce_len);
+      m_ghash->add_final_block(y0, 0, nonce_len);
+      }
+
+   secure_vector<uint8_t> m_enc_y0(GCM_BS);
+   m_cipher->encrypt(y0.data(), m_enc_y0.data());
+   m_ghash->start(m_enc_y0.data(), m_enc_y0.size());
+   m_initialized = true;
+   }
+
+void GMAC::final_result(uint8_t mac[])
+   {
+   // This ensures the GMAC computation has been initialized with a fresh
+   // nonce. The aim of this check is to prevent developers from re-using
+   // nonces (and potential nonce-reuse attacks).
+   if(m_initialized == false)
+      throw Invalid_State("GMAC was not used with a fresh nonce");
+
+   // process the rest of the aad buffer. Even if it is a partial block only
+   // ghash_update will process it properly.
+   if(m_aad_buf_pos > 0)
+       {
+       m_ghash->update_associated_data(m_aad_buf.data(), m_aad_buf_pos);
+       }
+
+   m_ghash->final(mac, output_length());
+   clear();
+   }
+
+MessageAuthenticationCode* GMAC::clone() const
+   {
+   return new GMAC(m_cipher->clone());
+   }
+}
diff --git a/third_party/botan/src/lib/mac/gmac/gmac.h b/third_party/botan/src/lib/mac/gmac/gmac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/gmac/gmac.h
@@ -0,0 +1,64 @@
+/*
+ * GMAC
+ * (C) 2016 Matthias Gierlings, René Korthaus
+ * (C) 2017 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#ifndef BOTAN_GMAC_H_
+#define BOTAN_GMAC_H_
+
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gmac.h)
+
+namespace Botan {
+
+class BlockCipher;
+class GHASH;
+
+/**
+* GMAC
+*
+* GMAC requires a unique initialization vector be used for each message.
+* This must be provided via the MessageAuthenticationCode::start() API
+*/
+class BOTAN_PUBLIC_API(2,0) GMAC final : public MessageAuthenticationCode
+   {
+   public:
+      void clear() override;
+      std::string name() const override;
+      size_t output_length() const override;
+      MessageAuthenticationCode* clone() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      /**
+      * Creates a new GMAC instance.
+      *
+      * @param cipher the underlying block cipher to use
+      */
+      explicit GMAC(BlockCipher* cipher);
+
+      GMAC(const GMAC&) = delete;
+      GMAC& operator=(const GMAC&) = delete;
+
+      ~GMAC();
+
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      void key_schedule(const uint8_t key[], size_t size) override;
+
+      static const size_t GCM_BS = 16;
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<GHASH> m_ghash;
+      secure_vector<uint8_t> m_aad_buf;
+      size_t m_aad_buf_pos;
+      bool m_initialized;
+   };
+
+}
+#endif
diff --git a/third_party/botan/src/lib/mac/hmac/hmac.cpp b/third_party/botan/src/lib/mac/hmac/hmac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/hmac/hmac.cpp
@@ -0,0 +1,150 @@
+/*
+* HMAC
+* (C) 1999-2007,2014,2020 Jack Lloyd
+*     2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hmac.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* Update a HMAC Calculation
+*/
+void HMAC::add_data(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_ikey.empty() == false);
+   m_hash->update(input, length);
+   }
+
+/*
+* Finalize a HMAC Calculation
+*/
+void HMAC::final_result(uint8_t mac[])
+   {
+   verify_key_set(m_okey.empty() == false);
+   m_hash->final(mac);
+   m_hash->update(m_okey);
+   m_hash->update(mac, m_hash_output_length);
+   m_hash->final(mac);
+   m_hash->update(m_ikey);
+   }
+
+Key_Length_Specification HMAC::key_spec() const
+   {
+   // Support very long lengths for things like PBKDF2 and the TLS PRF
+   return Key_Length_Specification(0, 4096);
+   }
+
+size_t HMAC::output_length() const
+   {
+   return m_hash_output_length;
+   }
+
+/*
+* HMAC Key Schedule
+*/
+void HMAC::key_schedule(const uint8_t key[], size_t length)
+   {
+   const uint8_t ipad = 0x36;
+   const uint8_t opad = 0x5C;
+
+   m_hash->clear();
+
+   m_ikey.resize(m_hash_block_size);
+   m_okey.resize(m_hash_block_size);
+
+   clear_mem(m_ikey.data(), m_ikey.size());
+   clear_mem(m_okey.data(), m_okey.size());
+
+   /*
+   * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it
+   * reveals the length of the passphrase. Make some attempt to hide this to
+   * side channels. Clearly if the secret is longer than the block size then the
+   * branch to hash first reveals that. In addition, counting the number of
+   * compression functions executed reveals the size at the granularity of the
+   * hash function's block size.
+   *
+   * The greater concern is for smaller keys; being able to detect when a
+   * passphrase is say 4 bytes may assist choosing weaker targets. Even though
+   * the loop bounds are constant, we can only actually read key[0..length] so
+   * it doesn't seem possible to make this computation truly constant time.
+   *
+   * We don't mind leaking if the length is exactly zero since that's
+   * trivial to simply check.
+   */
+
+   if(length > m_hash_block_size)
+      {
+      m_hash->update(key, length);
+      m_hash->final(m_ikey.data());
+      }
+   else if(length > 0)
+      {
+      for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i)
+         {
+         /*
+         access key[i % length] but avoiding division due to variable
+         time computation on some processors.
+         */
+         auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length);
+         i_mod_length = needs_reduction.select(0, i_mod_length);
+         const uint8_t kb = key[i_mod_length];
+
+         auto in_range = CT::Mask<size_t>::is_lt(i, length);
+         m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb));
+         i_mod_length += 1;
+         }
+      }
+
+   for(size_t i = 0; i != m_hash_block_size; ++i)
+      {
+      m_ikey[i] ^= ipad;
+      m_okey[i] = m_ikey[i] ^ ipad ^ opad;
+      }
+
+   m_hash->update(m_ikey);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void HMAC::clear()
+   {
+   m_hash->clear();
+   zap(m_ikey);
+   zap(m_okey);
+   }
+
+/*
+* Return the name of this type
+*/
+std::string HMAC::name() const
+   {
+   return "HMAC(" + m_hash->name() + ")";
+   }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* HMAC::clone() const
+   {
+   return new HMAC(m_hash->clone());
+   }
+
+/*
+* HMAC Constructor
+*/
+HMAC::HMAC(HashFunction* hash) :
+   m_hash(hash),
+   m_hash_output_length(m_hash->output_length()),
+   m_hash_block_size(m_hash->hash_block_size())
+   {
+   BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length,
+                   "HMAC is not compatible with this hash function");
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/hmac/hmac.h b/third_party/botan/src/lib/mac/hmac/hmac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/hmac/hmac.h
@@ -0,0 +1,52 @@
+/*
+* HMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HMAC_H_
+#define BOTAN_HMAC_H_
+
+#include <botan/mac.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(hmac.h)
+
+namespace Botan {
+
+/**
+* HMAC
+*/
+class BOTAN_PUBLIC_API(2,0) HMAC final : public MessageAuthenticationCode
+   {
+   public:
+      void clear() override;
+      std::string name() const override;
+      MessageAuthenticationCode* clone() const override;
+
+      size_t output_length() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      /**
+      * @param hash the hash to use for HMACing
+      */
+      explicit HMAC(HashFunction* hash);
+
+      HMAC(const HMAC&) = delete;
+      HMAC& operator=(const HMAC&) = delete;
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+      secure_vector<uint8_t> m_ikey, m_okey;
+      size_t m_hash_output_length;
+      size_t m_hash_block_size;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/mac.cpp b/third_party/botan/src/lib/mac/mac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/mac.cpp
@@ -0,0 +1,171 @@
+/*
+* Message Authentication Code base class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mac.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+#include <botan/mem_ops.h>
+
+#if defined(BOTAN_HAS_CBC_MAC)
+  #include <botan/cbc_mac.h>
+#endif
+
+#if defined(BOTAN_HAS_CMAC)
+  #include <botan/cmac.h>
+#endif
+
+#if defined(BOTAN_HAS_GMAC)
+  #include <botan/gmac.h>
+  #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC)
+  #include <botan/hmac.h>
+  #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_POLY1305)
+  #include <botan/poly1305.h>
+#endif
+
+#if defined(BOTAN_HAS_SIPHASH)
+  #include <botan/siphash.h>
+#endif
+
+#if defined(BOTAN_HAS_ANSI_X919_MAC)
+  #include <botan/x919_mac.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<MessageAuthenticationCode>
+MessageAuthenticationCode::create(const std::string& algo_spec,
+                                  const std::string& provider)
+   {
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_GMAC)
+   if(req.algo_name() == "GMAC" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto bc = BlockCipher::create(req.arg(0)))
+            return std::unique_ptr<MessageAuthenticationCode>(new GMAC(bc.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_HMAC)
+   if(req.algo_name() == "HMAC" && req.arg_count() == 1)
+      {
+      // TODO OpenSSL
+      if(provider.empty() || provider == "base")
+         {
+         if(auto h = HashFunction::create(req.arg(0)))
+            return std::unique_ptr<MessageAuthenticationCode>(new HMAC(h.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_POLY1305)
+   if(req.algo_name() == "Poly1305" && req.arg_count() == 0)
+      {
+      if(provider.empty() || provider == "base")
+         return std::unique_ptr<MessageAuthenticationCode>(new Poly1305);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SIPHASH)
+   if(req.algo_name() == "SipHash")
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return std::unique_ptr<MessageAuthenticationCode>(
+            new SipHash(req.arg_as_integer(0, 2), req.arg_as_integer(1, 4)));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_CMAC)
+   if((req.algo_name() == "CMAC" || req.algo_name() == "OMAC") && req.arg_count() == 1)
+      {
+      // TODO: OpenSSL CMAC
+      if(provider.empty() || provider == "base")
+         {
+         if(auto bc = BlockCipher::create(req.arg(0)))
+            return std::unique_ptr<MessageAuthenticationCode>(new CMAC(bc.release()));
+         }
+      }
+#endif
+
+
+#if defined(BOTAN_HAS_CBC_MAC)
+   if(req.algo_name() == "CBC-MAC" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto bc = BlockCipher::create(req.arg(0)))
+            return std::unique_ptr<MessageAuthenticationCode>(new CBC_MAC(bc.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_ANSI_X919_MAC)
+   if(req.algo_name() == "X9.19-MAC")
+      {
+      if(provider.empty() || provider == "base")
+         {
+         return std::unique_ptr<MessageAuthenticationCode>(new ANSI_X919_MAC);
+         }
+      }
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+std::vector<std::string>
+MessageAuthenticationCode::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<MessageAuthenticationCode>(algo_spec, {"base", "openssl"});
+   }
+
+//static
+std::unique_ptr<MessageAuthenticationCode>
+MessageAuthenticationCode::create_or_throw(const std::string& algo,
+                                           const std::string& provider)
+   {
+   if(auto mac = MessageAuthenticationCode::create(algo, provider))
+      {
+      return mac;
+      }
+   throw Lookup_Error("MAC", algo, provider);
+   }
+
+void MessageAuthenticationCode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   BOTAN_UNUSED(nonce);
+   if(nonce_len > 0)
+      throw Invalid_IV_Length(name(), nonce_len);
+   }
+
+/*
+* Default (deterministic) MAC verification operation
+*/
+bool MessageAuthenticationCode::verify_mac(const uint8_t mac[], size_t length)
+   {
+   secure_vector<uint8_t> our_mac = final();
+
+   if(our_mac.size() != length)
+      return false;
+
+   return constant_time_compare(our_mac.data(), mac, length);
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/mac.h b/third_party/botan/src/lib/mac/mac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/mac.h
@@ -0,0 +1,143 @@
+/*
+* Base class for message authentiction codes
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MESSAGE_AUTH_CODE_BASE_H_
+#define BOTAN_MESSAGE_AUTH_CODE_BASE_H_
+
+#include <botan/buf_comp.h>
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+
+namespace Botan {
+
+/**
+* This class represents Message Authentication Code (MAC) objects.
+*/
+class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computation,
+                                            public SymmetricAlgorithm
+   {
+   public:
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to use
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<MessageAuthenticationCode>
+         create(const std::string& algo_spec,
+                const std::string& provider = "");
+
+      /*
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to use
+      * Throws a Lookup_Error if algo/provider combination cannot be found
+      */
+      static std::unique_ptr<MessageAuthenticationCode>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      virtual ~MessageAuthenticationCode() = default;
+
+      /**
+      * Prepare for processing a message under the specified nonce
+      *
+      * Most MACs neither require nor support a nonce; for these algorithms
+      * calling `start_msg` is optional and calling it with anything other than
+      * an empty string is an error. One MAC which *requires* a per-message
+      * nonce be specified is GMAC.
+      *
+      * @param nonce the message nonce bytes
+      * @param nonce_len the size of len in bytes
+      * Default implementation simply rejects all non-empty nonces
+      * since most hash/MAC algorithms do not support randomization
+      */
+      virtual void start_msg(const uint8_t nonce[], size_t nonce_len);
+
+      /**
+      * Begin processing a message with a nonce
+      *
+      * @param nonce the per message nonce
+      */
+      template<typename Alloc>
+      void start(const std::vector<uint8_t, Alloc>& nonce)
+         {
+         start_msg(nonce.data(), nonce.size());
+         }
+
+      /**
+      * Begin processing a message.
+      * @param nonce the per message nonce
+      * @param nonce_len length of nonce
+      */
+      void start(const uint8_t nonce[], size_t nonce_len)
+         {
+         start_msg(nonce, nonce_len);
+         }
+
+      /**
+      * Begin processing a message.
+      */
+      void start()
+         {
+         return start_msg(nullptr, 0);
+         }
+
+      /**
+      * Verify a MAC.
+      * @param in the MAC to verify as a byte array
+      * @param length the length of param in
+      * @return true if the MAC is valid, false otherwise
+      */
+      virtual bool verify_mac(const uint8_t in[], size_t length);
+
+      /**
+      * Verify a MAC.
+      * @param in the MAC to verify as a byte array
+      * @return true if the MAC is valid, false otherwise
+      */
+      virtual bool verify_mac(const std::vector<uint8_t>& in)
+         {
+         return verify_mac(in.data(), in.size());
+         }
+
+      /**
+      * Verify a MAC.
+      * @param in the MAC to verify as a byte array
+      * @return true if the MAC is valid, false otherwise
+      */
+      virtual bool verify_mac(const secure_vector<uint8_t>& in)
+         {
+         return verify_mac(in.data(), in.size());
+         }
+
+      /**
+      * Get a new object representing the same algorithm as *this
+      */
+      virtual MessageAuthenticationCode* clone() const = 0;
+
+      /**
+      * @return provider information about this implementation. Default is "base",
+      * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+      */
+      virtual std::string provider() const { return "base"; }
+
+   };
+
+typedef MessageAuthenticationCode MAC;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/poly1305/poly1305.cpp b/third_party/botan/src/lib/mac/poly1305/poly1305.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/poly1305/poly1305.cpp
@@ -0,0 +1,211 @@
+/*
+* Derived from poly1305-donna-64.h by Andrew Moon <liquidsun@gmail.com>
+* in https://github.com/floodyberry/poly1305-donna
+*
+* (C) 2014 Andrew Moon
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/poly1305.h>
+#include <botan/loadstor.h>
+#include <botan/mul128.h>
+#include <botan/internal/donna128.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+void poly1305_init(secure_vector<uint64_t>& X, const uint8_t key[32])
+   {
+   /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+   const uint64_t t0 = load_le<uint64_t>(key, 0);
+   const uint64_t t1 = load_le<uint64_t>(key, 1);
+
+   X[0] = ( t0                    ) & 0xffc0fffffff;
+   X[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
+   X[2] = ((t1 >> 24)             ) & 0x00ffffffc0f;
+
+   /* h = 0 */
+   X[3] = 0;
+   X[4] = 0;
+   X[5] = 0;
+
+   /* save pad for later */
+   X[6] = load_le<uint64_t>(key, 2);
+   X[7] = load_le<uint64_t>(key, 3);
+   }
+
+void poly1305_blocks(secure_vector<uint64_t>& X, const uint8_t *m, size_t blocks, bool is_final = false)
+   {
+#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+   typedef donna128 uint128_t;
+#endif
+
+   const uint64_t hibit = is_final ? 0 : (static_cast<uint64_t>(1) << 40); /* 1 << 128 */
+
+   const uint64_t r0 = X[0];
+   const uint64_t r1 = X[1];
+   const uint64_t r2 = X[2];
+
+   const uint64_t M44 = 0xFFFFFFFFFFF;
+   const uint64_t M42 = 0x3FFFFFFFFFF;
+
+   uint64_t h0 = X[3+0];
+   uint64_t h1 = X[3+1];
+   uint64_t h2 = X[3+2];
+
+   const uint64_t s1 = r1 * 20;
+   const uint64_t s2 = r2 * 20;
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      const uint64_t t0 = load_le<uint64_t>(m, 0);
+      const uint64_t t1 = load_le<uint64_t>(m, 1);
+
+      h0 += (( t0                    ) & M44);
+      h1 += (((t0 >> 44) | (t1 << 20)) & M44);
+      h2 += (((t1 >> 24)             ) & M42) | hibit;
+
+      const uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1;
+      const uint64_t c0 = carry_shift(d0, 44);
+
+      const uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2 + c0;
+      const uint64_t c1 = carry_shift(d1, 44);
+
+      const uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0 + c1;
+      const uint64_t c2 = carry_shift(d2, 42);
+
+      h0 = d0 & M44;
+      h1 = d1 & M44;
+      h2 = d2 & M42;
+
+      h0 += c2 * 5;
+      h1 += carry_shift(h0, 44);
+      h0 = h0 & M44;
+
+      m += 16;
+      }
+
+   X[3+0] = h0;
+   X[3+1] = h1;
+   X[3+2] = h2;
+   }
+
+void poly1305_finish(secure_vector<uint64_t>& X, uint8_t mac[16])
+   {
+   const uint64_t M44 = 0xFFFFFFFFFFF;
+   const uint64_t M42 = 0x3FFFFFFFFFF;
+
+   /* fully carry h */
+   uint64_t h0 = X[3+0];
+   uint64_t h1 = X[3+1];
+   uint64_t h2 = X[3+2];
+
+   uint64_t c;
+                c = (h1 >> 44); h1 &= M44;
+   h2 += c;     c = (h2 >> 42); h2 &= M42;
+   h0 += c * 5; c = (h0 >> 44); h0 &= M44;
+   h1 += c;     c = (h1 >> 44); h1 &= M44;
+   h2 += c;     c = (h2 >> 42); h2 &= M42;
+   h0 += c * 5; c = (h0 >> 44); h0 &= M44;
+   h1 += c;
+
+   /* compute h + -p */
+   uint64_t g0 = h0 + 5; c = (g0 >> 44); g0 &= M44;
+   uint64_t g1 = h1 + c; c = (g1 >> 44); g1 &= M44;
+   uint64_t g2 = h2 + c - (static_cast<uint64_t>(1) << 42);
+
+   /* select h if h < p, or h + -p if h >= p */
+   const auto c_mask = CT::Mask<uint64_t>::expand(c);
+   h0 = c_mask.select(g0, h0);
+   h1 = c_mask.select(g1, h1);
+   h2 = c_mask.select(g2, h2);
+
+   /* h = (h + pad) */
+   const uint64_t t0 = X[6];
+   const uint64_t t1 = X[7];
+
+   h0 += (( t0                    ) & M44)    ; c = (h0 >> 44); h0 &= M44;
+   h1 += (((t0 >> 44) | (t1 << 20)) & M44) + c; c = (h1 >> 44); h1 &= M44;
+   h2 += (((t1 >> 24)             ) & M42) + c;                 h2 &= M42;
+
+   /* mac = h % (2^128) */
+   h0 = ((h0      ) | (h1 << 44));
+   h1 = ((h1 >> 20) | (h2 << 24));
+
+   store_le(mac, h0, h1);
+
+   /* zero out the state */
+   clear_mem(X.data(), X.size());
+   }
+
+}
+
+void Poly1305::clear()
+   {
+   zap(m_poly);
+   zap(m_buf);
+   m_buf_pos = 0;
+   }
+
+void Poly1305::key_schedule(const uint8_t key[], size_t)
+   {
+   m_buf_pos = 0;
+   m_buf.resize(16);
+   m_poly.resize(8);
+
+   poly1305_init(m_poly, key);
+   }
+
+void Poly1305::add_data(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_poly.size() == 8);
+
+   if(m_buf_pos)
+      {
+      buffer_insert(m_buf, m_buf_pos, input, length);
+
+      if(m_buf_pos + length >= m_buf.size())
+         {
+         poly1305_blocks(m_poly, m_buf.data(), 1);
+         input += (m_buf.size() - m_buf_pos);
+         length -= (m_buf.size() - m_buf_pos);
+         m_buf_pos = 0;
+         }
+      }
+
+   const size_t full_blocks = length / m_buf.size();
+   const size_t remaining   = length % m_buf.size();
+
+   if(full_blocks)
+      poly1305_blocks(m_poly, input, full_blocks);
+
+   buffer_insert(m_buf, m_buf_pos, input + full_blocks * m_buf.size(), remaining);
+   m_buf_pos += remaining;
+   }
+
+void Poly1305::final_result(uint8_t out[])
+   {
+   verify_key_set(m_poly.size() == 8);
+
+   if(m_buf_pos != 0)
+      {
+      m_buf[m_buf_pos] = 1;
+      const size_t len = m_buf.size() - m_buf_pos - 1;
+      if (len > 0)
+         {
+         clear_mem(&m_buf[m_buf_pos+1], len);
+         }
+      poly1305_blocks(m_poly, m_buf.data(), 1, true);
+      }
+
+   poly1305_finish(m_poly, out);
+
+   m_poly.clear();
+   m_buf_pos = 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/poly1305/poly1305.h b/third_party/botan/src/lib/mac/poly1305/poly1305.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/poly1305/poly1305.h
@@ -0,0 +1,50 @@
+/*
+* Poly1305
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MAC_POLY1305_H_
+#define BOTAN_MAC_POLY1305_H_
+
+#include <botan/mac.h>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(poly1305.h)
+
+namespace Botan {
+
+/**
+* DJB's Poly1305
+* Important note: each key can only be used once
+*/
+class BOTAN_PUBLIC_API(2,0) Poly1305 final : public MessageAuthenticationCode
+   {
+   public:
+      std::string name() const override { return "Poly1305"; }
+
+      MessageAuthenticationCode* clone() const override { return new Poly1305; }
+
+      void clear() override;
+
+      size_t output_length() const override { return 16; }
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(32);
+         }
+
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      secure_vector<uint64_t> m_poly;
+      secure_vector<uint8_t> m_buf;
+      size_t m_buf_pos = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/siphash/siphash.cpp b/third_party/botan/src/lib/mac/siphash/siphash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/siphash/siphash.cpp
@@ -0,0 +1,136 @@
+/*
+* SipHash
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/siphash.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+void SipRounds(uint64_t M, secure_vector<uint64_t>& V, size_t r)
+   {
+   uint64_t V0 = V[0], V1 = V[1], V2 = V[2], V3 = V[3];
+
+   V3 ^= M;
+   for(size_t i = 0; i != r; ++i)
+      {
+      V0 += V1; V2 += V3;
+      V1 = rotl<13>(V1);
+      V3 = rotl<16>(V3);
+      V1 ^= V0; V3 ^= V2;
+      V0 = rotl<32>(V0);
+
+      V2 += V1; V0 += V3;
+      V1 = rotl<17>(V1);
+      V3 = rotl<21>(V3);
+      V1 ^= V2; V3 ^= V0;
+      V2 = rotl<32>(V2);
+      }
+   V0 ^= M;
+
+   V[0] = V0; V[1] = V1; V[2] = V2; V[3] = V3;
+   }
+
+}
+
+void SipHash::add_data(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_V.empty() == false);
+
+   // SipHash counts the message length mod 256
+   m_words += static_cast<uint8_t>(length);
+
+   if(m_mbuf_pos)
+      {
+      while(length && m_mbuf_pos != 8)
+         {
+         m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[0]) << 56);
+         ++m_mbuf_pos;
+         ++input;
+         length--;
+         }
+
+      if(m_mbuf_pos == 8)
+         {
+         SipRounds(m_mbuf, m_V, m_C);
+         m_mbuf_pos = 0;
+         m_mbuf = 0;
+         }
+      }
+
+   while(length >= 8)
+      {
+      SipRounds(load_le<uint64_t>(input, 0), m_V, m_C);
+      input += 8;
+      length -= 8;
+      }
+
+   for(size_t i = 0; i != length; ++i)
+      {
+      m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[i]) << 56);
+      m_mbuf_pos++;
+      }
+   }
+
+void SipHash::final_result(uint8_t mac[])
+   {
+   verify_key_set(m_V.empty() == false);
+
+   if(m_mbuf_pos == 0)
+      {
+      m_mbuf = (static_cast<uint64_t>(m_words) << 56);
+      }
+   else if(m_mbuf_pos < 8)
+      {
+      m_mbuf = (m_mbuf >> (64-m_mbuf_pos*8)) | (static_cast<uint64_t>(m_words) << 56);
+      }
+
+   SipRounds(m_mbuf, m_V, m_C);
+
+   m_V[2] ^= 0xFF;
+   SipRounds(0, m_V, m_D);
+
+   const uint64_t X = m_V[0] ^ m_V[1] ^ m_V[2] ^ m_V[3];
+
+   store_le(X, mac);
+
+   clear();
+   }
+
+void SipHash::key_schedule(const uint8_t key[], size_t)
+   {
+   const uint64_t K0 = load_le<uint64_t>(key, 0);
+   const uint64_t K1 = load_le<uint64_t>(key, 1);
+
+   m_V.resize(4);
+   m_V[0] = K0 ^ 0x736F6D6570736575;
+   m_V[1] = K1 ^ 0x646F72616E646F6D;
+   m_V[2] = K0 ^ 0x6C7967656E657261;
+   m_V[3] = K1 ^ 0x7465646279746573;
+   }
+
+void SipHash::clear()
+   {
+   zap(m_V);
+   m_mbuf = 0;
+   m_mbuf_pos = 0;
+   m_words = 0;
+   }
+
+std::string SipHash::name() const
+   {
+   return "SipHash(" + std::to_string(m_C) + "," + std::to_string(m_D) + ")";
+   }
+
+MessageAuthenticationCode* SipHash::clone() const
+   {
+   return new SipHash(m_C, m_D);
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/siphash/siphash.h b/third_party/botan/src/lib/mac/siphash/siphash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/siphash/siphash.h
@@ -0,0 +1,47 @@
+/*
+* SipHash
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SIPHASH_H_
+#define BOTAN_SIPHASH_H_
+
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(siphash.h)
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) SipHash final : public MessageAuthenticationCode
+   {
+   public:
+      SipHash(size_t c = 2, size_t d = 4) : m_C(c), m_D(d) {}
+
+      void clear() override;
+      std::string name() const override;
+
+      MessageAuthenticationCode* clone() const override;
+
+      size_t output_length() const override { return 8; }
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(16);
+         }
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      const size_t m_C, m_D;
+      secure_vector<uint64_t> m_V;
+      uint64_t m_mbuf = 0;
+      size_t m_mbuf_pos = 0;
+      uint8_t m_words = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp b/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp
@@ -0,0 +1,99 @@
+/*
+* ANSI X9.19 MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x919_mac.h>
+
+namespace Botan {
+
+/*
+* Update an ANSI X9.19 MAC Calculation
+*/
+void ANSI_X919_MAC::add_data(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   size_t xored = std::min(8 - m_position, length);
+   xor_buf(&m_state[m_position], input, xored);
+   m_position += xored;
+
+   if(m_position < 8) return;
+
+   m_des1->encrypt(m_state);
+   input += xored;
+   length -= xored;
+   while(length >= 8)
+      {
+      xor_buf(m_state, input, 8);
+      m_des1->encrypt(m_state);
+      input += 8;
+      length -= 8;
+      }
+
+   xor_buf(m_state, input, length);
+   m_position = length;
+   }
+
+/*
+* Finalize an ANSI X9.19 MAC Calculation
+*/
+void ANSI_X919_MAC::final_result(uint8_t mac[])
+   {
+   if(m_position)
+      m_des1->encrypt(m_state);
+   m_des2->decrypt(m_state.data(), mac);
+   m_des1->encrypt(mac);
+   zeroise(m_state);
+   m_position = 0;
+   }
+
+/*
+* ANSI X9.19 MAC Key Schedule
+*/
+void ANSI_X919_MAC::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_state.resize(8);
+
+   m_des1->set_key(key, 8);
+
+   if(length == 16)
+      key += 8;
+
+   m_des2->set_key(key, 8);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void ANSI_X919_MAC::clear()
+   {
+   m_des1->clear();
+   m_des2->clear();
+   zap(m_state);
+   m_position = 0;
+   }
+
+std::string ANSI_X919_MAC::name() const
+   {
+   return "X9.19-MAC";
+   }
+
+MessageAuthenticationCode* ANSI_X919_MAC::clone() const
+   {
+   return new ANSI_X919_MAC;
+   }
+
+/*
+* ANSI X9.19 MAC Constructor
+*/
+ANSI_X919_MAC::ANSI_X919_MAC() :
+   m_des1(BlockCipher::create("DES")),
+   m_des2(m_des1->clone()),
+   m_position(0)
+   {
+   }
+
+}
diff --git a/third_party/botan/src/lib/mac/x919_mac/x919_mac.h b/third_party/botan/src/lib/mac/x919_mac/x919_mac.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/mac/x919_mac/x919_mac.h
@@ -0,0 +1,51 @@
+/*
+* ANSI X9.19 MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ANSI_X919_MAC_H_
+#define BOTAN_ANSI_X919_MAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(x919_mac.h)
+
+namespace Botan {
+
+/**
+* DES/3DES-based MAC from ANSI X9.19
+*/
+class BOTAN_PUBLIC_API(2,0) ANSI_X919_MAC final : public MessageAuthenticationCode
+   {
+   public:
+      void clear() override;
+      std::string name() const override;
+      size_t output_length() const override { return 8; }
+
+      MessageAuthenticationCode* clone() const override;
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(8, 16, 8);
+         }
+
+      ANSI_X919_MAC();
+
+      ANSI_X919_MAC(const ANSI_X919_MAC&) = delete;
+      ANSI_X919_MAC& operator=(const ANSI_X919_MAC&) = delete;
+   private:
+      void add_data(const uint8_t[], size_t) override;
+      void final_result(uint8_t[]) override;
+      void key_schedule(const uint8_t[], size_t) override;
+
+      std::unique_ptr<BlockCipher> m_des1, m_des2;
+      secure_vector<uint8_t> m_state;
+      size_t m_position;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/bigint/big_code.cpp b/third_party/botan/src/lib/math/bigint/big_code.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/big_code.cpp
@@ -0,0 +1,200 @@
+/*
+* BigInt Encoding/Decoding
+* (C) 1999-2010,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/charset.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+std::string BigInt::to_dec_string() const
+   {
+   BigInt copy = *this;
+   copy.set_sign(Positive);
+
+   uint8_t remainder;
+   std::vector<uint8_t> digits;
+
+   while(copy > 0)
+      {
+      ct_divide_u8(copy, 10, copy, remainder);
+      digits.push_back(remainder);
+      }
+
+   std::string s;
+
+   for(auto i = digits.rbegin(); i != digits.rend(); ++i)
+      {
+      s.push_back(Charset::digit2char(*i));
+      }
+
+   if(s.empty())
+      s += "0";
+
+   return s;
+   }
+
+std::string BigInt::to_hex_string() const
+   {
+   const std::vector<uint8_t> bits = BigInt::encode(*this);
+   if(bits.empty())
+      return "00";
+   else
+      return hex_encode(bits);
+   }
+
+/*
+* Encode a BigInt
+*/
+void BigInt::encode(uint8_t output[], const BigInt& n, Base base)
+   {
+   secure_vector<uint8_t> enc = n.encode_locked(base);
+   copy_mem(output, enc.data(), enc.size());
+   }
+
+namespace {
+
+std::vector<uint8_t> str_to_vector(const std::string& s)
+   {
+   std::vector<uint8_t> v(s.size());
+   std::memcpy(v.data(), s.data(), s.size());
+   return v;
+   }
+
+secure_vector<uint8_t> str_to_lvector(const std::string& s)
+   {
+   secure_vector<uint8_t> v(s.size());
+   std::memcpy(v.data(), s.data(), s.size());
+   return v;
+   }
+
+}
+
+/*
+* Encode a BigInt
+*/
+std::vector<uint8_t> BigInt::encode(const BigInt& n, Base base)
+   {
+   if(base == Binary)
+      return BigInt::encode(n);
+   else if(base == Hexadecimal)
+      return str_to_vector(n.to_hex_string());
+   else if(base == Decimal)
+      return str_to_vector(n.to_dec_string());
+   else
+      throw Invalid_Argument("Unknown BigInt encoding base");
+   }
+
+/*
+* Encode a BigInt
+*/
+secure_vector<uint8_t> BigInt::encode_locked(const BigInt& n, Base base)
+   {
+   if(base == Binary)
+      return BigInt::encode_locked(n);
+   else if(base == Hexadecimal)
+      return str_to_lvector(n.to_hex_string());
+   else if(base == Decimal)
+      return str_to_lvector(n.to_dec_string());
+   else
+      throw Invalid_Argument("Unknown BigInt encoding base");
+   }
+
+/*
+* Encode a BigInt, with leading 0s if needed
+*/
+secure_vector<uint8_t> BigInt::encode_1363(const BigInt& n, size_t bytes)
+   {
+   if(n.bytes() > bytes)
+      throw Encoding_Error("encode_1363: n is too large to encode properly");
+
+   secure_vector<uint8_t> output(bytes);
+   n.binary_encode(output.data(), output.size());
+   return output;
+   }
+
+//static
+void BigInt::encode_1363(uint8_t output[], size_t bytes, const BigInt& n)
+   {
+   if(n.bytes() > bytes)
+      throw Encoding_Error("encode_1363: n is too large to encode properly");
+
+   n.binary_encode(output, bytes);
+   }
+
+/*
+* Encode two BigInt, with leading 0s if needed, and concatenate
+*/
+secure_vector<uint8_t> BigInt::encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes)
+   {
+   if(n1.bytes() > bytes || n2.bytes() > bytes)
+      throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
+   secure_vector<uint8_t> output(2 * bytes);
+   n1.binary_encode(output.data()        , bytes);
+   n2.binary_encode(output.data() + bytes, bytes);
+   return output;
+   }
+
+/*
+* Decode a BigInt
+*/
+BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base)
+   {
+   BigInt r;
+   if(base == Binary)
+      {
+      r.binary_decode(buf, length);
+      }
+   else if(base == Hexadecimal)
+      {
+      secure_vector<uint8_t> binary;
+
+      if(length % 2)
+         {
+         // Handle lack of leading 0
+         const char buf0_with_leading_0[2] =
+            { '0', static_cast<char>(buf[0]) };
+
+         binary = hex_decode_locked(buf0_with_leading_0, 2);
+
+         binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]),
+                                     length - 1,
+                                     false);
+         }
+      else
+         binary = hex_decode_locked(cast_uint8_ptr_to_char(buf),
+                                    length, false);
+
+      r.binary_decode(binary.data(), binary.size());
+      }
+   else if(base == Decimal)
+      {
+      for(size_t i = 0; i != length; ++i)
+         {
+         if(Charset::is_space(buf[i]))
+            continue;
+
+         if(!Charset::is_digit(buf[i]))
+            throw Invalid_Argument("BigInt::decode: "
+                                   "Invalid character in decimal input");
+
+         const uint8_t x = Charset::char2digit(buf[i]);
+
+         if(x >= 10)
+            throw Invalid_Argument("BigInt: Invalid decimal string");
+
+         r *= 10;
+         r += x;
+         }
+      }
+   else
+      throw Invalid_Argument("Unknown BigInt decoding method");
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/big_io.cpp b/third_party/botan/src/lib/math/bigint/big_io.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/big_io.cpp
@@ -0,0 +1,62 @@
+/*
+* BigInt Input/Output
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <istream>
+#include <ostream>
+
+namespace Botan {
+
+/*
+* Write the BigInt into a stream
+*/
+std::ostream& operator<<(std::ostream& stream, const BigInt& n)
+   {
+   size_t base = 10;
+   if(stream.flags() & std::ios::hex)
+      base = 16;
+   if(stream.flags() & std::ios::oct)
+      throw Invalid_Argument("Octal output of BigInt not supported");
+
+   if(n == 0)
+      stream.write("0", 1);
+   else
+      {
+      if(n < 0)
+         stream.write("-", 1);
+
+      std::string enc;
+
+      if(base == 10)
+         enc = n.to_dec_string();
+      else
+         enc = n.to_hex_string();
+
+      size_t skip = 0;
+      while(skip < enc.size() && enc[skip] == '0')
+         ++skip;
+      stream.write(&enc[skip], enc.size() - skip);
+      }
+   if(!stream.good())
+      throw Stream_IO_Error("BigInt output operator has failed");
+   return stream;
+   }
+
+/*
+* Read the BigInt from a stream
+*/
+std::istream& operator>>(std::istream& stream, BigInt& n)
+   {
+   std::string str;
+   std::getline(stream, str);
+   if(stream.bad() || (stream.fail() && !stream.eof()))
+      throw Stream_IO_Error("BigInt input operator has failed");
+   n = BigInt(str);
+   return stream;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/big_ops2.cpp b/third_party/botan/src/lib/math/bigint/big_ops2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/big_ops2.cpp
@@ -0,0 +1,314 @@
+/*
+* (C) 1999-2007,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+BigInt& BigInt::add(const word y[], size_t y_words, Sign y_sign)
+   {
+   const size_t x_sw = sig_words();
+
+   grow_to(std::max(x_sw, y_words) + 1);
+
+   if(sign() == y_sign)
+      {
+      bigint_add2(mutable_data(), size() - 1, y, y_words);
+      }
+   else
+      {
+      const int32_t relative_size = bigint_cmp(data(), x_sw, y, y_words);
+
+      if(relative_size >= 0)
+         {
+         // *this >= y
+         bigint_sub2(mutable_data(), x_sw, y, y_words);
+         }
+      else
+         {
+         // *this < y
+         bigint_sub2_rev(mutable_data(), y, y_words);
+         }
+
+      //this->sign_fixup(relative_size, y_sign);
+      if(relative_size < 0)
+         set_sign(y_sign);
+      else if(relative_size == 0)
+         set_sign(Positive);
+      }
+
+   return (*this);
+   }
+
+BigInt& BigInt::mod_add(const BigInt& s, const BigInt& mod, secure_vector<word>& ws)
+   {
+   if(this->is_negative() || s.is_negative() || mod.is_negative())
+      throw Invalid_Argument("BigInt::mod_add expects all arguments are positive");
+
+   BOTAN_DEBUG_ASSERT(*this < mod);
+   BOTAN_DEBUG_ASSERT(s < mod);
+
+   /*
+   t + s or t + s - p == t - (p - s)
+
+   So first compute ws = p - s
+
+   Then compute t + s and t - ws
+
+   If t - ws does not borrow, then that is the correct valued
+   */
+
+   const size_t mod_sw = mod.sig_words();
+   BOTAN_ARG_CHECK(mod_sw > 0, "BigInt::mod_add modulus must be positive");
+
+   this->grow_to(mod_sw);
+   s.grow_to(mod_sw);
+
+   // First mod_sw for p - s, 2*mod_sw for bigint_addsub workspace
+   if(ws.size() < 3*mod_sw)
+      ws.resize(3*mod_sw);
+
+   word borrow = bigint_sub3(&ws[0], mod.data(), mod_sw, s.data(), mod_sw);
+   BOTAN_DEBUG_ASSERT(borrow == 0);
+
+   // Compute t - ws
+   borrow = bigint_sub3(&ws[mod_sw], this->data(), mod_sw, &ws[0], mod_sw);
+
+   // Compute t + s
+   bigint_add3_nc(&ws[mod_sw*2], this->data(), mod_sw, s.data(), mod_sw);
+
+   CT::conditional_copy_mem(borrow, &ws[0], &ws[mod_sw*2], &ws[mod_sw], mod_sw);
+   set_words(&ws[0], mod_sw);
+
+   return (*this);
+   }
+
+BigInt& BigInt::mod_sub(const BigInt& s, const BigInt& mod, secure_vector<word>& ws)
+   {
+   if(this->is_negative() || s.is_negative() || mod.is_negative())
+      throw Invalid_Argument("BigInt::mod_sub expects all arguments are positive");
+
+   // We are assuming in this function that *this and s are no more than mod_sw words long
+   BOTAN_DEBUG_ASSERT(*this < mod);
+   BOTAN_DEBUG_ASSERT(s < mod);
+
+   const size_t mod_sw = mod.sig_words();
+
+   this->grow_to(mod_sw);
+   s.grow_to(mod_sw);
+
+   if(ws.size() < mod_sw)
+      ws.resize(mod_sw);
+
+   if(mod_sw == 4)
+      bigint_mod_sub_n<4>(mutable_data(), s.data(), mod.data(), ws.data());
+   else if(mod_sw == 6)
+      bigint_mod_sub_n<6>(mutable_data(), s.data(), mod.data(), ws.data());
+   else
+      bigint_mod_sub(mutable_data(), s.data(), mod.data(), mod_sw, ws.data());
+
+   return (*this);
+   }
+
+BigInt& BigInt::mod_mul(uint8_t y, const BigInt& mod, secure_vector<word>& ws)
+   {
+   BOTAN_ARG_CHECK(this->is_negative() == false, "*this must be positive");
+   BOTAN_ARG_CHECK(y < 16, "y too large");
+
+   BOTAN_DEBUG_ASSERT(*this < mod);
+
+   *this *= static_cast<word>(y);
+   this->reduce_below(mod, ws);
+   return (*this);
+   }
+
+BigInt& BigInt::rev_sub(const word y[], size_t y_sw, secure_vector<word>& ws)
+   {
+   if(this->sign() != BigInt::Positive)
+      throw Invalid_State("BigInt::sub_rev requires this is positive");
+
+   const size_t x_sw = this->sig_words();
+
+   ws.resize(std::max(x_sw, y_sw));
+   clear_mem(ws.data(), ws.size());
+
+   const int32_t relative_size = bigint_sub_abs(ws.data(), data(), x_sw, y, y_sw);
+
+   this->cond_flip_sign(relative_size > 0);
+   this->swap_reg(ws);
+
+   return (*this);
+   }
+
+/*
+* Multiplication Operator
+*/
+BigInt& BigInt::operator*=(const BigInt& y)
+   {
+   secure_vector<word> ws;
+   return this->mul(y, ws);
+   }
+
+BigInt& BigInt::mul(const BigInt& y, secure_vector<word>& ws)
+   {
+   const size_t x_sw = sig_words();
+   const size_t y_sw = y.sig_words();
+   set_sign((sign() == y.sign()) ? Positive : Negative);
+
+   if(x_sw == 0 || y_sw == 0)
+      {
+      clear();
+      set_sign(Positive);
+      }
+   else if(x_sw == 1 && y_sw)
+      {
+      grow_to(y_sw + 1);
+      bigint_linmul3(mutable_data(), y.data(), y_sw, word_at(0));
+      }
+   else if(y_sw == 1 && x_sw)
+      {
+      word carry = bigint_linmul2(mutable_data(), x_sw, y.word_at(0));
+      set_word_at(x_sw, carry);
+      }
+   else
+      {
+      const size_t new_size = x_sw + y_sw + 1;
+      ws.resize(new_size);
+      secure_vector<word> z_reg(new_size);
+
+      bigint_mul(z_reg.data(), z_reg.size(),
+                 data(), size(), x_sw,
+                 y.data(), y.size(), y_sw,
+                 ws.data(), ws.size());
+
+      this->swap_reg(z_reg);
+      }
+
+   return (*this);
+   }
+
+BigInt& BigInt::square(secure_vector<word>& ws)
+   {
+   const size_t sw = sig_words();
+
+   secure_vector<word> z(2*sw);
+   ws.resize(z.size());
+
+   bigint_sqr(z.data(), z.size(),
+              data(), size(), sw,
+              ws.data(), ws.size());
+
+   swap_reg(z);
+   set_sign(BigInt::Positive);
+
+   return (*this);
+   }
+
+BigInt& BigInt::operator*=(word y)
+   {
+   if(y == 0)
+      {
+      clear();
+      set_sign(Positive);
+      }
+
+   const word carry = bigint_linmul2(mutable_data(), size(), y);
+   set_word_at(size(), carry);
+
+   return (*this);
+   }
+
+/*
+* Division Operator
+*/
+BigInt& BigInt::operator/=(const BigInt& y)
+   {
+   if(y.sig_words() == 1 && is_power_of_2(y.word_at(0)))
+      (*this) >>= (y.bits() - 1);
+   else
+      (*this) = (*this) / y;
+   return (*this);
+   }
+
+/*
+* Modulo Operator
+*/
+BigInt& BigInt::operator%=(const BigInt& mod)
+   {
+   return (*this = (*this) % mod);
+   }
+
+/*
+* Modulo Operator
+*/
+word BigInt::operator%=(word mod)
+   {
+   if(mod == 0)
+      throw BigInt::DivideByZero();
+
+   word remainder = 0;
+
+   if(is_power_of_2(mod))
+       {
+       remainder = (word_at(0) & (mod - 1));
+       }
+   else
+      {
+      const size_t sw = sig_words();
+      for(size_t i = sw; i > 0; --i)
+         remainder = bigint_modop(remainder, word_at(i-1), mod);
+      }
+
+   if(remainder && sign() == BigInt::Negative)
+      remainder = mod - remainder;
+
+   m_data.set_to_zero();
+   m_data.set_word_at(0, remainder);
+   set_sign(BigInt::Positive);
+   return remainder;
+   }
+
+/*
+* Left Shift Operator
+*/
+BigInt& BigInt::operator<<=(size_t shift)
+   {
+   const size_t shift_words = shift / BOTAN_MP_WORD_BITS;
+   const size_t shift_bits  = shift % BOTAN_MP_WORD_BITS;
+   const size_t size = sig_words();
+
+   const size_t bits_free = top_bits_free();
+
+   const size_t new_size = size + shift_words + (bits_free < shift_bits);
+
+   m_data.grow_to(new_size);
+
+   bigint_shl1(m_data.mutable_data(), new_size, size, shift_words, shift_bits);
+
+   return (*this);
+   }
+
+/*
+* Right Shift Operator
+*/
+BigInt& BigInt::operator>>=(size_t shift)
+   {
+   const size_t shift_words = shift / BOTAN_MP_WORD_BITS;
+   const size_t shift_bits  = shift % BOTAN_MP_WORD_BITS;
+
+   bigint_shr1(m_data.mutable_data(), m_data.size(), shift_words, shift_bits);
+
+   if(is_negative() && is_zero())
+      set_sign(Positive);
+
+   return (*this);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/big_ops3.cpp b/third_party/botan/src/lib/math/bigint/big_ops3.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/big_ops3.cpp
@@ -0,0 +1,183 @@
+/*
+* BigInt Binary Operators
+* (C) 1999-2007,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+//static
+BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_words, BigInt::Sign y_sign)
+   {
+   const size_t x_sw = x.sig_words();
+
+   BigInt z(x.sign(), std::max(x_sw, y_words) + 1);
+
+   if(x.sign() == y_sign)
+      {
+      bigint_add3(z.mutable_data(), x.data(), x_sw, y, y_words);
+      }
+   else
+      {
+      const int32_t relative_size = bigint_sub_abs(z.mutable_data(), x.data(), x_sw, y, y_words);
+
+      //z.sign_fixup(relative_size, y_sign);
+      if(relative_size < 0)
+         z.set_sign(y_sign);
+      else if(relative_size == 0)
+         z.set_sign(BigInt::Positive);
+      }
+
+   return z;
+   }
+
+/*
+* Multiplication Operator
+*/
+BigInt operator*(const BigInt& x, const BigInt& y)
+   {
+   const size_t x_sw = x.sig_words();
+   const size_t y_sw = y.sig_words();
+
+   BigInt z(BigInt::Positive, x.size() + y.size());
+
+   if(x_sw == 1 && y_sw)
+      bigint_linmul3(z.mutable_data(), y.data(), y_sw, x.word_at(0));
+   else if(y_sw == 1 && x_sw)
+      bigint_linmul3(z.mutable_data(), x.data(), x_sw, y.word_at(0));
+   else if(x_sw && y_sw)
+      {
+      secure_vector<word> workspace(z.size());
+
+      bigint_mul(z.mutable_data(), z.size(),
+                 x.data(), x.size(), x_sw,
+                 y.data(), y.size(), y_sw,
+                 workspace.data(), workspace.size());
+      }
+
+   z.cond_flip_sign(x_sw > 0 && y_sw > 0 && x.sign() != y.sign());
+
+   return z;
+   }
+
+/*
+* Multiplication Operator
+*/
+BigInt operator*(const BigInt& x, word y)
+   {
+   const size_t x_sw = x.sig_words();
+
+   BigInt z(BigInt::Positive, x_sw + 1);
+
+   if(x_sw && y)
+      {
+      bigint_linmul3(z.mutable_data(), x.data(), x_sw, y);
+      z.set_sign(x.sign());
+      }
+
+   return z;
+   }
+
+/*
+* Division Operator
+*/
+BigInt operator/(const BigInt& x, const BigInt& y)
+   {
+   if(y.sig_words() == 1 && is_power_of_2(y.word_at(0)))
+      return (x >> (y.bits() - 1));
+
+   BigInt q, r;
+   divide(x, y, q, r);
+   return q;
+   }
+
+/*
+* Modulo Operator
+*/
+BigInt operator%(const BigInt& n, const BigInt& mod)
+   {
+   if(mod.is_zero())
+      throw BigInt::DivideByZero();
+   if(mod.is_negative())
+      throw Invalid_Argument("BigInt::operator%: modulus must be > 0");
+   if(n.is_positive() && mod.is_positive() && n < mod)
+      return n;
+
+   BigInt q, r;
+   divide(n, mod, q, r);
+   return r;
+   }
+
+/*
+* Modulo Operator
+*/
+word operator%(const BigInt& n, word mod)
+   {
+   if(mod == 0)
+      throw BigInt::DivideByZero();
+
+   if(mod == 1)
+      return 0;
+
+   word remainder = 0;
+
+   if(is_power_of_2(mod))
+      {
+      remainder = (n.word_at(0) & (mod - 1));
+      }
+   else
+      {
+      const size_t sw = n.sig_words();
+      for(size_t i = sw; i > 0; --i)
+         {
+         remainder = bigint_modop(remainder, n.word_at(i-1), mod);
+         }
+      }
+
+   if(remainder && n.sign() == BigInt::Negative)
+      return mod - remainder;
+   return remainder;
+   }
+
+/*
+* Left Shift Operator
+*/
+BigInt operator<<(const BigInt& x, size_t shift)
+   {
+   const size_t shift_words = shift / BOTAN_MP_WORD_BITS,
+                shift_bits  = shift % BOTAN_MP_WORD_BITS;
+
+   const size_t x_sw = x.sig_words();
+
+   BigInt y(x.sign(), x_sw + shift_words + (shift_bits ? 1 : 0));
+   bigint_shl2(y.mutable_data(), x.data(), x_sw, shift_words, shift_bits);
+   return y;
+   }
+
+/*
+* Right Shift Operator
+*/
+BigInt operator>>(const BigInt& x, size_t shift)
+   {
+   const size_t shift_words = shift / BOTAN_MP_WORD_BITS;
+   const size_t shift_bits  = shift % BOTAN_MP_WORD_BITS;
+   const size_t x_sw = x.sig_words();
+
+   BigInt y(x.sign(), x_sw - shift_words);
+   bigint_shr2(y.mutable_data(), x.data(), x_sw, shift_words, shift_bits);
+
+   if(x.is_negative() && y.is_zero())
+      y.set_sign(BigInt::Positive);
+
+   return y;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/big_rand.cpp b/third_party/botan/src/lib/math/bigint/big_rand.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/big_rand.cpp
@@ -0,0 +1,64 @@
+/*
+* BigInt Random Generation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/rng.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+/*
+* Randomize this number
+*/
+void BigInt::randomize(RandomNumberGenerator& rng,
+                       size_t bitsize, bool set_high_bit)
+   {
+   set_sign(Positive);
+
+   if(bitsize == 0)
+      {
+      clear();
+      }
+   else
+      {
+      secure_vector<uint8_t> array = rng.random_vec(round_up(bitsize, 8) / 8);
+
+      // Always cut unwanted bits
+      if(bitsize % 8)
+         array[0] &= 0xFF >> (8 - (bitsize % 8));
+
+      // Set the highest bit if wanted
+      if (set_high_bit)
+         array[0] |= 0x80 >> ((bitsize % 8) ? (8 - bitsize % 8) : 0);
+
+      binary_decode(array);
+      }
+   }
+
+/*
+* Generate a random integer within given range
+*/
+BigInt BigInt::random_integer(RandomNumberGenerator& rng,
+                              const BigInt& min, const BigInt& max)
+   {
+   if(min.is_negative() || max.is_negative() || max <= min)
+      throw Invalid_Argument("BigInt::random_integer invalid range");
+
+   BigInt r;
+
+   const size_t bits = max.bits();
+
+   do
+      {
+      r.randomize(rng, bits, false);
+      }
+   while(r < min || r >= max);
+
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/bigint.cpp b/third_party/botan/src/lib/math/bigint/bigint.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/bigint.cpp
@@ -0,0 +1,552 @@
+/*
+* BigInt Base
+* (C) 1999-2011,2012,2014,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+BigInt::BigInt(const word words[], size_t length)
+   {
+   m_data.set_words(words, length);
+   }
+
+/*
+* Construct a BigInt from a regular number
+*/
+BigInt::BigInt(uint64_t n)
+   {
+   if(n > 0)
+      {
+#if BOTAN_MP_WORD_BITS == 32
+      m_data.set_word_at(0, static_cast<word>(n));
+      m_data.set_word_at(1, static_cast<word>(n >> 32));
+#else
+      m_data.set_word_at(0, n);
+#endif
+      }
+
+   }
+
+/*
+* Construct a BigInt of the specified size
+*/
+BigInt::BigInt(Sign s, size_t size)
+   {
+   m_data.set_size(size);
+   m_signedness = s;
+   }
+
+/*
+* Construct a BigInt from a string
+*/
+BigInt::BigInt(const std::string& str)
+   {
+   Base base = Decimal;
+   size_t markers = 0;
+   bool negative = false;
+
+   if(str.length() > 0 && str[0] == '-')
+      {
+      markers += 1;
+      negative = true;
+      }
+
+   if(str.length() > markers + 2 && str[markers    ] == '0' &&
+                                    str[markers + 1] == 'x')
+      {
+      markers += 2;
+      base = Hexadecimal;
+      }
+
+   *this = decode(cast_char_ptr_to_uint8(str.data()) + markers,
+                  str.length() - markers, base);
+
+   if(negative) set_sign(Negative);
+   else         set_sign(Positive);
+   }
+
+BigInt::BigInt(const uint8_t input[], size_t length)
+   {
+   binary_decode(input, length);
+   }
+
+/*
+* Construct a BigInt from an encoded BigInt
+*/
+BigInt::BigInt(const uint8_t input[], size_t length, Base base)
+   {
+   *this = decode(input, length, base);
+   }
+
+BigInt::BigInt(const uint8_t buf[], size_t length, size_t max_bits)
+   {
+   const size_t max_bytes = std::min(length, (max_bits + 7) / 8);
+   binary_decode(buf, max_bytes);
+
+   const size_t b = this->bits();
+   if(b > max_bits)
+      {
+      *this >>= (b - max_bits);
+      }
+   }
+
+/*
+* Construct a BigInt from an encoded BigInt
+*/
+BigInt::BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit)
+   {
+   randomize(rng, bits, set_high_bit);
+   }
+
+uint8_t BigInt::byte_at(size_t n) const
+   {
+   return get_byte(sizeof(word) - (n % sizeof(word)) - 1,
+                   word_at(n / sizeof(word)));
+   }
+
+int32_t BigInt::cmp_word(word other) const
+   {
+   if(is_negative())
+      return -1; // other is positive ...
+
+   const size_t sw = this->sig_words();
+   if(sw > 1)
+      return 1; // must be larger since other is just one word ...
+
+   return bigint_cmp(this->data(), sw, &other, 1);
+   }
+
+/*
+* Comparison Function
+*/
+int32_t BigInt::cmp(const BigInt& other, bool check_signs) const
+   {
+   if(check_signs)
+      {
+      if(other.is_positive() && this->is_negative())
+         return -1;
+
+      if(other.is_negative() && this->is_positive())
+         return 1;
+
+      if(other.is_negative() && this->is_negative())
+         return (-bigint_cmp(this->data(), this->size(),
+                             other.data(), other.size()));
+      }
+
+   return bigint_cmp(this->data(), this->size(),
+                     other.data(), other.size());
+   }
+
+bool BigInt::is_equal(const BigInt& other) const
+   {
+   if(this->sign() != other.sign())
+      return false;
+
+   return bigint_ct_is_eq(this->data(), this->sig_words(),
+                          other.data(), other.sig_words()).is_set();
+   }
+
+bool BigInt::is_less_than(const BigInt& other) const
+   {
+   if(this->is_negative() && other.is_positive())
+      return true;
+
+   if(this->is_positive() && other.is_negative())
+      return false;
+
+   if(other.is_negative() && this->is_negative())
+      {
+      return !bigint_ct_is_lt(other.data(), other.sig_words(),
+                              this->data(), this->sig_words(), true).is_set();
+      }
+
+   return bigint_ct_is_lt(this->data(), this->sig_words(),
+                          other.data(), other.sig_words()).is_set();
+   }
+
+void BigInt::encode_words(word out[], size_t size) const
+   {
+   const size_t words = sig_words();
+
+   if(words > size)
+      throw Encoding_Error("BigInt::encode_words value too large to encode");
+
+   clear_mem(out, size);
+   copy_mem(out, data(), words);
+   }
+
+size_t BigInt::Data::calc_sig_words() const
+   {
+   const size_t sz = m_reg.size();
+   size_t sig = sz;
+
+   word sub = 1;
+
+   for(size_t i = 0; i != sz; ++i)
+      {
+      const word w = m_reg[sz - i - 1];
+      sub &= ct_is_zero(w);
+      sig -= sub;
+      }
+
+   /*
+   * This depends on the data so is poisoned, but unpoison it here as
+   * later conditionals are made on the size.
+   */
+   CT::unpoison(sig);
+
+   return sig;
+   }
+
+/*
+* Return bits {offset...offset+length}
+*/
+uint32_t BigInt::get_substring(size_t offset, size_t length) const
+   {
+   if(length == 0 || length > 32)
+      throw Invalid_Argument("BigInt::get_substring invalid substring length");
+
+   const uint32_t mask = 0xFFFFFFFF >> (32 - length);
+
+   const size_t word_offset = offset / BOTAN_MP_WORD_BITS;
+   const size_t wshift = (offset % BOTAN_MP_WORD_BITS);
+
+   /*
+   * The substring is contained within one or at most two words. The
+   * offset and length are not secret, so we can perform conditional
+   * operations on those values.
+   */
+   const word w0 = word_at(word_offset);
+
+   if(wshift == 0 || (offset + length) / BOTAN_MP_WORD_BITS == word_offset)
+      {
+      return static_cast<uint32_t>(w0 >> wshift) & mask;
+      }
+   else
+      {
+      const word w1 = word_at(word_offset + 1);
+      return static_cast<uint32_t>((w0 >> wshift) | (w1 << (BOTAN_MP_WORD_BITS - wshift))) & mask;
+      }
+   }
+
+/*
+* Convert this number to a uint32_t, if possible
+*/
+uint32_t BigInt::to_u32bit() const
+   {
+   if(is_negative())
+      throw Encoding_Error("BigInt::to_u32bit: Number is negative");
+   if(bits() > 32)
+      throw Encoding_Error("BigInt::to_u32bit: Number is too big to convert");
+
+   uint32_t out = 0;
+   for(size_t i = 0; i != 4; ++i)
+      out = (out << 8) | byte_at(3-i);
+   return out;
+   }
+
+/*
+* Set bit number n
+*/
+void BigInt::conditionally_set_bit(size_t n, bool set_it)
+   {
+   const size_t which = n / BOTAN_MP_WORD_BITS;
+   const word mask = static_cast<word>(set_it) << (n % BOTAN_MP_WORD_BITS);
+   m_data.set_word_at(which, word_at(which) | mask);
+   }
+
+/*
+* Clear bit number n
+*/
+void BigInt::clear_bit(size_t n)
+   {
+   const size_t which = n / BOTAN_MP_WORD_BITS;
+
+   if(which < size())
+      {
+      const word mask = ~(static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS));
+      m_data.set_word_at(which, word_at(which) & mask);
+      }
+   }
+
+size_t BigInt::bytes() const
+   {
+   return round_up(bits(), 8) / 8;
+   }
+
+size_t BigInt::top_bits_free() const
+   {
+   const size_t words = sig_words();
+
+   const word top_word = word_at(words - 1);
+   const size_t bits_used = high_bit(top_word);
+   CT::unpoison(bits_used);
+   return BOTAN_MP_WORD_BITS - bits_used;
+   }
+
+size_t BigInt::bits() const
+   {
+   const size_t words = sig_words();
+
+   if(words == 0)
+      return 0;
+
+   const size_t full_words = (words - 1) * BOTAN_MP_WORD_BITS;
+   const size_t top_bits = BOTAN_MP_WORD_BITS - top_bits_free();
+
+   return full_words + top_bits;
+   }
+
+/*
+* Calcluate the size in a certain base
+*/
+size_t BigInt::encoded_size(Base base) const
+   {
+   static const double LOG_2_BASE_10 = 0.30102999566;
+
+   if(base == Binary)
+      return bytes();
+   else if(base == Hexadecimal)
+      return 2*bytes();
+   else if(base == Decimal)
+      return static_cast<size_t>((bits() * LOG_2_BASE_10) + 1);
+   else
+      throw Invalid_Argument("Unknown base for BigInt encoding");
+   }
+
+/*
+* Return the negation of this number
+*/
+BigInt BigInt::operator-() const
+   {
+   BigInt x = (*this);
+   x.flip_sign();
+   return x;
+   }
+
+size_t BigInt::reduce_below(const BigInt& p, secure_vector<word>& ws)
+   {
+   if(p.is_negative() || this->is_negative())
+      throw Invalid_Argument("BigInt::reduce_below both values must be positive");
+
+   const size_t p_words = p.sig_words();
+
+   if(size() < p_words + 1)
+      grow_to(p_words + 1);
+
+   if(ws.size() < p_words + 1)
+      ws.resize(p_words + 1);
+
+   clear_mem(ws.data(), ws.size());
+
+   size_t reductions = 0;
+
+   for(;;)
+      {
+      word borrow = bigint_sub3(ws.data(), data(), p_words + 1, p.data(), p_words);
+      if(borrow)
+         break;
+
+      ++reductions;
+      swap_reg(ws);
+      }
+
+   return reductions;
+   }
+
+void BigInt::ct_reduce_below(const BigInt& mod, secure_vector<word>& ws, size_t bound)
+   {
+   if(mod.is_negative() || this->is_negative())
+      throw Invalid_Argument("BigInt::ct_reduce_below both values must be positive");
+
+   const size_t mod_words = mod.sig_words();
+
+   grow_to(mod_words);
+
+   const size_t sz = size();
+
+   ws.resize(sz);
+
+   clear_mem(ws.data(), sz);
+
+   for(size_t i = 0; i != bound; ++i)
+      {
+      word borrow = bigint_sub3(ws.data(), data(), sz, mod.data(), mod_words);
+
+      CT::Mask<word>::is_zero(borrow).select_n(mutable_data(), ws.data(), data(), sz);
+      }
+   }
+
+/*
+* Return the absolute value of this number
+*/
+BigInt BigInt::abs() const
+   {
+   BigInt x = (*this);
+   x.set_sign(Positive);
+   return x;
+   }
+
+void BigInt::binary_encode(uint8_t buf[]) const
+   {
+   this->binary_encode(buf, bytes());
+   }
+
+/*
+* Encode this number into bytes
+*/
+void BigInt::binary_encode(uint8_t output[], size_t len) const
+   {
+   const size_t full_words = len / sizeof(word);
+   const size_t extra_bytes = len % sizeof(word);
+
+   for(size_t i = 0; i != full_words; ++i)
+      {
+      const word w = word_at(i);
+      store_be(w, output + (len - (i+1)*sizeof(word)));
+      }
+
+   if(extra_bytes > 0)
+      {
+      const word w = word_at(full_words);
+
+      for(size_t i = 0; i != extra_bytes; ++i)
+         {
+         output[extra_bytes - i - 1] = get_byte(sizeof(word) - i - 1, w);
+         }
+      }
+   }
+
+/*
+* Set this number to the value in buf
+*/
+void BigInt::binary_decode(const uint8_t buf[], size_t length)
+   {
+   clear();
+
+   const size_t full_words = length / sizeof(word);
+   const size_t extra_bytes = length % sizeof(word);
+
+   secure_vector<word> reg((round_up(full_words + (extra_bytes > 0 ? 1 : 0), 8)));
+
+   for(size_t i = 0; i != full_words; ++i)
+      {
+      reg[i] = load_be<word>(buf + length - sizeof(word)*(i+1), 0);
+      }
+
+   if(extra_bytes > 0)
+      {
+      for(size_t i = 0; i != extra_bytes; ++i)
+         reg[full_words] = (reg[full_words] << 8) | buf[i];
+      }
+
+   m_data.swap(reg);
+   }
+
+void BigInt::ct_cond_add(bool predicate, const BigInt& value)
+   {
+   if(this->is_negative() || value.is_negative())
+      throw Invalid_Argument("BigInt::ct_cond_add requires both values to be positive");
+   this->grow_to(1 + value.sig_words());
+
+   bigint_cnd_add(static_cast<word>(predicate),
+                  this->mutable_data(), this->size(),
+                  value.data(), value.sig_words());
+   }
+
+void BigInt::ct_cond_swap(bool predicate, BigInt& other)
+   {
+   const size_t max_words = std::max(size(), other.size());
+   grow_to(max_words);
+   other.grow_to(max_words);
+
+   bigint_cnd_swap(predicate, this->mutable_data(), other.mutable_data(), max_words);
+   }
+
+void BigInt::cond_flip_sign(bool predicate)
+   {
+   // This code is assuming Negative == 0, Positive == 1
+
+   const auto mask = CT::Mask<uint8_t>::expand(predicate);
+
+   const uint8_t current_sign = static_cast<uint8_t>(sign());
+
+   const uint8_t new_sign = mask.select(current_sign ^ 1, current_sign);
+
+   set_sign(static_cast<Sign>(new_sign));
+   }
+
+void BigInt::ct_cond_assign(bool predicate, const BigInt& other)
+   {
+   const size_t t_words = size();
+   const size_t o_words = other.size();
+
+   if(o_words < t_words)
+      grow_to(o_words);
+
+   const size_t r_words = std::max(t_words, o_words);
+
+   const auto mask = CT::Mask<word>::expand(predicate);
+
+   for(size_t i = 0; i != r_words; ++i)
+      {
+      const word o_word = other.word_at(i);
+      const word t_word = this->word_at(i);
+      this->set_word_at(i, mask.select(o_word, t_word));
+      }
+
+   const bool different_sign = sign() != other.sign();
+   cond_flip_sign(predicate && different_sign);
+   }
+
+#if defined(BOTAN_HAS_VALGRIND)
+void BigInt::const_time_poison() const
+   {
+   CT::poison(m_data.const_data(), m_data.size());
+   }
+
+void BigInt::const_time_unpoison() const
+   {
+   CT::unpoison(m_data.const_data(), m_data.size());
+   }
+#endif
+
+void BigInt::const_time_lookup(secure_vector<word>& output,
+                               const std::vector<BigInt>& vec,
+                               size_t idx)
+   {
+   const size_t words = output.size();
+
+   clear_mem(output.data(), output.size());
+
+   CT::poison(&idx, sizeof(idx));
+
+   for(size_t i = 0; i != vec.size(); ++i)
+      {
+      BOTAN_ASSERT(vec[i].size() >= words,
+                   "Word size as expected in const_time_lookup");
+
+      const auto mask = CT::Mask<word>::is_equal(i, idx);
+
+      for(size_t w = 0; w != words; ++w)
+         {
+         const word viw = vec[i].word_at(w);
+         output[w] = mask.if_set_return(viw);
+         }
+      }
+
+   CT::unpoison(idx);
+   CT::unpoison(output.data(), output.size());
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/bigint.h b/third_party/botan/src/lib/math/bigint/bigint.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/bigint.h
@@ -0,0 +1,1152 @@
+/*
+* BigInt
+* (C) 1999-2008,2012,2018 Jack Lloyd
+*     2007 FlexSecure
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BIGINT_H_
+#define BOTAN_BIGINT_H_
+
+#include <botan/types.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <iosfwd>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Arbitrary precision integer
+*/
+class BOTAN_PUBLIC_API(2,0) BigInt final
+   {
+   public:
+     /**
+     * Base enumerator for encoding and decoding
+     */
+     enum Base { Decimal = 10, Hexadecimal = 16, Binary = 256 };
+
+     /**
+     * Sign symbol definitions for positive and negative numbers
+     */
+     enum Sign { Negative = 0, Positive = 1 };
+
+     /**
+     * DivideByZero Exception
+     *
+     * In a future release this exception will be removed and its usage
+     * replaced by Invalid_Argument
+     */
+     class BOTAN_PUBLIC_API(2,0) DivideByZero final : public Invalid_Argument
+        {
+        public:
+           DivideByZero() : Invalid_Argument("BigInt divide by zero") {}
+        };
+
+     /**
+     * Create empty BigInt
+     */
+     BigInt() = default;
+
+     /**
+     * Create BigInt from 64 bit integer
+     * @param n initial value of this BigInt
+     */
+     BigInt(uint64_t n);
+
+     /**
+     * Copy Constructor
+     * @param other the BigInt to copy
+     */
+     BigInt(const BigInt& other) = default;
+
+     /**
+     * Create BigInt from a string. If the string starts with 0x the
+     * rest of the string will be interpreted as hexadecimal digits.
+     * Otherwise, it will be interpreted as a decimal number.
+     *
+     * @param str the string to parse for an integer value
+     */
+     explicit BigInt(const std::string& str);
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the byte array holding the value
+     * @param length size of buf
+     */
+     BigInt(const uint8_t buf[], size_t length);
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param vec the byte vector holding the value
+     */
+     template<typename Alloc>
+     explicit BigInt(const std::vector<uint8_t, Alloc>& vec) : BigInt(vec.data(), vec.size()) {}
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the byte array holding the value
+     * @param length size of buf
+     * @param base is the number base of the integer in buf
+     */
+     BigInt(const uint8_t buf[], size_t length, Base base);
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the byte array holding the value
+     * @param length size of buf
+     * @param max_bits if the resulting integer is more than max_bits,
+     *        it will be shifted so it is at most max_bits in length.
+     */
+     BigInt(const uint8_t buf[], size_t length, size_t max_bits);
+
+     /**
+     * Create a BigInt from an array of words
+     * @param words the words
+     * @param length number of words
+     */
+     BigInt(const word words[], size_t length);
+
+     /**
+     * \brief Create a random BigInt of the specified size
+     *
+     * @param rng random number generator
+     * @param bits size in bits
+     * @param set_high_bit if true, the highest bit is always set
+     *
+     * @see randomize
+     */
+     BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit = true);
+
+     /**
+     * Create BigInt of specified size, all zeros
+     * @param sign the sign
+     * @param n size of the internal register in words
+     */
+     BigInt(Sign sign, size_t n);
+
+     /**
+     * Move constructor
+     */
+     BigInt(BigInt&& other)
+        {
+        this->swap(other);
+        }
+
+     ~BigInt() { const_time_unpoison(); }
+
+     /**
+     * Move assignment
+     */
+     BigInt& operator=(BigInt&& other)
+        {
+        if(this != &other)
+           this->swap(other);
+
+        return (*this);
+        }
+
+     /**
+     * Copy assignment
+     */
+     BigInt& operator=(const BigInt&) = default;
+
+     /**
+     * Swap this value with another
+     * @param other BigInt to swap values with
+     */
+     void swap(BigInt& other)
+        {
+        m_data.swap(other.m_data);
+        std::swap(m_signedness, other.m_signedness);
+        }
+
+     void swap_reg(secure_vector<word>& reg)
+        {
+        m_data.swap(reg);
+        // sign left unchanged
+        }
+
+     /**
+     * += operator
+     * @param y the BigInt to add to this
+     */
+     BigInt& operator+=(const BigInt& y)
+        {
+        return add(y.data(), y.sig_words(), y.sign());
+        }
+
+     /**
+     * += operator
+     * @param y the word to add to this
+     */
+     BigInt& operator+=(word y)
+        {
+        return add(&y, 1, Positive);
+        }
+
+     /**
+     * -= operator
+     * @param y the BigInt to subtract from this
+     */
+     BigInt& operator-=(const BigInt& y)
+        {
+        return sub(y.data(), y.sig_words(), y.sign());
+        }
+
+     /**
+     * -= operator
+     * @param y the word to subtract from this
+     */
+     BigInt& operator-=(word y)
+        {
+        return sub(&y, 1, Positive);
+        }
+
+     /**
+     * *= operator
+     * @param y the BigInt to multiply with this
+     */
+     BigInt& operator*=(const BigInt& y);
+
+     /**
+     * *= operator
+     * @param y the word to multiply with this
+     */
+     BigInt& operator*=(word y);
+
+     /**
+     * /= operator
+     * @param y the BigInt to divide this by
+     */
+     BigInt& operator/=(const BigInt& y);
+
+     /**
+     * Modulo operator
+     * @param y the modulus to reduce this by
+     */
+     BigInt& operator%=(const BigInt& y);
+
+     /**
+     * Modulo operator
+     * @param y the modulus (word) to reduce this by
+     */
+     word    operator%=(word y);
+
+     /**
+     * Left shift operator
+     * @param shift the number of bits to shift this left by
+     */
+     BigInt& operator<<=(size_t shift);
+
+     /**
+     * Right shift operator
+     * @param shift the number of bits to shift this right by
+     */
+     BigInt& operator>>=(size_t shift);
+
+     /**
+     * Increment operator
+     */
+     BigInt& operator++() { return (*this += 1); }
+
+     /**
+     * Decrement operator
+     */
+     BigInt& operator--() { return (*this -= 1); }
+
+     /**
+     * Postfix increment operator
+     */
+     BigInt  operator++(int) { BigInt x = (*this); ++(*this); return x; }
+
+     /**
+     * Postfix decrement operator
+     */
+     BigInt  operator--(int) { BigInt x = (*this); --(*this); return x; }
+
+     /**
+     * Unary negation operator
+     * @return negative this
+     */
+     BigInt operator-() const;
+
+     /**
+     * ! operator
+     * @return true iff this is zero, otherwise false
+     */
+     bool operator !() const { return (!is_nonzero()); }
+
+     static BigInt add2(const BigInt& x, const word y[], size_t y_words, Sign y_sign);
+
+     BigInt& add(const word y[], size_t y_words, Sign sign);
+
+     BigInt& sub(const word y[], size_t y_words, Sign sign)
+        {
+        return add(y, y_words, sign == Positive ? Negative : Positive);
+        }
+
+     /**
+     * Multiply this with y
+     * @param y the BigInt to multiply with this
+     * @param ws a temp workspace
+     */
+     BigInt& mul(const BigInt& y, secure_vector<word>& ws);
+
+     /**
+     * Square value of *this
+     * @param ws a temp workspace
+     */
+     BigInt& square(secure_vector<word>& ws);
+
+     /**
+     * Set *this to y - *this
+     * @param y the BigInt to subtract from as a sequence of words
+     * @param y_words length of y in words
+     * @param ws a temp workspace
+     */
+     BigInt& rev_sub(const word y[], size_t y_words, secure_vector<word>& ws);
+
+     /**
+     * Set *this to (*this + y) % mod
+     * This function assumes *this is >= 0 && < mod
+     * @param y the BigInt to add - assumed y >= 0 and y < mod
+     * @param mod the positive modulus
+     * @param ws a temp workspace
+     */
+     BigInt& mod_add(const BigInt& y, const BigInt& mod, secure_vector<word>& ws);
+
+     /**
+     * Set *this to (*this - y) % mod
+     * This function assumes *this is >= 0 && < mod
+     * @param y the BigInt to subtract - assumed y >= 0 and y < mod
+     * @param mod the positive modulus
+     * @param ws a temp workspace
+     */
+     BigInt& mod_sub(const BigInt& y, const BigInt& mod, secure_vector<word>& ws);
+
+     /**
+     * Set *this to (*this * y) % mod
+     * This function assumes *this is >= 0 && < mod
+     * y should be small, less than 16
+     * @param y the small integer to multiply by
+     * @param mod the positive modulus
+     * @param ws a temp workspace
+     */
+     BigInt& mod_mul(uint8_t y, const BigInt& mod, secure_vector<word>& ws);
+
+     /**
+     * Return *this % mod
+     *
+     * Assumes that *this is (if anything) only slightly larger than
+     * mod and performs repeated subtractions. It should not be used if
+     * *this is much larger than mod, instead use modulo operator.
+     */
+     size_t reduce_below(const BigInt& mod, secure_vector<word> &ws);
+
+     /**
+     * Return *this % mod
+     *
+     * Assumes that *this is (if anything) only slightly larger than mod and
+     * performs repeated subtractions. It should not be used if *this is much
+     * larger than mod, instead use modulo operator.
+     *
+     * Performs exactly bound subtractions, so if *this is >= bound*mod then the
+     * result will not be fully reduced. If bound is zero, nothing happens.
+     */
+     void ct_reduce_below(const BigInt& mod, secure_vector<word> &ws, size_t bound);
+
+     /**
+     * Zeroize the BigInt. The size of the underlying register is not
+     * modified.
+     */
+     void clear() { m_data.set_to_zero(); m_signedness = Positive; }
+
+     /**
+     * Compare this to another BigInt
+     * @param n the BigInt value to compare with
+     * @param check_signs include sign in comparison?
+     * @result if (this<n) return -1, if (this>n) return 1, if both
+     * values are identical return 0 [like Perl's <=> operator]
+     */
+     int32_t cmp(const BigInt& n, bool check_signs = true) const;
+
+     /**
+     * Compare this to another BigInt
+     * @param n the BigInt value to compare with
+     * @result true if this == n or false otherwise
+     */
+     bool is_equal(const BigInt& n) const;
+
+     /**
+     * Compare this to another BigInt
+     * @param n the BigInt value to compare with
+     * @result true if this < n or false otherwise
+     */
+     bool is_less_than(const BigInt& n) const;
+
+     /**
+     * Compare this to an integer
+     * @param n the value to compare with
+     * @result if (this<n) return -1, if (this>n) return 1, if both
+     * values are identical return 0 [like Perl's <=> operator]
+     */
+     int32_t cmp_word(word n) const;
+
+     /**
+     * Test if the integer has an even value
+     * @result true if the integer is even, false otherwise
+     */
+     bool is_even() const { return (get_bit(0) == 0); }
+
+     /**
+     * Test if the integer has an odd value
+     * @result true if the integer is odd, false otherwise
+     */
+     bool is_odd()  const { return (get_bit(0) == 1); }
+
+     /**
+     * Test if the integer is not zero
+     * @result true if the integer is non-zero, false otherwise
+     */
+     bool is_nonzero() const { return (!is_zero()); }
+
+     /**
+     * Test if the integer is zero
+     * @result true if the integer is zero, false otherwise
+     */
+     bool is_zero() const
+        {
+        return (sig_words() == 0);
+        }
+
+     /**
+     * Set bit at specified position
+     * @param n bit position to set
+     */
+     void set_bit(size_t n)
+        {
+        conditionally_set_bit(n, true);
+        }
+
+     /**
+     * Conditionally set bit at specified position. Note if set_it is
+     * false, nothing happens, and if the bit is already set, it
+     * remains set.
+     *
+     * @param n bit position to set
+     * @param set_it if the bit should be set
+     */
+     void conditionally_set_bit(size_t n, bool set_it);
+
+     /**
+     * Clear bit at specified position
+     * @param n bit position to clear
+     */
+     void clear_bit(size_t n);
+
+     /**
+     * Clear all but the lowest n bits
+     * @param n amount of bits to keep
+     */
+     void mask_bits(size_t n)
+        {
+        m_data.mask_bits(n);
+        }
+
+     /**
+     * Return bit value at specified position
+     * @param n the bit offset to test
+     * @result true, if the bit at position n is set, false otherwise
+     */
+     bool get_bit(size_t n) const
+        {
+        return ((word_at(n / BOTAN_MP_WORD_BITS) >> (n % BOTAN_MP_WORD_BITS)) & 1);
+        }
+
+     /**
+     * Return (a maximum of) 32 bits of the complete value
+     * @param offset the offset to start extracting
+     * @param length amount of bits to extract (starting at offset)
+     * @result the integer extracted from the register starting at
+     * offset with specified length
+     */
+     uint32_t get_substring(size_t offset, size_t length) const;
+
+     /**
+     * Convert this value into a uint32_t, if it is in the range
+     * [0 ... 2**32-1], or otherwise throw an exception.
+     * @result the value as a uint32_t if conversion is possible
+     */
+     uint32_t to_u32bit() const;
+
+     /**
+     * Convert this value to a decimal string.
+     * Warning: decimal conversions are relatively slow
+     */
+     std::string to_dec_string() const;
+
+     /**
+     * Convert this value to a hexadecimal string.
+     */
+     std::string to_hex_string() const;
+
+     /**
+     * @param n the offset to get a byte from
+     * @result byte at offset n
+     */
+     uint8_t byte_at(size_t n) const;
+
+     /**
+     * Return the word at a specified position of the internal register
+     * @param n position in the register
+     * @return value at position n
+     */
+     word word_at(size_t n) const
+        {
+        return m_data.get_word_at(n);
+        }
+
+     void set_word_at(size_t i, word w)
+        {
+        m_data.set_word_at(i, w);
+        }
+
+     void set_words(const word w[], size_t len)
+        {
+        m_data.set_words(w, len);
+        }
+
+     /**
+     * Tests if the sign of the integer is negative
+     * @result true, iff the integer has a negative sign
+     */
+     bool is_negative() const { return (sign() == Negative); }
+
+     /**
+     * Tests if the sign of the integer is positive
+     * @result true, iff the integer has a positive sign
+     */
+     bool is_positive() const { return (sign() == Positive); }
+
+     /**
+     * Return the sign of the integer
+     * @result the sign of the integer
+     */
+     Sign sign() const { return (m_signedness); }
+
+     /**
+     * @result the opposite sign of the represented integer value
+     */
+     Sign reverse_sign() const
+        {
+        if(sign() == Positive)
+           return Negative;
+        return Positive;
+        }
+
+     /**
+     * Flip the sign of this BigInt
+     */
+     void flip_sign()
+        {
+        set_sign(reverse_sign());
+        }
+
+     /**
+     * Set sign of the integer
+     * @param sign new Sign to set
+     */
+     void set_sign(Sign sign)
+        {
+        if(sign == Negative && is_zero())
+           sign = Positive;
+
+        m_signedness = sign;
+        }
+
+     /**
+     * @result absolute (positive) value of this
+     */
+     BigInt abs() const;
+
+     /**
+     * Give size of internal register
+     * @result size of internal register in words
+     */
+     size_t size() const { return m_data.size(); }
+
+     /**
+     * Return how many words we need to hold this value
+     * @result significant words of the represented integer value
+     */
+     size_t sig_words() const
+        {
+        return m_data.sig_words();
+        }
+
+     /**
+     * Give byte length of the integer
+     * @result byte length of the represented integer value
+     */
+     size_t bytes() const;
+
+     /**
+     * Get the bit length of the integer
+     * @result bit length of the represented integer value
+     */
+     size_t bits() const;
+
+     /**
+     * Get the number of high bits unset in the top (allocated) word
+     * of this integer. Returns BOTAN_MP_WORD_BITS only iff *this is
+     * zero. Ignores sign.
+     */
+     size_t top_bits_free() const;
+
+     /**
+     * Return a mutable pointer to the register
+     * @result a pointer to the start of the internal register
+     */
+     word* mutable_data() { return m_data.mutable_data(); }
+
+     /**
+     * Return a const pointer to the register
+     * @result a pointer to the start of the internal register
+     */
+     const word* data() const { return m_data.const_data(); }
+
+     /**
+     * Don't use this function in application code
+     */
+     secure_vector<word>& get_word_vector() { return m_data.mutable_vector(); }
+
+     /**
+     * Don't use this function in application code
+     */
+     const secure_vector<word>& get_word_vector() const { return m_data.const_vector(); }
+
+     /**
+     * Increase internal register buffer to at least n words
+     * @param n new size of register
+     */
+     void grow_to(size_t n) const { m_data.grow_to(n); }
+
+     /**
+     * Resize the vector to the minimum word size to hold the integer, or
+     * min_size words, whichever is larger
+     */
+     void BOTAN_DEPRECATED("Use resize if required") shrink_to_fit(size_t min_size = 0)
+        {
+        m_data.shrink_to_fit(min_size);
+        }
+
+     void resize(size_t s) { m_data.resize(s); }
+
+     /**
+     * Fill BigInt with a random number with size of bitsize
+     *
+     * If \p set_high_bit is true, the highest bit will be set, which causes
+     * the entropy to be \a bits-1. Otherwise the highest bit is randomly chosen
+     * by the rng, causing the entropy to be \a bits.
+     *
+     * @param rng the random number generator to use
+     * @param bitsize number of bits the created random value should have
+     * @param set_high_bit if true, the highest bit is always set
+     */
+     void randomize(RandomNumberGenerator& rng, size_t bitsize, bool set_high_bit = true);
+
+     /**
+     * Store BigInt-value in a given byte array
+     * @param buf destination byte array for the integer value
+     */
+     void binary_encode(uint8_t buf[]) const;
+
+     /**
+     * Store BigInt-value in a given byte array. If len is less than
+     * the size of the value, then it will be truncated. If len is
+     * greater than the size of the value, it will be zero-padded.
+     * If len exactly equals this->bytes(), this function behaves identically
+     * to binary_encode.
+     *
+     * @param buf destination byte array for the integer value
+     * @param len how many bytes to write
+     */
+     void binary_encode(uint8_t buf[], size_t len) const;
+
+     /**
+     * Read integer value from a byte array with given size
+     * @param buf byte array buffer containing the integer
+     * @param length size of buf
+     */
+     void binary_decode(const uint8_t buf[], size_t length);
+
+     /**
+     * Read integer value from a byte vector
+     * @param buf the vector to load from
+     */
+     template<typename Alloc>
+     void binary_decode(const std::vector<uint8_t, Alloc>& buf)
+        {
+        binary_decode(buf.data(), buf.size());
+        }
+
+     /**
+     * @param base the base to measure the size for
+     * @return size of this integer in base base
+     *
+     * Deprecated. This is only needed when using the `encode` and
+     * `encode_locked` functions, which are also deprecated.
+     */
+     BOTAN_DEPRECATED("See comments on declaration")
+     size_t encoded_size(Base base = Binary) const;
+
+     /**
+     * Place the value into out, zero-padding up to size words
+     * Throw if *this cannot be represented in size words
+     */
+     void encode_words(word out[], size_t size) const;
+
+     /**
+     * If predicate is true assign other to *this
+     * Uses a masked operation to avoid side channels
+     */
+     void ct_cond_assign(bool predicate, const BigInt& other);
+
+     /**
+     * If predicate is true swap *this and other
+     * Uses a masked operation to avoid side channels
+     */
+     void ct_cond_swap(bool predicate, BigInt& other);
+
+     /**
+     * If predicate is true add value to *this
+     */
+     void ct_cond_add(bool predicate, const BigInt& value);
+
+     /**
+     * If predicate is true flip the sign of *this
+     */
+     void cond_flip_sign(bool predicate);
+
+#if defined(BOTAN_HAS_VALGRIND)
+     void const_time_poison() const;
+     void const_time_unpoison() const;
+#else
+     void const_time_poison() const {}
+     void const_time_unpoison() const {}
+#endif
+
+     /**
+     * @param rng a random number generator
+     * @param min the minimum value (must be non-negative)
+     * @param max the maximum value (must be non-negative and > min)
+     * @return random integer in [min,max)
+     */
+     static BigInt random_integer(RandomNumberGenerator& rng,
+                                  const BigInt& min,
+                                  const BigInt& max);
+
+     /**
+     * Create a power of two
+     * @param n the power of two to create
+     * @return bigint representing 2^n
+     */
+     static BigInt power_of_2(size_t n)
+        {
+        BigInt b;
+        b.set_bit(n);
+        return b;
+        }
+
+     /**
+     * Encode the integer value from a BigInt to a std::vector of bytes
+     * @param n the BigInt to use as integer source
+     * @result secure_vector of bytes containing the bytes of the integer
+     */
+     static std::vector<uint8_t> encode(const BigInt& n)
+        {
+        std::vector<uint8_t> output(n.bytes());
+        n.binary_encode(output.data());
+        return output;
+        }
+
+     /**
+     * Encode the integer value from a BigInt to a secure_vector of bytes
+     * @param n the BigInt to use as integer source
+     * @result secure_vector of bytes containing the bytes of the integer
+     */
+     static secure_vector<uint8_t> encode_locked(const BigInt& n)
+        {
+        secure_vector<uint8_t> output(n.bytes());
+        n.binary_encode(output.data());
+        return output;
+        }
+
+     /**
+     * Encode the integer value from a BigInt to a byte array
+     * @param buf destination byte array for the encoded integer
+     * @param n the BigInt to use as integer source
+     */
+     static BOTAN_DEPRECATED("Use n.binary_encode") void encode(uint8_t buf[], const BigInt& n)
+        {
+        n.binary_encode(buf);
+        }
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the binary value to load
+     * @param length size of buf
+     * @result BigInt representing the integer in the byte array
+     */
+     static BigInt decode(const uint8_t buf[], size_t length)
+        {
+        return BigInt(buf, length);
+        }
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the binary value to load
+     * @result BigInt representing the integer in the byte array
+     */
+     template<typename Alloc>
+     static BigInt decode(const std::vector<uint8_t, Alloc>& buf)
+        {
+        return BigInt(buf);
+        }
+
+     /**
+     * Encode the integer value from a BigInt to a std::vector of bytes
+     * @param n the BigInt to use as integer source
+     * @param base number-base of resulting byte array representation
+     * @result secure_vector of bytes containing the integer with given base
+     *
+     * Deprecated. If you need Binary, call the version of encode that doesn't
+     * take a Base. If you need Hex or Decimal output, use to_hex_string or
+     * to_dec_string resp.
+     */
+     BOTAN_DEPRECATED("See comments on declaration")
+     static std::vector<uint8_t> encode(const BigInt& n, Base base);
+
+     /**
+     * Encode the integer value from a BigInt to a secure_vector of bytes
+     * @param n the BigInt to use as integer source
+     * @param base number-base of resulting byte array representation
+     * @result secure_vector of bytes containing the integer with given base
+     *
+     * Deprecated. If you need Binary, call the version of encode_locked that
+     * doesn't take a Base. If you need Hex or Decimal output, use to_hex_string
+     * or to_dec_string resp.
+     */
+     BOTAN_DEPRECATED("See comments on declaration")
+     static secure_vector<uint8_t> encode_locked(const BigInt& n,
+                                                 Base base);
+
+     /**
+     * Encode the integer value from a BigInt to a byte array
+     * @param buf destination byte array for the encoded integer
+     * value with given base
+     * @param n the BigInt to use as integer source
+     * @param base number-base of resulting byte array representation
+     *
+     * Deprecated. If you need Binary, call binary_encode. If you need
+     * Hex or Decimal output, use to_hex_string or to_dec_string resp.
+     */
+     BOTAN_DEPRECATED("See comments on declaration")
+     static void encode(uint8_t buf[], const BigInt& n, Base base);
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the binary value to load
+     * @param length size of buf
+     * @param base number-base of the integer in buf
+     * @result BigInt representing the integer in the byte array
+     */
+     static BigInt decode(const uint8_t buf[], size_t length,
+                          Base base);
+
+     /**
+     * Create a BigInt from an integer in a byte array
+     * @param buf the binary value to load
+     * @param base number-base of the integer in buf
+     * @result BigInt representing the integer in the byte array
+     */
+     template<typename Alloc>
+     static BigInt decode(const std::vector<uint8_t, Alloc>& buf, Base base)
+        {
+        if(base == Binary)
+           return BigInt(buf);
+        return BigInt::decode(buf.data(), buf.size(), base);
+        }
+
+     /**
+     * Encode a BigInt to a byte array according to IEEE 1363
+     * @param n the BigInt to encode
+     * @param bytes the length of the resulting secure_vector<uint8_t>
+     * @result a secure_vector<uint8_t> containing the encoded BigInt
+     */
+     static secure_vector<uint8_t> encode_1363(const BigInt& n, size_t bytes);
+
+     static void encode_1363(uint8_t out[], size_t bytes, const BigInt& n);
+
+     /**
+     * Encode two BigInt to a byte array according to IEEE 1363
+     * @param n1 the first BigInt to encode
+     * @param n2 the second BigInt to encode
+     * @param bytes the length of the encoding of each single BigInt
+     * @result a secure_vector<uint8_t> containing the concatenation of the two encoded BigInt
+     */
+     static secure_vector<uint8_t> encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes);
+
+     /**
+     * Set output = vec[idx].m_reg in constant time
+     *
+     * All elements of vec must have the same size, and output must be
+     * pre-allocated with the same size.
+     */
+     static void BOTAN_DEPRECATED("No longer in use") const_time_lookup(
+        secure_vector<word>& output,
+        const std::vector<BigInt>& vec,
+        size_t idx);
+
+   private:
+
+     class Data
+        {
+        public:
+           word* mutable_data()
+              {
+              invalidate_sig_words();
+              return m_reg.data();
+              }
+
+           const word* const_data() const
+              {
+              return m_reg.data();
+              }
+
+           secure_vector<word>& mutable_vector()
+              {
+              invalidate_sig_words();
+              return m_reg;
+              }
+
+           const secure_vector<word>& const_vector() const
+              {
+              return m_reg;
+              }
+
+           word get_word_at(size_t n) const
+              {
+              if(n < m_reg.size())
+                 return m_reg[n];
+              return 0;
+              }
+
+           void set_word_at(size_t i, word w)
+              {
+              invalidate_sig_words();
+              if(i >= m_reg.size())
+                 {
+                 if(w == 0)
+                    return;
+                 grow_to(i + 1);
+                 }
+              m_reg[i] = w;
+              }
+
+           void set_words(const word w[], size_t len)
+              {
+              invalidate_sig_words();
+              m_reg.assign(w, w + len);
+              }
+
+           void set_to_zero()
+              {
+              m_reg.resize(m_reg.capacity());
+              clear_mem(m_reg.data(), m_reg.size());
+              m_sig_words = 0;
+              }
+
+           void set_size(size_t s)
+              {
+              invalidate_sig_words();
+              clear_mem(m_reg.data(), m_reg.size());
+              m_reg.resize(s + (8 - (s % 8)));
+              }
+
+           void mask_bits(size_t n)
+              {
+              if(n == 0) { return set_to_zero(); }
+
+              const size_t top_word = n / BOTAN_MP_WORD_BITS;
+
+              // if(top_word < sig_words()) ?
+              if(top_word < size())
+                 {
+                 const word mask = (static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS)) - 1;
+                 const size_t len = size() - (top_word + 1);
+                 if(len > 0)
+                    {
+                    clear_mem(&m_reg[top_word+1], len);
+                    }
+                 m_reg[top_word] &= mask;
+                 invalidate_sig_words();
+                 }
+              }
+
+           void grow_to(size_t n) const
+              {
+              if(n > size())
+                 {
+                 if(n <= m_reg.capacity())
+                    m_reg.resize(n);
+                 else
+                    m_reg.resize(n + (8 - (n % 8)));
+                 }
+              }
+
+           size_t size() const { return m_reg.size(); }
+
+           void shrink_to_fit(size_t min_size = 0)
+              {
+              const size_t words = std::max(min_size, sig_words());
+              m_reg.resize(words);
+              }
+
+           void resize(size_t s)
+              {
+              m_reg.resize(s);
+              }
+
+           void swap(Data& other)
+              {
+              m_reg.swap(other.m_reg);
+              std::swap(m_sig_words, other.m_sig_words);
+              }
+
+           void swap(secure_vector<word>& reg)
+              {
+              m_reg.swap(reg);
+              invalidate_sig_words();
+              }
+
+           void invalidate_sig_words() const
+              {
+              m_sig_words = sig_words_npos;
+              }
+
+           size_t sig_words() const
+              {
+              if(m_sig_words == sig_words_npos)
+                 {
+                 m_sig_words = calc_sig_words();
+                 }
+              else
+                 {
+                 BOTAN_DEBUG_ASSERT(m_sig_words == calc_sig_words());
+                 }
+              return m_sig_words;
+              }
+        private:
+           static const size_t sig_words_npos = static_cast<size_t>(-1);
+
+           size_t calc_sig_words() const;
+
+           mutable secure_vector<word> m_reg;
+           mutable size_t m_sig_words = sig_words_npos;
+        };
+
+      Data m_data;
+      Sign m_signedness = Positive;
+   };
+
+/*
+* Arithmetic Operators
+*/
+inline BigInt operator+(const BigInt& x, const BigInt& y)
+   {
+   return BigInt::add2(x, y.data(), y.sig_words(), y.sign());
+   }
+
+inline BigInt operator+(const BigInt& x, word y)
+   {
+   return BigInt::add2(x, &y, 1, BigInt::Positive);
+   }
+
+inline BigInt operator+(word x, const BigInt& y)
+   {
+   return y + x;
+   }
+
+inline BigInt operator-(const BigInt& x, const BigInt& y)
+   {
+   return BigInt::add2(x, y.data(), y.sig_words(), y.reverse_sign());
+   }
+
+inline BigInt operator-(const BigInt& x, word y)
+   {
+   return BigInt::add2(x, &y, 1, BigInt::Negative);
+   }
+
+BigInt BOTAN_PUBLIC_API(2,0) operator*(const BigInt& x, const BigInt& y);
+BigInt BOTAN_PUBLIC_API(2,8) operator*(const BigInt& x, word y);
+inline BigInt operator*(word x, const BigInt& y) { return y*x; }
+
+BigInt BOTAN_PUBLIC_API(2,0) operator/(const BigInt& x, const BigInt& d);
+BigInt BOTAN_PUBLIC_API(2,0) operator%(const BigInt& x, const BigInt& m);
+word   BOTAN_PUBLIC_API(2,0) operator%(const BigInt& x, word m);
+BigInt BOTAN_PUBLIC_API(2,0) operator<<(const BigInt& x, size_t n);
+BigInt BOTAN_PUBLIC_API(2,0) operator>>(const BigInt& x, size_t n);
+
+/*
+* Comparison Operators
+*/
+inline bool operator==(const BigInt& a, const BigInt& b)
+   { return a.is_equal(b); }
+inline bool operator!=(const BigInt& a, const BigInt& b)
+   { return !a.is_equal(b); }
+inline bool operator<=(const BigInt& a, const BigInt& b)
+   { return (a.cmp(b) <= 0); }
+inline bool operator>=(const BigInt& a, const BigInt& b)
+   { return (a.cmp(b) >= 0); }
+inline bool operator<(const BigInt& a, const BigInt& b)
+   { return a.is_less_than(b); }
+inline bool operator>(const BigInt& a, const BigInt& b)
+   { return b.is_less_than(a); }
+
+inline bool operator==(const BigInt& a, word b)
+   { return (a.cmp_word(b) == 0); }
+inline bool operator!=(const BigInt& a, word b)
+   { return (a.cmp_word(b) != 0); }
+inline bool operator<=(const BigInt& a, word b)
+   { return (a.cmp_word(b) <= 0); }
+inline bool operator>=(const BigInt& a, word b)
+   { return (a.cmp_word(b) >= 0); }
+inline bool operator<(const BigInt& a, word b)
+   { return (a.cmp_word(b) < 0); }
+inline bool operator>(const BigInt& a, word b)
+   { return (a.cmp_word(b) > 0); }
+
+/*
+* I/O Operators
+*/
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream&, const BigInt&);
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream&, BigInt&);
+
+}
+
+namespace std {
+
+template<>
+inline void swap<Botan::BigInt>(Botan::BigInt& x, Botan::BigInt& y)
+   {
+   x.swap(y);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/bigint/divide.cpp b/third_party/botan/src/lib/math/bigint/divide.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/divide.cpp
@@ -0,0 +1,236 @@
+/*
+* Division Algorithm
+* (C) 1999-2007,2012,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/divide.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_madd.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Handle signed operands, if necessary
+*/
+void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r)
+   {
+   q.cond_flip_sign(x.sign() != y.sign());
+
+   if(x.is_negative() && r.is_nonzero())
+      {
+      q -= 1;
+      r = y.abs() - r;
+      }
+   }
+
+inline bool division_check(word q, word y2, word y1,
+                           word x3, word x2, word x1)
+   {
+   /*
+   Compute (y3,y2,y1) = (y2,y1) * q
+   and return true if (y3,y2,y1) > (x3,x2,x1)
+   */
+
+   word y3 = 0;
+   y1 = word_madd2(q, y1, &y3);
+   y2 = word_madd2(q, y2, &y3);
+
+   const word x[3] = { x1, x2, x3 };
+   const word y[3] = { y1, y2, y3 };
+
+   return bigint_ct_is_lt(x, 3, y, 3).is_set();
+   }
+
+}
+
+void ct_divide(const BigInt& x, const BigInt& y, BigInt& q_out, BigInt& r_out)
+   {
+   const size_t x_words = x.sig_words();
+   const size_t y_words = y.sig_words();
+
+   const size_t x_bits = x.bits();
+
+   BigInt q(BigInt::Positive, x_words);
+   BigInt r(BigInt::Positive, y_words);
+   BigInt t(BigInt::Positive, y_words); // a temporary
+
+   for(size_t i = 0; i != x_bits; ++i)
+      {
+      const size_t b = x_bits - 1 - i;
+      const bool x_b = x.get_bit(b);
+
+      r *= 2;
+      r.conditionally_set_bit(0, x_b);
+
+      const bool r_gte_y = bigint_sub3(t.mutable_data(), r.data(), r.size(), y.data(), y_words) == 0;
+
+      q.conditionally_set_bit(b, r_gte_y);
+      r.ct_cond_swap(r_gte_y, t);
+      }
+
+   sign_fixup(x, y, q, r);
+   r_out = r;
+   q_out = q;
+   }
+
+void ct_divide_u8(const BigInt& x, uint8_t y, BigInt& q_out, uint8_t& r_out)
+   {
+   const size_t x_words = x.sig_words();
+   const size_t x_bits = x.bits();
+
+   BigInt q(BigInt::Positive, x_words);
+   uint32_t r = 0;
+
+   for(size_t i = 0; i != x_bits; ++i)
+      {
+      const size_t b = x_bits - 1 - i;
+      const bool x_b = x.get_bit(b);
+
+      r *= 2;
+      r += x_b;
+
+      const auto r_gte_y = CT::Mask<uint32_t>::is_gte(r, y);
+
+      q.conditionally_set_bit(b, r_gte_y.is_set());
+      r = r_gte_y.select(r - y, r);
+      }
+
+   if(x.is_negative())
+      {
+      q.flip_sign();
+      if(r != 0)
+         {
+         --q;
+         r = y - r;
+         }
+      }
+
+   r_out = static_cast<uint8_t>(r);
+   q_out = q;
+   }
+
+BigInt ct_modulo(const BigInt& x, const BigInt& y)
+   {
+   if(y.is_negative() || y.is_zero())
+      throw Invalid_Argument("ct_modulo requires y > 0");
+
+   const size_t y_words = y.sig_words();
+
+   const size_t x_bits = x.bits();
+
+   BigInt r(BigInt::Positive, y_words);
+   BigInt t(BigInt::Positive, y_words);
+
+   for(size_t i = 0; i != x_bits; ++i)
+      {
+      const size_t b = x_bits - 1 - i;
+      const bool x_b = x.get_bit(b);
+
+      r *= 2;
+      r.conditionally_set_bit(0, x_b);
+
+      const bool r_gte_y = bigint_sub3(t.mutable_data(), r.data(), r.size(), y.data(), y_words) == 0;
+
+      r.ct_cond_swap(r_gte_y, t);
+      }
+
+   if(x.is_negative())
+      {
+      if(r.is_nonzero())
+         {
+         r = y - r;
+         }
+      }
+
+   return r;
+   }
+
+/*
+* Solve x = q * y + r
+*
+* See Handbook of Applied Cryptography section 14.2.5
+*/
+void divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& r_out)
+   {
+   if(y_arg.is_zero())
+      throw BigInt::DivideByZero();
+
+   const size_t y_words = y_arg.sig_words();
+
+   BOTAN_ASSERT_NOMSG(y_words > 0);
+
+   BigInt y = y_arg;
+
+   BigInt r = x;
+   BigInt q = 0;
+   secure_vector<word> ws;
+
+   r.set_sign(BigInt::Positive);
+   y.set_sign(BigInt::Positive);
+
+   // Calculate shifts needed to normalize y with high bit set
+   const size_t shifts = y.top_bits_free();
+
+   y <<= shifts;
+   r <<= shifts;
+
+   // we know y has not changed size, since we only shifted up to set high bit
+   const size_t t = y_words - 1;
+   const size_t n = std::max(y_words, r.sig_words()) - 1; // r may have changed size however
+
+   BOTAN_ASSERT_NOMSG(n >= t);
+
+   q.grow_to(n - t + 1);
+
+   word* q_words = q.mutable_data();
+
+   BigInt shifted_y = y << (BOTAN_MP_WORD_BITS * (n-t));
+
+   // Set q_{n-t} to number of times r > shifted_y
+   q_words[n-t] = r.reduce_below(shifted_y, ws);
+
+   const word y_t0  = y.word_at(t);
+   const word y_t1  = y.word_at(t-1);
+   BOTAN_DEBUG_ASSERT((y_t0 >> (BOTAN_MP_WORD_BITS-1)) == 1);
+
+   for(size_t j = n; j != t; --j)
+      {
+      const word x_j0  = r.word_at(j);
+      const word x_j1 = r.word_at(j-1);
+      const word x_j2 = r.word_at(j-2);
+
+      word qjt = bigint_divop(x_j0, x_j1, y_t0);
+
+      qjt = CT::Mask<word>::is_equal(x_j0, y_t0).select(MP_WORD_MAX, qjt);
+
+      // Per HAC 14.23, this operation is required at most twice
+      qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2);
+      qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2);
+      BOTAN_DEBUG_ASSERT(division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2) == false);
+
+      shifted_y >>= BOTAN_MP_WORD_BITS;
+      // Now shifted_y == y << (BOTAN_MP_WORD_BITS * (j-t-1))
+
+      // TODO this sequence could be better
+      r -= qjt * shifted_y;
+      qjt -= r.is_negative();
+      r += static_cast<word>(r.is_negative()) * shifted_y;
+
+      q_words[j-t-1] = qjt;
+      }
+
+   r >>= shifts;
+
+   sign_fixup(x, y_arg, q, r);
+
+   r_out = r;
+   q_out = q;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/bigint/divide.h b/third_party/botan/src/lib/math/bigint/divide.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/bigint/divide.h
@@ -0,0 +1,93 @@
+/*
+* Division
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DIVISON_ALGORITHM_H_
+#define BOTAN_DIVISON_ALGORITHM_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(divide.h)
+
+namespace Botan {
+
+/**
+* BigInt Division
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_PUBLIC_API(2,0) divide(const BigInt& x,
+                                  const BigInt& y,
+                                  BigInt& q,
+                                  BigInt& r);
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_PUBLIC_API(2,9) ct_divide(const BigInt& x,
+                                     const BigInt& y,
+                                     BigInt& q,
+                                     BigInt& r);
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @return x/y with remainder discarded
+*/
+inline BigInt ct_divide(const BigInt& x, const BigInt& y)
+   {
+   BigInt q, r;
+   ct_divide(x, y, q, r);
+   return q;
+   }
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_PUBLIC_API(2,9) ct_divide_u8(const BigInt& x,
+                                        uint8_t y,
+                                        BigInt& q,
+                                        uint8_t& r);
+
+/**
+* BigInt modulo, const time variant
+*
+* Using this function is (slightly) cheaper than calling ct_divide and
+* using only the remainder.
+*
+* @param x a non-negative integer
+* @param modulo a positive integer
+* @return result x % modulo
+*/
+BigInt BOTAN_PUBLIC_API(2,9) ct_modulo(const BigInt& x,
+                                       const BigInt& modulo);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/mp/mp_asmi.h b/third_party/botan/src/lib/math/mp/mp_asmi.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_asmi.h
@@ -0,0 +1,611 @@
+/*
+* Lowest Level MPI Algorithms
+* (C) 1999-2010 Jack Lloyd
+*     2006 Luca Piccarreta
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_ASM_INTERNAL_H_
+#define BOTAN_MP_ASM_INTERNAL_H_
+
+#include <botan/internal/mp_madd.h>
+
+namespace Botan {
+
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+
+#define ADDSUB2_OP(OPERATION, INDEX)                     \
+        ASM("movl 4*" #INDEX "(%[y]), %[carry]")         \
+        ASM(OPERATION " %[carry], 4*" #INDEX "(%[x])")   \
+
+#define ADDSUB3_OP(OPERATION, INDEX)                     \
+        ASM("movl 4*" #INDEX "(%[x]), %[carry]")         \
+        ASM(OPERATION " 4*" #INDEX "(%[y]), %[carry]")   \
+        ASM("movl %[carry], 4*" #INDEX "(%[z])")         \
+
+#define LINMUL_OP(WRITE_TO, INDEX)                       \
+        ASM("movl 4*" #INDEX "(%[x]),%%eax")             \
+        ASM("mull %[y]")                                 \
+        ASM("addl %[carry],%%eax")                       \
+        ASM("adcl $0,%%edx")                             \
+        ASM("movl %%edx,%[carry]")                       \
+        ASM("movl %%eax, 4*" #INDEX "(%[" WRITE_TO "])")
+
+#define MULADD_OP(IGNORED, INDEX)                        \
+        ASM("movl 4*" #INDEX "(%[x]),%%eax")             \
+        ASM("mull %[y]")                                 \
+        ASM("addl %[carry],%%eax")                       \
+        ASM("adcl $0,%%edx")                             \
+        ASM("addl 4*" #INDEX "(%[z]),%%eax")             \
+        ASM("adcl $0,%%edx")                             \
+        ASM("movl %%edx,%[carry]")                       \
+        ASM("movl %%eax, 4*" #INDEX " (%[z])")
+
+#define ADD_OR_SUBTRACT(CORE_CODE)     \
+        ASM("rorl %[carry]")           \
+        CORE_CODE                      \
+        ASM("sbbl %[carry],%[carry]")  \
+        ASM("negl %[carry]")
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+#define ADDSUB2_OP(OPERATION, INDEX)                     \
+        ASM("movq 8*" #INDEX "(%[y]), %[carry]")         \
+        ASM(OPERATION " %[carry], 8*" #INDEX "(%[x])")   \
+
+#define ADDSUB3_OP(OPERATION, INDEX)                     \
+        ASM("movq 8*" #INDEX "(%[x]), %[carry]")         \
+        ASM(OPERATION " 8*" #INDEX "(%[y]), %[carry]")   \
+        ASM("movq %[carry], 8*" #INDEX "(%[z])")         \
+
+#define LINMUL_OP(WRITE_TO, INDEX)                       \
+        ASM("movq 8*" #INDEX "(%[x]),%%rax")             \
+        ASM("mulq %[y]")                                 \
+        ASM("addq %[carry],%%rax")                       \
+        ASM("adcq $0,%%rdx")                             \
+        ASM("movq %%rdx,%[carry]")                       \
+        ASM("movq %%rax, 8*" #INDEX "(%[" WRITE_TO "])")
+
+#define MULADD_OP(IGNORED, INDEX)                        \
+        ASM("movq 8*" #INDEX "(%[x]),%%rax")             \
+        ASM("mulq %[y]")                                 \
+        ASM("addq %[carry],%%rax")                       \
+        ASM("adcq $0,%%rdx")                             \
+        ASM("addq 8*" #INDEX "(%[z]),%%rax")             \
+        ASM("adcq $0,%%rdx")                             \
+        ASM("movq %%rdx,%[carry]")                       \
+        ASM("movq %%rax, 8*" #INDEX " (%[z])")
+
+#define ADD_OR_SUBTRACT(CORE_CODE)     \
+        ASM("rorq %[carry]")           \
+        CORE_CODE                      \
+        ASM("sbbq %[carry],%[carry]")  \
+        ASM("negq %[carry]")
+
+#endif
+
+#if defined(ADD_OR_SUBTRACT)
+
+#define ASM(x) x "\n\t"
+
+#define DO_8_TIMES(MACRO, ARG) \
+        MACRO(ARG, 0) \
+        MACRO(ARG, 1) \
+        MACRO(ARG, 2) \
+        MACRO(ARG, 3) \
+        MACRO(ARG, 4) \
+        MACRO(ARG, 5) \
+        MACRO(ARG, 6) \
+        MACRO(ARG, 7)
+
+#endif
+
+/*
+* Word Addition
+*/
+inline word word_add(word x, word y, word* carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(ASM("adcl %[y],%[x]"))
+      : [x]"=r"(x), [carry]"=r"(*carry)
+      : "0"(x), [y]"rm"(y), "1"(*carry)
+      : "cc");
+   return x;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(ASM("adcq %[y],%[x]"))
+      : [x]"=r"(x), [carry]"=r"(*carry)
+      : "0"(x), [y]"rm"(y), "1"(*carry)
+      : "cc");
+   return x;
+
+#else
+   word z = x + y;
+   word c1 = (z < x);
+   z += *carry;
+   *carry = c1 | (z < *carry);
+   return z;
+#endif
+   }
+
+/*
+* Eight Word Block Addition, Two Argument
+*/
+inline word word8_add2(word x[8], const word y[8], word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "adcl"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "adcq"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#else
+   x[0] = word_add(x[0], y[0], &carry);
+   x[1] = word_add(x[1], y[1], &carry);
+   x[2] = word_add(x[2], y[2], &carry);
+   x[3] = word_add(x[3], y[3], &carry);
+   x[4] = word_add(x[4], y[4], &carry);
+   x[5] = word_add(x[5], y[5], &carry);
+   x[6] = word_add(x[6], y[6], &carry);
+   x[7] = word_add(x[7], y[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Addition, Three Argument
+*/
+inline word word8_add3(word z[8], const word x[8],
+                       const word y[8], word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "adcl"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "adcq"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#else
+   z[0] = word_add(x[0], y[0], &carry);
+   z[1] = word_add(x[1], y[1], &carry);
+   z[2] = word_add(x[2], y[2], &carry);
+   z[3] = word_add(x[3], y[3], &carry);
+   z[4] = word_add(x[4], y[4], &carry);
+   z[5] = word_add(x[5], y[5], &carry);
+   z[6] = word_add(x[6], y[6], &carry);
+   z[7] = word_add(x[7], y[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Word Subtraction
+*/
+inline word word_sub(word x, word y, word* carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(ASM("sbbl %[y],%[x]"))
+      : [x]"=r"(x), [carry]"=r"(*carry)
+      : "0"(x), [y]"rm"(y), "1"(*carry)
+      : "cc");
+   return x;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(ASM("sbbq %[y],%[x]"))
+      : [x]"=r"(x), [carry]"=r"(*carry)
+      : "0"(x), [y]"rm"(y), "1"(*carry)
+      : "cc");
+   return x;
+
+#else
+   word t0 = x - y;
+   word c1 = (t0 > x);
+   word z = t0 - *carry;
+   *carry = c1 | (z > t0);
+   return z;
+#endif
+   }
+
+/*
+* Eight Word Block Subtraction, Two Argument
+*/
+inline word word8_sub2(word x[8], const word y[8], word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "sbbl"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "sbbq"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#else
+   x[0] = word_sub(x[0], y[0], &carry);
+   x[1] = word_sub(x[1], y[1], &carry);
+   x[2] = word_sub(x[2], y[2], &carry);
+   x[3] = word_sub(x[3], y[3], &carry);
+   x[4] = word_sub(x[4], y[4], &carry);
+   x[5] = word_sub(x[5], y[5], &carry);
+   x[6] = word_sub(x[6], y[6], &carry);
+   x[7] = word_sub(x[7], y[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Subtraction, Two Argument
+*/
+inline word word8_sub2_rev(word x[8], const word y[8], word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbl"))
+      : [carry]"=r"(carry)
+      : [x]"r"(y), [y]"r"(x), [z]"r"(x), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbq"))
+      : [carry]"=r"(carry)
+      : [x]"r"(y), [y]"r"(x), [z]"r"(x), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#else
+   x[0] = word_sub(y[0], x[0], &carry);
+   x[1] = word_sub(y[1], x[1], &carry);
+   x[2] = word_sub(y[2], x[2], &carry);
+   x[3] = word_sub(y[3], x[3], &carry);
+   x[4] = word_sub(y[4], x[4], &carry);
+   x[5] = word_sub(y[5], x[5], &carry);
+   x[6] = word_sub(y[6], x[6], &carry);
+   x[7] = word_sub(y[7], x[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Subtraction, Three Argument
+*/
+inline word word8_sub3(word z[8], const word x[8],
+                       const word y[8], word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbl"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbq"))
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+      : "cc", "memory");
+   return carry;
+
+#else
+   z[0] = word_sub(x[0], y[0], &carry);
+   z[1] = word_sub(x[1], y[1], &carry);
+   z[2] = word_sub(x[2], y[2], &carry);
+   z[3] = word_sub(x[3], y[3], &carry);
+   z[4] = word_sub(x[4], y[4], &carry);
+   z[5] = word_sub(x[5], y[5], &carry);
+   z[6] = word_sub(x[6], y[6], &carry);
+   z[7] = word_sub(x[7], y[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Linear Multiplication
+*/
+inline word word8_linmul2(word x[8], word y, word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      DO_8_TIMES(LINMUL_OP, "x")
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%eax", "%edx");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      DO_8_TIMES(LINMUL_OP, "x")
+      : [carry]"=r"(carry)
+      : [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%rax", "%rdx");
+   return carry;
+
+#else
+   x[0] = word_madd2(x[0], y, &carry);
+   x[1] = word_madd2(x[1], y, &carry);
+   x[2] = word_madd2(x[2], y, &carry);
+   x[3] = word_madd2(x[3], y, &carry);
+   x[4] = word_madd2(x[4], y, &carry);
+   x[5] = word_madd2(x[5], y, &carry);
+   x[6] = word_madd2(x[6], y, &carry);
+   x[7] = word_madd2(x[7], y, &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Linear Multiplication
+*/
+inline word word8_linmul3(word z[8], const word x[8], word y, word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      DO_8_TIMES(LINMUL_OP, "z")
+      : [carry]"=r"(carry)
+      : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%eax", "%edx");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+   asm(
+      DO_8_TIMES(LINMUL_OP, "z")
+      : [carry]"=r"(carry)
+      : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%rax", "%rdx");
+   return carry;
+
+#else
+   z[0] = word_madd2(x[0], y, &carry);
+   z[1] = word_madd2(x[1], y, &carry);
+   z[2] = word_madd2(x[2], y, &carry);
+   z[3] = word_madd2(x[3], y, &carry);
+   z[4] = word_madd2(x[4], y, &carry);
+   z[5] = word_madd2(x[5], y, &carry);
+   z[6] = word_madd2(x[6], y, &carry);
+   z[7] = word_madd2(x[7], y, &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Eight Word Block Multiply/Add
+*/
+inline word word8_madd3(word z[8], const word x[8], word y, word carry)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(
+      DO_8_TIMES(MULADD_OP, "")
+      : [carry]"=r"(carry)
+      : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%eax", "%edx");
+   return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(
+      DO_8_TIMES(MULADD_OP, "")
+      : [carry]"=r"(carry)
+      : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+      : "cc", "%rax", "%rdx");
+   return carry;
+
+#else
+   z[0] = word_madd3(x[0], y, z[0], &carry);
+   z[1] = word_madd3(x[1], y, z[1], &carry);
+   z[2] = word_madd3(x[2], y, z[2], &carry);
+   z[3] = word_madd3(x[3], y, z[3], &carry);
+   z[4] = word_madd3(x[4], y, z[4], &carry);
+   z[5] = word_madd3(x[5], y, z[5], &carry);
+   z[6] = word_madd3(x[6], y, z[6], &carry);
+   z[7] = word_madd3(x[7], y, z[7], &carry);
+   return carry;
+#endif
+   }
+
+/*
+* Multiply-Add Accumulator
+* (w2,w1,w0) += x * y
+*/
+inline void word3_muladd(word* w2, word* w1, word* w0, word x, word y)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   word z0 = 0, z1 = 0;
+
+   asm("mull %[y]"
+        : "=a"(z0),"=d"(z1)
+        : "a"(x), [y]"rm"(y)
+        : "cc");
+
+   asm(R"(
+       addl %[z0],%[w0]
+       adcl %[z1],%[w1]
+       adcl $0,%[w2]
+       )"
+       : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+       : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+       : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   word z0 = 0, z1 = 0;
+
+   asm("mulq %[y]"
+        : "=a"(z0),"=d"(z1)
+        : "a"(x), [y]"rm"(y)
+        : "cc");
+
+   asm(R"(
+       addq %[z0],%[w0]
+       adcq %[z1],%[w1]
+       adcq $0,%[w2]
+       )"
+       : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+       : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+       : "cc");
+
+#else
+   word carry = *w0;
+   *w0 = word_madd2(x, y, &carry);
+   *w1 += carry;
+   *w2 += (*w1 < carry);
+#endif
+   }
+
+/*
+* 3-word addition
+* (w2,w1,w0) += x
+*/
+inline void word3_add(word* w2, word* w1, word* w0, word x)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(R"(
+      addl %[x],%[w0]
+      adcl $0,%[w1]
+      adcl $0,%[w2]
+      )"
+      : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+      : [x]"r"(x), "0"(*w0), "1"(*w1), "2"(*w2)
+      : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   asm(R"(
+      addq %[x],%[w0]
+      adcq $0,%[w1]
+      adcq $0,%[w2]
+      )"
+      : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+      : [x]"r"(x), "0"(*w0), "1"(*w1), "2"(*w2)
+      : "cc");
+
+#else
+   *w0 += x;
+   word c1 = (*w0 < x);
+   *w1 += c1;
+   word c2 = (*w1 < c1);
+   *w2 += c2;
+#endif
+   }
+
+/*
+* Multiply-Add Accumulator
+* (w2,w1,w0) += 2 * x * y
+*/
+inline void word3_muladd_2(word* w2, word* w1, word* w0, word x, word y)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+
+   word z0 = 0, z1 = 0;
+
+   asm("mull %[y]"
+        : "=a"(z0),"=d"(z1)
+        : "a"(x), [y]"rm"(y)
+        : "cc");
+
+   asm(R"(
+      addl %[z0],%[w0]
+      adcl %[z1],%[w1]
+      adcl $0,%[w2]
+
+      addl %[z0],%[w0]
+      adcl %[z1],%[w1]
+      adcl $0,%[w2]
+      )"
+      : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+      : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+      : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+   word z0 = 0, z1 = 0;
+
+   asm("mulq %[y]"
+        : "=a"(z0),"=d"(z1)
+        : "a"(x), [y]"rm"(y)
+        : "cc");
+
+   asm(R"(
+      addq %[z0],%[w0]
+      adcq %[z1],%[w1]
+      adcq $0,%[w2]
+
+      addq %[z0],%[w0]
+      adcq %[z1],%[w1]
+      adcq $0,%[w2]
+      )"
+      : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+      : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+      : "cc");
+
+#else
+   word carry = 0;
+   x = word_madd2(x, y, &carry);
+   y = carry;
+
+   word top = (y >> (BOTAN_MP_WORD_BITS-1));
+   y <<= 1;
+   y |= (x >> (BOTAN_MP_WORD_BITS-1));
+   x <<= 1;
+
+   carry = 0;
+   *w0 = word_add(*w0, x, &carry);
+   *w1 = word_add(*w1, y, &carry);
+   *w2 = word_add(*w2, top, &carry);
+#endif
+   }
+
+#if defined(ASM)
+  #undef ASM
+  #undef DO_8_TIMES
+  #undef ADD_OR_SUBTRACT
+  #undef ADDSUB2_OP
+  #undef ADDSUB3_OP
+  #undef LINMUL_OP
+  #undef MULADD_OP
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/mp/mp_comba.cpp b/third_party/botan/src/lib/math/mp/mp_comba.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_comba.cpp
@@ -0,0 +1,2211 @@
+/*
+* Comba Multiplication and Squaring
+*
+* This file was automatically generated by ./src/scripts/comba.py on 2018-05-08
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+
+namespace Botan {
+
+/*
+* Comba 4x4 Squaring
+*/
+void bigint_comba_sqr4(word z[8], const word x[4])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0;
+   z[ 7] = w1;
+   }
+
+/*
+* Comba 4x4 Multiplication
+*/
+void bigint_comba_mul4(word z[8], const word x[4], const word y[4])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   z[ 6] = w0;
+   z[ 7] = w1;
+   }
+
+/*
+* Comba 6x6 Squaring
+*/
+void bigint_comba_sqr6(word z[12], const word x[6])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+   word3_muladd  (&w1, &w0, &w2, x[ 4], x[ 4]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd  (&w0, &w2, &w1, x[ 5], x[ 5]);
+   z[10] = w1;
+   z[11] = w2;
+   }
+
+/*
+* Comba 6x6 Multiplication
+*/
+void bigint_comba_mul6(word z[12], const word x[6], const word y[6])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+   z[10] = w1;
+   z[11] = w2;
+   }
+
+/*
+* Comba 8x8 Squaring
+*/
+void bigint_comba_sqr8(word z[16], const word x[8])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+   word3_muladd  (&w1, &w0, &w2, x[ 4], x[ 4]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+   word3_muladd  (&w0, &w2, &w1, x[ 5], x[ 5]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+   word3_muladd  (&w2, &w1, &w0, x[ 6], x[ 6]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd  (&w1, &w0, &w2, x[ 7], x[ 7]);
+   z[14] = w2;
+   z[15] = w0;
+   }
+
+/*
+* Comba 8x8 Multiplication
+*/
+void bigint_comba_mul8(word z[16], const word x[8], const word y[8])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+   z[14] = w2;
+   z[15] = w0;
+   }
+
+/*
+* Comba 9x9 Squaring
+*/
+void bigint_comba_sqr9(word z[18], const word x[9])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+   word3_muladd  (&w1, &w0, &w2, x[ 4], x[ 4]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+   word3_muladd  (&w0, &w2, &w1, x[ 5], x[ 5]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+   word3_muladd  (&w2, &w1, &w0, x[ 6], x[ 6]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+   word3_muladd  (&w1, &w0, &w2, x[ 7], x[ 7]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd  (&w0, &w2, &w1, x[ 8], x[ 8]);
+   z[16] = w1;
+   z[17] = w2;
+   }
+
+/*
+* Comba 9x9 Multiplication
+*/
+void bigint_comba_mul9(word z[18], const word x[9], const word y[9])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+   z[16] = w1;
+   z[17] = w2;
+   }
+
+/*
+* Comba 16x16 Squaring
+*/
+void bigint_comba_sqr16(word z[32], const word x[16])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+   word3_muladd  (&w1, &w0, &w2, x[ 4], x[ 4]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 9]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+   word3_muladd  (&w0, &w2, &w1, x[ 5], x[ 5]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 9]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[10]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+   word3_muladd  (&w2, &w1, &w0, x[ 6], x[ 6]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 9]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 9]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+   word3_muladd  (&w1, &w0, &w2, x[ 7], x[ 7]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[10]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[ 9]);
+   word3_muladd  (&w0, &w2, &w1, x[ 8], x[ 8]);
+   z[16] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[ 9]);
+   z[17] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[10]);
+   word3_muladd  (&w2, &w1, &w0, x[ 9], x[ 9]);
+   z[18] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[10]);
+   z[19] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[11]);
+   word3_muladd  (&w1, &w0, &w2, x[10], x[10]);
+   z[20] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[11]);
+   z[21] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[12]);
+   word3_muladd  (&w0, &w2, &w1, x[11], x[11]);
+   z[22] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[10], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[12]);
+   z[23] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[11], x[13]);
+   word3_muladd  (&w2, &w1, &w0, x[12], x[12]);
+   z[24] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[11], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[12], x[13]);
+   z[25] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[12], x[14]);
+   word3_muladd  (&w1, &w0, &w2, x[13], x[13]);
+   z[26] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[12], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[13], x[14]);
+   z[27] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[13], x[15]);
+   word3_muladd  (&w0, &w2, &w1, x[14], x[14]);
+   z[28] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[14], x[15]);
+   z[29] = w2; w2 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[15], x[15]);
+   z[30] = w0;
+   z[31] = w1;
+   }
+
+/*
+* Comba 16x16 Multiplication
+*/
+void bigint_comba_mul16(word z[32], const word x[16], const word y[16])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 0]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 0]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 0]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 0]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 0]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 0]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 0]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 1]);
+   z[16] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 2]);
+   z[17] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 3]);
+   z[18] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 4]);
+   z[19] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 5]);
+   z[20] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 6]);
+   z[21] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 7]);
+   z[22] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 8]);
+   z[23] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 9]);
+   z[24] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[10], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[10]);
+   z[25] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[11], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[11]);
+   z[26] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[12], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[12]);
+   z[27] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[13], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[13]);
+   z[28] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[14], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[14]);
+   z[29] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[15], y[15]);
+   z[30] = w0;
+   z[31] = w1;
+   }
+
+/*
+* Comba 24x24 Squaring
+*/
+void bigint_comba_sqr24(word z[48], const word x[24])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd  (&w2, &w1, &w0, x[ 0], x[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+   word3_muladd  (&w1, &w0, &w2, x[ 1], x[ 1]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+   word3_muladd  (&w0, &w2, &w1, x[ 2], x[ 2]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+   word3_muladd  (&w2, &w1, &w0, x[ 3], x[ 3]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+   word3_muladd  (&w1, &w0, &w2, x[ 4], x[ 4]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 9]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+   word3_muladd  (&w0, &w2, &w1, x[ 5], x[ 5]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 9]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[10]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+   word3_muladd  (&w2, &w1, &w0, x[ 6], x[ 6]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 9]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 9]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+   word3_muladd  (&w1, &w0, &w2, x[ 7], x[ 7]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[10]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[ 9]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[16]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[10]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[ 9]);
+   word3_muladd  (&w0, &w2, &w1, x[ 8], x[ 8]);
+   z[16] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[17]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[16]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[11]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[10]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[ 9]);
+   z[17] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[17]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[16]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[11]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[10]);
+   word3_muladd  (&w2, &w1, &w0, x[ 9], x[ 9]);
+   z[18] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[18]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[17]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[16]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[12]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[11]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[10]);
+   z[19] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[18]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[17]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[16]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[12]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[11]);
+   word3_muladd  (&w1, &w0, &w2, x[10], x[10]);
+   z[20] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 0], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[19]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[17]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[16]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[13]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[12]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[11]);
+   z[21] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 0], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 1], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[18]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[17]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[16]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[13]);
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[12]);
+   word3_muladd  (&w0, &w2, &w1, x[11], x[11]);
+   z[22] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 0], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 1], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 2], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[18]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[17]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[16]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[14]);
+   word3_muladd_2(&w1, &w0, &w2, x[10], x[13]);
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[12]);
+   z[23] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 1], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 2], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 3], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[19]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[17]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[16]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[14]);
+   word3_muladd_2(&w2, &w1, &w0, x[11], x[13]);
+   word3_muladd  (&w2, &w1, &w0, x[12], x[12]);
+   z[24] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 2], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 3], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 4], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[18]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[17]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[16]);
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[15]);
+   word3_muladd_2(&w0, &w2, &w1, x[11], x[14]);
+   word3_muladd_2(&w0, &w2, &w1, x[12], x[13]);
+   z[25] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 3], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 4], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 5], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[18]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[17]);
+   word3_muladd_2(&w1, &w0, &w2, x[10], x[16]);
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[15]);
+   word3_muladd_2(&w1, &w0, &w2, x[12], x[14]);
+   word3_muladd  (&w1, &w0, &w2, x[13], x[13]);
+   z[26] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 4], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 5], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 6], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[19]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[17]);
+   word3_muladd_2(&w2, &w1, &w0, x[11], x[16]);
+   word3_muladd_2(&w2, &w1, &w0, x[12], x[15]);
+   word3_muladd_2(&w2, &w1, &w0, x[13], x[14]);
+   z[27] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 5], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 6], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 7], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[18]);
+   word3_muladd_2(&w0, &w2, &w1, x[11], x[17]);
+   word3_muladd_2(&w0, &w2, &w1, x[12], x[16]);
+   word3_muladd_2(&w0, &w2, &w1, x[13], x[15]);
+   word3_muladd  (&w0, &w2, &w1, x[14], x[14]);
+   z[28] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 6], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 7], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 8], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[10], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[18]);
+   word3_muladd_2(&w1, &w0, &w2, x[12], x[17]);
+   word3_muladd_2(&w1, &w0, &w2, x[13], x[16]);
+   word3_muladd_2(&w1, &w0, &w2, x[14], x[15]);
+   z[29] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[ 7], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 8], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[ 9], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[11], x[19]);
+   word3_muladd_2(&w2, &w1, &w0, x[12], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[13], x[17]);
+   word3_muladd_2(&w2, &w1, &w0, x[14], x[16]);
+   word3_muladd  (&w2, &w1, &w0, x[15], x[15]);
+   z[30] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[ 8], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[ 9], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[10], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[11], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[12], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[13], x[18]);
+   word3_muladd_2(&w0, &w2, &w1, x[14], x[17]);
+   word3_muladd_2(&w0, &w2, &w1, x[15], x[16]);
+   z[31] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[ 9], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[10], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[11], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[12], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[13], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[14], x[18]);
+   word3_muladd_2(&w1, &w0, &w2, x[15], x[17]);
+   word3_muladd  (&w1, &w0, &w2, x[16], x[16]);
+   z[32] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[10], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[11], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[12], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[13], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[14], x[19]);
+   word3_muladd_2(&w2, &w1, &w0, x[15], x[18]);
+   word3_muladd_2(&w2, &w1, &w0, x[16], x[17]);
+   z[33] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[11], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[12], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[13], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[14], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[15], x[19]);
+   word3_muladd_2(&w0, &w2, &w1, x[16], x[18]);
+   word3_muladd  (&w0, &w2, &w1, x[17], x[17]);
+   z[34] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[12], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[13], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[14], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[15], x[20]);
+   word3_muladd_2(&w1, &w0, &w2, x[16], x[19]);
+   word3_muladd_2(&w1, &w0, &w2, x[17], x[18]);
+   z[35] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[13], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[14], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[15], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[16], x[20]);
+   word3_muladd_2(&w2, &w1, &w0, x[17], x[19]);
+   word3_muladd  (&w2, &w1, &w0, x[18], x[18]);
+   z[36] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[14], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[15], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[16], x[21]);
+   word3_muladd_2(&w0, &w2, &w1, x[17], x[20]);
+   word3_muladd_2(&w0, &w2, &w1, x[18], x[19]);
+   z[37] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[15], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[16], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[17], x[21]);
+   word3_muladd_2(&w1, &w0, &w2, x[18], x[20]);
+   word3_muladd  (&w1, &w0, &w2, x[19], x[19]);
+   z[38] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[16], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[17], x[22]);
+   word3_muladd_2(&w2, &w1, &w0, x[18], x[21]);
+   word3_muladd_2(&w2, &w1, &w0, x[19], x[20]);
+   z[39] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[17], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[18], x[22]);
+   word3_muladd_2(&w0, &w2, &w1, x[19], x[21]);
+   word3_muladd  (&w0, &w2, &w1, x[20], x[20]);
+   z[40] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[18], x[23]);
+   word3_muladd_2(&w1, &w0, &w2, x[19], x[22]);
+   word3_muladd_2(&w1, &w0, &w2, x[20], x[21]);
+   z[41] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[19], x[23]);
+   word3_muladd_2(&w2, &w1, &w0, x[20], x[22]);
+   word3_muladd  (&w2, &w1, &w0, x[21], x[21]);
+   z[42] = w0; w0 = 0;
+
+   word3_muladd_2(&w0, &w2, &w1, x[20], x[23]);
+   word3_muladd_2(&w0, &w2, &w1, x[21], x[22]);
+   z[43] = w1; w1 = 0;
+
+   word3_muladd_2(&w1, &w0, &w2, x[21], x[23]);
+   word3_muladd  (&w1, &w0, &w2, x[22], x[22]);
+   z[44] = w2; w2 = 0;
+
+   word3_muladd_2(&w2, &w1, &w0, x[22], x[23]);
+   z[45] = w0; w0 = 0;
+
+   word3_muladd  (&w0, &w2, &w1, x[23], x[23]);
+   z[46] = w1;
+   z[47] = w2;
+   }
+
+/*
+* Comba 24x24 Multiplication
+*/
+void bigint_comba_mul24(word z[48], const word x[24], const word y[24])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+   z[ 0] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+   z[ 1] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+   z[ 2] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+   z[ 3] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+   z[ 4] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+   z[ 5] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+   z[ 6] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+   z[ 7] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+   z[ 8] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 0]);
+   z[ 9] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 0]);
+   z[10] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 0]);
+   z[11] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 0]);
+   z[12] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 0]);
+   z[13] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 0]);
+   z[14] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 0]);
+   z[15] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[ 0]);
+   z[16] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[ 0]);
+   z[17] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[ 0]);
+   z[18] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[ 0]);
+   z[19] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[ 0]);
+   z[20] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 0], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[ 1]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[ 0]);
+   z[21] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 0], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[ 1], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[ 2]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[ 1]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[ 0]);
+   z[22] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 0], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[ 1], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[ 2], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[ 3]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[ 2]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[ 1]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[ 0]);
+   z[23] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 1], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[ 2], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[ 3], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[ 4]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[ 3]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[ 2]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[ 1]);
+   z[24] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 2], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[ 3], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[ 4], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[ 5]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[ 4]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[ 3]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[ 2]);
+   z[25] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 3], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[ 4], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[ 5], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[ 6]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[ 5]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[ 4]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[ 3]);
+   z[26] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 4], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[ 5], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[ 6], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[ 7]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[ 6]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[ 5]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[ 4]);
+   z[27] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 5], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[ 6], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[ 7], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[ 8]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[ 7]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[ 6]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[ 5]);
+   z[28] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 6], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[ 7], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[ 8], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[ 9]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[ 8]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[ 7]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[ 6]);
+   z[29] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[ 7], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[ 8], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[ 9], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[10], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[10]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[ 9]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[ 8]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[ 7]);
+   z[30] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[ 8], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[ 9], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[10], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[11], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[11]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[10]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[ 9]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[ 8]);
+   z[31] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[ 9], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[10], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[11], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[12], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[12]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[11]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[10]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[ 9]);
+   z[32] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[10], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[11], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[12], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[13], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[13]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[12]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[11]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[10]);
+   z[33] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[11], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[12], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[13], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[14], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[14]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[13]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[12]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[11]);
+   z[34] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[12], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[13], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[14], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[15], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[15]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[14]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[13]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[12]);
+   z[35] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[13], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[14], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[15], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[16], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[16]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[15]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[14]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[13]);
+   z[36] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[14], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[15], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[16], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[17], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[17]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[16]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[15]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[14]);
+   z[37] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[15], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[16], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[17], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[18], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[18]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[17]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[16]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[15]);
+   z[38] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[16], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[17], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[18], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[19], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[19]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[18]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[17]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[16]);
+   z[39] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[17], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[18], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[19], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[20], y[20]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[19]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[18]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[17]);
+   z[40] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[18], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[19], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[20], y[21]);
+   word3_muladd(&w1, &w0, &w2, x[21], y[20]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[19]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[18]);
+   z[41] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[19], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[20], y[22]);
+   word3_muladd(&w2, &w1, &w0, x[21], y[21]);
+   word3_muladd(&w2, &w1, &w0, x[22], y[20]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[19]);
+   z[42] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[20], y[23]);
+   word3_muladd(&w0, &w2, &w1, x[21], y[22]);
+   word3_muladd(&w0, &w2, &w1, x[22], y[21]);
+   word3_muladd(&w0, &w2, &w1, x[23], y[20]);
+   z[43] = w1; w1 = 0;
+
+   word3_muladd(&w1, &w0, &w2, x[21], y[23]);
+   word3_muladd(&w1, &w0, &w2, x[22], y[22]);
+   word3_muladd(&w1, &w0, &w2, x[23], y[21]);
+   z[44] = w2; w2 = 0;
+
+   word3_muladd(&w2, &w1, &w0, x[22], y[23]);
+   word3_muladd(&w2, &w1, &w0, x[23], y[22]);
+   z[45] = w0; w0 = 0;
+
+   word3_muladd(&w0, &w2, &w1, x[23], y[23]);
+   z[46] = w1;
+   z[47] = w2;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/mp/mp_core.h b/third_party/botan/src/lib/math/mp/mp_core.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_core.h
@@ -0,0 +1,819 @@
+/*
+* MPI Algorithms
+* (C) 1999-2010,2018 Jack Lloyd
+*     2006 Luca Piccarreta
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_CORE_OPS_H_
+#define BOTAN_MP_CORE_OPS_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <algorithm>
+
+namespace Botan {
+
+const word MP_WORD_MAX = ~static_cast<word>(0);
+
+/*
+* If cond == 0, does nothing.
+* If cond > 0, swaps x[0:size] with y[0:size]
+* Runs in constant time
+*/
+inline void bigint_cnd_swap(word cnd, word x[], word y[], size_t size)
+   {
+   const auto mask = CT::Mask<word>::expand(cnd);
+
+   for(size_t i = 0; i != size; ++i)
+      {
+      const word a = x[i];
+      const word b = y[i];
+      x[i] = mask.select(b, a);
+      y[i] = mask.select(a, b);
+      }
+   }
+
+inline word bigint_cnd_add(word cnd, word x[], word x_size,
+                           const word y[], size_t y_size)
+   {
+   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+   const auto mask = CT::Mask<word>::expand(cnd);
+
+   word carry = 0;
+
+   const size_t blocks = y_size - (y_size % 8);
+   word z[8] = { 0 };
+
+   for(size_t i = 0; i != blocks; i += 8)
+      {
+      carry = word8_add3(z, x + i, y + i, carry);
+      mask.select_n(x + i, z, x + i, 8);
+      }
+
+   for(size_t i = blocks; i != y_size; ++i)
+      {
+      z[0] = word_add(x[i], y[i], &carry);
+      x[i] = mask.select(z[0], x[i]);
+      }
+
+   for(size_t i = y_size; i != x_size; ++i)
+      {
+      z[0] = word_add(x[i], 0, &carry);
+      x[i] = mask.select(z[0], x[i]);
+      }
+
+   return mask.if_set_return(carry);
+   }
+
+/*
+* If cond > 0 adds x[0:size] and y[0:size] and returns carry
+* Runs in constant time
+*/
+inline word bigint_cnd_add(word cnd, word x[], const word y[], size_t size)
+   {
+   return bigint_cnd_add(cnd, x, size, y, size);
+   }
+
+/*
+* If cond > 0 subtracts x[0:size] and y[0:size] and returns borrow
+* Runs in constant time
+*/
+inline word bigint_cnd_sub(word cnd,
+                           word x[], size_t x_size,
+                           const word y[], size_t y_size)
+   {
+   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+   const auto mask = CT::Mask<word>::expand(cnd);
+
+   word carry = 0;
+
+   const size_t blocks = y_size - (y_size % 8);
+   word z[8] = { 0 };
+
+   for(size_t i = 0; i != blocks; i += 8)
+      {
+      carry = word8_sub3(z, x + i, y + i, carry);
+      mask.select_n(x + i, z, x + i, 8);
+      }
+
+   for(size_t i = blocks; i != y_size; ++i)
+      {
+      z[0] = word_sub(x[i], y[i], &carry);
+      x[i] = mask.select(z[0], x[i]);
+      }
+
+   for(size_t i = y_size; i != x_size; ++i)
+      {
+      z[0] = word_sub(x[i], 0, &carry);
+      x[i] = mask.select(z[0], x[i]);
+      }
+
+   return mask.if_set_return(carry);
+   }
+
+/*
+* If cond > 0 adds x[0:size] and y[0:size] and returns carry
+* Runs in constant time
+*/
+inline word bigint_cnd_sub(word cnd, word x[], const word y[], size_t size)
+   {
+   return bigint_cnd_sub(cnd, x, size, y, size);
+   }
+
+
+/*
+* Equivalent to
+*   bigint_cnd_add( mask, x, y, size);
+*   bigint_cnd_sub(~mask, x, y, size);
+*
+* Mask must be either 0 or all 1 bits
+*/
+inline void bigint_cnd_add_or_sub(CT::Mask<word> mask, word x[], const word y[], size_t size)
+   {
+   const size_t blocks = size - (size % 8);
+
+   word carry = 0;
+   word borrow = 0;
+
+   word t0[8] = { 0 };
+   word t1[8] = { 0 };
+
+   for(size_t i = 0; i != blocks; i += 8)
+      {
+      carry = word8_add3(t0, x + i, y + i, carry);
+      borrow = word8_sub3(t1, x + i, y + i, borrow);
+
+      for(size_t j = 0; j != 8; ++j)
+         x[i+j] = mask.select(t0[j], t1[j]);
+      }
+
+   for(size_t i = blocks; i != size; ++i)
+      {
+      const word a = word_add(x[i], y[i], &carry);
+      const word s = word_sub(x[i], y[i], &borrow);
+
+      x[i] = mask.select(a, s);
+      }
+   }
+
+/*
+* Equivalent to
+*   bigint_cnd_add( mask, x, size, y, size);
+*   bigint_cnd_sub(~mask, x, size, z, size);
+*
+* Mask must be either 0 or all 1 bits
+*
+* Returns the carry or borrow resp
+*/
+inline word bigint_cnd_addsub(CT::Mask<word> mask, word x[],
+                              const word y[], const word z[],
+                              size_t size)
+   {
+   const size_t blocks = size - (size % 8);
+
+   word carry = 0;
+   word borrow = 0;
+
+   word t0[8] = { 0 };
+   word t1[8] = { 0 };
+
+   for(size_t i = 0; i != blocks; i += 8)
+      {
+      carry = word8_add3(t0, x + i, y + i, carry);
+      borrow = word8_sub3(t1, x + i, z + i, borrow);
+
+      for(size_t j = 0; j != 8; ++j)
+         x[i+j] = mask.select(t0[j], t1[j]);
+      }
+
+   for(size_t i = blocks; i != size; ++i)
+      {
+      t0[0] = word_add(x[i], y[i], &carry);
+      t1[0] = word_sub(x[i], z[i], &borrow);
+      x[i] = mask.select(t0[0], t1[0]);
+      }
+
+   return mask.select(carry, borrow);
+   }
+
+/*
+* 2s complement absolute value
+* If cond > 0 sets x to ~x + 1
+* Runs in constant time
+*/
+inline void bigint_cnd_abs(word cnd, word x[], size_t size)
+   {
+   const auto mask = CT::Mask<word>::expand(cnd);
+
+   word carry = mask.if_set_return(1);
+   for(size_t i = 0; i != size; ++i)
+      {
+      const word z = word_add(~x[i], 0, &carry);
+      x[i] = mask.select(z, x[i]);
+      }
+   }
+
+/**
+* Two operand addition with carry out
+*/
+inline word bigint_add2_nc(word x[], size_t x_size, const word y[], size_t y_size)
+   {
+   word carry = 0;
+
+   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+   const size_t blocks = y_size - (y_size % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      carry = word8_add2(x + i, y + i, carry);
+
+   for(size_t i = blocks; i != y_size; ++i)
+      x[i] = word_add(x[i], y[i], &carry);
+
+   for(size_t i = y_size; i != x_size; ++i)
+      x[i] = word_add(x[i], 0, &carry);
+
+   return carry;
+   }
+
+/**
+* Three operand addition with carry out
+*/
+inline word bigint_add3_nc(word z[],
+                           const word x[], size_t x_size,
+                           const word y[], size_t y_size)
+   {
+   if(x_size < y_size)
+      { return bigint_add3_nc(z, y, y_size, x, x_size); }
+
+   word carry = 0;
+
+   const size_t blocks = y_size - (y_size % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      carry = word8_add3(z + i, x + i, y + i, carry);
+
+   for(size_t i = blocks; i != y_size; ++i)
+      z[i] = word_add(x[i], y[i], &carry);
+
+   for(size_t i = y_size; i != x_size; ++i)
+      z[i] = word_add(x[i], 0, &carry);
+
+   return carry;
+   }
+
+/**
+* Two operand addition
+* @param x the first operand (and output)
+* @param x_size size of x
+* @param y the second operand
+* @param y_size size of y (must be >= x_size)
+*/
+inline void bigint_add2(word x[], size_t x_size,
+                        const word y[], size_t y_size)
+   {
+   x[x_size] += bigint_add2_nc(x, x_size, y, y_size);
+   }
+
+/**
+* Three operand addition
+*/
+inline void bigint_add3(word z[],
+                        const word x[], size_t x_size,
+                        const word y[], size_t y_size)
+   {
+   z[x_size > y_size ? x_size : y_size] +=
+      bigint_add3_nc(z, x, x_size, y, y_size);
+   }
+
+/**
+* Two operand subtraction
+*/
+inline word bigint_sub2(word x[], size_t x_size,
+                        const word y[], size_t y_size)
+   {
+   word borrow = 0;
+
+   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+   const size_t blocks = y_size - (y_size % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      borrow = word8_sub2(x + i, y + i, borrow);
+
+   for(size_t i = blocks; i != y_size; ++i)
+      x[i] = word_sub(x[i], y[i], &borrow);
+
+   for(size_t i = y_size; i != x_size; ++i)
+      x[i] = word_sub(x[i], 0, &borrow);
+
+   return borrow;
+   }
+
+/**
+* Two operand subtraction, x = y - x; assumes y >= x
+*/
+inline void bigint_sub2_rev(word x[], const word y[], size_t y_size)
+   {
+   word borrow = 0;
+
+   const size_t blocks = y_size - (y_size % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      borrow = word8_sub2_rev(x + i, y + i, borrow);
+
+   for(size_t i = blocks; i != y_size; ++i)
+      x[i] = word_sub(y[i], x[i], &borrow);
+
+   BOTAN_ASSERT(borrow == 0, "y must be greater than x");
+   }
+
+/**
+* Three operand subtraction
+*/
+inline word bigint_sub3(word z[],
+                        const word x[], size_t x_size,
+                        const word y[], size_t y_size)
+   {
+   word borrow = 0;
+
+   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+   const size_t blocks = y_size - (y_size % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      borrow = word8_sub3(z + i, x + i, y + i, borrow);
+
+   for(size_t i = blocks; i != y_size; ++i)
+      z[i] = word_sub(x[i], y[i], &borrow);
+
+   for(size_t i = y_size; i != x_size; ++i)
+      z[i] = word_sub(x[i], 0, &borrow);
+
+   return borrow;
+   }
+
+/**
+* Return abs(x-y), ie if x >= y, then compute z = x - y
+* Otherwise compute z = y - x
+* No borrow is possible since the result is always >= 0
+*
+* Returns ~0 if x >= y or 0 if x < y
+* @param z output array of at least N words
+* @param x input array of N words
+* @param y input array of N words
+* @param N length of x and y
+* @param ws array of at least 2*N words
+*/
+inline CT::Mask<word>
+bigint_sub_abs(word z[],
+               const word x[], const word y[], size_t N,
+               word ws[])
+   {
+   // Subtract in both direction then conditional copy out the result
+
+   word* ws0 = ws;
+   word* ws1 = ws + N;
+
+   word borrow0 = 0;
+   word borrow1 = 0;
+
+   const size_t blocks = N - (N % 8);
+
+   for(size_t i = 0; i != blocks; i += 8)
+      {
+      borrow0 = word8_sub3(ws0 + i, x + i, y + i, borrow0);
+      borrow1 = word8_sub3(ws1 + i, y + i, x + i, borrow1);
+      }
+
+   for(size_t i = blocks; i != N; ++i)
+      {
+      ws0[i] = word_sub(x[i], y[i], &borrow0);
+      ws1[i] = word_sub(y[i], x[i], &borrow1);
+      }
+
+   return CT::conditional_copy_mem(borrow0, z, ws1, ws0, N);
+   }
+
+/*
+* Shift Operations
+*/
+inline void bigint_shl1(word x[], size_t x_size, size_t x_words,
+                        size_t word_shift, size_t bit_shift)
+   {
+   copy_mem(x + word_shift, x, x_words);
+   clear_mem(x, word_shift);
+
+   const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+   const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+   word carry = 0;
+   for(size_t i = word_shift; i != x_size; ++i)
+      {
+      const word w = x[i];
+      x[i] = (w << bit_shift) | carry;
+      carry = carry_mask.if_set_return(w >> carry_shift);
+      }
+   }
+
+inline void bigint_shr1(word x[], size_t x_size,
+                        size_t word_shift, size_t bit_shift)
+   {
+   const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0;
+
+   if(top > 0)
+      copy_mem(x, x + word_shift, top);
+   clear_mem(x + top, std::min(word_shift, x_size));
+
+   const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+   const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+   word carry = 0;
+
+   for(size_t i = 0; i != top; ++i)
+      {
+      const word w = x[top - i - 1];
+      x[top-i-1] = (w >> bit_shift) | carry;
+      carry = carry_mask.if_set_return(w << carry_shift);
+      }
+   }
+
+inline void bigint_shl2(word y[], const word x[], size_t x_size,
+                        size_t word_shift, size_t bit_shift)
+   {
+   copy_mem(y + word_shift, x, x_size);
+
+   const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+   const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+   word carry = 0;
+   for(size_t i = word_shift; i != x_size + word_shift + 1; ++i)
+      {
+      const word w = y[i];
+      y[i] = (w << bit_shift) | carry;
+      carry = carry_mask.if_set_return(w >> carry_shift);
+      }
+   }
+
+inline void bigint_shr2(word y[], const word x[], size_t x_size,
+                        size_t word_shift, size_t bit_shift)
+   {
+   const size_t new_size = x_size < word_shift ? 0 : (x_size - word_shift);
+
+   if(new_size > 0)
+      copy_mem(y, x + word_shift, new_size);
+
+   const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+   const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+   word carry = 0;
+   for(size_t i = new_size; i > 0; --i)
+      {
+      word w = y[i-1];
+      y[i-1] = (w >> bit_shift) | carry;
+      carry = carry_mask.if_set_return(w << carry_shift);
+      }
+   }
+
+/*
+* Linear Multiply - returns the carry
+*/
+inline word BOTAN_WARN_UNUSED_RESULT bigint_linmul2(word x[], size_t x_size, word y)
+   {
+   const size_t blocks = x_size - (x_size % 8);
+
+   word carry = 0;
+
+   for(size_t i = 0; i != blocks; i += 8)
+      carry = word8_linmul2(x + i, y, carry);
+
+   for(size_t i = blocks; i != x_size; ++i)
+      x[i] = word_madd2(x[i], y, &carry);
+
+   return carry;
+   }
+
+inline void bigint_linmul3(word z[], const word x[], size_t x_size, word y)
+   {
+   const size_t blocks = x_size - (x_size % 8);
+
+   word carry = 0;
+
+   for(size_t i = 0; i != blocks; i += 8)
+      carry = word8_linmul3(z + i, x + i, y, carry);
+
+   for(size_t i = blocks; i != x_size; ++i)
+      z[i] = word_madd2(x[i], y, &carry);
+
+   z[x_size] = carry;
+   }
+
+/**
+* Compare x and y
+* Return -1 if x < y
+* Return 0 if x == y
+* Return 1 if x > y
+*/
+inline int32_t bigint_cmp(const word x[], size_t x_size,
+                          const word y[], size_t y_size)
+   {
+   static_assert(sizeof(word) >= sizeof(uint32_t), "Size assumption");
+
+   const word LT = static_cast<word>(-1);
+   const word EQ = 0;
+   const word GT = 1;
+
+   const size_t common_elems = std::min(x_size, y_size);
+
+   word result = EQ; // until found otherwise
+
+   for(size_t i = 0; i != common_elems; i++)
+      {
+      const auto is_eq = CT::Mask<word>::is_equal(x[i], y[i]);
+      const auto is_lt = CT::Mask<word>::is_lt(x[i], y[i]);
+
+      result = is_eq.select(result, is_lt.select(LT, GT));
+      }
+
+   if(x_size < y_size)
+      {
+      word mask = 0;
+      for(size_t i = x_size; i != y_size; i++)
+         mask |= y[i];
+
+      // If any bits were set in high part of y, then x < y
+      result = CT::Mask<word>::is_zero(mask).select(result, LT);
+      }
+   else if(y_size < x_size)
+      {
+      word mask = 0;
+      for(size_t i = y_size; i != x_size; i++)
+         mask |= x[i];
+
+      // If any bits were set in high part of x, then x > y
+      result = CT::Mask<word>::is_zero(mask).select(result, GT);
+      }
+
+   CT::unpoison(result);
+   BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
+   return static_cast<int32_t>(result);
+   }
+
+/**
+* Compare x and y
+* Return ~0 if x[0:x_size] < y[0:y_size] or 0 otherwise
+* If lt_or_equal is true, returns ~0 also for x == y
+*/
+inline CT::Mask<word>
+bigint_ct_is_lt(const word x[], size_t x_size,
+                const word y[], size_t y_size,
+                bool lt_or_equal = false)
+   {
+   const size_t common_elems = std::min(x_size, y_size);
+
+   auto is_lt = CT::Mask<word>::expand(lt_or_equal);
+
+   for(size_t i = 0; i != common_elems; i++)
+      {
+      const auto eq = CT::Mask<word>::is_equal(x[i], y[i]);
+      const auto lt = CT::Mask<word>::is_lt(x[i], y[i]);
+      is_lt = eq.select_mask(is_lt, lt);
+      }
+
+   if(x_size < y_size)
+      {
+      word mask = 0;
+      for(size_t i = x_size; i != y_size; i++)
+         mask |= y[i];
+      // If any bits were set in high part of y, then is_lt should be forced true
+      is_lt |= CT::Mask<word>::expand(mask);
+      }
+   else if(y_size < x_size)
+      {
+      word mask = 0;
+      for(size_t i = y_size; i != x_size; i++)
+         mask |= x[i];
+
+      // If any bits were set in high part of x, then is_lt should be false
+      is_lt &= CT::Mask<word>::is_zero(mask);
+      }
+
+   return is_lt;
+   }
+
+inline CT::Mask<word>
+bigint_ct_is_eq(const word x[], size_t x_size,
+                const word y[], size_t y_size)
+   {
+   const size_t common_elems = std::min(x_size, y_size);
+
+   word diff = 0;
+
+   for(size_t i = 0; i != common_elems; i++)
+      {
+      diff |= (x[i] ^ y[i]);
+      }
+
+   // If any bits were set in high part of x/y, then they are not equal
+   if(x_size < y_size)
+      {
+      for(size_t i = x_size; i != y_size; i++)
+         diff |= y[i];
+      }
+   else if(y_size < x_size)
+      {
+      for(size_t i = y_size; i != x_size; i++)
+         diff |= x[i];
+      }
+
+   return CT::Mask<word>::is_zero(diff);
+   }
+
+/**
+* Set z to abs(x-y), ie if x >= y, then compute z = x - y
+* Otherwise compute z = y - x
+* No borrow is possible since the result is always >= 0
+*
+* Return the relative size of x vs y (-1, 0, 1)
+*
+* @param z output array of max(x_size,y_size) words
+* @param x input param
+* @param x_size length of x
+* @param y input param
+* @param y_size length of y
+*/
+inline int32_t
+bigint_sub_abs(word z[],
+               const word x[], size_t x_size,
+               const word y[], size_t y_size)
+   {
+   const int32_t relative_size = bigint_cmp(x, x_size, y, y_size);
+
+   // Swap if relative_size == -1
+   const bool need_swap = relative_size < 0;
+   CT::conditional_swap_ptr(need_swap, x, y);
+   CT::conditional_swap(need_swap, x_size, y_size);
+
+   /*
+   * We know at this point that x >= y so if y_size is larger than
+   * x_size, we are guaranteed they are just leading zeros which can
+   * be ignored
+   */
+   y_size = std::min(x_size, y_size);
+
+   bigint_sub3(z, x, x_size, y, y_size);
+
+   return relative_size;
+   }
+
+/**
+* Set t to t-s modulo mod
+*
+* @param t first integer
+* @param s second integer
+* @param mod the modulus
+* @param mod_sw size of t, s, and mod
+* @param ws workspace of size mod_sw
+*/
+inline void
+bigint_mod_sub(word t[], const word s[], const word mod[], size_t mod_sw, word ws[])
+   {
+   // is t < s or not?
+   const auto is_lt = bigint_ct_is_lt(t, mod_sw, s, mod_sw);
+
+   // ws = p - s
+   const word borrow = bigint_sub3(ws, mod, mod_sw, s, mod_sw);
+
+   // Compute either (t - s) or (t + (p - s)) depending on mask
+   const word carry = bigint_cnd_addsub(is_lt, t, ws, s, mod_sw);
+
+   BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
+   BOTAN_UNUSED(carry, borrow);
+   }
+
+template<size_t N>
+inline void bigint_mod_sub_n(word t[], const word s[], const word mod[], word ws[])
+   {
+   // is t < s or not?
+   const auto is_lt = bigint_ct_is_lt(t, N, s, N);
+
+   // ws = p - s
+   const word borrow = bigint_sub3(ws, mod, N, s, N);
+
+   // Compute either (t - s) or (t + (p - s)) depending on mask
+   const word carry = bigint_cnd_addsub(is_lt, t, ws, s, N);
+
+   BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
+   BOTAN_UNUSED(carry, borrow);
+   }
+
+/**
+* Compute ((n1<<bits) + n0) / d
+*/
+inline word bigint_divop(word n1, word n0, word d)
+   {
+   if(d == 0)
+      throw Invalid_Argument("bigint_divop divide by zero");
+
+#if defined(BOTAN_HAS_MP_DWORD)
+   return ((static_cast<dword>(n1) << BOTAN_MP_WORD_BITS) | n0) / d;
+#else
+
+   word high = n1 % d;
+   word quotient = 0;
+
+   for(size_t i = 0; i != BOTAN_MP_WORD_BITS; ++i)
+      {
+      const word high_top_bit = high >> (BOTAN_MP_WORD_BITS-1);
+
+      high <<= 1;
+      high |= (n0 >> (BOTAN_MP_WORD_BITS-1-i)) & 1;
+      quotient <<= 1;
+
+      if(high_top_bit || high >= d)
+         {
+         high -= d;
+         quotient |= 1;
+         }
+      }
+
+   return quotient;
+#endif
+   }
+
+/**
+* Compute ((n1<<bits) + n0) % d
+*/
+inline word bigint_modop(word n1, word n0, word d)
+   {
+   if(d == 0)
+      throw Invalid_Argument("bigint_modop divide by zero");
+
+#if defined(BOTAN_HAS_MP_DWORD)
+   return ((static_cast<dword>(n1) << BOTAN_MP_WORD_BITS) | n0) % d;
+#else
+   word z = bigint_divop(n1, n0, d);
+   word dummy = 0;
+   z = word_madd2(z, d, &dummy);
+   return (n0-z);
+#endif
+   }
+
+/*
+* Comba Multiplication / Squaring
+*/
+void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
+void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
+void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
+void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
+void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
+void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);
+
+void bigint_comba_sqr4(word out[8], const word in[4]);
+void bigint_comba_sqr6(word out[12], const word in[6]);
+void bigint_comba_sqr8(word out[16], const word in[8]);
+void bigint_comba_sqr9(word out[18], const word in[9]);
+void bigint_comba_sqr16(word out[32], const word in[16]);
+void bigint_comba_sqr24(word out[48], const word in[24]);
+
+/**
+* Montgomery Reduction
+* @param z integer to reduce, of size exactly 2*(p_size+1).
+           Output is in the first p_size+1 words, higher
+           words are set to zero.
+* @param p modulus
+* @param p_size size of p
+* @param p_dash Montgomery value
+* @param workspace array of at least 2*(p_size+1) words
+* @param ws_size size of workspace in words
+*/
+void bigint_monty_redc(word z[],
+                       const word p[], size_t p_size,
+                       word p_dash,
+                       word workspace[],
+                       size_t ws_size);
+
+/*
+* High Level Multiplication/Squaring Interfaces
+*/
+
+void bigint_mul(word z[], size_t z_size,
+                const word x[], size_t x_size, size_t x_sw,
+                const word y[], size_t y_size, size_t y_sw,
+                word workspace[], size_t ws_size);
+
+void bigint_sqr(word z[], size_t z_size,
+                const word x[], size_t x_size, size_t x_sw,
+                word workspace[], size_t ws_size);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/mp/mp_karat.cpp b/third_party/botan/src/lib/math/mp/mp_karat.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_karat.cpp
@@ -0,0 +1,408 @@
+/*
+* Multiplication and Squaring
+* (C) 1999-2010,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+const size_t KARATSUBA_MULTIPLY_THRESHOLD = 32;
+const size_t KARATSUBA_SQUARE_THRESHOLD = 32;
+
+/*
+* Simple O(N^2) Multiplication
+*/
+void basecase_mul(word z[], size_t z_size,
+                  const word x[], size_t x_size,
+                  const word y[], size_t y_size)
+   {
+   if(z_size < x_size + y_size)
+      throw Invalid_Argument("basecase_mul z_size too small");
+
+   const size_t x_size_8 = x_size - (x_size % 8);
+
+   clear_mem(z, z_size);
+
+   for(size_t i = 0; i != y_size; ++i)
+      {
+      const word y_i = y[i];
+
+      word carry = 0;
+
+      for(size_t j = 0; j != x_size_8; j += 8)
+         carry = word8_madd3(z + i + j, x + j, y_i, carry);
+
+      for(size_t j = x_size_8; j != x_size; ++j)
+         z[i+j] = word_madd3(x[j], y_i, z[i+j], &carry);
+
+      z[x_size+i] = carry;
+      }
+   }
+
+void basecase_sqr(word z[], size_t z_size,
+                  const word x[], size_t x_size)
+   {
+   if(z_size < 2*x_size)
+      throw Invalid_Argument("basecase_sqr z_size too small");
+
+   const size_t x_size_8 = x_size - (x_size % 8);
+
+   clear_mem(z, z_size);
+
+   for(size_t i = 0; i != x_size; ++i)
+      {
+      const word x_i = x[i];
+
+      word carry = 0;
+
+      for(size_t j = 0; j != x_size_8; j += 8)
+         carry = word8_madd3(z + i + j, x + j, x_i, carry);
+
+      for(size_t j = x_size_8; j != x_size; ++j)
+         z[i+j] = word_madd3(x[j], x_i, z[i+j], &carry);
+
+      z[x_size+i] = carry;
+      }
+   }
+
+/*
+* Karatsuba Multiplication Operation
+*/
+void karatsuba_mul(word z[], const word x[], const word y[], size_t N,
+                   word workspace[])
+   {
+   if(N < KARATSUBA_MULTIPLY_THRESHOLD || N % 2)
+      {
+      switch(N)
+         {
+         case 6:
+            return bigint_comba_mul6(z, x, y);
+         case 8:
+            return bigint_comba_mul8(z, x, y);
+         case 9:
+            return bigint_comba_mul9(z, x, y);
+         case 16:
+            return bigint_comba_mul16(z, x, y);
+         case 24:
+            return bigint_comba_mul24(z, x, y);
+         default:
+            return basecase_mul(z, 2*N, x, N, y, N);
+         }
+      }
+
+   const size_t N2 = N / 2;
+
+   const word* x0 = x;
+   const word* x1 = x + N2;
+   const word* y0 = y;
+   const word* y1 = y + N2;
+   word* z0 = z;
+   word* z1 = z + N;
+
+   word* ws0 = workspace;
+   word* ws1 = workspace + N;
+
+   clear_mem(workspace, 2*N);
+
+   /*
+   * If either of cmp0 or cmp1 is zero then z0 or z1 resp is zero here,
+   * resulting in a no-op - z0*z1 will be equal to zero so we don't need to do
+   * anything, clear_mem above already set the correct result.
+   *
+   * However we ignore the result of the comparisons and always perform the
+   * subtractions and recursively multiply to avoid the timing channel.
+   */
+
+   // First compute (X_lo - X_hi)*(Y_hi - Y_lo)
+   const auto cmp0 = bigint_sub_abs(z0, x0, x1, N2, workspace);
+   const auto cmp1 = bigint_sub_abs(z1, y1, y0, N2, workspace);
+   const auto neg_mask = ~(cmp0 ^ cmp1);
+
+   karatsuba_mul(ws0, z0, z1, N2, ws1);
+
+   // Compute X_lo * Y_lo
+   karatsuba_mul(z0, x0, y0, N2, ws1);
+
+   // Compute X_hi * Y_hi
+   karatsuba_mul(z1, x1, y1, N2, ws1);
+
+   const word ws_carry = bigint_add3_nc(ws1, z0, N, z1, N);
+   word z_carry = bigint_add2_nc(z + N2, N, ws1, N);
+
+   z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1);
+   bigint_add2_nc(z + N + N2, N2, &z_carry, 1);
+
+   clear_mem(workspace + N, N2);
+
+   bigint_cnd_add_or_sub(neg_mask, z + N2, workspace, 2*N-N2);
+   }
+
+/*
+* Karatsuba Squaring Operation
+*/
+void karatsuba_sqr(word z[], const word x[], size_t N, word workspace[])
+   {
+   if(N < KARATSUBA_SQUARE_THRESHOLD || N % 2)
+      {
+      switch(N)
+         {
+         case 6:
+            return bigint_comba_sqr6(z, x);
+         case 8:
+            return bigint_comba_sqr8(z, x);
+         case 9:
+            return bigint_comba_sqr9(z, x);
+         case 16:
+            return bigint_comba_sqr16(z, x);
+         case 24:
+            return bigint_comba_sqr24(z, x);
+         default:
+            return basecase_sqr(z, 2*N, x, N);
+         }
+      }
+
+   const size_t N2 = N / 2;
+
+   const word* x0 = x;
+   const word* x1 = x + N2;
+   word* z0 = z;
+   word* z1 = z + N;
+
+   word* ws0 = workspace;
+   word* ws1 = workspace + N;
+
+   clear_mem(workspace, 2*N);
+
+   // See comment in karatsuba_mul
+   bigint_sub_abs(z0, x0, x1, N2, workspace);
+   karatsuba_sqr(ws0, z0, N2, ws1);
+
+   karatsuba_sqr(z0, x0, N2, ws1);
+   karatsuba_sqr(z1, x1, N2, ws1);
+
+   const word ws_carry = bigint_add3_nc(ws1, z0, N, z1, N);
+   word z_carry = bigint_add2_nc(z + N2, N, ws1, N);
+
+   z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1);
+   bigint_add2_nc(z + N + N2, N2, &z_carry, 1);
+
+   /*
+   * This is only actually required if cmp (result of bigint_sub_abs) is != 0,
+   * however if cmp==0 then ws0[0:N] == 0 and avoiding the jump hides a
+   * timing channel.
+   */
+   bigint_sub2(z + N2, 2*N-N2, ws0, N);
+   }
+
+/*
+* Pick a good size for the Karatsuba multiply
+*/
+size_t karatsuba_size(size_t z_size,
+                      size_t x_size, size_t x_sw,
+                      size_t y_size, size_t y_sw)
+   {
+   if(x_sw > x_size || x_sw > y_size || y_sw > x_size || y_sw > y_size)
+      return 0;
+
+   if(((x_size == x_sw) && (x_size % 2)) ||
+      ((y_size == y_sw) && (y_size % 2)))
+      return 0;
+
+   const size_t start = (x_sw > y_sw) ? x_sw : y_sw;
+   const size_t end = (x_size < y_size) ? x_size : y_size;
+
+   if(start == end)
+      {
+      if(start % 2)
+         return 0;
+      return start;
+      }
+
+   for(size_t j = start; j <= end; ++j)
+      {
+      if(j % 2)
+         continue;
+
+      if(2*j > z_size)
+         return 0;
+
+      if(x_sw <= j && j <= x_size && y_sw <= j && j <= y_size)
+         {
+         if(j % 4 == 2 &&
+            (j+2) <= x_size && (j+2) <= y_size && 2*(j+2) <= z_size)
+            return j+2;
+         return j;
+         }
+      }
+
+   return 0;
+   }
+
+/*
+* Pick a good size for the Karatsuba squaring
+*/
+size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw)
+   {
+   if(x_sw == x_size)
+      {
+      if(x_sw % 2)
+         return 0;
+      return x_sw;
+      }
+
+   for(size_t j = x_sw; j <= x_size; ++j)
+      {
+      if(j % 2)
+         continue;
+
+      if(2*j > z_size)
+         return 0;
+
+      if(j % 4 == 2 && (j+2) <= x_size && 2*(j+2) <= z_size)
+         return j+2;
+      return j;
+      }
+
+   return 0;
+   }
+
+template<size_t SZ>
+inline bool sized_for_comba_mul(size_t x_sw, size_t x_size,
+                                size_t y_sw, size_t y_size,
+                                size_t z_size)
+   {
+   return (x_sw <= SZ && x_size >= SZ &&
+           y_sw <= SZ && y_size >= SZ &&
+           z_size >= 2*SZ);
+   }
+
+template<size_t SZ>
+inline bool sized_for_comba_sqr(size_t x_sw, size_t x_size,
+                                size_t z_size)
+   {
+   return (x_sw <= SZ && x_size >= SZ && z_size >= 2*SZ);
+   }
+
+}
+
+void bigint_mul(word z[], size_t z_size,
+                const word x[], size_t x_size, size_t x_sw,
+                const word y[], size_t y_size, size_t y_sw,
+                word workspace[], size_t ws_size)
+   {
+   clear_mem(z, z_size);
+
+   if(x_sw == 1)
+      {
+      bigint_linmul3(z, y, y_sw, x[0]);
+      }
+   else if(y_sw == 1)
+      {
+      bigint_linmul3(z, x, x_sw, y[0]);
+      }
+   else if(sized_for_comba_mul<4>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul4(z, x, y);
+      }
+   else if(sized_for_comba_mul<6>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul6(z, x, y);
+      }
+   else if(sized_for_comba_mul<8>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul8(z, x, y);
+      }
+   else if(sized_for_comba_mul<9>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul9(z, x, y);
+      }
+   else if(sized_for_comba_mul<16>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul16(z, x, y);
+      }
+   else if(sized_for_comba_mul<24>(x_sw, x_size, y_sw, y_size, z_size))
+      {
+      bigint_comba_mul24(z, x, y);
+      }
+   else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD ||
+           y_sw < KARATSUBA_MULTIPLY_THRESHOLD ||
+           !workspace)
+      {
+      basecase_mul(z, z_size, x, x_sw, y, y_sw);
+      }
+   else
+      {
+      const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw);
+
+      if(N && z_size >= 2*N && ws_size >= 2*N)
+         karatsuba_mul(z, x, y, N, workspace);
+      else
+         basecase_mul(z, z_size, x, x_sw, y, y_sw);
+      }
+   }
+
+/*
+* Squaring Algorithm Dispatcher
+*/
+void bigint_sqr(word z[], size_t z_size,
+                const word x[], size_t x_size, size_t x_sw,
+                word workspace[], size_t ws_size)
+   {
+   clear_mem(z, z_size);
+
+   BOTAN_ASSERT(z_size/2 >= x_sw, "Output size is sufficient");
+
+   if(x_sw == 1)
+      {
+      bigint_linmul3(z, x, x_sw, x[0]);
+      }
+   else if(sized_for_comba_sqr<4>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr4(z, x);
+      }
+   else if(sized_for_comba_sqr<6>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr6(z, x);
+      }
+   else if(sized_for_comba_sqr<8>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr8(z, x);
+      }
+   else if(sized_for_comba_sqr<9>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr9(z, x);
+      }
+   else if(sized_for_comba_sqr<16>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr16(z, x);
+      }
+   else if(sized_for_comba_sqr<24>(x_sw, x_size, z_size))
+      {
+      bigint_comba_sqr24(z, x);
+      }
+   else if(x_size < KARATSUBA_SQUARE_THRESHOLD || !workspace)
+      {
+      basecase_sqr(z, z_size, x, x_sw);
+      }
+   else
+      {
+      const size_t N = karatsuba_size(z_size, x_size, x_sw);
+
+      if(N && z_size >= 2*N && ws_size >= 2*N)
+         karatsuba_sqr(z, x, N, workspace);
+      else
+         basecase_sqr(z, z_size, x, x_sw);
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/mp/mp_madd.h b/third_party/botan/src/lib/math/mp/mp_madd.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_madd.h
@@ -0,0 +1,146 @@
+/*
+* Lowest Level MPI Algorithms
+* (C) 1999-2008,2013 Jack Lloyd
+*     2006 Luca Piccarreta
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_WORD_MULADD_H_
+#define BOTAN_MP_WORD_MULADD_H_
+
+#include <botan/types.h>
+#include <botan/mul128.h>
+
+namespace Botan {
+
+#if (BOTAN_MP_WORD_BITS == 32)
+  typedef uint64_t dword;
+  #define BOTAN_HAS_MP_DWORD
+
+#elif (BOTAN_MP_WORD_BITS == 64)
+  #if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+    typedef uint128_t dword;
+    #define BOTAN_HAS_MP_DWORD
+  #else
+    // No native 128 bit integer type; use mul64x64_128 instead
+  #endif
+
+#else
+  #error BOTAN_MP_WORD_BITS must be 32 or 64
+#endif
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+
+  #if defined(BOTAN_TARGET_ARCH_IS_X86_32) && (BOTAN_MP_WORD_BITS == 32)
+    #define BOTAN_MP_USE_X86_32_ASM
+  #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && (BOTAN_MP_WORD_BITS == 64)
+    #define BOTAN_MP_USE_X86_64_ASM
+  #endif
+
+#endif
+
+/*
+* Word Multiply/Add
+*/
+inline word word_madd2(word a, word b, word* c)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(R"(
+      mull %[b]
+      addl %[c],%[a]
+      adcl $0,%[carry]
+      )"
+      : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*c)
+      : "0"(a), "1"(b), [c]"g"(*c) : "cc");
+
+   return a;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+      asm(R"(
+         mulq %[b]
+         addq %[c],%[a]
+         adcq $0,%[carry]
+      )"
+      : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*c)
+      : "0"(a), "1"(b), [c]"g"(*c) : "cc");
+
+   return a;
+
+#elif defined(BOTAN_HAS_MP_DWORD)
+   const dword s = static_cast<dword>(a) * b + *c;
+   *c = static_cast<word>(s >> BOTAN_MP_WORD_BITS);
+   return static_cast<word>(s);
+#else
+   static_assert(BOTAN_MP_WORD_BITS == 64, "Unexpected word size");
+
+   word hi = 0, lo = 0;
+
+   mul64x64_128(a, b, &lo, &hi);
+
+   lo += *c;
+   hi += (lo < *c); // carry?
+
+   *c = hi;
+   return lo;
+#endif
+   }
+
+/*
+* Word Multiply/Add
+*/
+inline word word_madd3(word a, word b, word c, word* d)
+   {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+   asm(R"(
+      mull %[b]
+
+      addl %[c],%[a]
+      adcl $0,%[carry]
+
+      addl %[d],%[a]
+      adcl $0,%[carry]
+      )"
+      : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*d)
+      : "0"(a), "1"(b), [c]"g"(c), [d]"g"(*d) : "cc");
+
+   return a;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+   asm(R"(
+      mulq %[b]
+      addq %[c],%[a]
+      adcq $0,%[carry]
+      addq %[d],%[a]
+      adcq $0,%[carry]
+      )"
+      : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*d)
+      : "0"(a), "1"(b), [c]"g"(c), [d]"g"(*d) : "cc");
+
+   return a;
+
+#elif defined(BOTAN_HAS_MP_DWORD)
+   const dword s = static_cast<dword>(a) * b + c + *d;
+   *d = static_cast<word>(s >> BOTAN_MP_WORD_BITS);
+   return static_cast<word>(s);
+#else
+   static_assert(BOTAN_MP_WORD_BITS == 64, "Unexpected word size");
+
+   word hi = 0, lo = 0;
+
+   mul64x64_128(a, b, &lo, &hi);
+
+   lo += c;
+   hi += (lo < c); // carry?
+
+   lo += *d;
+   hi += (lo < *d); // carry?
+
+   *d = hi;
+   return lo;
+#endif
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/mp/mp_monty.cpp b/third_party/botan/src/lib/math/mp/mp_monty.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_monty.cpp
@@ -0,0 +1,133 @@
+/*
+* Montgomery Reduction
+* (C) 1999-2011 Jack Lloyd
+*     2006 Luca Piccarreta
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_monty.h>
+#include <botan/internal/mp_madd.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Montgomery reduction - product scanning form
+*
+* https://www.iacr.org/archive/ches2005/006.pdf
+* https://eprint.iacr.org/2013/882.pdf
+* https://www.microsoft.com/en-us/research/wp-content/uploads/1996/01/j37acmon.pdf
+*/
+void bigint_monty_redc_generic(word z[], size_t z_size,
+                               const word p[], size_t p_size, word p_dash,
+                               word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+
+   w0 = z[0];
+
+   ws[0] = w0 * p_dash;
+
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+
+   w0 = w1;
+   w1 = w2;
+   w2 = 0;
+
+   for(size_t i = 1; i != p_size; ++i)
+      {
+      for(size_t j = 0; j < i; ++j)
+         {
+         word3_muladd(&w2, &w1, &w0, ws[j], p[i-j]);
+         }
+
+      word3_add(&w2, &w1, &w0, z[i]);
+
+      ws[i] = w0 * p_dash;
+
+      word3_muladd(&w2, &w1, &w0, ws[i], p[0]);
+
+      w0 = w1;
+      w1 = w2;
+      w2 = 0;
+      }
+
+   for(size_t i = 0; i != p_size; ++i)
+      {
+      for(size_t j = i + 1; j != p_size; ++j)
+         {
+         word3_muladd(&w2, &w1, &w0, ws[j], p[p_size + i-j]);
+         }
+
+      word3_add(&w2, &w1, &w0, z[p_size+i]);
+
+      ws[i] = w0;
+      w0 = w1;
+      w1 = w2;
+      w2 = 0;
+      }
+
+   word3_add(&w2, &w1, &w0, z[z_size-1]);
+
+   ws[p_size] = w0;
+   ws[p_size+1] = w1;
+
+   /*
+   * The result might need to be reduced mod p. To avoid a timing
+   * channel, always perform the subtraction. If in the compution
+   * of x - p a borrow is required then x was already < p.
+   *
+   * x starts at ws[0] and is p_size+1 bytes long.
+   * x - p starts at ws[p_size+1] and is also p_size+1 bytes log
+   *
+   * Select which address to copy from indexing off of the final
+   * borrow.
+   */
+
+   // word borrow = bigint_sub3(ws + p_size + 1, ws, p_size + 1, p, p_size);
+   word borrow = 0;
+   for(size_t i = 0; i != p_size; ++i)
+      ws[p_size + 1 + i] = word_sub(ws[i], p[i], &borrow);
+   ws[2*p_size+1] = word_sub(ws[p_size], 0, &borrow);
+
+   BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+
+   CT::conditional_copy_mem(borrow, z, ws, ws + (p_size + 1), (p_size + 1));
+   clear_mem(z + p_size, z_size - p_size - 2);
+   }
+
+}
+
+void bigint_monty_redc(word z[],
+                       const word p[], size_t p_size, word p_dash,
+                       word ws[], size_t ws_size)
+   {
+   const size_t z_size = 2*(p_size+1);
+
+   BOTAN_ARG_CHECK(ws_size >= z_size, "workspace too small");
+
+   if(p_size == 4)
+      bigint_monty_redc_4(z, p, p_dash, ws);
+   else if(p_size == 6)
+      bigint_monty_redc_6(z, p, p_dash, ws);
+   else if(p_size == 8)
+      bigint_monty_redc_8(z, p, p_dash, ws);
+   else if(p_size == 16)
+      bigint_monty_redc_16(z, p, p_dash, ws);
+   else if(p_size == 24)
+      bigint_monty_redc_24(z, p, p_dash, ws);
+   else if(p_size == 32)
+      bigint_monty_redc_32(z, p, p_dash, ws);
+   else
+      bigint_monty_redc_generic(z, z_size, p, p_size, p_dash, ws);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/mp/mp_monty.h b/third_party/botan/src/lib/math/mp/mp_monty.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_monty.h
@@ -0,0 +1,31 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_MONTY_H_
+#define BOTAN_MP_MONTY_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/*
+* Each of these functions makes the following assumptions:
+*
+* z_size >= 2*(p_size + 1)
+* ws_size >= z_size
+*/
+
+void bigint_monty_redc_4(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_6(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_8(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_16(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_24(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_32(word z[], const word p[], word p_dash, word ws[]);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/mp/mp_monty_n.cpp b/third_party/botan/src/lib/math/mp/mp_monty_n.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/mp/mp_monty_n.cpp
@@ -0,0 +1,2614 @@
+/*
+* This file was automatically generated by ./src/scripts/monty.py on 2018-06-11
+* All manual changes will be lost. Edit the script instead.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_monty.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+void bigint_monty_redc_4(word z[], const word p[4], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[4] = w0;
+   ws[5] = w1;
+   word borrow = 0;
+   ws[5] = word_sub(ws[0], p[0], &borrow);
+   ws[6] = word_sub(ws[1], p[1], &borrow);
+   ws[7] = word_sub(ws[2], p[2], &borrow);
+   ws[8] = word_sub(ws[3], p[3], &borrow);
+   ws[9] = word_sub(ws[4], 0, &borrow);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 5, 5);
+   clear_mem(z + 4, 2*(4+1) - 4);
+   }
+
+void bigint_monty_redc_6(word z[], const word p[6], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[4] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[5] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+   word3_add(&w2, &w1, &w0, z[8]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+   word3_add(&w2, &w1, &w0, z[10]);
+   ws[4] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[11]);
+   ws[5] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[13]);
+   ws[6] = w0;
+   ws[7] = w1;
+   word borrow = 0;
+   ws[7] = word_sub(ws[0], p[0], &borrow);
+   ws[8] = word_sub(ws[1], p[1], &borrow);
+   ws[9] = word_sub(ws[2], p[2], &borrow);
+   ws[10] = word_sub(ws[3], p[3], &borrow);
+   ws[11] = word_sub(ws[4], p[4], &borrow);
+   ws[12] = word_sub(ws[5], p[5], &borrow);
+   ws[13] = word_sub(ws[6], 0, &borrow);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 7, 7);
+   clear_mem(z + 6, 2*(6+1) - 6);
+   }
+
+void bigint_monty_redc_8(word z[], const word p[8], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[4] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[5] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[6] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[7] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+   word3_add(&w2, &w1, &w0, z[8]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+   word3_add(&w2, &w1, &w0, z[10]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+   word3_add(&w2, &w1, &w0, z[11]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+   word3_add(&w2, &w1, &w0, z[12]);
+   ws[4] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+   word3_add(&w2, &w1, &w0, z[13]);
+   ws[5] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+   word3_add(&w2, &w1, &w0, z[14]);
+   ws[6] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[15]);
+   ws[7] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[17]);
+   ws[8] = w0;
+   ws[9] = w1;
+   word borrow = 0;
+   ws[9] = word_sub(ws[0], p[0], &borrow);
+   ws[10] = word_sub(ws[1], p[1], &borrow);
+   ws[11] = word_sub(ws[2], p[2], &borrow);
+   ws[12] = word_sub(ws[3], p[3], &borrow);
+   ws[13] = word_sub(ws[4], p[4], &borrow);
+   ws[14] = word_sub(ws[5], p[5], &borrow);
+   ws[15] = word_sub(ws[6], p[6], &borrow);
+   ws[16] = word_sub(ws[7], p[7], &borrow);
+   ws[17] = word_sub(ws[8], 0, &borrow);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 9, 9);
+   clear_mem(z + 8, 2*(8+1) - 8);
+   }
+
+void bigint_monty_redc_16(word z[], const word p[16], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[4] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[5] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[6] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[7] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+   word3_add(&w2, &w1, &w0, z[8]);
+   ws[8] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[9] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+   word3_add(&w2, &w1, &w0, z[10]);
+   ws[10] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+   word3_add(&w2, &w1, &w0, z[11]);
+   ws[11] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+   word3_add(&w2, &w1, &w0, z[12]);
+   ws[12] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+   word3_add(&w2, &w1, &w0, z[13]);
+   ws[13] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+   word3_add(&w2, &w1, &w0, z[14]);
+   ws[14] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+   word3_add(&w2, &w1, &w0, z[15]);
+   ws[15] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+   word3_add(&w2, &w1, &w0, z[16]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+   word3_add(&w2, &w1, &w0, z[17]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+   word3_add(&w2, &w1, &w0, z[18]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+   word3_add(&w2, &w1, &w0, z[19]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+   word3_add(&w2, &w1, &w0, z[20]);
+   ws[4] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+   word3_add(&w2, &w1, &w0, z[21]);
+   ws[5] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+   word3_add(&w2, &w1, &w0, z[22]);
+   ws[6] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+   word3_add(&w2, &w1, &w0, z[23]);
+   ws[7] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+   word3_add(&w2, &w1, &w0, z[24]);
+   ws[8] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+   word3_add(&w2, &w1, &w0, z[25]);
+   ws[9] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+   word3_add(&w2, &w1, &w0, z[26]);
+   ws[10] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+   word3_add(&w2, &w1, &w0, z[27]);
+   ws[11] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+   word3_add(&w2, &w1, &w0, z[28]);
+   ws[12] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+   word3_add(&w2, &w1, &w0, z[29]);
+   ws[13] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+   word3_add(&w2, &w1, &w0, z[30]);
+   ws[14] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[31]);
+   ws[15] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[33]);
+   ws[16] = w0;
+   ws[17] = w1;
+   word borrow = bigint_sub3(ws + 16 + 1, ws, 16 + 1, p, 16);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 17, 17);
+   clear_mem(z + 16, 2*(16+1) - 16);
+   }
+
+void bigint_monty_redc_24(word z[], const word p[24], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[4] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[5] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[6] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[7] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+   word3_add(&w2, &w1, &w0, z[8]);
+   ws[8] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[9] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+   word3_add(&w2, &w1, &w0, z[10]);
+   ws[10] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+   word3_add(&w2, &w1, &w0, z[11]);
+   ws[11] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+   word3_add(&w2, &w1, &w0, z[12]);
+   ws[12] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+   word3_add(&w2, &w1, &w0, z[13]);
+   ws[13] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+   word3_add(&w2, &w1, &w0, z[14]);
+   ws[14] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+   word3_add(&w2, &w1, &w0, z[15]);
+   ws[15] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+   word3_add(&w2, &w1, &w0, z[16]);
+   ws[16] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[16], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[1]);
+   word3_add(&w2, &w1, &w0, z[17]);
+   ws[17] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[17], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[1]);
+   word3_add(&w2, &w1, &w0, z[18]);
+   ws[18] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[18], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[1]);
+   word3_add(&w2, &w1, &w0, z[19]);
+   ws[19] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[19], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[1]);
+   word3_add(&w2, &w1, &w0, z[20]);
+   ws[20] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[20], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[1]);
+   word3_add(&w2, &w1, &w0, z[21]);
+   ws[21] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[21], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[1]);
+   word3_add(&w2, &w1, &w0, z[22]);
+   ws[22] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[22], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[1]);
+   word3_add(&w2, &w1, &w0, z[23]);
+   ws[23] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[23], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[1]);
+   word3_add(&w2, &w1, &w0, z[24]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[2]);
+   word3_add(&w2, &w1, &w0, z[25]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[3]);
+   word3_add(&w2, &w1, &w0, z[26]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[4]);
+   word3_add(&w2, &w1, &w0, z[27]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[5]);
+   word3_add(&w2, &w1, &w0, z[28]);
+   ws[4] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[6]);
+   word3_add(&w2, &w1, &w0, z[29]);
+   ws[5] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[7]);
+   word3_add(&w2, &w1, &w0, z[30]);
+   ws[6] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[8]);
+   word3_add(&w2, &w1, &w0, z[31]);
+   ws[7] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[9]);
+   word3_add(&w2, &w1, &w0, z[32]);
+   ws[8] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[10]);
+   word3_add(&w2, &w1, &w0, z[33]);
+   ws[9] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[11]);
+   word3_add(&w2, &w1, &w0, z[34]);
+   ws[10] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[12]);
+   word3_add(&w2, &w1, &w0, z[35]);
+   ws[11] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[13]);
+   word3_add(&w2, &w1, &w0, z[36]);
+   ws[12] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[14]);
+   word3_add(&w2, &w1, &w0, z[37]);
+   ws[13] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[15]);
+   word3_add(&w2, &w1, &w0, z[38]);
+   ws[14] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[16], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[16]);
+   word3_add(&w2, &w1, &w0, z[39]);
+   ws[15] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[17], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[17]);
+   word3_add(&w2, &w1, &w0, z[40]);
+   ws[16] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[18], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[18]);
+   word3_add(&w2, &w1, &w0, z[41]);
+   ws[17] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[19], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[19]);
+   word3_add(&w2, &w1, &w0, z[42]);
+   ws[18] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[20], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[20]);
+   word3_add(&w2, &w1, &w0, z[43]);
+   ws[19] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[21], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[21]);
+   word3_add(&w2, &w1, &w0, z[44]);
+   ws[20] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[22], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[22]);
+   word3_add(&w2, &w1, &w0, z[45]);
+   ws[21] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[23], p[23]);
+   word3_add(&w2, &w1, &w0, z[46]);
+   ws[22] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[47]);
+   ws[23] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[49]);
+   ws[24] = w0;
+   ws[25] = w1;
+   word borrow = bigint_sub3(ws + 24 + 1, ws, 24 + 1, p, 24);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 25, 25);
+   clear_mem(z + 24, 2*(24+1) - 24);
+   }
+
+void bigint_monty_redc_32(word z[], const word p[32], word p_dash, word ws[])
+   {
+   word w2 = 0, w1 = 0, w0 = 0;
+   w0 = z[0];
+   ws[0] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+   word3_add(&w2, &w1, &w0, z[1]);
+   ws[1] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+   word3_add(&w2, &w1, &w0, z[2]);
+   ws[2] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+   word3_add(&w2, &w1, &w0, z[3]);
+   ws[3] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+   word3_add(&w2, &w1, &w0, z[4]);
+   ws[4] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+   word3_add(&w2, &w1, &w0, z[5]);
+   ws[5] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+   word3_add(&w2, &w1, &w0, z[6]);
+   ws[6] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+   word3_add(&w2, &w1, &w0, z[7]);
+   ws[7] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+   word3_add(&w2, &w1, &w0, z[8]);
+   ws[8] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+   word3_add(&w2, &w1, &w0, z[9]);
+   ws[9] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+   word3_add(&w2, &w1, &w0, z[10]);
+   ws[10] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+   word3_add(&w2, &w1, &w0, z[11]);
+   ws[11] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+   word3_add(&w2, &w1, &w0, z[12]);
+   ws[12] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+   word3_add(&w2, &w1, &w0, z[13]);
+   ws[13] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+   word3_add(&w2, &w1, &w0, z[14]);
+   ws[14] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+   word3_add(&w2, &w1, &w0, z[15]);
+   ws[15] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+   word3_add(&w2, &w1, &w0, z[16]);
+   ws[16] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[16], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[1]);
+   word3_add(&w2, &w1, &w0, z[17]);
+   ws[17] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[17], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[1]);
+   word3_add(&w2, &w1, &w0, z[18]);
+   ws[18] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[18], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[1]);
+   word3_add(&w2, &w1, &w0, z[19]);
+   ws[19] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[19], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[1]);
+   word3_add(&w2, &w1, &w0, z[20]);
+   ws[20] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[20], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[1]);
+   word3_add(&w2, &w1, &w0, z[21]);
+   ws[21] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[21], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[1]);
+   word3_add(&w2, &w1, &w0, z[22]);
+   ws[22] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[22], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[1]);
+   word3_add(&w2, &w1, &w0, z[23]);
+   ws[23] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[23], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[1]);
+   word3_add(&w2, &w1, &w0, z[24]);
+   ws[24] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[24], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[1]);
+   word3_add(&w2, &w1, &w0, z[25]);
+   ws[25] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[25], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[1]);
+   word3_add(&w2, &w1, &w0, z[26]);
+   ws[26] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[26], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[1]);
+   word3_add(&w2, &w1, &w0, z[27]);
+   ws[27] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[27], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[1]);
+   word3_add(&w2, &w1, &w0, z[28]);
+   ws[28] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[28], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[1]);
+   word3_add(&w2, &w1, &w0, z[29]);
+   ws[29] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[29], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[1]);
+   word3_add(&w2, &w1, &w0, z[30]);
+   ws[30] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[30], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[0], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[1], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[1]);
+   word3_add(&w2, &w1, &w0, z[31]);
+   ws[31] = w0 * p_dash;
+   word3_muladd(&w2, &w1, &w0, ws[31], p[0]);
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[1], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[2], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[2]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[1]);
+   word3_add(&w2, &w1, &w0, z[32]);
+   ws[0] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[2], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[3], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[3]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[2]);
+   word3_add(&w2, &w1, &w0, z[33]);
+   ws[1] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[3], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[4], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[4]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[3]);
+   word3_add(&w2, &w1, &w0, z[34]);
+   ws[2] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[4], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[5], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[5]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[4]);
+   word3_add(&w2, &w1, &w0, z[35]);
+   ws[3] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[5], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[6], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[6]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[5]);
+   word3_add(&w2, &w1, &w0, z[36]);
+   ws[4] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[6], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[7], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[7]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[6]);
+   word3_add(&w2, &w1, &w0, z[37]);
+   ws[5] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[7], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[8], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[8]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[7]);
+   word3_add(&w2, &w1, &w0, z[38]);
+   ws[6] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[8], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[9], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[9]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[8]);
+   word3_add(&w2, &w1, &w0, z[39]);
+   ws[7] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[9], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[10], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[10]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[9]);
+   word3_add(&w2, &w1, &w0, z[40]);
+   ws[8] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[10], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[11], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[11]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[10]);
+   word3_add(&w2, &w1, &w0, z[41]);
+   ws[9] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[11], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[12], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[12]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[11]);
+   word3_add(&w2, &w1, &w0, z[42]);
+   ws[10] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[12], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[13], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[13]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[12]);
+   word3_add(&w2, &w1, &w0, z[43]);
+   ws[11] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[13], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[14], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[14]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[13]);
+   word3_add(&w2, &w1, &w0, z[44]);
+   ws[12] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[14], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[15], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[15]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[14]);
+   word3_add(&w2, &w1, &w0, z[45]);
+   ws[13] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[15], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[16], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[16]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[15]);
+   word3_add(&w2, &w1, &w0, z[46]);
+   ws[14] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[16], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[17], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[17]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[16]);
+   word3_add(&w2, &w1, &w0, z[47]);
+   ws[15] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[17], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[18], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[18]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[17]);
+   word3_add(&w2, &w1, &w0, z[48]);
+   ws[16] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[18], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[19], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[19]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[18]);
+   word3_add(&w2, &w1, &w0, z[49]);
+   ws[17] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[19], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[20], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[20]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[19]);
+   word3_add(&w2, &w1, &w0, z[50]);
+   ws[18] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[20], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[21], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[21]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[20]);
+   word3_add(&w2, &w1, &w0, z[51]);
+   ws[19] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[21], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[22], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[22]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[21]);
+   word3_add(&w2, &w1, &w0, z[52]);
+   ws[20] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[22], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[23], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[23]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[22]);
+   word3_add(&w2, &w1, &w0, z[53]);
+   ws[21] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[23], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[24], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[24]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[23]);
+   word3_add(&w2, &w1, &w0, z[54]);
+   ws[22] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[24], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[25], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[25]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[24]);
+   word3_add(&w2, &w1, &w0, z[55]);
+   ws[23] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[25], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[26], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[26]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[25]);
+   word3_add(&w2, &w1, &w0, z[56]);
+   ws[24] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[26], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[27], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[27]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[26]);
+   word3_add(&w2, &w1, &w0, z[57]);
+   ws[25] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[27], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[28], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[28]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[27]);
+   word3_add(&w2, &w1, &w0, z[58]);
+   ws[26] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[28], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[29], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[29]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[28]);
+   word3_add(&w2, &w1, &w0, z[59]);
+   ws[27] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[29], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[30], p[30]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[29]);
+   word3_add(&w2, &w1, &w0, z[60]);
+   ws[28] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[30], p[31]);
+   word3_muladd(&w2, &w1, &w0, ws[31], p[30]);
+   word3_add(&w2, &w1, &w0, z[61]);
+   ws[29] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_muladd(&w2, &w1, &w0, ws[31], p[31]);
+   word3_add(&w2, &w1, &w0, z[62]);
+   ws[30] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[63]);
+   ws[31] = w0;
+   w0 = w1; w1 = w2; w2 = 0;
+   word3_add(&w2, &w1, &w0, z[65]);
+   ws[32] = w0;
+   ws[33] = w1;
+   word borrow = bigint_sub3(ws + 32 + 1, ws, 32 + 1, p, 32);
+   CT::conditional_copy_mem(borrow, z, ws, ws + 33, 33);
+   clear_mem(z + 32, 2*(32+1) - 32);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/curve_nistp.h b/third_party/botan/src/lib/math/numbertheory/curve_nistp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/curve_nistp.h
@@ -0,0 +1,49 @@
+/*
+* Arithmetic operations specialized for NIST ECC primes
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NIST_PRIMES_H_
+#define BOTAN_NIST_PRIMES_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(curve_nistp.h)
+
+namespace Botan {
+
+/**
+* NIST Prime reduction functions.
+*
+* Reduces the value in place
+*
+* ws is a workspace function which is used as a temporary,
+* and will be resized as needed.
+*/
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p521();
+BOTAN_PUBLIC_API(2,0) void redc_p521(BigInt& x, secure_vector<word>& ws);
+
+/*
+Previously this macro indicated if the P-{192,224,256,384} reducers
+were available. Now they are always enabled and this macro has no meaning.
+The define will be removed in a future major release.
+*/
+#define BOTAN_HAS_NIST_PRIME_REDUCERS_W32
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p384();
+BOTAN_PUBLIC_API(2,0) void redc_p384(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p256();
+BOTAN_PUBLIC_API(2,0) void redc_p256(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p224();
+BOTAN_PUBLIC_API(2,0) void redc_p224(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p192();
+BOTAN_PUBLIC_API(2,0) void redc_p192(BigInt& x, secure_vector<word>& ws);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp b/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp
@@ -0,0 +1,136 @@
+/*
+* DSA Parameter Generation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/hash.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Check if this size is allowed by FIPS 186-3
+*/
+bool fips186_3_valid_size(size_t pbits, size_t qbits)
+   {
+   if(qbits == 160)
+      return (pbits == 1024);
+
+   if(qbits == 224)
+      return (pbits == 2048);
+
+   if(qbits == 256)
+      return (pbits == 2048 || pbits == 3072);
+
+   return false;
+   }
+
+}
+
+/*
+* Attempt DSA prime generation with given seed
+*/
+bool generate_dsa_primes(RandomNumberGenerator& rng,
+                         BigInt& p, BigInt& q,
+                         size_t pbits, size_t qbits,
+                         const std::vector<uint8_t>& seed_c,
+                         size_t offset)
+   {
+   if(!fips186_3_valid_size(pbits, qbits))
+      throw Invalid_Argument(
+         "FIPS 186-3 does not allow DSA domain parameters of " +
+         std::to_string(pbits) + "/" + std::to_string(qbits) + " bits long");
+
+   if(seed_c.size() * 8 < qbits)
+      throw Invalid_Argument(
+         "Generating a DSA parameter set with a " + std::to_string(qbits) +
+         " bit long q requires a seed at least as many bits long");
+
+   const std::string hash_name = "SHA-" + std::to_string(qbits);
+   std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+   const size_t HASH_SIZE = hash->output_length();
+
+   class Seed final
+      {
+      public:
+         explicit Seed(const std::vector<uint8_t>& s) : m_seed(s) {}
+
+         const std::vector<uint8_t>& value() const { return m_seed; }
+
+         Seed& operator++()
+            {
+            for(size_t j = m_seed.size(); j > 0; --j)
+               if(++m_seed[j-1])
+                  break;
+            return (*this);
+            }
+      private:
+         std::vector<uint8_t> m_seed;
+      };
+
+   Seed seed(seed_c);
+
+   q.binary_decode(hash->process(seed.value()));
+   q.set_bit(qbits-1);
+   q.set_bit(0);
+
+   if(!is_prime(q, rng, 128, true))
+      return false;
+
+   const size_t n = (pbits-1) / (HASH_SIZE * 8),
+                b = (pbits-1) % (HASH_SIZE * 8);
+
+   BigInt X;
+   std::vector<uint8_t> V(HASH_SIZE * (n+1));
+
+   Modular_Reducer mod_2q(2*q);
+
+   for(size_t j = 0; j != 4*pbits; ++j)
+      {
+      for(size_t k = 0; k <= n; ++k)
+         {
+         ++seed;
+         hash->update(seed.value());
+         hash->final(&V[HASH_SIZE * (n-k)]);
+         }
+
+      if(j >= offset)
+         {
+         X.binary_decode(&V[HASH_SIZE - 1 - b/8],
+                         V.size() - (HASH_SIZE - 1 - b/8));
+         X.set_bit(pbits-1);
+
+         p = X - (mod_2q.reduce(X) - 1);
+
+         if(p.bits() == pbits && is_prime(p, rng, 128, true))
+            return true;
+         }
+      }
+   return false;
+   }
+
+/*
+* Generate DSA Primes
+*/
+std::vector<uint8_t> generate_dsa_primes(RandomNumberGenerator& rng,
+                                      BigInt& p, BigInt& q,
+                                      size_t pbits, size_t qbits)
+   {
+   while(true)
+      {
+      std::vector<uint8_t> seed(qbits / 8);
+      rng.randomize(seed.data(), seed.size());
+
+      if(generate_dsa_primes(rng, p, q, pbits, qbits, seed))
+         return seed;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/jacobi.cpp b/third_party/botan/src/lib/math/numbertheory/jacobi.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/jacobi.cpp
@@ -0,0 +1,52 @@
+/*
+* Jacobi Function
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+/*
+* Calculate the Jacobi symbol
+*/
+int32_t jacobi(const BigInt& a, const BigInt& n)
+   {
+   if(n.is_even() || n < 2)
+      throw Invalid_Argument("jacobi: second argument must be odd and > 1");
+
+   BigInt x = a % n;
+   BigInt y = n;
+   int32_t J = 1;
+
+   while(y > 1)
+      {
+      x %= y;
+      if(x > y / 2)
+         {
+         x = y - x;
+         if(y % 4 == 3)
+            J = -J;
+         }
+      if(x.is_zero())
+         return 0;
+
+      size_t shifts = low_zero_bits(x);
+      x >>= shifts;
+      if(shifts % 2)
+         {
+         word y_mod_8 = y % 8;
+         if(y_mod_8 == 3 || y_mod_8 == 5)
+            J = -J;
+         }
+
+      if(x % 4 == 3 && y % 4 == 3)
+         J = -J;
+      std::swap(x, y);
+      }
+   return J;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/make_prm.cpp b/third_party/botan/src/lib/math/numbertheory/make_prm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/make_prm.cpp
@@ -0,0 +1,293 @@
+/*
+* Prime Generation
+* (C) 1999-2007,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/rng.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/loadstor.h>
+#include <botan/reducer.h>
+#include <botan/internal/primality.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+class Prime_Sieve final
+   {
+   public:
+      Prime_Sieve(const BigInt& init_value, size_t sieve_size) :
+         m_sieve(std::min(sieve_size, PRIME_TABLE_SIZE))
+         {
+         for(size_t i = 0; i != m_sieve.size(); ++i)
+            m_sieve[i] = static_cast<uint16_t>(init_value % PRIMES[i]);
+         }
+
+      void step(word increment)
+         {
+         for(size_t i = 0; i != m_sieve.size(); ++i)
+            {
+            m_sieve[i] = (m_sieve[i] + increment) % PRIMES[i];
+            }
+         }
+
+      bool passes(bool check_2p1 = false) const
+         {
+         for(size_t i = 0; i != m_sieve.size(); ++i)
+            {
+            /*
+            In this case, p is a multiple of PRIMES[i]
+            */
+            if(m_sieve[i] == 0)
+               return false;
+
+            if(check_2p1)
+               {
+               /*
+               In this case, 2*p+1 will be a multiple of PRIMES[i]
+
+               So if potentially generating a safe prime, we want to
+               avoid this value because 2*p+1 will certainly not be prime.
+
+               See "Safe Prime Generation with a Combined Sieve" M. Wiener
+               https://eprint.iacr.org/2003/186.pdf
+               */
+               if(m_sieve[i] == (PRIMES[i] - 1) / 2)
+                  return false;
+               }
+            }
+
+         return true;
+         }
+
+   private:
+      std::vector<uint16_t> m_sieve;
+   };
+
+}
+
+
+/*
+* Generate a random prime
+*/
+BigInt random_prime(RandomNumberGenerator& rng,
+                    size_t bits, const BigInt& coprime,
+                    size_t equiv, size_t modulo,
+                    size_t prob)
+   {
+   if(bits <= 1)
+      {
+      throw Invalid_Argument("random_prime: Can't make a prime of " +
+                             std::to_string(bits) + " bits");
+      }
+   if(coprime.is_negative() || (!coprime.is_zero() && coprime.is_even()) || coprime.bits() >= bits)
+      {
+      throw Invalid_Argument("random_prime: invalid coprime");
+      }
+   if(modulo == 0)
+      {
+      throw Invalid_Argument("random_prime: Invalid modulo value");
+      }
+
+   equiv %= modulo;
+
+   if(equiv == 0)
+      throw Invalid_Argument("random_prime Invalid value for equiv/modulo");
+
+   // Handle small values:
+
+   if(bits <= 16)
+      {
+      if(equiv != 1 || modulo != 2 || coprime != 0)
+         throw Not_Implemented("random_prime equiv/modulo/coprime options not usable for small primes");
+
+      if(bits == 2)
+         {
+         return ((rng.next_byte() % 2) ? 2 : 3);
+         }
+      else if(bits == 3)
+         {
+         return ((rng.next_byte() % 2) ? 5 : 7);
+         }
+      else if(bits == 4)
+         {
+         return ((rng.next_byte() % 2) ? 11 : 13);
+         }
+      else
+         {
+         for(;;)
+            {
+            // This is slightly biased, but for small primes it does not seem to matter
+            uint8_t b[4];
+            rng.randomize(b, 4);
+            const size_t idx = load_le<uint32_t>(b, 0) % PRIME_TABLE_SIZE;
+            const uint16_t small_prime = PRIMES[idx];
+
+            if(high_bit(small_prime) == bits)
+               return small_prime;
+            }
+         }
+      }
+
+   const size_t MAX_ATTEMPTS = 32*1024;
+
+   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
+
+   while(true)
+      {
+      BigInt p(rng, bits);
+
+      // Force lowest and two top bits on
+      p.set_bit(bits - 1);
+      p.set_bit(bits - 2);
+      p.set_bit(0);
+
+      // Force p to be equal to equiv mod modulo
+      p += (modulo - (p % modulo)) + equiv;
+
+      Prime_Sieve sieve(p, bits);
+
+      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt)
+         {
+         p += modulo;
+
+         sieve.step(modulo);
+
+         // p can be even if modulo is odd, continue on in that case
+         if(p.is_even() || sieve.passes(true) == false)
+            continue;
+
+         Modular_Reducer mod_p(p);
+
+         if(coprime > 1)
+            {
+            /*
+            First do a single M-R iteration to quickly elimate most non-primes,
+            before doing the coprimality check which is expensive
+            */
+            if(is_miller_rabin_probable_prime(p, mod_p, rng, 1) == false)
+               continue;
+
+            /*
+            * Check if p - 1 and coprime are relatively prime, using gcd.
+            * The gcd computation is const-time
+            */
+            if(gcd(p - 1, coprime) > 1)
+               continue;
+            }
+
+         if(p.bits() > bits)
+            break;
+
+         if(is_miller_rabin_probable_prime(p, mod_p, rng, mr_trials) == false)
+            continue;
+
+         if(prob > 32 && !is_lucas_probable_prime(p, mod_p))
+            continue;
+
+         return p;
+         }
+      }
+   }
+
+BigInt generate_rsa_prime(RandomNumberGenerator& keygen_rng,
+                          RandomNumberGenerator& prime_test_rng,
+                          size_t bits,
+                          const BigInt& coprime,
+                          size_t prob)
+   {
+   if(bits < 512)
+      throw Invalid_Argument("generate_rsa_prime bits too small");
+
+   /*
+   * The restriction on coprime <= 64 bits is arbitrary but generally speaking
+   * very large RSA public exponents are a bad idea both for performance and due
+   * to attacks on small d.
+   */
+   if(coprime <= 1 || coprime.is_even() || coprime.bits() > 64)
+      throw Invalid_Argument("generate_rsa_prime coprime must be small odd positive integer");
+
+   const size_t MAX_ATTEMPTS = 32*1024;
+
+   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
+
+   while(true)
+      {
+      BigInt p(keygen_rng, bits);
+
+      // Force high two bits so multiplication always results in expected n bit integer
+      p.set_bit(bits - 1);
+      p.set_bit(bits - 2);
+      p.set_bit(0);
+
+      const word step = 2;
+
+      Prime_Sieve sieve(p, bits);
+
+      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt)
+         {
+         p += step;
+
+         sieve.step(step);
+
+         if(sieve.passes() == false)
+            continue;
+
+         Modular_Reducer mod_p(p);
+
+         /*
+         * Do a single primality test first before checking coprimality, since
+         * currently a single Miller-Rabin test is faster than computing gcd,
+         * and this eliminates almost all wasted gcd computations.
+         */
+         if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, 1) == false)
+            continue;
+
+         /*
+         * Check if p - 1 and coprime are relatively prime.
+         */
+         if(gcd(p - 1, coprime) > 1)
+            continue;
+
+         if(p.bits() > bits)
+            break;
+
+         if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, mr_trials) == true)
+            return p;
+         }
+      }
+   }
+
+/*
+* Generate a random safe prime
+*/
+BigInt random_safe_prime(RandomNumberGenerator& rng, size_t bits)
+   {
+   if(bits <= 64)
+      throw Invalid_Argument("random_safe_prime: Can't make a prime of " +
+                             std::to_string(bits) + " bits");
+
+   const size_t error_bound = 128;
+
+   BigInt q, p;
+   for(;;)
+      {
+      /*
+      Generate q == 2 (mod 3), since otherwise [in the case of q == 1 (mod 3)],
+      2*q+1 == 3 (mod 3) and so certainly not prime.
+      */
+      q = random_prime(rng, bits - 1, 0, 2, 3, error_bound);
+      p = (q << 1) + 1;
+
+      if(is_prime(p, rng, error_bound, true))
+         {
+         return p;
+         }
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp b/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp
@@ -0,0 +1,356 @@
+/*
+* (C) 1999-2011,2016,2018,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/divide.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+/*
+Sets result to a^-1 * 2^k mod a
+with n <= k <= 2n
+Returns k
+
+"The Montgomery Modular Inverse - Revisited" Çetin Koç, E. Savas
+https://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.75.8377
+
+A const time implementation of this algorithm is described in
+"Constant Time Modular Inversion" Joppe W. Bos
+http://www.joppebos.com/files/CTInversion.pdf
+*/
+size_t almost_montgomery_inverse(BigInt& result,
+                                 const BigInt& a,
+                                 const BigInt& p)
+   {
+   size_t k = 0;
+
+   BigInt u = p, v = a, r = 0, s = 1;
+
+   while(v > 0)
+      {
+      if(u.is_even())
+         {
+         u >>= 1;
+         s <<= 1;
+         }
+      else if(v.is_even())
+         {
+         v >>= 1;
+         r <<= 1;
+         }
+      else if(u > v)
+         {
+         u -= v;
+         u >>= 1;
+         r += s;
+         s <<= 1;
+         }
+      else
+         {
+         v -= u;
+         v >>= 1;
+         s += r;
+         r <<= 1;
+         }
+
+      ++k;
+      }
+
+   if(r >= p)
+      {
+      r -= p;
+      }
+
+   result = p - r;
+
+   return k;
+   }
+
+BigInt normalized_montgomery_inverse(const BigInt& a, const BigInt& p)
+   {
+   BigInt r;
+   size_t k = almost_montgomery_inverse(r, a, p);
+
+   for(size_t i = 0; i != k; ++i)
+      {
+      if(r.is_odd())
+         r += p;
+      r >>= 1;
+      }
+
+   return r;
+   }
+
+namespace {
+
+BigInt inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
+   {
+   // Caller should assure these preconditions:
+   BOTAN_DEBUG_ASSERT(n.is_positive());
+   BOTAN_DEBUG_ASSERT(mod.is_positive());
+   BOTAN_DEBUG_ASSERT(n < mod);
+   BOTAN_DEBUG_ASSERT(mod >= 3 && mod.is_odd());
+
+   /*
+   This uses a modular inversion algorithm designed by Niels Möller
+   and implemented in Nettle. The same algorithm was later also
+   adapted to GMP in mpn_sec_invert.
+
+   It can be easily implemented in a way that does not depend on
+   secret branches or memory lookups, providing resistance against
+   some forms of side channel attack.
+
+   There is also a description of the algorithm in Appendix 5 of "Fast
+   Software Polynomial Multiplication on ARM Processors using the NEON Engine"
+   by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
+   Dahab in LNCS 8182
+      https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf
+
+   Thanks to Niels for creating the algorithm, explaining some things
+   about it, and the reference to the paper.
+   */
+
+   const size_t mod_words = mod.sig_words();
+   BOTAN_ASSERT(mod_words > 0, "Not empty");
+
+   secure_vector<word> tmp_mem(5*mod_words);
+
+   word* v_w = &tmp_mem[0];
+   word* u_w = &tmp_mem[1*mod_words];
+   word* b_w = &tmp_mem[2*mod_words];
+   word* a_w = &tmp_mem[3*mod_words];
+   word* mp1o2 = &tmp_mem[4*mod_words];
+
+   CT::poison(tmp_mem.data(), tmp_mem.size());
+
+   copy_mem(a_w, n.data(), std::min(n.size(), mod_words));
+   copy_mem(b_w, mod.data(), std::min(mod.size(), mod_words));
+   u_w[0] = 1;
+   // v_w = 0
+
+   // compute (mod + 1) / 2 which [because mod is odd] is equal to
+   // (mod / 2) + 1
+   copy_mem(mp1o2, mod.data(), std::min(mod.size(), mod_words));
+   bigint_shr1(mp1o2, mod_words, 0, 1);
+   word carry = bigint_add2_nc(mp1o2, mod_words, u_w, 1);
+   BOTAN_ASSERT_NOMSG(carry == 0);
+
+   // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
+   const size_t execs = 2 * mod.bits();
+
+   for(size_t i = 0; i != execs; ++i)
+      {
+      const word odd_a = a_w[0] & 1;
+
+      //if(odd_a) a -= b
+      word underflow = bigint_cnd_sub(odd_a, a_w, b_w, mod_words);
+
+      //if(underflow) { b -= a; a = abs(a); swap(u, v); }
+      bigint_cnd_add(underflow, b_w, a_w, mod_words);
+      bigint_cnd_abs(underflow, a_w, mod_words);
+      bigint_cnd_swap(underflow, u_w, v_w, mod_words);
+
+      // a >>= 1
+      bigint_shr1(a_w, mod_words, 0, 1);
+
+      //if(odd_a) u -= v;
+      word borrow = bigint_cnd_sub(odd_a, u_w, v_w, mod_words);
+
+      // if(borrow) u += p
+      bigint_cnd_add(borrow, u_w, mod.data(), mod_words);
+
+      const word odd_u = u_w[0] & 1;
+
+      // u >>= 1
+      bigint_shr1(u_w, mod_words, 0, 1);
+
+      //if(odd_u) u += mp1o2;
+      bigint_cnd_add(odd_u, u_w, mp1o2, mod_words);
+      }
+
+   auto a_is_0 = CT::Mask<word>::set();
+   for(size_t i = 0; i != mod_words; ++i)
+      a_is_0 &= CT::Mask<word>::is_zero(a_w[i]);
+
+   auto b_is_1 = CT::Mask<word>::is_equal(b_w[0], 1);
+   for(size_t i = 1; i != mod_words; ++i)
+      b_is_1 &= CT::Mask<word>::is_zero(b_w[i]);
+
+   BOTAN_ASSERT(a_is_0.is_set(), "A is zero");
+
+   // if b != 1 then gcd(n,mod) > 1 and inverse does not exist
+   // in which case zero out the result to indicate this
+   (~b_is_1).if_set_zero_out(v_w, mod_words);
+
+   /*
+   * We've placed the result in the lowest words of the temp buffer.
+   * So just clear out the other values and then give that buffer to a
+   * BigInt.
+   */
+   clear_mem(&tmp_mem[mod_words], 4*mod_words);
+
+   CT::unpoison(tmp_mem.data(), tmp_mem.size());
+
+   BigInt r;
+   r.swap_reg(tmp_mem);
+   return r;
+   }
+
+BigInt inverse_mod_pow2(const BigInt& a1, size_t k)
+   {
+   /*
+   * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
+   * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
+   */
+
+   if(a1.is_even())
+      return 0;
+
+   BigInt a = a1;
+   a.mask_bits(k);
+
+   BigInt b = 1;
+   BigInt X = 0;
+   BigInt newb;
+
+   const size_t a_words = a.sig_words();
+
+   X.grow_to(round_up(k, BOTAN_MP_WORD_BITS) / BOTAN_MP_WORD_BITS);
+   b.grow_to(a_words);
+
+   /*
+   Hide the exact value of k. k is anyway known to word length
+   granularity because of the length of a, so no point in doing more
+   than this.
+   */
+   const size_t iter = round_up(k, BOTAN_MP_WORD_BITS);
+
+   for(size_t i = 0; i != iter; ++i)
+      {
+      const bool b0 = b.get_bit(0);
+      X.conditionally_set_bit(i, b0);
+      newb = b - a;
+      b.ct_cond_assign(b0, newb);
+      b >>= 1;
+      }
+
+   X.mask_bits(k);
+   X.const_time_unpoison();
+   return X;
+   }
+
+}
+
+BigInt inverse_mod(const BigInt& n, const BigInt& mod)
+   {
+   if(mod.is_zero())
+      throw BigInt::DivideByZero();
+   if(mod.is_negative() || n.is_negative())
+      throw Invalid_Argument("inverse_mod: arguments must be non-negative");
+   if(n.is_zero() || (n.is_even() && mod.is_even()))
+      return 0;
+
+   if(mod.is_odd())
+      {
+      /*
+      Fastpath for common case. This leaks information if n > mod
+      but we don't guarantee const time behavior in that case.
+      */
+      if(n < mod)
+         return inverse_mod_odd_modulus(n, mod);
+      else
+         return inverse_mod_odd_modulus(ct_modulo(n, mod), mod);
+      }
+
+   const size_t mod_lz = low_zero_bits(mod);
+   BOTAN_ASSERT_NOMSG(mod_lz > 0);
+   const size_t mod_bits = mod.bits();
+   BOTAN_ASSERT_NOMSG(mod_bits > mod_lz);
+
+   if(mod_lz == mod_bits - 1)
+      {
+      // In this case we are performing an inversion modulo 2^k
+      return inverse_mod_pow2(n, mod_lz);
+      }
+
+   /*
+   * In this case we are performing an inversion modulo 2^k*o for
+   * some k > 1 and some odd (not necessarily prime) integer.
+   * Compute the inversions modulo 2^k and modulo o, then combine them
+   * using CRT, which is possible because 2^k and o are relatively prime.
+   */
+
+   const BigInt o = mod >> mod_lz;
+   const BigInt n_redc = ct_modulo(n, o);
+   const BigInt inv_o = inverse_mod_odd_modulus(n_redc, o);
+   const BigInt inv_2k = inverse_mod_pow2(n, mod_lz);
+
+   // No modular inverse in this case:
+   if(inv_o == 0 || inv_2k == 0)
+      return 0;
+
+   const BigInt m2k = BigInt::power_of_2(mod_lz);
+   // Compute the CRT parameter
+   const BigInt c = inverse_mod_pow2(o, mod_lz);
+
+   // Compute h = c*(inv_2k-inv_o) mod 2^k
+   BigInt h = c * (inv_2k - inv_o);
+   const bool h_neg = h.is_negative();
+   h.set_sign(BigInt::Positive);
+   h.mask_bits(mod_lz);
+   const bool h_nonzero = h.is_nonzero();
+   h.ct_cond_assign(h_nonzero && h_neg, m2k - h);
+
+   // Return result inv_o + h * o
+   h *= o;
+   h += inv_o;
+   return h;
+   }
+
+// Deprecated forwarding functions:
+BigInt inverse_euclid(const BigInt& x, const BigInt& modulus)
+   {
+   return inverse_mod(x, modulus);
+   }
+
+BigInt ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
+   {
+   return inverse_mod_odd_modulus(n, mod);
+   }
+
+word monty_inverse(word a)
+   {
+   if(a % 2 == 0)
+      throw Invalid_Argument("monty_inverse only valid for odd integers");
+
+   /*
+   * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
+   * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
+   */
+
+   word b = 1;
+   word r = 0;
+
+   for(size_t i = 0; i != BOTAN_MP_WORD_BITS; ++i)
+      {
+      const word bi = b % 2;
+      r >>= 1;
+      r += bi << (BOTAN_MP_WORD_BITS - 1);
+
+      b -= a * bi;
+      b >>= 1;
+      }
+
+   // Now invert in addition space
+   r = (MP_WORD_MAX - r) + 1;
+
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/monty.cpp b/third_party/botan/src/lib/math/numbertheory/monty.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/monty.cpp
@@ -0,0 +1,444 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/monty.h>
+#include <botan/reducer.h>
+#include <botan/internal/mp_core.h>
+
+namespace Botan {
+
+Montgomery_Params::Montgomery_Params(const BigInt& p,
+                                     const Modular_Reducer& mod_p)
+   {
+   if(p.is_even() || p < 3)
+      throw Invalid_Argument("Montgomery_Params invalid modulus");
+
+   m_p = p;
+   m_p_words = m_p.sig_words();
+   m_p_dash = monty_inverse(m_p.word_at(0));
+
+   const BigInt r = BigInt::power_of_2(m_p_words * BOTAN_MP_WORD_BITS);
+
+   m_r1 = mod_p.reduce(r);
+   m_r2 = mod_p.square(m_r1);
+   m_r3 = mod_p.multiply(m_r1, m_r2);
+   }
+
+Montgomery_Params::Montgomery_Params(const BigInt& p)
+   {
+
+   if(p.is_negative() || p.is_even())
+      throw Invalid_Argument("Montgomery_Params invalid modulus");
+
+   m_p = p;
+   m_p_words = m_p.sig_words();
+   m_p_dash = monty_inverse(m_p.word_at(0));
+
+   const BigInt r = BigInt::power_of_2(m_p_words * BOTAN_MP_WORD_BITS);
+
+   // It might be faster to use ct_modulo here vs setting up Barrett reduction?
+   Modular_Reducer mod_p(p);
+
+   m_r1 = mod_p.reduce(r);
+   m_r2 = mod_p.square(m_r1);
+   m_r3 = mod_p.multiply(m_r1, m_r2);
+   }
+
+BigInt Montgomery_Params::inv_mod_p(const BigInt& x) const
+   {
+   // TODO use Montgomery inverse here?
+   return inverse_mod(x, p());
+   }
+
+BigInt Montgomery_Params::redc(const BigInt& x, secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < output_size)
+      ws.resize(output_size);
+
+   BigInt z = x;
+   z.grow_to(output_size);
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+
+   return z;
+   }
+
+BigInt Montgomery_Params::mul(const BigInt& x, const BigInt& y,
+                              secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < output_size)
+      ws.resize(output_size);
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+   BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+   BigInt z(BigInt::Positive, output_size);
+   bigint_mul(z.mutable_data(), z.size(),
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws.data(), ws.size());
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+
+   return z;
+   }
+
+BigInt Montgomery_Params::mul(const BigInt& x,
+                              const secure_vector<word>& y,
+                              secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+   if(ws.size() < output_size)
+      ws.resize(output_size);
+   BigInt z(BigInt::Positive, output_size);
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+   bigint_mul(z.mutable_data(), z.size(),
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws.data(), ws.size());
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+
+   return z;
+   }
+
+void Montgomery_Params::mul_by(BigInt& x,
+                               const secure_vector<word>& y,
+                               secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < 2*output_size)
+      ws.resize(2*output_size);
+
+   word* z_data = &ws[0];
+   word* ws_data = &ws[output_size];
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+   bigint_mul(z_data, output_size,
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws_data, output_size);
+
+   bigint_monty_redc(z_data,
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws_data, output_size);
+
+   if(x.size() < output_size)
+      x.grow_to(output_size);
+   copy_mem(x.mutable_data(), z_data, output_size);
+   }
+
+void Montgomery_Params::mul_by(BigInt& x,
+                               const BigInt& y,
+                               secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < 2*output_size)
+      ws.resize(2*output_size);
+
+   word* z_data = &ws[0];
+   word* ws_data = &ws[output_size];
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+   bigint_mul(z_data, output_size,
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws_data, output_size);
+
+   bigint_monty_redc(z_data,
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws_data, output_size);
+
+   if(x.size() < output_size)
+      x.grow_to(output_size);
+   copy_mem(x.mutable_data(), z_data, output_size);
+   }
+
+BigInt Montgomery_Params::sqr(const BigInt& x, secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < output_size)
+      ws.resize(output_size);
+
+   BigInt z(BigInt::Positive, output_size);
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+   bigint_sqr(z.mutable_data(), z.size(),
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              ws.data(), ws.size());
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+
+   return z;
+   }
+
+void Montgomery_Params::square_this(BigInt& x,
+                                    secure_vector<word>& ws) const
+   {
+   const size_t output_size = 2*m_p_words + 2;
+
+   if(ws.size() < 2*output_size)
+      ws.resize(2*output_size);
+
+   word* z_data = &ws[0];
+   word* ws_data = &ws[output_size];
+
+   BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+   bigint_sqr(z_data, output_size,
+              x.data(), x.size(), std::min(m_p_words, x.size()),
+              ws_data, output_size);
+
+   bigint_monty_redc(z_data,
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws_data, output_size);
+
+   if(x.size() < output_size)
+      x.grow_to(output_size);
+   copy_mem(x.mutable_data(), z_data, output_size);
+   }
+
+Montgomery_Int::Montgomery_Int(const std::shared_ptr<const Montgomery_Params> params,
+                               const BigInt& v,
+                               bool redc_needed) :
+   m_params(params)
+   {
+   if(redc_needed == false)
+      {
+      m_v = v;
+      }
+   else
+      {
+      BOTAN_ASSERT_NOMSG(m_v < m_params->p());
+      secure_vector<word> ws;
+      m_v = m_params->mul(v, m_params->R2(), ws);
+      }
+   }
+
+Montgomery_Int::Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+                               const uint8_t bits[], size_t len,
+                               bool redc_needed) :
+   m_params(params),
+   m_v(bits, len)
+   {
+   if(redc_needed)
+      {
+      BOTAN_ASSERT_NOMSG(m_v < m_params->p());
+      secure_vector<word> ws;
+      m_v = m_params->mul(m_v, m_params->R2(), ws);
+      }
+   }
+
+Montgomery_Int::Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+                               const word words[], size_t len,
+                               bool redc_needed) :
+   m_params(params),
+   m_v(words, len)
+   {
+   if(redc_needed)
+      {
+      BOTAN_ASSERT_NOMSG(m_v < m_params->p());
+      secure_vector<word> ws;
+      m_v = m_params->mul(m_v, m_params->R2(), ws);
+      }
+   }
+
+void Montgomery_Int::fix_size()
+   {
+   const size_t p_words = m_params->p_words();
+
+   if(m_v.sig_words() > p_words)
+      throw Internal_Error("Montgomery_Int::fix_size v too large");
+
+   m_v.grow_to(p_words);
+   }
+
+bool Montgomery_Int::operator==(const Montgomery_Int& other) const
+   {
+   return m_v == other.m_v && m_params->p() == other.m_params->p();
+   }
+
+std::vector<uint8_t> Montgomery_Int::serialize() const
+   {
+   std::vector<uint8_t> v(size());
+   BigInt::encode_1363(v.data(), v.size(), value());
+   return v;
+   }
+
+size_t Montgomery_Int::size() const
+   {
+   return m_params->p().bytes();
+   }
+
+bool Montgomery_Int::is_one() const
+   {
+   return m_v == m_params->R1();
+   }
+
+bool Montgomery_Int::is_zero() const
+   {
+   return m_v.is_zero();
+   }
+
+BigInt Montgomery_Int::value() const
+   {
+   secure_vector<word> ws;
+   return m_params->redc(m_v, ws);
+   }
+
+Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const
+   {
+   secure_vector<word> ws;
+   BigInt z = m_v;
+   z.mod_add(other.m_v, m_params->p(), ws);
+   return Montgomery_Int(m_params, z, false);
+   }
+
+Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const
+   {
+   secure_vector<word> ws;
+   BigInt z = m_v;
+   z.mod_sub(other.m_v, m_params->p(), ws);
+   return Montgomery_Int(m_params, z, false);
+   }
+
+Montgomery_Int& Montgomery_Int::operator+=(const Montgomery_Int& other)
+   {
+   secure_vector<word> ws;
+   return this->add(other, ws);
+   }
+
+Montgomery_Int& Montgomery_Int::add(const Montgomery_Int& other, secure_vector<word>& ws)
+   {
+   m_v.mod_add(other.m_v, m_params->p(), ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::operator-=(const Montgomery_Int& other)
+   {
+   secure_vector<word> ws;
+   return this->sub(other, ws);
+   }
+
+Montgomery_Int& Montgomery_Int::sub(const Montgomery_Int& other, secure_vector<word>& ws)
+   {
+   m_v.mod_sub(other.m_v, m_params->p(), ws);
+   return (*this);
+   }
+
+Montgomery_Int Montgomery_Int::operator*(const Montgomery_Int& other) const
+   {
+   secure_vector<word> ws;
+   return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false);
+   }
+
+Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other,
+                                   secure_vector<word>& ws) const
+   {
+   return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other,
+                                       secure_vector<word>& ws)
+   {
+   m_params->mul_by(m_v, other.m_v, ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by(const secure_vector<word>& other,
+                                       secure_vector<word>& ws)
+   {
+   m_params->mul_by(m_v, other, ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::operator*=(const Montgomery_Int& other)
+   {
+   secure_vector<word> ws;
+   return mul_by(other, ws);
+   }
+
+Montgomery_Int& Montgomery_Int::operator*=(const secure_vector<word>& other)
+   {
+   secure_vector<word> ws;
+   return mul_by(other, ws);
+   }
+
+Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector<word>& ws, size_t n)
+   {
+   for(size_t i = 0; i != n; ++i)
+      m_params->square_this(m_v, ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::square_this(secure_vector<word>& ws)
+   {
+   m_params->square_this(m_v, ws);
+   return (*this);
+   }
+
+Montgomery_Int Montgomery_Int::square(secure_vector<word>& ws) const
+   {
+   return Montgomery_Int(m_params, m_params->sqr(m_v, ws), false);
+   }
+
+Montgomery_Int Montgomery_Int::multiplicative_inverse() const
+   {
+   secure_vector<word> ws;
+   const BigInt iv = m_params->mul(m_params->inv_mod_p(m_v), m_params->R3(), ws);
+   return Montgomery_Int(m_params, iv, false);
+   }
+
+Montgomery_Int Montgomery_Int::additive_inverse() const
+   {
+   return Montgomery_Int(m_params, m_params->p()) - (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by_2(secure_vector<word>& ws)
+   {
+   m_v.mod_mul(2, m_params->p(), ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by_3(secure_vector<word>& ws)
+   {
+   m_v.mod_mul(3, m_params->p(), ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by_4(secure_vector<word>& ws)
+   {
+   m_v.mod_mul(4, m_params->p(), ws);
+   return (*this);
+   }
+
+Montgomery_Int& Montgomery_Int::mul_by_8(secure_vector<word>& ws)
+   {
+   m_v.mod_mul(8, m_params->p(), ws);
+   return (*this);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/monty.h b/third_party/botan/src/lib/math/numbertheory/monty.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/monty.h
@@ -0,0 +1,190 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MONTY_INT_H_
+#define BOTAN_MONTY_INT_H_
+
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class Modular_Reducer;
+
+class Montgomery_Params;
+
+/**
+* The Montgomery representation of an integer
+*/
+class BOTAN_UNSTABLE_API Montgomery_Int final
+   {
+   public:
+      /**
+      * Create a zero-initialized Montgomery_Int
+      */
+      Montgomery_Int(std::shared_ptr<const Montgomery_Params> params) : m_params(params) {}
+
+      /**
+      * Create a Montgomery_Int
+      */
+      Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+                     const BigInt& v,
+                     bool redc_needed = true);
+
+      /**
+      * Create a Montgomery_Int
+      */
+      Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+                     const uint8_t bits[], size_t len,
+                     bool redc_needed = true);
+
+      /**
+      * Create a Montgomery_Int
+      */
+      Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+                     const word words[], size_t len,
+                     bool redc_needed = true);
+
+      bool operator==(const Montgomery_Int& other) const;
+      bool operator!=(const Montgomery_Int& other) const { return (m_v != other.m_v); }
+
+      std::vector<uint8_t> serialize() const;
+
+      size_t size() const;
+      bool is_one() const;
+      bool is_zero() const;
+
+      void fix_size();
+
+      /**
+      * Return the value to normal mod-p space
+      */
+      BigInt value() const;
+
+      /**
+      * Return the Montgomery representation
+      */
+      const BigInt& repr() const { return m_v; }
+
+      Montgomery_Int operator+(const Montgomery_Int& other) const;
+
+      Montgomery_Int operator-(const Montgomery_Int& other) const;
+
+      Montgomery_Int& operator+=(const Montgomery_Int& other);
+
+      Montgomery_Int& operator-=(const Montgomery_Int& other);
+
+      Montgomery_Int operator*(const Montgomery_Int& other) const;
+
+      Montgomery_Int& operator*=(const Montgomery_Int& other);
+
+      Montgomery_Int& operator*=(const secure_vector<word>& other);
+
+      Montgomery_Int& add(const Montgomery_Int& other,
+                          secure_vector<word>& ws);
+
+      Montgomery_Int& sub(const Montgomery_Int& other,
+                          secure_vector<word>& ws);
+
+      Montgomery_Int mul(const Montgomery_Int& other,
+                         secure_vector<word>& ws) const;
+
+      Montgomery_Int& mul_by(const Montgomery_Int& other,
+                             secure_vector<word>& ws);
+
+      Montgomery_Int& mul_by(const secure_vector<word>& other,
+                             secure_vector<word>& ws);
+
+      Montgomery_Int square(secure_vector<word>& ws) const;
+
+      Montgomery_Int& square_this(secure_vector<word>& ws);
+
+      Montgomery_Int& square_this_n_times(secure_vector<word>& ws, size_t n);
+
+      Montgomery_Int multiplicative_inverse() const;
+
+      Montgomery_Int additive_inverse() const;
+
+      Montgomery_Int& mul_by_2(secure_vector<word>& ws);
+
+      Montgomery_Int& mul_by_3(secure_vector<word>& ws);
+
+      Montgomery_Int& mul_by_4(secure_vector<word>& ws);
+
+      Montgomery_Int& mul_by_8(secure_vector<word>& ws);
+
+      void const_time_poison() const { m_v.const_time_poison(); }
+      void const_time_unpoison() const { return m_v.const_time_unpoison(); }
+
+   private:
+      std::shared_ptr<const Montgomery_Params> m_params;
+      BigInt m_v;
+   };
+
+/**
+* Parameters for Montgomery Reduction
+*/
+class BOTAN_UNSTABLE_API Montgomery_Params final
+   {
+   public:
+      /**
+      * Initialize a set of Montgomery reduction parameters. These values
+      * can be shared by all values in a specific Montgomery domain.
+      */
+      Montgomery_Params(const BigInt& p, const Modular_Reducer& mod_p);
+
+      /**
+      * Initialize a set of Montgomery reduction parameters. These values
+      * can be shared by all values in a specific Montgomery domain.
+      */
+      Montgomery_Params(const BigInt& p);
+
+      const BigInt& p() const { return m_p; }
+      const BigInt& R1() const { return m_r1; }
+      const BigInt& R2() const { return m_r2; }
+      const BigInt& R3() const { return m_r3; }
+
+      word p_dash() const { return m_p_dash; }
+
+      size_t p_words() const { return m_p_words; }
+
+      BigInt redc(const BigInt& x,
+                  secure_vector<word>& ws) const;
+
+      BigInt mul(const BigInt& x,
+                 const BigInt& y,
+                 secure_vector<word>& ws) const;
+
+      BigInt mul(const BigInt& x,
+                 const secure_vector<word>& y,
+                 secure_vector<word>& ws) const;
+
+      void mul_by(BigInt& x,
+                  const secure_vector<word>& y,
+                  secure_vector<word>& ws) const;
+
+      void mul_by(BigInt& x, const BigInt& y,
+                  secure_vector<word>& ws) const;
+
+      BigInt sqr(const BigInt& x,
+                 secure_vector<word>& ws) const;
+
+      void square_this(BigInt& x,
+                       secure_vector<word>& ws) const;
+
+      BigInt inv_mod_p(const BigInt& x) const;
+
+   private:
+      BigInt m_p;
+      BigInt m_r1;
+      BigInt m_r2;
+      BigInt m_r3;
+      word m_p_dash;
+      size_t m_p_words;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp b/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp
@@ -0,0 +1,254 @@
+/*
+* Montgomery Exponentiation
+* (C) 1999-2010,2012,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/rounding.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+
+namespace Botan {
+
+class Montgomery_Exponentation_State
+   {
+   public:
+      Montgomery_Exponentation_State(std::shared_ptr<const Montgomery_Params> params,
+                                     const BigInt& g,
+                                     size_t window_bits,
+                                     bool const_time);
+
+      BigInt exponentiation(const BigInt& k, size_t max_k_bits) const;
+
+      BigInt exponentiation_vartime(const BigInt& k) const;
+   private:
+      std::shared_ptr<const Montgomery_Params> m_params;
+      std::vector<Montgomery_Int> m_g;
+      size_t m_window_bits;
+      bool m_const_time;
+   };
+
+Montgomery_Exponentation_State::Montgomery_Exponentation_State(std::shared_ptr<const Montgomery_Params> params,
+                                                               const BigInt& g,
+                                                               size_t window_bits,
+                                                               bool const_time) :
+   m_params(params),
+   m_window_bits(window_bits == 0 ? 4 : window_bits),
+   m_const_time(const_time)
+   {
+   BOTAN_ARG_CHECK(g < m_params->p(), "Montgomery base too big");
+
+   if(m_window_bits < 1 || m_window_bits > 12) // really even 8 is too large ...
+      throw Invalid_Argument("Invalid window bits for Montgomery exponentiation");
+
+   const size_t window_size = (static_cast<size_t>(1) << m_window_bits);
+
+   m_g.reserve(window_size);
+
+   m_g.push_back(Montgomery_Int(m_params, m_params->R1(), false));
+
+   m_g.push_back(Montgomery_Int(m_params, g));
+
+   for(size_t i = 2; i != window_size; ++i)
+      {
+      m_g.push_back(m_g[1] * m_g[i - 1]);
+      }
+
+   // Resize each element to exactly p words
+   for(size_t i = 0; i != window_size; ++i)
+      {
+      m_g[i].fix_size();
+      if(const_time)
+         m_g[i].const_time_poison();
+      }
+   }
+
+namespace {
+
+void const_time_lookup(secure_vector<word>& output,
+                       const std::vector<Montgomery_Int>& g,
+                       size_t nibble)
+   {
+   BOTAN_ASSERT_NOMSG(g.size() % 2 == 0); // actually a power of 2
+
+   const size_t words = output.size();
+
+   clear_mem(output.data(), output.size());
+
+   for(size_t i = 0; i != g.size(); i += 2)
+      {
+      const secure_vector<word>& vec_0 = g[i  ].repr().get_word_vector();
+      const secure_vector<word>& vec_1 = g[i+1].repr().get_word_vector();
+
+      BOTAN_ASSERT_NOMSG(vec_0.size() >= words && vec_1.size() >= words);
+
+      const auto mask_0 = CT::Mask<word>::is_equal(nibble, i);
+      const auto mask_1 = CT::Mask<word>::is_equal(nibble, i+1);
+
+      for(size_t w = 0; w != words; ++w)
+         {
+         output[w] |= mask_0.if_set_return(vec_0[w]);
+         output[w] |= mask_1.if_set_return(vec_1[w]);
+         }
+      }
+   }
+
+}
+
+BigInt Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const
+   {
+   BOTAN_DEBUG_ASSERT(scalar.bits() <= max_k_bits);
+   // TODO add a const-time implementation of above assert and use it in release builds
+
+   const size_t exp_nibbles = (max_k_bits + m_window_bits - 1) / m_window_bits;
+
+   if(exp_nibbles == 0)
+      return 1;
+
+   secure_vector<word> e_bits(m_params->p_words());
+   secure_vector<word> ws;
+
+   const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits*(exp_nibbles-1), m_window_bits));
+   Montgomery_Int x(m_params, e_bits.data(), e_bits.size(), false);
+
+   for(size_t i = exp_nibbles - 1; i > 0; --i)
+      {
+      x.square_this_n_times(ws, m_window_bits);
+      const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits*(i-1), m_window_bits));
+      x.mul_by(e_bits, ws);
+      }
+
+   x.const_time_unpoison();
+   return x.value();
+   }
+
+BigInt Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const
+   {
+   BOTAN_ASSERT_NOMSG(m_const_time == false);
+
+   const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits;
+
+   secure_vector<word> ws;
+
+   if(exp_nibbles == 0)
+      return 1;
+
+   Montgomery_Int x = m_g[scalar.get_substring(m_window_bits*(exp_nibbles-1), m_window_bits)];
+
+   for(size_t i = exp_nibbles - 1; i > 0; --i)
+      {
+      x.square_this_n_times(ws, m_window_bits);
+
+      const uint32_t nibble = scalar.get_substring(m_window_bits*(i-1), m_window_bits);
+      if(nibble > 0)
+         x.mul_by(m_g[nibble], ws);
+      }
+
+   x.const_time_unpoison();
+   return x.value();
+   }
+
+std::shared_ptr<const Montgomery_Exponentation_State>
+monty_precompute(std::shared_ptr<const Montgomery_Params> params,
+                 const BigInt& g,
+                 size_t window_bits,
+                 bool const_time)
+   {
+   return std::make_shared<const Montgomery_Exponentation_State>(params, g, window_bits, const_time);
+   }
+
+BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state,
+                     const BigInt& k, size_t max_k_bits)
+   {
+   return precomputed_state.exponentiation(k, max_k_bits);
+   }
+
+BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state,
+                             const BigInt& k)
+   {
+   return precomputed_state.exponentiation_vartime(k);
+   }
+
+BigInt monty_multi_exp(std::shared_ptr<const Montgomery_Params> params_p,
+                       const BigInt& x_bn,
+                       const BigInt& z1,
+                       const BigInt& y_bn,
+                       const BigInt& z2)
+   {
+   if(z1.is_negative() || z2.is_negative())
+      throw Invalid_Argument("multi_exponentiate exponents must be positive");
+
+   const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);
+
+   secure_vector<word> ws;
+
+   const Montgomery_Int one(params_p, params_p->R1(), false);
+   //const Montgomery_Int one(params_p, 1);
+
+   const Montgomery_Int x1(params_p, x_bn);
+   const Montgomery_Int x2 = x1.square(ws);
+   const Montgomery_Int x3 = x2.mul(x1, ws);
+
+   const Montgomery_Int y1(params_p, y_bn);
+   const Montgomery_Int y2 = y1.square(ws);
+   const Montgomery_Int y3 = y2.mul(y1, ws);
+
+   const Montgomery_Int y1x1 = y1.mul(x1, ws);
+   const Montgomery_Int y1x2 = y1.mul(x2, ws);
+   const Montgomery_Int y1x3 = y1.mul(x3, ws);
+
+   const Montgomery_Int y2x1 = y2.mul(x1, ws);
+   const Montgomery_Int y2x2 = y2.mul(x2, ws);
+   const Montgomery_Int y2x3 = y2.mul(x3, ws);
+
+   const Montgomery_Int y3x1 = y3.mul(x1, ws);
+   const Montgomery_Int y3x2 = y3.mul(x2, ws);
+   const Montgomery_Int y3x3 = y3.mul(x3, ws);
+
+   const Montgomery_Int* M[16] = {
+      &one,
+      &x1,                    // 0001
+      &x2,                    // 0010
+      &x3,                    // 0011
+      &y1,                    // 0100
+      &y1x1,
+      &y1x2,
+      &y1x3,
+      &y2,                    // 1000
+      &y2x1,
+      &y2x2,
+      &y2x3,
+      &y3,                    // 1100
+      &y3x1,
+      &y3x2,
+      &y3x3
+   };
+
+   Montgomery_Int H = one;
+
+   for(size_t i = 0; i != z_bits; i += 2)
+      {
+      if(i > 0)
+         {
+         H.square_this(ws);
+         H.square_this(ws);
+         }
+
+      const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
+      const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
+
+      const uint32_t z12 = (4*z2_b) + z1_b;
+
+      H.mul_by(*M[z12], ws);
+      }
+
+   return H.value();
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/math/numbertheory/monty_exp.h b/third_party/botan/src/lib/math/numbertheory/monty_exp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/monty_exp.h
@@ -0,0 +1,54 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MONTY_EXP_H_
+#define BOTAN_MONTY_EXP_H_
+
+#include <memory>
+
+namespace Botan {
+
+class BigInt;
+class Modular_Reducer;
+
+class Montgomery_Params;
+
+class Montgomery_Exponentation_State;
+
+/*
+* Precompute for calculating values g^x mod p
+*/
+std::shared_ptr<const Montgomery_Exponentation_State>
+monty_precompute(std::shared_ptr<const Montgomery_Params> params_p,
+                 const BigInt& g,
+                 size_t window_bits,
+                 bool const_time = true);
+
+/*
+* Return g^k mod p
+*/
+BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state,
+                     const BigInt& k, size_t max_k_bits);
+
+/*
+* Return g^k mod p taking variable time depending on k
+* @warning only use this if k is public
+*/
+BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state,
+                             const BigInt& k);
+
+/**
+* Return (x^z1 * y^z2) % p
+*/
+BigInt monty_multi_exp(std::shared_ptr<const Montgomery_Params> params_p,
+                       const BigInt& x,
+                       const BigInt& z1,
+                       const BigInt& y,
+                       const BigInt& z2);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp b/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp
@@ -0,0 +1,84 @@
+/*
+* Fused and Important MP Algorithms
+* (C) 1999-2007 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Square a BigInt
+*/
+BigInt square(const BigInt& x)
+   {
+   BigInt z = x;
+   secure_vector<word> ws;
+   z.square(ws);
+   return z;
+   }
+
+/*
+* Multiply-Add Operation
+*/
+BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c)
+   {
+   if(c.is_negative())
+      throw Invalid_Argument("mul_add: Third argument must be > 0");
+
+   BigInt::Sign sign = BigInt::Positive;
+   if(a.sign() != b.sign())
+      sign = BigInt::Negative;
+
+   const size_t a_sw = a.sig_words();
+   const size_t b_sw = b.sig_words();
+   const size_t c_sw = c.sig_words();
+
+   BigInt r(sign, std::max(a_sw + b_sw, c_sw) + 1);
+   secure_vector<word> workspace(r.size());
+
+   bigint_mul(r.mutable_data(), r.size(),
+              a.data(), a.size(), a_sw,
+              b.data(), b.size(), b_sw,
+              workspace.data(), workspace.size());
+
+   const size_t r_size = std::max(r.sig_words(), c_sw);
+   bigint_add2(r.mutable_data(), r_size, c.data(), c_sw);
+   return r;
+   }
+
+/*
+* Subtract-Multiply Operation
+*/
+BigInt sub_mul(const BigInt& a, const BigInt& b, const BigInt& c)
+   {
+   if(a.is_negative() || b.is_negative())
+      throw Invalid_Argument("sub_mul: First two arguments must be >= 0");
+
+   BigInt r = a;
+   r -= b;
+   r *= c;
+   return r;
+   }
+
+/*
+* Multiply-Subtract Operation
+*/
+BigInt mul_sub(const BigInt& a, const BigInt& b, const BigInt& c)
+   {
+   if(c.is_negative() || c.is_zero())
+      throw Invalid_Argument("mul_sub: Third argument must be > 0");
+
+   BigInt r = a;
+   r *= b;
+   r -= c;
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp b/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp
@@ -0,0 +1,583 @@
+/*
+* NIST prime reductions
+* (C) 2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve_nistp.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+const BigInt& prime_p521()
+   {
+   static const BigInt p521("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                               "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+   return p521;
+   }
+
+void redc_p521(BigInt& x, secure_vector<word>& ws)
+   {
+   const size_t p_full_words = 521 / BOTAN_MP_WORD_BITS;
+   const size_t p_top_bits = 521 % BOTAN_MP_WORD_BITS;
+   const size_t p_words = p_full_words + 1;
+
+#if (BOTAN_MP_WORD_BITS == 64)
+   static const word p521_words[p_words] = {
+      0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+      0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+      0x1FF };
+#else
+   static const word p521_words[p_words] = {
+      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+      0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+      0x1FF };
+#endif
+
+   if(ws.size() < p_words + 1)
+      ws.resize(p_words + 1);
+
+   clear_mem(ws.data(), ws.size());
+   bigint_shr2(ws.data(), x.data(), std::min(x.size(), 2*p_words), p_full_words, p_top_bits);
+
+   x.mask_bits(521);
+   x.grow_to(p_words);
+
+   // Word-level carry will be zero
+   word carry = bigint_add3_nc(x.mutable_data(), x.data(), p_words, ws.data(), p_words);
+   BOTAN_ASSERT_EQUAL(carry, 0, "Final carry in P-521 reduction");
+
+   const word top_word = x.word_at(p_full_words);
+
+   /*
+   * Check if we need to reduce modulo P
+   * There are two possible cases:
+   * - The result overflowed past 521 bits, in which case bit 522 will be set
+   * - The result is exactly 2**521 - 1
+   */
+   const auto bit_522_set = CT::Mask<word>::expand(top_word >> p_top_bits);
+
+   word and_512 = MP_WORD_MAX;
+   for(size_t i = 0; i != p_full_words; ++i)
+      and_512 &= x.word_at(i);
+   const auto all_512_low_bits_set = CT::Mask<word>::is_equal(and_512, MP_WORD_MAX);
+   const auto has_p521_top_word = CT::Mask<word>::is_equal(top_word, 0x1FF);
+   const auto is_p521 = all_512_low_bits_set & has_p521_top_word;
+
+   const auto needs_reduction = is_p521 | bit_522_set;
+
+   bigint_cnd_sub(needs_reduction.value(), x.mutable_data(), p521_words, p_words);
+   }
+
+namespace {
+
+/**
+* Treating this MPI as a sequence of 32-bit words in big-endian
+* order, return word i. The array is assumed to be large enough.
+*/
+inline uint32_t get_uint32(const word xw[], size_t i)
+   {
+#if (BOTAN_MP_WORD_BITS == 32)
+   return xw[i];
+#else
+   return static_cast<uint32_t>(xw[i/2] >> ((i % 2)*32));
+#endif
+   }
+
+inline void set_words(word x[], size_t i, uint32_t R0, uint32_t R1)
+   {
+#if (BOTAN_MP_WORD_BITS == 32)
+   x[i] = R0;
+   x[i+1] = R1;
+#else
+   x[i/2] = (static_cast<uint64_t>(R1) << 32) | R0;
+#endif
+   }
+
+}
+
+const BigInt& prime_p192()
+   {
+   static const BigInt p192("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
+   return p192;
+   }
+
+void redc_p192(BigInt& x, secure_vector<word>& ws)
+   {
+   BOTAN_UNUSED(ws);
+
+   static const size_t p192_limbs = 192 / BOTAN_MP_WORD_BITS;
+
+   x.grow_to(2*p192_limbs);
+   word* xw = x.mutable_data();
+
+   const uint64_t X00 = get_uint32(xw,  0);
+   const uint64_t X01 = get_uint32(xw,  1);
+   const uint64_t X02 = get_uint32(xw,  2);
+   const uint64_t X03 = get_uint32(xw,  3);
+   const uint64_t X04 = get_uint32(xw,  4);
+   const uint64_t X05 = get_uint32(xw,  5);
+   const uint64_t X06 = get_uint32(xw,  6);
+   const uint64_t X07 = get_uint32(xw,  7);
+   const uint64_t X08 = get_uint32(xw,  8);
+   const uint64_t X09 = get_uint32(xw,  9);
+   const uint64_t X10 = get_uint32(xw, 10);
+   const uint64_t X11 = get_uint32(xw, 11);
+
+   const uint64_t S0 = X00 + X06 + X10;
+   const uint64_t S1 = X01 + X07 + X11;
+   const uint64_t S2 = X02 + X06 + X08 + X10;
+   const uint64_t S3 = X03 + X07 + X09 + X11;
+   const uint64_t S4 = X04 + X08 + X10;
+   const uint64_t S5 = X05 + X09 + X11;
+
+   uint64_t S = 0;
+   uint32_t R0 = 0, R1 = 0;
+
+   S += S0;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S1;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 0, R0, R1);
+
+   S += S2;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S3;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 2, R0, R1);
+
+   S += S4;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S5;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 4, R0, R1);
+
+   // No underflow possible
+
+   /*
+   This is a table of (i*P-192) % 2**192 for i in 1...3
+   */
+   static const word p192_mults[3][p192_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+      {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF},
+      {0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFF},
+      {0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF},
+#else
+      {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+#endif
+   };
+
+   CT::unpoison(S);
+   BOTAN_ASSERT(S <= 2, "Expected overflow");
+
+   BOTAN_ASSERT_NOMSG(x.size() >= p192_limbs + 1);
+   x.mask_bits(192);
+   word borrow = bigint_sub2(x.mutable_data(), p192_limbs + 1, p192_mults[S], p192_limbs);
+   BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+   bigint_cnd_add(borrow, x.mutable_data(), p192_limbs + 1, p192_mults[0], p192_limbs);
+   }
+
+const BigInt& prime_p224()
+   {
+   static const BigInt p224("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
+   return p224;
+   }
+
+void redc_p224(BigInt& x, secure_vector<word>& ws)
+   {
+   static const size_t p224_limbs = (BOTAN_MP_WORD_BITS == 32) ? 7 : 4;
+
+   BOTAN_UNUSED(ws);
+
+   x.grow_to(2*p224_limbs);
+   word* xw = x.mutable_data();
+
+   const int64_t X00 = get_uint32(xw,  0);
+   const int64_t X01 = get_uint32(xw,  1);
+   const int64_t X02 = get_uint32(xw,  2);
+   const int64_t X03 = get_uint32(xw,  3);
+   const int64_t X04 = get_uint32(xw,  4);
+   const int64_t X05 = get_uint32(xw,  5);
+   const int64_t X06 = get_uint32(xw,  6);
+   const int64_t X07 = get_uint32(xw,  7);
+   const int64_t X08 = get_uint32(xw,  8);
+   const int64_t X09 = get_uint32(xw,  9);
+   const int64_t X10 = get_uint32(xw, 10);
+   const int64_t X11 = get_uint32(xw, 11);
+   const int64_t X12 = get_uint32(xw, 12);
+   const int64_t X13 = get_uint32(xw, 13);
+
+   // One full copy of P224 is added, so the result is always positive
+
+   const int64_t S0 = 0x00000001 + X00 - X07 - X11;
+   const int64_t S1 = 0x00000000 + X01 - X08 - X12;
+   const int64_t S2 = 0x00000000 + X02 - X09 - X13;
+   const int64_t S3 = 0xFFFFFFFF + X03 + X07 + X11 - X10;
+   const int64_t S4 = 0xFFFFFFFF + X04 + X08 + X12 - X11;
+   const int64_t S5 = 0xFFFFFFFF + X05 + X09 + X13 - X12;
+   const int64_t S6 = 0xFFFFFFFF + X06 + X10 - X13;
+
+   int64_t S = 0;
+   uint32_t R0 = 0, R1 = 0;
+
+   S += S0;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S1;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 0, R0, R1);
+
+   S += S2;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S3;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 2, R0, R1);
+
+   S += S4;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S5;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 4, R0, R1);
+
+   S += S6;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 6, R0, 0);
+
+   static const word p224_mults[3][p224_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+    {0x0000000000000001, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+    {0x0000000000000002, 0xFFFFFFFE00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+    {0x0000000000000003, 0xFFFFFFFD00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+#else
+    {0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0x00000003, 0x00000000, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
+#endif
+
+   };
+
+   CT::unpoison(S);
+   BOTAN_ASSERT(S >= 0 && S <= 2, "Expected overflow");
+
+   BOTAN_ASSERT_NOMSG(x.size() >= p224_limbs + 1);
+   x.mask_bits(224);
+   word borrow = bigint_sub2(x.mutable_data(), p224_limbs + 1, p224_mults[S], p224_limbs);
+   BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+   bigint_cnd_add(borrow, x.mutable_data(), p224_limbs + 1, p224_mults[0], p224_limbs);
+   }
+
+const BigInt& prime_p256()
+   {
+   static const BigInt p256("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
+   return p256;
+   }
+
+void redc_p256(BigInt& x, secure_vector<word>& ws)
+   {
+   static const size_t p256_limbs = (BOTAN_MP_WORD_BITS == 32) ? 8 : 4;
+
+   BOTAN_UNUSED(ws);
+
+   x.grow_to(2*p256_limbs);
+   word* xw = x.mutable_data();
+
+   const int64_t X00 = get_uint32(xw,  0);
+   const int64_t X01 = get_uint32(xw,  1);
+   const int64_t X02 = get_uint32(xw,  2);
+   const int64_t X03 = get_uint32(xw,  3);
+   const int64_t X04 = get_uint32(xw,  4);
+   const int64_t X05 = get_uint32(xw,  5);
+   const int64_t X06 = get_uint32(xw,  6);
+   const int64_t X07 = get_uint32(xw,  7);
+   const int64_t X08 = get_uint32(xw,  8);
+   const int64_t X09 = get_uint32(xw,  9);
+   const int64_t X10 = get_uint32(xw, 10);
+   const int64_t X11 = get_uint32(xw, 11);
+   const int64_t X12 = get_uint32(xw, 12);
+   const int64_t X13 = get_uint32(xw, 13);
+   const int64_t X14 = get_uint32(xw, 14);
+   const int64_t X15 = get_uint32(xw, 15);
+
+   // Adds 6 * P-256 to prevent underflow
+   const int64_t S0 = 0xFFFFFFFA + X00 + X08 + X09 - (X11 + X12 + X13) - X14;
+   const int64_t S1 = 0xFFFFFFFF + X01 + X09 + X10 - X12 - (X13 + X14 + X15);
+   const int64_t S2 = 0xFFFFFFFF + X02 + X10 + X11 - (X13 + X14 + X15);
+   const int64_t S3 = 0x00000005 + X03 + (X11 + X12)*2 + X13 - X15 - X08 - X09;
+   const int64_t S4 = 0x00000000 + X04 + (X12 + X13)*2 + X14 - X09 - X10;
+   const int64_t S5 = 0x00000000 + X05 + (X13 + X14)*2 + X15 - X10 - X11;
+   const int64_t S6 = 0x00000006 + X06 + X13 + X14*3 + X15*2 - X08 - X09;
+   const int64_t S7 = 0xFFFFFFFA + X07 + X15*3 + X08 - X10 - (X11 + X12 + X13);
+
+   int64_t S = 0;
+
+   uint32_t R0 = 0, R1 = 0;
+
+   S += S0;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S1;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 0, R0, R1);
+
+   S += S2;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S3;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 2, R0, R1);
+
+   S += S4;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S5;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 4, R0, R1);
+
+   S += S6;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S7;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+   set_words(xw, 6, R0, R1);
+
+   S += 5; // the top digits of 6*P-256
+
+   /*
+   This is a table of (i*P-256) % 2**256 for i in 1...10
+   */
+   static const word p256_mults[11][p256_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+      {0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF, 0x0000000000000000, 0xFFFFFFFF00000001},
+      {0xFFFFFFFFFFFFFFFE, 0x00000001FFFFFFFF, 0x0000000000000000, 0xFFFFFFFE00000002},
+      {0xFFFFFFFFFFFFFFFD, 0x00000002FFFFFFFF, 0x0000000000000000, 0xFFFFFFFD00000003},
+      {0xFFFFFFFFFFFFFFFC, 0x00000003FFFFFFFF, 0x0000000000000000, 0xFFFFFFFC00000004},
+      {0xFFFFFFFFFFFFFFFB, 0x00000004FFFFFFFF, 0x0000000000000000, 0xFFFFFFFB00000005},
+      {0xFFFFFFFFFFFFFFFA, 0x00000005FFFFFFFF, 0x0000000000000000, 0xFFFFFFFA00000006},
+      {0xFFFFFFFFFFFFFFF9, 0x00000006FFFFFFFF, 0x0000000000000000, 0xFFFFFFF900000007},
+      {0xFFFFFFFFFFFFFFF8, 0x00000007FFFFFFFF, 0x0000000000000000, 0xFFFFFFF800000008},
+      {0xFFFFFFFFFFFFFFF7, 0x00000008FFFFFFFF, 0x0000000000000000, 0xFFFFFFF700000009},
+      {0xFFFFFFFFFFFFFFF6, 0x00000009FFFFFFFF, 0x0000000000000000, 0xFFFFFFF60000000A},
+      {0xFFFFFFFFFFFFFFF5, 0x0000000AFFFFFFFF, 0x0000000000000000, 0xFFFFFFF50000000B},
+#else
+      {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0xFFFFFFFF},
+      {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0x00000000, 0x00000000, 0x00000002, 0xFFFFFFFE},
+      {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00000000, 0x00000000, 0x00000003, 0xFFFFFFFD},
+      {0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, 0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFC},
+      {0xFFFFFFFB, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000004, 0x00000000, 0x00000000, 0x00000005, 0xFFFFFFFB},
+      {0xFFFFFFFA, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000005, 0x00000000, 0x00000000, 0x00000006, 0xFFFFFFFA},
+      {0xFFFFFFF9, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000006, 0x00000000, 0x00000000, 0x00000007, 0xFFFFFFF9},
+      {0xFFFFFFF8, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000, 0x00000000, 0x00000008, 0xFFFFFFF8},
+      {0xFFFFFFF7, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000008, 0x00000000, 0x00000000, 0x00000009, 0xFFFFFFF7},
+      {0xFFFFFFF6, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000009, 0x00000000, 0x00000000, 0x0000000A, 0xFFFFFFF6},
+      {0xFFFFFFF5, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000A, 0x00000000, 0x00000000, 0x0000000B, 0xFFFFFFF5},
+#endif
+   };
+
+   CT::unpoison(S);
+   BOTAN_ASSERT(S >= 0 && S <= 10, "Expected overflow");
+
+   BOTAN_ASSERT_NOMSG(x.size() >= p256_limbs + 1);
+   x.mask_bits(256);
+   word borrow = bigint_sub2(x.mutable_data(), p256_limbs + 1, p256_mults[S], p256_limbs);
+   BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+   bigint_cnd_add(borrow, x.mutable_data(), p256_limbs + 1, p256_mults[0], p256_limbs);
+   }
+
+const BigInt& prime_p384()
+   {
+   static const BigInt p384("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
+   return p384;
+   }
+
+void redc_p384(BigInt& x, secure_vector<word>& ws)
+   {
+   BOTAN_UNUSED(ws);
+
+   static const size_t p384_limbs = (BOTAN_MP_WORD_BITS == 32) ? 12 : 6;
+
+   x.grow_to(2*p384_limbs);
+   word* xw = x.mutable_data();
+
+   const int64_t X00 = get_uint32(xw,  0);
+   const int64_t X01 = get_uint32(xw,  1);
+   const int64_t X02 = get_uint32(xw,  2);
+   const int64_t X03 = get_uint32(xw,  3);
+   const int64_t X04 = get_uint32(xw,  4);
+   const int64_t X05 = get_uint32(xw,  5);
+   const int64_t X06 = get_uint32(xw,  6);
+   const int64_t X07 = get_uint32(xw,  7);
+   const int64_t X08 = get_uint32(xw,  8);
+   const int64_t X09 = get_uint32(xw,  9);
+   const int64_t X10 = get_uint32(xw, 10);
+   const int64_t X11 = get_uint32(xw, 11);
+   const int64_t X12 = get_uint32(xw, 12);
+   const int64_t X13 = get_uint32(xw, 13);
+   const int64_t X14 = get_uint32(xw, 14);
+   const int64_t X15 = get_uint32(xw, 15);
+   const int64_t X16 = get_uint32(xw, 16);
+   const int64_t X17 = get_uint32(xw, 17);
+   const int64_t X18 = get_uint32(xw, 18);
+   const int64_t X19 = get_uint32(xw, 19);
+   const int64_t X20 = get_uint32(xw, 20);
+   const int64_t X21 = get_uint32(xw, 21);
+   const int64_t X22 = get_uint32(xw, 22);
+   const int64_t X23 = get_uint32(xw, 23);
+
+   // One copy of P-384 is added to prevent underflow
+   const int64_t S0 = 0xFFFFFFFF + X00 + X12 + X20 + X21 - X23;
+   const int64_t S1 = 0x00000000 + X01 + X13 + X22 + X23 - X12 - X20;
+   const int64_t S2 = 0x00000000 + X02 + X14 + X23 - X13 - X21;
+   const int64_t S3 = 0xFFFFFFFF + X03 + X12 + X15 + X20 + X21 - X14 - X22 - X23;
+   const int64_t S4 = 0xFFFFFFFE + X04 + X12 + X13 + X16 + X20 + X21*2 + X22 - X15 - X23*2;
+   const int64_t S5 = 0xFFFFFFFF + X05 + X13 + X14 + X17 + X21 + X22*2 + X23 - X16;
+   const int64_t S6 = 0xFFFFFFFF + X06 + X14 + X15 + X18 + X22 + X23*2 - X17;
+   const int64_t S7 = 0xFFFFFFFF + X07 + X15 + X16 + X19 + X23 - X18;
+   const int64_t S8 = 0xFFFFFFFF + X08 + X16 + X17 + X20 - X19;
+   const int64_t S9 = 0xFFFFFFFF + X09 + X17 + X18 + X21 - X20;
+   const int64_t SA = 0xFFFFFFFF + X10 + X18 + X19 + X22 - X21;
+   const int64_t SB = 0xFFFFFFFF + X11 + X19 + X20 + X23 - X22;
+
+   int64_t S = 0;
+
+   uint32_t R0 = 0, R1 = 0;
+
+   S += S0;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S1;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 0, R0, R1);
+
+   S += S2;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S3;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 2, R0, R1);
+
+   S += S4;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S5;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 4, R0, R1);
+
+   S += S6;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S7;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 6, R0, R1);
+
+   S += S8;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += S9;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 8, R0, R1);
+
+   S += SA;
+   R0 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   S += SB;
+   R1 = static_cast<uint32_t>(S);
+   S >>= 32;
+
+   set_words(xw, 10, R0, R1);
+
+   /*
+   This is a table of (i*P-384) % 2**384 for i in 1...4
+   */
+   static const word p384_mults[5][p384_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+      {0x00000000FFFFFFFF, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+      {0x00000001FFFFFFFE, 0xFFFFFFFE00000000, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+      {0x00000002FFFFFFFD, 0xFFFFFFFD00000000, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+      {0x00000003FFFFFFFC, 0xFFFFFFFC00000000, 0xFFFFFFFFFFFFFFFB, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+      {0x00000004FFFFFFFB, 0xFFFFFFFB00000000, 0xFFFFFFFFFFFFFFFA, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+
+#else
+      {0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
+       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFE, 0x00000001, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF,
+       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFD, 0x00000002, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFF,
+       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFC, 0x00000003, 0x00000000, 0xFFFFFFFC, 0xFFFFFFFB, 0xFFFFFFFF,
+       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+      {0xFFFFFFFB, 0x00000004, 0x00000000, 0xFFFFFFFB, 0xFFFFFFFA, 0xFFFFFFFF,
+       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+#endif
+   };
+
+   CT::unpoison(S);
+   BOTAN_ASSERT(S >= 0 && S <= 4, "Expected overflow");
+
+   BOTAN_ASSERT_NOMSG(x.size() >= p384_limbs + 1);
+   x.mask_bits(384);
+   word borrow = bigint_sub2(x.mutable_data(), p384_limbs + 1, p384_mults[S], p384_limbs);
+   BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+   bigint_cnd_add(borrow, x.mutable_data(), p384_limbs + 1, p384_mults[0], p384_limbs);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/numthry.cpp b/third_party/botan/src/lib/math/numbertheory/numthry.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/numthry.cpp
@@ -0,0 +1,250 @@
+/*
+* Number Theory Functions
+* (C) 1999-2011,2016,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/divide.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/primality.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+void sub_abs(BigInt& z, const BigInt& x, const BigInt& y)
+   {
+   const size_t x_sw = x.sig_words();
+   const size_t y_sw = y.sig_words();
+   z.resize(std::max(x_sw, y_sw));
+
+   bigint_sub_abs(z.mutable_data(),
+                  x.data(), x_sw,
+                  y.data(), y_sw);
+   }
+
+}
+
+/*
+* Return the number of 0 bits at the end of n
+*/
+size_t low_zero_bits(const BigInt& n)
+   {
+   size_t low_zero = 0;
+
+   auto seen_nonempty_word = CT::Mask<word>::cleared();
+
+   for(size_t i = 0; i != n.size(); ++i)
+      {
+      const word x = n.word_at(i);
+
+      // ctz(0) will return sizeof(word)
+      const size_t tz_x = ctz(x);
+
+      // if x > 0 we want to count tz_x in total but not any
+      // further words, so set the mask after the addition
+      low_zero += seen_nonempty_word.if_not_set_return(tz_x);
+
+      seen_nonempty_word |= CT::Mask<word>::expand(x);
+      }
+
+   // if we saw no words with x > 0 then n == 0 and the value we have
+   // computed is meaningless. Instead return 0 in that case.
+   return seen_nonempty_word.if_set_return(low_zero);
+   }
+
+/*
+* Calculate the GCD
+*/
+BigInt gcd(const BigInt& a, const BigInt& b)
+   {
+   if(a.is_zero() || b.is_zero())
+      return 0;
+   if(a == 1 || b == 1)
+      return 1;
+
+   // See https://gcd.cr.yp.to/safegcd-20190413.pdf fig 1.2
+
+   BigInt f = a;
+   BigInt g = b;
+   f.const_time_poison();
+   g.const_time_poison();
+
+   f.set_sign(BigInt::Positive);
+   g.set_sign(BigInt::Positive);
+
+   const size_t common2s = std::min(low_zero_bits(f), low_zero_bits(g));
+   CT::unpoison(common2s);
+
+   f >>= common2s;
+   g >>= common2s;
+
+   f.ct_cond_swap(f.is_even(), g);
+
+   int32_t delta = 1;
+
+   const size_t loop_cnt = 4 + 3*std::max(f.bits(), g.bits());
+
+   BigInt newg, t;
+   for(size_t i = 0; i != loop_cnt; ++i)
+      {
+      sub_abs(newg, f, g);
+
+      const bool need_swap = (g.is_odd() && delta > 0);
+
+      // if(need_swap) delta *= -1
+      delta *= CT::Mask<uint8_t>::expand(need_swap).select(0, 2) - 1;
+      f.ct_cond_swap(need_swap, g);
+      g.ct_cond_swap(need_swap, newg);
+
+      delta += 1;
+
+      g.ct_cond_add(g.is_odd(), f);
+      g >>= 1;
+      }
+
+   f <<= common2s;
+
+   f.const_time_unpoison();
+   g.const_time_unpoison();
+
+   return f;
+   }
+
+/*
+* Calculate the LCM
+*/
+BigInt lcm(const BigInt& a, const BigInt& b)
+   {
+   return ct_divide(a * b, gcd(a, b));
+   }
+
+/*
+* Modular Exponentiation
+*/
+BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod)
+   {
+   if(mod.is_negative() || mod == 1)
+      {
+      return 0;
+      }
+
+   if(base.is_zero() || mod.is_zero())
+      {
+      if(exp.is_zero())
+         return 1;
+      return 0;
+      }
+
+   Modular_Reducer reduce_mod(mod);
+
+   const size_t exp_bits = exp.bits();
+
+   if(mod.is_odd())
+      {
+      const size_t powm_window = 4;
+
+      auto monty_mod = std::make_shared<Montgomery_Params>(mod, reduce_mod);
+      auto powm_base_mod = monty_precompute(monty_mod, reduce_mod.reduce(base), powm_window);
+      return monty_execute(*powm_base_mod, exp, exp_bits);
+      }
+
+   /*
+   Support for even modulus is just a convenience and not considered
+   cryptographically important, so this implementation is slow ...
+   */
+   BigInt accum = 1;
+   BigInt g = reduce_mod.reduce(base);
+   BigInt t;
+
+   for(size_t i = 0; i != exp_bits; ++i)
+      {
+      t = reduce_mod.multiply(g, accum);
+      g = reduce_mod.square(g);
+      accum.ct_cond_assign(exp.get_bit(i), t);
+      }
+   return accum;
+   }
+
+
+BigInt is_perfect_square(const BigInt& C)
+   {
+   if(C < 1)
+      throw Invalid_Argument("is_perfect_square requires C >= 1");
+   if(C == 1)
+      return 1;
+
+   const size_t n = C.bits();
+   const size_t m = (n + 1) / 2;
+   const BigInt B = C + BigInt::power_of_2(m);
+
+   BigInt X = BigInt::power_of_2(m) - 1;
+   BigInt X2 = (X*X);
+
+   for(;;)
+      {
+      X = (X2 + C) / (2*X);
+      X2 = (X*X);
+
+      if(X2 < B)
+         break;
+      }
+
+   if(X2 == C)
+      return X;
+   else
+      return 0;
+   }
+
+/*
+* Test for primality using Miller-Rabin
+*/
+bool is_prime(const BigInt& n,
+              RandomNumberGenerator& rng,
+              size_t prob,
+              bool is_random)
+   {
+   if(n == 2)
+      return true;
+   if(n <= 1 || n.is_even())
+      return false;
+
+   const size_t n_bits = n.bits();
+
+   // Fast path testing for small numbers (<= 65521)
+   if(n_bits <= 16)
+      {
+      const uint16_t num = static_cast<uint16_t>(n.word_at(0));
+
+      return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
+      }
+
+   Modular_Reducer mod_n(n);
+
+   if(rng.is_seeded())
+      {
+      const size_t t = miller_rabin_test_iterations(n_bits, prob, is_random);
+
+      if(is_miller_rabin_probable_prime(n, mod_n, rng, t) == false)
+         return false;
+
+      if(is_random)
+         return true;
+      else
+         return is_lucas_probable_prime(n, mod_n);
+      }
+   else
+      {
+      return is_bailie_psw_probable_prime(n, mod_n);
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/numthry.h b/third_party/botan/src/lib/math/numbertheory/numthry.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/numthry.h
@@ -0,0 +1,287 @@
+/*
+* Number Theory Functions
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NUMBER_THEORY_H_
+#define BOTAN_NUMBER_THEORY_H_
+
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Fused multiply-add
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a*b)+c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) mul_add(const BigInt& a,
+                                     const BigInt& b,
+                                     const BigInt& c);
+
+/**
+* Fused subtract-multiply
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a-b)*c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) sub_mul(const BigInt& a,
+                                     const BigInt& b,
+                                     const BigInt& c);
+
+/**
+* Fused multiply-subtract
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a*b)-c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) mul_sub(const BigInt& a,
+                                     const BigInt& b,
+                                     const BigInt& c);
+
+/**
+* Return the absolute value
+* @param n an integer
+* @return absolute value of n
+*/
+inline BigInt abs(const BigInt& n) { return n.abs(); }
+
+/**
+* Compute the greatest common divisor
+* @param x a positive integer
+* @param y a positive integer
+* @return gcd(x,y)
+*/
+BigInt BOTAN_PUBLIC_API(2,0) gcd(const BigInt& x, const BigInt& y);
+
+/**
+* Least common multiple
+* @param x a positive integer
+* @param y a positive integer
+* @return z, smallest integer such that z % x == 0 and z % y == 0
+*/
+BigInt BOTAN_PUBLIC_API(2,0) lcm(const BigInt& x, const BigInt& y);
+
+/**
+* @param x an integer
+* @return (x*x)
+*/
+BigInt BOTAN_PUBLIC_API(2,0) square(const BigInt& x);
+
+/**
+* Modular inversion. This algorithm is const time with respect to x,
+* as long as x is less than modulus. It also avoids leaking
+* information about the modulus, except that it does leak which of 3
+* categories the modulus is in: an odd integer, a power of 2, or some
+* other even number, and if the modulus is even, leaks the power of 2
+* which divides the modulus.
+*
+* @param x a positive integer
+* @param modulus a positive integer
+* @return y st (x*y) % modulus == 1 or 0 if no such value
+*/
+BigInt BOTAN_PUBLIC_API(2,0) inverse_mod(const BigInt& x,
+                                         const BigInt& modulus);
+
+/**
+* Deprecated modular inversion function. Use inverse_mod instead.
+* @param x a positive integer
+* @param modulus a positive integer
+* @return y st (x*y) % modulus == 1 or 0 if no such value
+*/
+BigInt BOTAN_DEPRECATED_API("Use inverse_mod") inverse_euclid(const BigInt& x, const BigInt& modulus);
+
+/**
+* Deprecated modular inversion function. Use inverse_mod instead.
+*/
+BigInt BOTAN_DEPRECATED_API("Use inverse_mod") ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod);
+
+/**
+* Return a^-1 * 2^k mod b
+* Returns k, between n and 2n
+* Not const time
+*/
+size_t BOTAN_PUBLIC_API(2,0) almost_montgomery_inverse(BigInt& result,
+                                                       const BigInt& a,
+                                                       const BigInt& b);
+
+/**
+* Call almost_montgomery_inverse and correct the result to a^-1 mod b
+*/
+BigInt BOTAN_PUBLIC_API(2,0) normalized_montgomery_inverse(const BigInt& a, const BigInt& b);
+
+
+/**
+* Compute the Jacobi symbol. If n is prime, this is equivalent
+* to the Legendre symbol.
+* @see http://mathworld.wolfram.com/JacobiSymbol.html
+*
+* @param a is a non-negative integer
+* @param n is an odd integer > 1
+* @return (n / m)
+*/
+int32_t BOTAN_PUBLIC_API(2,0) jacobi(const BigInt& a, const BigInt& n);
+
+/**
+* Modular exponentation
+* @param b an integer base
+* @param x a positive exponent
+* @param m a positive modulus
+* @return (b^x) % m
+*/
+BigInt BOTAN_PUBLIC_API(2,0) power_mod(const BigInt& b,
+                                       const BigInt& x,
+                                       const BigInt& m);
+
+/**
+* Compute the square root of x modulo a prime using the
+* Shanks-Tonnelli algorithm
+*
+* @param x the input
+* @param p the prime
+* @return y such that (y*y)%p == x, or -1 if no such integer
+*/
+BigInt BOTAN_PUBLIC_API(2,0) ressol(const BigInt& x, const BigInt& p);
+
+/*
+* Compute -input^-1 mod 2^MP_WORD_BITS. Throws an exception if input
+* is even. If input is odd, then input and 2^n are relatively prime
+* and an inverse exists.
+*/
+word BOTAN_PUBLIC_API(2,0) monty_inverse(word input);
+
+/**
+* @param x a positive integer
+* @return count of the zero bits in x, or, equivalently, the largest
+*         value of n such that 2^n divides x evenly. Returns zero if
+*         n is less than or equal to zero.
+*/
+size_t BOTAN_PUBLIC_API(2,0) low_zero_bits(const BigInt& x);
+
+/**
+* Check for primality
+* @param n a positive integer to test for primality
+* @param rng a random number generator
+* @param prob chance of false positive is bounded by 1/2**prob
+* @param is_random true if n was randomly chosen by us
+* @return true if all primality tests passed, otherwise false
+*/
+bool BOTAN_PUBLIC_API(2,0) is_prime(const BigInt& n,
+                                    RandomNumberGenerator& rng,
+                                    size_t prob = 64,
+                                    bool is_random = false);
+
+/**
+* Test if the positive integer x is a perfect square ie if there
+* exists some positive integer y st y*y == x
+* See FIPS 186-4 sec C.4
+* @return 0 if the integer is not a perfect square, otherwise
+*         returns the positive y st y*y == x
+*/
+BigInt BOTAN_PUBLIC_API(2,8) is_perfect_square(const BigInt& x);
+
+inline bool quick_check_prime(const BigInt& n, RandomNumberGenerator& rng)
+   { return is_prime(n, rng, 32); }
+
+inline bool check_prime(const BigInt& n, RandomNumberGenerator& rng)
+   { return is_prime(n, rng, 56); }
+
+inline bool verify_prime(const BigInt& n, RandomNumberGenerator& rng)
+   { return is_prime(n, rng, 80); }
+
+/**
+* Randomly generate a prime suitable for discrete logarithm parameters
+* @param rng a random number generator
+* @param bits how large the resulting prime should be in bits
+* @param coprime a positive integer that (prime - 1) should be coprime to
+* @param equiv a non-negative number that the result should be
+               equivalent to modulo equiv_mod
+* @param equiv_mod the modulus equiv should be checked against
+* @param prob use test so false positive is bounded by 1/2**prob
+* @return random prime with the specified criteria
+*/
+BigInt BOTAN_PUBLIC_API(2,0) random_prime(RandomNumberGenerator& rng,
+                                          size_t bits,
+                                          const BigInt& coprime = 0,
+                                          size_t equiv = 1,
+                                          size_t equiv_mod = 2,
+                                          size_t prob = 128);
+
+/**
+* Generate a prime suitable for RSA p/q
+* @param keygen_rng a random number generator
+* @param prime_test_rng a random number generator
+* @param bits how large the resulting prime should be in bits (must be >= 512)
+* @param coprime a positive integer that (prime - 1) should be coprime to
+* @param prob use test so false positive is bounded by 1/2**prob
+* @return random prime with the specified criteria
+*/
+BigInt BOTAN_PUBLIC_API(2,7) generate_rsa_prime(RandomNumberGenerator& keygen_rng,
+                                                RandomNumberGenerator& prime_test_rng,
+                                                size_t bits,
+                                                const BigInt& coprime,
+                                                size_t prob = 128);
+
+/**
+* Return a 'safe' prime, of the form p=2*q+1 with q prime
+* @param rng a random number generator
+* @param bits is how long the resulting prime should be
+* @return prime randomly chosen from safe primes of length bits
+*/
+BigInt BOTAN_PUBLIC_API(2,0) random_safe_prime(RandomNumberGenerator& rng,
+                                               size_t bits);
+
+/**
+* Generate DSA parameters using the FIPS 186 kosherizer
+* @param rng a random number generator
+* @param p_out where the prime p will be stored
+* @param q_out where the prime q will be stored
+* @param pbits how long p will be in bits
+* @param qbits how long q will be in bits
+* @return random seed used to generate this parameter set
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+generate_dsa_primes(RandomNumberGenerator& rng,
+                    BigInt& p_out, BigInt& q_out,
+                    size_t pbits, size_t qbits);
+
+/**
+* Generate DSA parameters using the FIPS 186 kosherizer
+* @param rng a random number generator
+* @param p_out where the prime p will be stored
+* @param q_out where the prime q will be stored
+* @param pbits how long p will be in bits
+* @param qbits how long q will be in bits
+* @param seed the seed used to generate the parameters
+* @param offset optional offset from seed to start searching at
+* @return true if seed generated a valid DSA parameter set, otherwise
+          false. p_out and q_out are only valid if true was returned.
+*/
+bool BOTAN_PUBLIC_API(2,0)
+generate_dsa_primes(RandomNumberGenerator& rng,
+                    BigInt& p_out, BigInt& q_out,
+                    size_t pbits, size_t qbits,
+                    const std::vector<uint8_t>& seed,
+                    size_t offset = 0);
+
+/**
+* The size of the PRIMES[] array
+*/
+const size_t PRIME_TABLE_SIZE = 6541;
+
+/**
+* A const array of all primes less than 65535
+*/
+extern const uint16_t BOTAN_PUBLIC_API(2,0) PRIMES[];
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp b/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp
@@ -0,0 +1,328 @@
+/*
+* Modular Exponentiation Proxy
+* (C) 1999-2007,2012,2018,2019 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pow_mod.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/rounding.h>
+#include <vector>
+
+namespace Botan {
+
+class Modular_Exponentiator
+   {
+   public:
+      virtual void set_base(const BigInt&) = 0;
+      virtual void set_exponent(const BigInt&) = 0;
+      virtual BigInt execute() const = 0;
+      virtual Modular_Exponentiator* copy() const = 0;
+
+      Modular_Exponentiator() = default;
+      Modular_Exponentiator(const Modular_Exponentiator&) = default;
+      Modular_Exponentiator & operator=(const Modular_Exponentiator&) = default;
+      virtual ~Modular_Exponentiator() = default;
+   };
+
+namespace {
+
+/**
+* Fixed Window Exponentiator
+*/
+class Fixed_Window_Exponentiator final : public Modular_Exponentiator
+   {
+   public:
+      void set_exponent(const BigInt& e) override { m_exp = e; }
+      void set_base(const BigInt&) override;
+      BigInt execute() const override;
+
+      Modular_Exponentiator* copy() const override
+         { return new Fixed_Window_Exponentiator(*this); }
+
+      Fixed_Window_Exponentiator(const BigInt&, Power_Mod::Usage_Hints);
+   private:
+      Modular_Reducer m_reducer;
+      BigInt m_exp;
+      size_t m_window_bits;
+      std::vector<BigInt> m_g;
+      Power_Mod::Usage_Hints m_hints;
+   };
+
+void Fixed_Window_Exponentiator::set_base(const BigInt& base)
+   {
+   m_window_bits = Power_Mod::window_bits(m_exp.bits(), base.bits(), m_hints);
+
+   m_g.resize(static_cast<size_t>(1) << m_window_bits);
+   m_g[0] = 1;
+   m_g[1] = m_reducer.reduce(base);
+
+   for(size_t i = 2; i != m_g.size(); ++i)
+      m_g[i] = m_reducer.multiply(m_g[i-1], m_g[1]);
+   }
+
+BigInt Fixed_Window_Exponentiator::execute() const
+   {
+   const size_t exp_nibbles = (m_exp.bits() + m_window_bits - 1) / m_window_bits;
+
+   BigInt x = 1;
+
+   for(size_t i = exp_nibbles; i > 0; --i)
+      {
+      for(size_t j = 0; j != m_window_bits; ++j)
+         x = m_reducer.square(x);
+
+      const uint32_t nibble = m_exp.get_substring(m_window_bits*(i-1), m_window_bits);
+
+      // not const time:
+      x = m_reducer.multiply(x, m_g[nibble]);
+      }
+   return x;
+   }
+
+/*
+* Fixed_Window_Exponentiator Constructor
+*/
+Fixed_Window_Exponentiator::Fixed_Window_Exponentiator(const BigInt& n,
+                                                       Power_Mod::Usage_Hints hints)
+   : m_reducer{Modular_Reducer(n)}, m_exp{}, m_window_bits{}, m_g{}, m_hints{hints}
+   {}
+
+class Montgomery_Exponentiator final : public Modular_Exponentiator
+   {
+   public:
+      void set_exponent(const BigInt& e) override { m_e = e; }
+      void set_base(const BigInt&) override;
+      BigInt execute() const override;
+
+      Modular_Exponentiator* copy() const override
+         { return new Montgomery_Exponentiator(*this); }
+
+      Montgomery_Exponentiator(const BigInt&, Power_Mod::Usage_Hints);
+   private:
+      BigInt m_p;
+      Modular_Reducer m_mod_p;
+      std::shared_ptr<const Montgomery_Params> m_monty_params;
+      std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
+
+      BigInt m_e;
+      Power_Mod::Usage_Hints m_hints;
+   };
+
+void Montgomery_Exponentiator::set_base(const BigInt& base)
+   {
+   size_t window_bits = Power_Mod::window_bits(m_e.bits(), base.bits(), m_hints);
+   m_monty = monty_precompute(m_monty_params, m_mod_p.reduce(base), window_bits);
+   }
+
+BigInt Montgomery_Exponentiator::execute() const
+   {
+   /*
+   This leaks size of e via loop iterations, not possible to fix without
+   breaking this API. Round up to avoid leaking fine details.
+   */
+   return monty_execute(*m_monty, m_e, round_up(m_e.bits(), 8));
+   }
+
+Montgomery_Exponentiator::Montgomery_Exponentiator(const BigInt& mod,
+                                                   Power_Mod::Usage_Hints hints) :
+   m_p(mod),
+   m_mod_p(mod),
+   m_monty_params(std::make_shared<Montgomery_Params>(m_p, m_mod_p)),
+   m_hints(hints)
+   {
+   }
+
+}
+
+/*
+* Power_Mod Constructor
+*/
+Power_Mod::Power_Mod(const BigInt& n, Usage_Hints hints, bool disable_monty)
+   {
+   set_modulus(n, hints, disable_monty);
+   }
+
+Power_Mod::~Power_Mod() { /* for ~unique_ptr */ }
+
+/*
+* Power_Mod Copy Constructor
+*/
+Power_Mod::Power_Mod(const Power_Mod& other)
+   {
+   if(other.m_core.get())
+      m_core.reset(other.m_core->copy());
+   }
+
+/*
+* Power_Mod Assignment Operator
+*/
+Power_Mod& Power_Mod::operator=(const Power_Mod& other)
+   {
+   if(this != &other)
+      {
+      if(other.m_core)
+         m_core.reset(other.m_core->copy());
+      else
+         m_core.reset();
+      }
+   return (*this);
+   }
+
+/*
+* Set the modulus
+*/
+void Power_Mod::set_modulus(const BigInt& n, Usage_Hints hints, bool disable_monty) const
+   {
+   // Allow set_modulus(0) to mean "drop old state"
+
+   m_core.reset();
+
+   if(n != 0)
+      {
+      if(n.is_odd() && disable_monty == false)
+         m_core.reset(new Montgomery_Exponentiator(n, hints));
+      else
+         m_core.reset(new Fixed_Window_Exponentiator(n, hints));
+      }
+   }
+
+/*
+* Set the base
+*/
+void Power_Mod::set_base(const BigInt& b) const
+   {
+   if(b.is_negative())
+      throw Invalid_Argument("Power_Mod::set_base: arg must be non-negative");
+
+   if(!m_core)
+      throw Internal_Error("Power_Mod::set_base: m_core was NULL");
+   m_core->set_base(b);
+   }
+
+/*
+* Set the exponent
+*/
+void Power_Mod::set_exponent(const BigInt& e) const
+   {
+   if(e.is_negative())
+      throw Invalid_Argument("Power_Mod::set_exponent: arg must be > 0");
+
+   if(!m_core)
+      throw Internal_Error("Power_Mod::set_exponent: m_core was NULL");
+   m_core->set_exponent(e);
+   }
+
+/*
+* Compute the result
+*/
+BigInt Power_Mod::execute() const
+   {
+   if(!m_core)
+      throw Internal_Error("Power_Mod::execute: m_core was NULL");
+   return m_core->execute();
+   }
+
+/*
+* Try to choose a good window size
+*/
+size_t Power_Mod::window_bits(size_t exp_bits, size_t,
+                              Power_Mod::Usage_Hints hints)
+   {
+   static const size_t wsize[][2] = {
+      { 1434, 7 },
+      {  539, 6 },
+      {  197, 4 },
+      {   70, 3 },
+      {   17, 2 },
+      {    0, 0 }
+   };
+
+   size_t window_bits = 1;
+
+   if(exp_bits)
+      {
+      for(size_t j = 0; wsize[j][0]; ++j)
+         {
+         if(exp_bits >= wsize[j][0])
+            {
+            window_bits += wsize[j][1];
+            break;
+            }
+         }
+      }
+
+   if(hints & Power_Mod::BASE_IS_FIXED)
+      window_bits += 2;
+   if(hints & Power_Mod::EXP_IS_LARGE)
+      ++window_bits;
+
+   return window_bits;
+   }
+
+namespace {
+
+/*
+* Choose potentially useful hints
+*/
+Power_Mod::Usage_Hints choose_base_hints(const BigInt& b, const BigInt& n)
+   {
+   if(b == 2)
+      return Power_Mod::Usage_Hints(Power_Mod::BASE_IS_2 |
+                                    Power_Mod::BASE_IS_SMALL);
+
+   const size_t b_bits = b.bits();
+   const size_t n_bits = n.bits();
+
+   if(b_bits < n_bits / 32)
+      return Power_Mod::BASE_IS_SMALL;
+   if(b_bits > n_bits / 4)
+      return Power_Mod::BASE_IS_LARGE;
+
+   return Power_Mod::NO_HINTS;
+   }
+
+/*
+* Choose potentially useful hints
+*/
+Power_Mod::Usage_Hints choose_exp_hints(const BigInt& e, const BigInt& n)
+   {
+   const size_t e_bits = e.bits();
+   const size_t n_bits = n.bits();
+
+   if(e_bits < n_bits / 32)
+      return Power_Mod::BASE_IS_SMALL;
+   if(e_bits > n_bits / 4)
+      return Power_Mod::BASE_IS_LARGE;
+   return Power_Mod::NO_HINTS;
+   }
+
+}
+
+/*
+* Fixed_Exponent_Power_Mod Constructor
+*/
+Fixed_Exponent_Power_Mod::Fixed_Exponent_Power_Mod(const BigInt& e,
+                                                   const BigInt& n,
+                                                   Usage_Hints hints) :
+   Power_Mod(n, Usage_Hints(hints | EXP_IS_FIXED | choose_exp_hints(e, n)))
+   {
+   set_exponent(e);
+   }
+
+/*
+* Fixed_Base_Power_Mod Constructor
+*/
+Fixed_Base_Power_Mod::Fixed_Base_Power_Mod(const BigInt& b, const BigInt& n,
+                                           Usage_Hints hints) :
+   Power_Mod(n, Usage_Hints(hints | BASE_IS_FIXED | choose_base_hints(b, n)))
+   {
+   set_base(b);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/pow_mod.h b/third_party/botan/src/lib/math/numbertheory/pow_mod.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/pow_mod.h
@@ -0,0 +1,122 @@
+/*
+* Modular Exponentiator
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POWER_MOD_H_
+#define BOTAN_POWER_MOD_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pow_mod.h)
+
+namespace Botan {
+
+class Modular_Exponentiator;
+
+/**
+* Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Power_Mod
+   {
+   public:
+
+      enum Usage_Hints {
+         NO_HINTS        = 0x0000,
+
+         BASE_IS_FIXED   = 0x0001,
+         BASE_IS_SMALL   = 0x0002,
+         BASE_IS_LARGE   = 0x0004,
+         BASE_IS_2       = 0x0008,
+
+         EXP_IS_FIXED    = 0x0100,
+         EXP_IS_SMALL    = 0x0200,
+         EXP_IS_LARGE    = 0x0400
+      };
+
+      /*
+      * Try to choose a good window size
+      */
+      static size_t window_bits(size_t exp_bits, size_t base_bits,
+                                Power_Mod::Usage_Hints hints);
+
+      /**
+      * @param modulus the modulus
+      * @param hints Passed to set_modulus if modulus > 0
+      * @param disable_montgomery_arith Disables use of Montgomery
+      * representation. Likely only useful for testing.
+      */
+      void set_modulus(const BigInt& modulus,
+                       Usage_Hints hints = NO_HINTS,
+                       bool disable_montgomery_arith = false) const;
+
+      /**
+      * Set the base
+      */
+      void set_base(const BigInt& base) const;
+
+      /**
+      * Set the exponent
+      */
+      void set_exponent(const BigInt& exponent) const;
+
+      /**
+      * All three of the above functions must have already been called.
+      * @return result of g^x%p
+      */
+      BigInt execute() const;
+
+      Power_Mod& operator=(const Power_Mod&);
+
+      /**
+      * @param modulus Optionally call set_modulus
+      * @param hints Passed to set_modulus if modulus > 0
+      * @param disable_montgomery_arith Disables use of Montgomery
+      * representation. Likely only useful for testing.
+      */
+      Power_Mod(const BigInt& modulus = 0,
+                Usage_Hints hints = NO_HINTS,
+                bool disable_montgomery_arith = false);
+      Power_Mod(const Power_Mod&);
+      virtual ~Power_Mod();
+   private:
+      mutable std::unique_ptr<Modular_Exponentiator> m_core;
+   };
+
+/**
+* Fixed Exponent Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Fixed_Exponent_Power_Mod final : public Power_Mod
+   {
+   public:
+      BigInt operator()(const BigInt& b) const
+         { set_base(b); return execute(); }
+
+      Fixed_Exponent_Power_Mod() = default;
+
+      Fixed_Exponent_Power_Mod(const BigInt& exponent,
+                               const BigInt& modulus,
+                               Usage_Hints hints = NO_HINTS);
+   };
+
+/**
+* Fixed Base Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Fixed_Base_Power_Mod final : public Power_Mod
+   {
+   public:
+      BigInt operator()(const BigInt& e) const
+         { set_exponent(e); return execute(); }
+
+      Fixed_Base_Power_Mod() = default;
+
+      Fixed_Base_Power_Mod(const BigInt& base,
+                           const BigInt& modulus,
+                           Usage_Hints hints = NO_HINTS);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/primality.cpp b/third_party/botan/src/lib/math/numbertheory/primality.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/primality.cpp
@@ -0,0 +1,203 @@
+/*
+* (C) 2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/primality.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/bigint.h>
+#include <botan/monty.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+#include <algorithm>
+
+namespace Botan {
+
+bool is_lucas_probable_prime(const BigInt& C, const Modular_Reducer& mod_C)
+   {
+   if(C <= 1)
+      return false;
+   else if(C == 2)
+      return true;
+   else if(C.is_even())
+      return false;
+   else if(C == 3 || C == 5 || C == 7 || C == 11 || C == 13)
+      return true;
+
+   BigInt D = 5;
+
+   for(;;)
+      {
+      int32_t j = jacobi(D, C);
+      if(j == 0)
+         return false;
+
+      if(j == -1)
+         break;
+
+      // Check 5, -7, 9, -11, 13, -15, 17, ...
+      if(D.is_negative())
+         {
+         D.flip_sign();
+         D += 2;
+         }
+      else
+         {
+         D += 2;
+         D.flip_sign();
+         }
+
+      if(D == 17 && is_perfect_square(C).is_nonzero())
+         return false;
+      }
+
+   const BigInt K = C + 1;
+   const size_t K_bits = K.bits() - 1;
+
+   BigInt U = 1;
+   BigInt V = 1;
+
+   BigInt Ut, Vt, U2, V2;
+
+   for(size_t i = 0; i != K_bits; ++i)
+      {
+      const bool k_bit = K.get_bit(K_bits - 1 - i);
+
+      Ut = mod_C.multiply(U, V);
+
+      Vt = mod_C.reduce(mod_C.square(V) + mod_C.multiply(D, mod_C.square(U)));
+      Vt.ct_cond_add(Vt.is_odd(), C);
+      Vt >>= 1;
+      Vt = mod_C.reduce(Vt);
+
+      U = Ut;
+      V = Vt;
+
+      U2 = mod_C.reduce(Ut + Vt);
+      U2.ct_cond_add(U2.is_odd(), C);
+      U2 >>= 1;
+
+      V2 = mod_C.reduce(Vt + Ut*D);
+      V2.ct_cond_add(V2.is_odd(), C);
+      V2 >>= 1;
+
+      U.ct_cond_assign(k_bit, U2);
+      V.ct_cond_assign(k_bit, V2);
+      }
+
+   return (U == 0);
+   }
+
+bool is_bailie_psw_probable_prime(const BigInt& n, const Modular_Reducer& mod_n)
+   {
+   auto monty_n = std::make_shared<Montgomery_Params>(n, mod_n);
+   return passes_miller_rabin_test(n, mod_n, monty_n, 2) && is_lucas_probable_prime(n, mod_n);
+   }
+
+bool is_bailie_psw_probable_prime(const BigInt& n)
+   {
+   Modular_Reducer mod_n(n);
+   return is_bailie_psw_probable_prime(n, mod_n);
+   }
+
+bool passes_miller_rabin_test(const BigInt& n,
+                              const Modular_Reducer& mod_n,
+                              const std::shared_ptr<Montgomery_Params>& monty_n,
+                              const BigInt& a)
+   {
+   BOTAN_ASSERT_NOMSG(n > 1);
+
+   const BigInt n_minus_1 = n - 1;
+   const size_t s = low_zero_bits(n_minus_1);
+   const BigInt nm1_s = n_minus_1 >> s;
+   const size_t n_bits = n.bits();
+
+   const size_t powm_window = 4;
+
+   auto powm_a_n = monty_precompute(monty_n, a, powm_window);
+
+   BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits);
+
+   if(y == 1 || y == n_minus_1)
+      return true;
+
+   for(size_t i = 1; i != s; ++i)
+      {
+      y = mod_n.square(y);
+
+      if(y == 1) // found a non-trivial square root
+         return false;
+
+      /*
+      -1 is the trivial square root of unity, so ``a`` is not a
+      witness for this number - give up
+      */
+      if(y == n_minus_1)
+         return true;
+      }
+
+   return false;
+   }
+
+bool is_miller_rabin_probable_prime(const BigInt& n,
+                                    const Modular_Reducer& mod_n,
+                                    RandomNumberGenerator& rng,
+                                    size_t test_iterations)
+   {
+   BOTAN_ASSERT_NOMSG(n > 1);
+
+   auto monty_n = std::make_shared<Montgomery_Params>(n, mod_n);
+
+   for(size_t i = 0; i != test_iterations; ++i)
+      {
+      const BigInt a = BigInt::random_integer(rng, 2, n);
+
+      if(!passes_miller_rabin_test(n, mod_n, monty_n, a))
+         return false;
+      }
+
+   // Failed to find a counterexample
+   return true;
+   }
+
+
+size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random)
+   {
+   const size_t base = (prob + 2) / 2; // worst case 4^-t error rate
+
+   /*
+   * If the candidate prime was maliciously constructed, we can't rely
+   * on arguments based on p being random.
+   */
+   if(random == false)
+      return base;
+
+   /*
+   * For randomly chosen numbers we can use the estimates from
+   * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
+   *
+   * These values are derived from the inequality for p(k,t) given on
+   * the second page.
+   */
+   if(prob <= 128)
+      {
+      if(n_bits >= 1536)
+         return 4; // < 2^-133
+      if(n_bits >= 1024)
+         return 6; // < 2^-133
+      if(n_bits >= 512)
+         return 12; // < 2^-129
+      if(n_bits >= 256)
+         return 29; // < 2^-128
+      }
+
+   /*
+   If the user desires a smaller error probability than we have
+   precomputed error estimates for, just fall back to using the worst
+   case error rate.
+   */
+   return base;
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/primality.h b/third_party/botan/src/lib/math/numbertheory/primality.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/primality.h
@@ -0,0 +1,100 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PRIMALITY_TEST_H_
+#define BOTAN_PRIMALITY_TEST_H_
+
+#include <botan/types.h>
+#include <memory>
+
+namespace Botan {
+
+class BigInt;
+class Modular_Reducer;
+class Montgomery_Params;
+class RandomNumberGenerator;
+
+/**
+* Perform Lucas primality test
+* @see FIPS 186-4 C.3.3
+*
+* @warning it is possible to construct composite integers which pass
+* this test alone.
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer for n
+* @return true if n seems probably prime, false if n is composite
+*/
+bool BOTAN_TEST_API is_lucas_probable_prime(const BigInt& n, const Modular_Reducer& mod_n);
+
+/**
+* Perform Bailie-PSW primality test
+*
+* This is a combination of Miller-Rabin with base 2 and a Lucas test. No known
+* composite integer passes both tests, though it is conjectured that infinitely
+* many composite counterexamples exist.
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer for n
+* @return true if n seems probably prime, false if n is composite
+*/
+bool BOTAN_TEST_API is_bailie_psw_probable_prime(const BigInt& n, const Modular_Reducer& mod_n);
+
+/**
+* Perform Bailie-PSW primality test
+*
+* This is a combination of Miller-Rabin with base 2 and a Lucas test. No known
+* composite integer passes both tests, though it is conjectured that infinitely
+* many composite counterexamples exist.
+*
+* @param n the positive integer to test
+* @return true if n seems probably prime, false if n is composite
+*/
+bool is_bailie_psw_probable_prime(const BigInt& n);
+
+/**
+* Return required number of Miller-Rabin tests in order to
+* reach the specified probability of error.
+*
+* @param n_bits the bit-length of the integer being tested
+* @param prob chance of false positive is bounded by 1/2**prob
+* @param random is set if (and only if) the integer was randomly generated by us
+*        and thus cannot have been maliciously constructed.
+*/
+size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random);
+
+/**
+* Perform a single Miller-Rabin test with specified base
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer for n
+* @param monty_n Montgomery parameters for n
+* @param a the base to check
+* @return result of primality test
+*/
+bool passes_miller_rabin_test(const BigInt& n,
+                              const Modular_Reducer& mod_n,
+                              const std::shared_ptr<Montgomery_Params>& monty_n,
+                              const BigInt& a);
+
+/**
+* Perform t iterations of a Miller-Rabin primality test with random bases
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer for n
+* @param rng a random number generator
+* @param t number of tests to perform
+*
+* @return result of primality test
+*/
+bool BOTAN_TEST_API is_miller_rabin_probable_prime(const BigInt& n,
+                                                   const Modular_Reducer& mod_n,
+                                                   RandomNumberGenerator& rng,
+                                                   size_t t);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/primes.cpp b/third_party/botan/src/lib/math/numbertheory/primes.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/primes.cpp
@@ -0,0 +1,609 @@
+/*
+* Small Primes Table
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+const uint16_t PRIMES[PRIME_TABLE_SIZE+1] = {
+    3,     5,     7,    11,    13,    17,    19,    23,    29,    31,    37,
+   41,    43,    47,    53,    59,    61,    67,    71,    73,    79,    83,
+   89,    97,   101,   103,   107,   109,   113,   127,   131,   137,   139,
+  149,   151,   157,   163,   167,   173,   179,   181,   191,   193,   197,
+  199,   211,   223,   227,   229,   233,   239,   241,   251,   257,   263,
+  269,   271,   277,   281,   283,   293,   307,   311,   313,   317,   331,
+  337,   347,   349,   353,   359,   367,   373,   379,   383,   389,   397,
+  401,   409,   419,   421,   431,   433,   439,   443,   449,   457,   461,
+  463,   467,   479,   487,   491,   499,   503,   509,   521,   523,   541,
+  547,   557,   563,   569,   571,   577,   587,   593,   599,   601,   607,
+  613,   617,   619,   631,   641,   643,   647,   653,   659,   661,   673,
+  677,   683,   691,   701,   709,   719,   727,   733,   739,   743,   751,
+  757,   761,   769,   773,   787,   797,   809,   811,   821,   823,   827,
+  829,   839,   853,   857,   859,   863,   877,   881,   883,   887,   907,
+  911,   919,   929,   937,   941,   947,   953,   967,   971,   977,   983,
+  991,   997,  1009,  1013,  1019,  1021,  1031,  1033,  1039,  1049,  1051,
+ 1061,  1063,  1069,  1087,  1091,  1093,  1097,  1103,  1109,  1117,  1123,
+ 1129,  1151,  1153,  1163,  1171,  1181,  1187,  1193,  1201,  1213,  1217,
+ 1223,  1229,  1231,  1237,  1249,  1259,  1277,  1279,  1283,  1289,  1291,
+ 1297,  1301,  1303,  1307,  1319,  1321,  1327,  1361,  1367,  1373,  1381,
+ 1399,  1409,  1423,  1427,  1429,  1433,  1439,  1447,  1451,  1453,  1459,
+ 1471,  1481,  1483,  1487,  1489,  1493,  1499,  1511,  1523,  1531,  1543,
+ 1549,  1553,  1559,  1567,  1571,  1579,  1583,  1597,  1601,  1607,  1609,
+ 1613,  1619,  1621,  1627,  1637,  1657,  1663,  1667,  1669,  1693,  1697,
+ 1699,  1709,  1721,  1723,  1733,  1741,  1747,  1753,  1759,  1777,  1783,
+ 1787,  1789,  1801,  1811,  1823,  1831,  1847,  1861,  1867,  1871,  1873,
+ 1877,  1879,  1889,  1901,  1907,  1913,  1931,  1933,  1949,  1951,  1973,
+ 1979,  1987,  1993,  1997,  1999,  2003,  2011,  2017,  2027,  2029,  2039,
+ 2053,  2063,  2069,  2081,  2083,  2087,  2089,  2099,  2111,  2113,  2129,
+ 2131,  2137,  2141,  2143,  2153,  2161,  2179,  2203,  2207,  2213,  2221,
+ 2237,  2239,  2243,  2251,  2267,  2269,  2273,  2281,  2287,  2293,  2297,
+ 2309,  2311,  2333,  2339,  2341,  2347,  2351,  2357,  2371,  2377,  2381,
+ 2383,  2389,  2393,  2399,  2411,  2417,  2423,  2437,  2441,  2447,  2459,
+ 2467,  2473,  2477,  2503,  2521,  2531,  2539,  2543,  2549,  2551,  2557,
+ 2579,  2591,  2593,  2609,  2617,  2621,  2633,  2647,  2657,  2659,  2663,
+ 2671,  2677,  2683,  2687,  2689,  2693,  2699,  2707,  2711,  2713,  2719,
+ 2729,  2731,  2741,  2749,  2753,  2767,  2777,  2789,  2791,  2797,  2801,
+ 2803,  2819,  2833,  2837,  2843,  2851,  2857,  2861,  2879,  2887,  2897,
+ 2903,  2909,  2917,  2927,  2939,  2953,  2957,  2963,  2969,  2971,  2999,
+ 3001,  3011,  3019,  3023,  3037,  3041,  3049,  3061,  3067,  3079,  3083,
+ 3089,  3109,  3119,  3121,  3137,  3163,  3167,  3169,  3181,  3187,  3191,
+ 3203,  3209,  3217,  3221,  3229,  3251,  3253,  3257,  3259,  3271,  3299,
+ 3301,  3307,  3313,  3319,  3323,  3329,  3331,  3343,  3347,  3359,  3361,
+ 3371,  3373,  3389,  3391,  3407,  3413,  3433,  3449,  3457,  3461,  3463,
+ 3467,  3469,  3491,  3499,  3511,  3517,  3527,  3529,  3533,  3539,  3541,
+ 3547,  3557,  3559,  3571,  3581,  3583,  3593,  3607,  3613,  3617,  3623,
+ 3631,  3637,  3643,  3659,  3671,  3673,  3677,  3691,  3697,  3701,  3709,
+ 3719,  3727,  3733,  3739,  3761,  3767,  3769,  3779,  3793,  3797,  3803,
+ 3821,  3823,  3833,  3847,  3851,  3853,  3863,  3877,  3881,  3889,  3907,
+ 3911,  3917,  3919,  3923,  3929,  3931,  3943,  3947,  3967,  3989,  4001,
+ 4003,  4007,  4013,  4019,  4021,  4027,  4049,  4051,  4057,  4073,  4079,
+ 4091,  4093,  4099,  4111,  4127,  4129,  4133,  4139,  4153,  4157,  4159,
+ 4177,  4201,  4211,  4217,  4219,  4229,  4231,  4241,  4243,  4253,  4259,
+ 4261,  4271,  4273,  4283,  4289,  4297,  4327,  4337,  4339,  4349,  4357,
+ 4363,  4373,  4391,  4397,  4409,  4421,  4423,  4441,  4447,  4451,  4457,
+ 4463,  4481,  4483,  4493,  4507,  4513,  4517,  4519,  4523,  4547,  4549,
+ 4561,  4567,  4583,  4591,  4597,  4603,  4621,  4637,  4639,  4643,  4649,
+ 4651,  4657,  4663,  4673,  4679,  4691,  4703,  4721,  4723,  4729,  4733,
+ 4751,  4759,  4783,  4787,  4789,  4793,  4799,  4801,  4813,  4817,  4831,
+ 4861,  4871,  4877,  4889,  4903,  4909,  4919,  4931,  4933,  4937,  4943,
+ 4951,  4957,  4967,  4969,  4973,  4987,  4993,  4999,  5003,  5009,  5011,
+ 5021,  5023,  5039,  5051,  5059,  5077,  5081,  5087,  5099,  5101,  5107,
+ 5113,  5119,  5147,  5153,  5167,  5171,  5179,  5189,  5197,  5209,  5227,
+ 5231,  5233,  5237,  5261,  5273,  5279,  5281,  5297,  5303,  5309,  5323,
+ 5333,  5347,  5351,  5381,  5387,  5393,  5399,  5407,  5413,  5417,  5419,
+ 5431,  5437,  5441,  5443,  5449,  5471,  5477,  5479,  5483,  5501,  5503,
+ 5507,  5519,  5521,  5527,  5531,  5557,  5563,  5569,  5573,  5581,  5591,
+ 5623,  5639,  5641,  5647,  5651,  5653,  5657,  5659,  5669,  5683,  5689,
+ 5693,  5701,  5711,  5717,  5737,  5741,  5743,  5749,  5779,  5783,  5791,
+ 5801,  5807,  5813,  5821,  5827,  5839,  5843,  5849,  5851,  5857,  5861,
+ 5867,  5869,  5879,  5881,  5897,  5903,  5923,  5927,  5939,  5953,  5981,
+ 5987,  6007,  6011,  6029,  6037,  6043,  6047,  6053,  6067,  6073,  6079,
+ 6089,  6091,  6101,  6113,  6121,  6131,  6133,  6143,  6151,  6163,  6173,
+ 6197,  6199,  6203,  6211,  6217,  6221,  6229,  6247,  6257,  6263,  6269,
+ 6271,  6277,  6287,  6299,  6301,  6311,  6317,  6323,  6329,  6337,  6343,
+ 6353,  6359,  6361,  6367,  6373,  6379,  6389,  6397,  6421,  6427,  6449,
+ 6451,  6469,  6473,  6481,  6491,  6521,  6529,  6547,  6551,  6553,  6563,
+ 6569,  6571,  6577,  6581,  6599,  6607,  6619,  6637,  6653,  6659,  6661,
+ 6673,  6679,  6689,  6691,  6701,  6703,  6709,  6719,  6733,  6737,  6761,
+ 6763,  6779,  6781,  6791,  6793,  6803,  6823,  6827,  6829,  6833,  6841,
+ 6857,  6863,  6869,  6871,  6883,  6899,  6907,  6911,  6917,  6947,  6949,
+ 6959,  6961,  6967,  6971,  6977,  6983,  6991,  6997,  7001,  7013,  7019,
+ 7027,  7039,  7043,  7057,  7069,  7079,  7103,  7109,  7121,  7127,  7129,
+ 7151,  7159,  7177,  7187,  7193,  7207,  7211,  7213,  7219,  7229,  7237,
+ 7243,  7247,  7253,  7283,  7297,  7307,  7309,  7321,  7331,  7333,  7349,
+ 7351,  7369,  7393,  7411,  7417,  7433,  7451,  7457,  7459,  7477,  7481,
+ 7487,  7489,  7499,  7507,  7517,  7523,  7529,  7537,  7541,  7547,  7549,
+ 7559,  7561,  7573,  7577,  7583,  7589,  7591,  7603,  7607,  7621,  7639,
+ 7643,  7649,  7669,  7673,  7681,  7687,  7691,  7699,  7703,  7717,  7723,
+ 7727,  7741,  7753,  7757,  7759,  7789,  7793,  7817,  7823,  7829,  7841,
+ 7853,  7867,  7873,  7877,  7879,  7883,  7901,  7907,  7919,  7927,  7933,
+ 7937,  7949,  7951,  7963,  7993,  8009,  8011,  8017,  8039,  8053,  8059,
+ 8069,  8081,  8087,  8089,  8093,  8101,  8111,  8117,  8123,  8147,  8161,
+ 8167,  8171,  8179,  8191,  8209,  8219,  8221,  8231,  8233,  8237,  8243,
+ 8263,  8269,  8273,  8287,  8291,  8293,  8297,  8311,  8317,  8329,  8353,
+ 8363,  8369,  8377,  8387,  8389,  8419,  8423,  8429,  8431,  8443,  8447,
+ 8461,  8467,  8501,  8513,  8521,  8527,  8537,  8539,  8543,  8563,  8573,
+ 8581,  8597,  8599,  8609,  8623,  8627,  8629,  8641,  8647,  8663,  8669,
+ 8677,  8681,  8689,  8693,  8699,  8707,  8713,  8719,  8731,  8737,  8741,
+ 8747,  8753,  8761,  8779,  8783,  8803,  8807,  8819,  8821,  8831,  8837,
+ 8839,  8849,  8861,  8863,  8867,  8887,  8893,  8923,  8929,  8933,  8941,
+ 8951,  8963,  8969,  8971,  8999,  9001,  9007,  9011,  9013,  9029,  9041,
+ 9043,  9049,  9059,  9067,  9091,  9103,  9109,  9127,  9133,  9137,  9151,
+ 9157,  9161,  9173,  9181,  9187,  9199,  9203,  9209,  9221,  9227,  9239,
+ 9241,  9257,  9277,  9281,  9283,  9293,  9311,  9319,  9323,  9337,  9341,
+ 9343,  9349,  9371,  9377,  9391,  9397,  9403,  9413,  9419,  9421,  9431,
+ 9433,  9437,  9439,  9461,  9463,  9467,  9473,  9479,  9491,  9497,  9511,
+ 9521,  9533,  9539,  9547,  9551,  9587,  9601,  9613,  9619,  9623,  9629,
+ 9631,  9643,  9649,  9661,  9677,  9679,  9689,  9697,  9719,  9721,  9733,
+ 9739,  9743,  9749,  9767,  9769,  9781,  9787,  9791,  9803,  9811,  9817,
+ 9829,  9833,  9839,  9851,  9857,  9859,  9871,  9883,  9887,  9901,  9907,
+ 9923,  9929,  9931,  9941,  9949,  9967,  9973, 10007, 10009, 10037, 10039,
+10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139,
+10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243,
+10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321,
+10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433,
+10453, 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531,
+10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651,
+10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739,
+10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861,
+10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973,
+10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083,
+11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173,
+11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287,
+11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399,
+11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497,
+11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621,
+11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743,
+11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833,
+11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939,
+11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041,
+12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143,
+12149, 12157, 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251,
+12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347,
+12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451,
+12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539,
+12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619,
+12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721,
+12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829,
+12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941,
+12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033,
+13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147,
+13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241,
+13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339,
+13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463,
+13469, 13477, 13487, 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591,
+13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691,
+13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763,
+13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879,
+13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997,
+13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087,
+14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243,
+14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369,
+14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449,
+14461, 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557,
+14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657,
+14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753,
+14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843,
+14851, 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947,
+14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077,
+15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173,
+15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271,
+15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359,
+15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451,
+15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569,
+15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661,
+15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761,
+15767, 15773, 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877,
+15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971,
+15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073,
+16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189,
+16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319,
+16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427,
+16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547,
+16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651,
+16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759,
+16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889,
+16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987,
+16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093,
+17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 17203,
+17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327,
+17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417,
+17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497,
+17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609,
+17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729, 17737,
+17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851,
+17863, 17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957,
+17959, 17971, 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049,
+18059, 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143,
+18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, 18251,
+18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, 18329, 18341,
+18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, 18443,
+18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, 18523, 18539, 18541,
+18553, 18583, 18587, 18593, 18617, 18637, 18661, 18671, 18679, 18691, 18701,
+18713, 18719, 18731, 18743, 18749, 18757, 18773, 18787, 18793, 18797, 18803,
+18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973,
+18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081,
+19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213,
+19219, 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319,
+19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427,
+19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489,
+19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, 19577, 19583, 19597,
+19603, 19609, 19661, 19681, 19687, 19697, 19699, 19709, 19717, 19727, 19739,
+19751, 19753, 19759, 19763, 19777, 19793, 19801, 19813, 19819, 19841, 19843,
+19853, 19861, 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961,
+19963, 19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, 20029, 20047,
+20051, 20063, 20071, 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143,
+20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249,
+20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357,
+20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, 20477,
+20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593,
+20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719,
+20731, 20743, 20747, 20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809,
+20849, 20857, 20873, 20879, 20887, 20897, 20899, 20903, 20921, 20929, 20939,
+20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, 21019, 21023,
+21031, 21059, 21061, 21067, 21089, 21101, 21107, 21121, 21139, 21143, 21149,
+21157, 21163, 21169, 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247,
+21269, 21277, 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379,
+21383, 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491,
+21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, 21569,
+21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, 21649, 21661,
+21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, 21773,
+21787, 21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871,
+21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003,
+22013, 22027, 22031, 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091,
+22093, 22109, 22111, 22123, 22129, 22133, 22147, 22153, 22157, 22159, 22171,
+22189, 22193, 22229, 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291,
+22303, 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433,
+22441, 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543,
+22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, 22651,
+22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, 22739, 22741,
+22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861,
+22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, 22973, 22993,
+23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, 23059,
+23063, 23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173,
+23189, 23197, 23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293,
+23297, 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417,
+23431, 23447, 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557,
+23561, 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629,
+23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, 23747,
+23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833,
+23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, 23911, 23917, 23929,
+23957, 23971, 23977, 23981, 23993, 24001, 24007, 24019, 24023, 24029, 24043,
+24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, 24107, 24109, 24113,
+24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203, 24223, 24229,
+24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, 24379,
+24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499,
+24509, 24517, 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631,
+24659, 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767,
+24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889,
+24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, 24979, 24989,
+25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, 25111, 25117, 25121,
+25127, 25147, 25153, 25163, 25169, 25171, 25183, 25189, 25219, 25229, 25237,
+25243, 25247, 25253, 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343,
+25349, 25357, 25367, 25373, 25391, 25409, 25411, 25423, 25439, 25447, 25453,
+25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, 25577, 25579, 25583,
+25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673,
+25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793,
+25799, 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913,
+25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003,
+26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119,
+26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, 26209, 26227, 26237,
+26249, 26251, 26261, 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339,
+26347, 26357, 26371, 26387, 26393, 26399, 26407, 26417, 26423, 26431, 26437,
+26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539, 26557, 26561, 26573,
+26591, 26597, 26627, 26633, 26641, 26647, 26669, 26681, 26683, 26687, 26693,
+26699, 26701, 26711, 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777,
+26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881,
+26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987,
+26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, 27091,
+27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241,
+27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367,
+27397, 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487,
+27509, 27527, 27529, 27539, 27541, 27551, 27581, 27583, 27611, 27617, 27631,
+27647, 27653, 27673, 27689, 27691, 27697, 27701, 27733, 27737, 27739, 27743,
+27749, 27751, 27763, 27767, 27773, 27779, 27791, 27793, 27799, 27803, 27809,
+27817, 27823, 27827, 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941,
+27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031,
+28051, 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151,
+28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, 28289,
+28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, 28409, 28411,
+28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, 28537,
+28541, 28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607,
+28619, 28621, 28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687,
+28697, 28703, 28711, 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793,
+28807, 28813, 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909,
+28921, 28927, 28933, 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027,
+29033, 29059, 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153,
+29167, 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251,
+29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, 29383,
+29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453, 29473,
+29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, 29581, 29587, 29599,
+29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717, 29723, 29741,
+29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863, 29867,
+29873, 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011,
+30013, 30029, 30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113,
+30119, 30133, 30137, 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211,
+30223, 30241, 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323,
+30341, 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469,
+30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, 30577,
+30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, 30697, 30703,
+30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, 30809, 30817, 30829,
+30839, 30841, 30851, 30853, 30859, 30869, 30871, 30881, 30893, 30911, 30931,
+30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051,
+31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, 31151, 31153,
+31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237, 31247,
+31249, 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333,
+31337, 31357, 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489,
+31511, 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601,
+31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723,
+31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847, 31849,
+31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, 31981, 31991, 32003,
+32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069, 32077, 32083, 32089,
+32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, 32191, 32203,
+32213, 32233, 32237, 32251, 32257, 32261, 32297, 32299, 32303, 32309, 32321,
+32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371, 32377, 32381, 32401,
+32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503,
+32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603,
+32609, 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717,
+32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833,
+32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957,
+32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, 33029, 33037, 33049,
+33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, 33151, 33161,
+33179, 33181, 33191, 33199, 33203, 33211, 33223, 33247, 33287, 33289, 33301,
+33311, 33317, 33329, 33331, 33343, 33347, 33349, 33353, 33359, 33377, 33391,
+33403, 33409, 33413, 33427, 33457, 33461, 33469, 33479, 33487, 33493, 33503,
+33521, 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599,
+33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703,
+33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797,
+33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911,
+33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039,
+34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183,
+34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297,
+34301, 34303, 34313, 34319, 34327, 34337, 34351, 34361, 34367, 34369, 34381,
+34403, 34421, 34429, 34439, 34457, 34469, 34471, 34483, 34487, 34499, 34501,
+34511, 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, 34607,
+34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721,
+34729, 34739, 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843,
+34847, 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961,
+34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089,
+35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, 35171, 35201,
+35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317, 35323,
+35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437,
+35447, 35449, 35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537,
+35543, 35569, 35573, 35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729,
+35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, 35809, 35831, 35837,
+35839, 35851, 35863, 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951,
+35963, 35969, 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037,
+36061, 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161,
+36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293,
+36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389, 36433,
+36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523, 36527, 36529,
+36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637,
+36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, 36739,
+36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833,
+36847, 36857, 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929,
+36931, 36943, 36947, 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039,
+37049, 37057, 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181,
+37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309,
+37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409,
+37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, 37511, 37517,
+37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591,
+37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, 37717,
+37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, 37853, 37861, 37871,
+37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, 37991, 37993,
+37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, 38153,
+38167, 38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261,
+38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351,
+38371, 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543,
+38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, 38651,
+38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, 38723, 38729,
+38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, 38851,
+38861, 38867, 38873, 38891, 38903, 38917, 38921, 38923, 38933, 38953, 38959,
+38971, 38977, 38993, 39019, 39023, 39041, 39043, 39047, 39079, 39089, 39097,
+39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191,
+39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301,
+39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397,
+39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521,
+39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667,
+39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779,
+39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869,
+39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989,
+40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123,
+40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231,
+40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387,
+40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507,
+40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627,
+40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771,
+40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867,
+40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993,
+41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,
+41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203,
+41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299,
+41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443,
+41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549,
+41579, 41593, 41597, 41603, 41609, 41611, 41617, 41621, 41627, 41641, 41647,
+41651, 41659, 41669, 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771,
+41777, 41801, 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893,
+41897, 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981,
+41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, 42083,
+42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, 42193, 42197,
+42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299, 42307,
+42323, 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407,
+42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487,
+42491, 42499, 42509, 42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641,
+42643, 42649, 42667, 42677, 42683, 42689, 42697, 42701, 42703, 42709, 42719,
+42727, 42737, 42743, 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829,
+42839, 42841, 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943,
+42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051,
+43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, 43201,
+43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, 43321, 43331,
+43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, 43457, 43481, 43487,
+43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, 43607, 43609,
+43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, 43721,
+43753, 43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867,
+43889, 43891, 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987,
+43991, 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087,
+44089, 44101, 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189,
+44201, 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279,
+44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, 44453,
+44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, 44543, 44549,
+44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647, 44651, 44657,
+44683, 44687, 44699, 44701, 44711, 44729, 44741, 44753, 44771, 44773, 44777,
+44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867, 44879, 44887, 44893,
+44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983, 44987, 45007,
+45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, 45139,
+45161, 45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289,
+45293, 45307, 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389,
+45403, 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533,
+45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659,
+45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, 45763, 45767,
+45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, 45863, 45869, 45887,
+45893, 45943, 45949, 45953, 45959, 45971, 45979, 45989, 46021, 46027, 46049,
+46051, 46061, 46073, 46091, 46093, 46099, 46103, 46133, 46141, 46147, 46153,
+46171, 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271, 46273,
+46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, 46381, 46399, 46411,
+46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511,
+46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639,
+46643, 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747,
+46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853,
+46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017,
+47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, 47123, 47129, 47137,
+47143, 47147, 47149, 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279,
+47287, 47293, 47297, 47303, 47309, 47317, 47339, 47351, 47353, 47363, 47381,
+47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, 47491, 47497, 47501,
+47507, 47513, 47521, 47527, 47533, 47543, 47563, 47569, 47581, 47591, 47599,
+47609, 47623, 47629, 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711,
+47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809,
+47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939,
+47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, 48073,
+48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193,
+48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337,
+48341, 48353, 48371, 48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463,
+48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, 48533, 48539, 48541,
+48563, 48571, 48589, 48593, 48611, 48619, 48623, 48647, 48649, 48661, 48673,
+48677, 48679, 48731, 48733, 48751, 48757, 48761, 48767, 48779, 48781, 48787,
+48799, 48809, 48817, 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883,
+48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031,
+49033, 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123,
+49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, 49223,
+49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367,
+49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, 49463,
+49477, 49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597,
+49603, 49613, 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711,
+49727, 49739, 49741, 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811,
+49823, 49831, 49843, 49853, 49871, 49877, 49891, 49919, 49921, 49927, 49937,
+49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051,
+50053, 50069, 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131,
+50147, 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273,
+50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, 50383,
+50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, 50527,
+50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, 50599, 50627, 50647,
+50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773, 50777, 50789,
+50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, 50923,
+50929, 50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047,
+51059, 51061, 51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193,
+51197, 51199, 51203, 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287,
+51307, 51329, 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419,
+51421, 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487,
+51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, 51599,
+51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, 51691, 51713,
+51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, 51817, 51827, 51829,
+51839, 51853, 51859, 51869, 51871, 51893, 51899, 51907, 51913, 51929, 51941,
+51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067,
+52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, 52181, 52183,
+52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291, 52301,
+52313, 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457,
+52489, 52501, 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571,
+52579, 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709,
+52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, 52817,
+52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, 52937, 52951,
+52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, 53047, 53051, 53069,
+53077, 53087, 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161,
+53171, 53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, 53269, 53279,
+53281, 53299, 53309, 53323, 53327, 53353, 53359, 53377, 53381, 53401, 53407,
+53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, 53527, 53549, 53551,
+53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639,
+53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777,
+53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891,
+53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001,
+54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139,
+54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, 54277, 54287, 54293,
+54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, 54401, 54403,
+54409, 54413, 54419, 54421, 54437, 54443, 54449, 54469, 54493, 54497, 54499,
+54503, 54517, 54521, 54539, 54541, 54547, 54559, 54563, 54577, 54581, 54583,
+54601, 54617, 54623, 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713,
+54721, 54727, 54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851,
+54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979,
+54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103,
+55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, 55219,
+55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343,
+55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501,
+55511, 55529, 55541, 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631,
+55633, 55639, 55661, 55663, 55667, 55673, 55681, 55691, 55697, 55711, 55717,
+55721, 55733, 55763, 55787, 55793, 55799, 55807, 55813, 55817, 55819, 55823,
+55829, 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, 55927,
+55931, 55933, 55949, 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053,
+56081, 56087, 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171,
+56179, 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299,
+56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437,
+56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, 56509, 56519,
+56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, 56633,
+56659, 56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747,
+56767, 56773, 56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857,
+56873, 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951,
+56957, 56963, 56983, 56989, 56993, 56999, 57037, 57041, 57047, 57059, 57073,
+57077, 57089, 57097, 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173,
+57179, 57191, 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271,
+57283, 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389,
+57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, 57557,
+57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, 57667, 57679,
+57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, 57751, 57773, 57781,
+57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853, 57859, 57881,
+57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, 58027,
+58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129,
+58147, 58151, 58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217,
+58229, 58231, 58237, 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367,
+58369, 58379, 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451,
+58453, 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601,
+58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, 58727,
+58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, 58897, 58901,
+58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997,
+59009, 59011, 59021, 59023, 59029, 59051, 59053, 59063, 59069, 59077, 59083,
+59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, 59167, 59183, 59197,
+59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, 59281, 59333,
+59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407, 59417,
+59419, 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513,
+59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651,
+59659, 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747,
+59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, 59921,
+59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041,
+60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, 60133, 60139, 60149,
+60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257, 60259, 60271, 60289,
+60293, 60317, 60331, 60337, 60343, 60353, 60373, 60383, 60397, 60413, 60427,
+60443, 60449, 60457, 60493, 60497, 60509, 60521, 60527, 60539, 60589, 60601,
+60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649, 60659, 60661, 60679,
+60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779,
+60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917,
+60919, 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043,
+61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211,
+61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343,
+61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, 61463, 61469, 61471,
+61483, 61487, 61493, 61507, 61511, 61519, 61543, 61547, 61553, 61559, 61561,
+61583, 61603, 61609, 61613, 61627, 61631, 61637, 61643, 61651, 61657, 61667,
+61673, 61681, 61687, 61703, 61717, 61723, 61729, 61751, 61757, 61781, 61813,
+61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933, 61949, 61961,
+61967, 61979, 61981, 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053,
+62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171,
+62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303,
+62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, 62467,
+62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581,
+62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687,
+62701, 62723, 62731, 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827,
+62851, 62861, 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969,
+62971, 62981, 62983, 62987, 62989, 63029, 63031, 63059, 63067, 63073, 63079,
+63097, 63103, 63113, 63127, 63131, 63149, 63179, 63197, 63199, 63211, 63241,
+63247, 63277, 63281, 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353,
+63361, 63367, 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443,
+63463, 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559,
+63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, 63649,
+63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, 63727, 63737,
+63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839, 63841,
+63853, 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007,
+64013, 64019, 64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151,
+64153, 64157, 64171, 64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279,
+64283, 64301, 64303, 64319, 64327, 64333, 64373, 64381, 64399, 64403, 64433,
+64439, 64451, 64453, 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579,
+64591, 64601, 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679,
+64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849,
+64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, 64951,
+64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, 65071, 65089,
+65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173,
+65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, 65293, 65309,
+65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419, 65423,
+65437, 65447, 65449, 65479, 65497, 65519, 65521, 0 };
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/reducer.cpp b/third_party/botan/src/lib/math/numbertheory/reducer.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/reducer.cpp
@@ -0,0 +1,119 @@
+/*
+* Modular Reducer
+* (C) 1999-2011,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/reducer.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/divide.h>
+
+namespace Botan {
+
+/*
+* Modular_Reducer Constructor
+*/
+Modular_Reducer::Modular_Reducer(const BigInt& mod)
+   {
+   if(mod < 0)
+      throw Invalid_Argument("Modular_Reducer: modulus must be positive");
+
+   // Left uninitialized if mod == 0
+   m_mod_words = 0;
+
+   if(mod > 0)
+      {
+      m_modulus = mod;
+      m_mod_words = m_modulus.sig_words();
+
+      // Compute mu = floor(2^{2k} / m)
+      m_mu.set_bit(2 * BOTAN_MP_WORD_BITS * m_mod_words);
+      m_mu = ct_divide(m_mu, m_modulus);
+      }
+   }
+
+BigInt Modular_Reducer::reduce(const BigInt& x) const
+   {
+   BigInt r;
+   secure_vector<word> ws;
+   reduce(r, x, ws);
+   return r;
+   }
+
+namespace {
+
+/*
+* Like if(cnd) x.rev_sub(...) but in const time
+*/
+void cnd_rev_sub(bool cnd, BigInt& x, const word y[], size_t y_sw, secure_vector<word>& ws)
+   {
+   if(x.sign() != BigInt::Positive)
+      throw Invalid_State("BigInt::sub_rev requires this is positive");
+
+   const size_t x_sw = x.sig_words();
+
+   const size_t max_words = std::max(x_sw, y_sw);
+   ws.resize(std::max(x_sw, y_sw));
+   clear_mem(ws.data(), ws.size());
+   x.grow_to(max_words);
+
+   const int32_t relative_size = bigint_sub_abs(ws.data(), x.data(), x_sw, y, y_sw);
+
+   x.cond_flip_sign((relative_size > 0) && cnd);
+   bigint_cnd_swap(cnd, x.mutable_data(), ws.data(), max_words);
+   }
+
+}
+
+void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector<word>& ws) const
+   {
+   if(&t1 == &x)
+      throw Invalid_State("Modular_Reducer arguments cannot alias");
+   if(m_mod_words == 0)
+      throw Invalid_State("Modular_Reducer: Never initalized");
+
+   const size_t x_sw = x.sig_words();
+
+   if(x_sw > 2*m_mod_words)
+      {
+      // too big, fall back to slow boat division
+      t1 = ct_modulo(x, m_modulus);
+      return;
+      }
+
+   t1 = x;
+   t1.set_sign(BigInt::Positive);
+   t1 >>= (BOTAN_MP_WORD_BITS * (m_mod_words - 1));
+
+   t1.mul(m_mu, ws);
+   t1 >>= (BOTAN_MP_WORD_BITS * (m_mod_words + 1));
+
+   // TODO add masked mul to avoid computing high bits
+   t1.mul(m_modulus, ws);
+   t1.mask_bits(BOTAN_MP_WORD_BITS * (m_mod_words + 1));
+
+   t1.rev_sub(x.data(), std::min(x_sw, m_mod_words + 1), ws);
+
+   /*
+   * If t1 < 0 then we must add b^(k+1) where b = 2^w. To avoid a
+   * side channel perform the addition unconditionally, with ws set
+   * to either b^(k+1) or else 0.
+   */
+   const word t1_neg = t1.is_negative();
+
+   if(ws.size() < m_mod_words + 2)
+      ws.resize(m_mod_words + 2);
+   clear_mem(ws.data(), ws.size());
+   ws[m_mod_words + 1] = t1_neg;
+
+   t1.add(ws.data(), m_mod_words + 2, BigInt::Positive);
+
+   // Per HAC this step requires at most 2 subtractions
+   t1.ct_reduce_below(m_modulus, ws, 2);
+
+   cnd_rev_sub(t1.is_nonzero() && x.is_negative(), t1, m_modulus.data(), m_modulus.size(), ws);
+   }
+
+}
diff --git a/third_party/botan/src/lib/math/numbertheory/reducer.h b/third_party/botan/src/lib/math/numbertheory/reducer.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/reducer.h
@@ -0,0 +1,69 @@
+/*
+* Modular Reducer
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODULAR_REDUCER_H_
+#define BOTAN_MODULAR_REDUCER_H_
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+/**
+* Modular Reducer (using Barrett's technique)
+*/
+class BOTAN_PUBLIC_API(2,0) Modular_Reducer
+   {
+   public:
+      const BigInt& get_modulus() const { return m_modulus; }
+
+      BigInt reduce(const BigInt& x) const;
+
+      /**
+      * Multiply mod p
+      * @param x the first operand
+      * @param y the second operand
+      * @return (x * y) % p
+      */
+      BigInt multiply(const BigInt& x, const BigInt& y) const
+         { return reduce(x * y); }
+
+      /**
+      * Square mod p
+      * @param x the value to square
+      * @return (x * x) % p
+      */
+      BigInt square(const BigInt& x) const
+         { return reduce(Botan::square(x)); }
+
+      /**
+      * Cube mod p
+      * @param x the value to cube
+      * @return (x * x * x) % p
+      */
+      BigInt cube(const BigInt& x) const
+         { return multiply(x, this->square(x)); }
+
+      /**
+      * Low level reduction function. Mostly for internal use.
+      * Sometimes useful for performance by reducing temporaries
+      * Reduce x mod p and place the output in out. ** X and out must not reference each other **
+      * ws is a temporary workspace.
+      */
+      void reduce(BigInt& out, const BigInt& x, secure_vector<word>& ws) const;
+
+      bool initialized() const { return (m_mod_words != 0); }
+
+      Modular_Reducer() { m_mod_words = 0; }
+      explicit Modular_Reducer(const BigInt& mod);
+   private:
+      BigInt m_modulus, m_mu;
+      size_t m_mod_words;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/math/numbertheory/ressol.cpp b/third_party/botan/src/lib/math/numbertheory/ressol.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/math/numbertheory/ressol.cpp
@@ -0,0 +1,87 @@
+/*
+* Shanks-Tonnelli (RESSOL)
+* (C) 2007-2008 Falko Strenzke, FlexSecure GmbH
+* (C) 2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+namespace Botan {
+
+/*
+* Shanks-Tonnelli algorithm
+*/
+BigInt ressol(const BigInt& a, const BigInt& p)
+   {
+   if(a == 0)
+      return 0;
+   else if(a < 0)
+      throw Invalid_Argument("ressol: value to solve for must be positive");
+   else if(a >= p)
+      throw Invalid_Argument("ressol: value to solve for must be less than p");
+
+   if(p == 2)
+      return a;
+   else if(p <= 1)
+      throw Invalid_Argument("ressol: prime must be > 1 a");
+   else if(p.is_even())
+      throw Invalid_Argument("ressol: invalid prime");
+
+   if(jacobi(a, p) != 1) // not a quadratic residue
+      return -BigInt(1);
+
+   if(p % 4 == 3)
+      return power_mod(a, ((p+1) >> 2), p);
+
+   size_t s = low_zero_bits(p - 1);
+   BigInt q = p >> s;
+
+   q -= 1;
+   q >>= 1;
+
+   Modular_Reducer mod_p(p);
+
+   BigInt r = power_mod(a, q, p);
+   BigInt n = mod_p.multiply(a, mod_p.square(r));
+   r = mod_p.multiply(r, a);
+
+   if(n == 1)
+      return r;
+
+   // find random non quadratic residue z
+   BigInt z = 2;
+   while(jacobi(z, p) == 1) // while z quadratic residue
+      ++z;
+
+   BigInt c = power_mod(z, (q << 1) + 1, p);
+
+   while(n > 1)
+      {
+      q = n;
+
+      size_t i = 0;
+      while(q != 1)
+         {
+         q = mod_p.square(q);
+         ++i;
+
+         if(i >= s)
+            {
+            return -BigInt(1);
+            }
+         }
+
+      c = power_mod(c, BigInt::power_of_2(s-i-1), p);
+      r = mod_p.multiply(r, c);
+      c = mod_p.square(c);
+      n = mod_p.multiply(n, c);
+      s = i;
+      }
+
+   return r;
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/aont/package.cpp b/third_party/botan/src/lib/misc/aont/package.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/aont/package.cpp
@@ -0,0 +1,125 @@
+/*
+* Rivest's Package Tranform
+*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/package.h>
+#include <botan/filters.h>
+#include <botan/ctr.h>
+#include <botan/loadstor.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void aont_package(RandomNumberGenerator& rng,
+                  BlockCipher* cipher,
+                  const uint8_t input[], size_t input_len,
+                  uint8_t output[])
+   {
+   if(input_len <= 1)
+      throw Encoding_Error("Package transform cannot encode small inputs");
+
+   const size_t BLOCK_SIZE = cipher->block_size();
+
+   if(!cipher->valid_keylength(BLOCK_SIZE))
+      throw Invalid_Argument("AONT::package: Invalid cipher");
+
+   // The all-zero string which is used both as the CTR IV and as K0
+   const std::string all_zeros(BLOCK_SIZE*2, '0');
+
+   SymmetricKey package_key(rng, BLOCK_SIZE);
+
+   Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key));
+
+   pipe.process_msg(input, input_len);
+   const size_t remaining = pipe.remaining();
+   BOTAN_ASSERT_EQUAL(remaining, pipe.read(output, remaining), "Expected read size");
+
+   // Set K0 (the all zero key)
+   cipher->set_key(SymmetricKey(all_zeros));
+
+   secure_vector<uint8_t> buf(BLOCK_SIZE);
+
+   const size_t blocks =
+      (input_len + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+   uint8_t* final_block = output + input_len;
+   clear_mem(final_block, BLOCK_SIZE);
+
+   // XOR the hash blocks into the final block
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      const size_t left = std::min<size_t>(BLOCK_SIZE,
+                                           input_len - BLOCK_SIZE * i);
+
+      zeroise(buf);
+      copy_mem(buf.data(), output + (BLOCK_SIZE * i), left);
+
+      for(size_t j = 0; j != sizeof(i); ++j)
+         buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i);
+
+      cipher->encrypt(buf.data());
+
+      xor_buf(final_block, buf.data(), BLOCK_SIZE);
+      }
+
+   // XOR the random package key into the final block
+   xor_buf(final_block, package_key.begin(), BLOCK_SIZE);
+   }
+
+void aont_unpackage(BlockCipher* cipher,
+                    const uint8_t input[], size_t input_len,
+                    uint8_t output[])
+   {
+   const size_t BLOCK_SIZE = cipher->block_size();
+
+   if(!cipher->valid_keylength(BLOCK_SIZE))
+      throw Invalid_Argument("AONT::unpackage: Invalid cipher");
+
+   if(input_len < BLOCK_SIZE)
+      throw Invalid_Argument("AONT::unpackage: Input too short");
+
+   // The all-zero string which is used both as the CTR IV and as K0
+   const std::string all_zeros(BLOCK_SIZE*2, '0');
+
+   cipher->set_key(SymmetricKey(all_zeros));
+
+   secure_vector<uint8_t> package_key(BLOCK_SIZE);
+   secure_vector<uint8_t> buf(BLOCK_SIZE);
+
+   // Copy the package key (masked with the block hashes)
+   copy_mem(package_key.data(),
+            input + (input_len - BLOCK_SIZE),
+            BLOCK_SIZE);
+
+   const size_t blocks = ((input_len - 1) / BLOCK_SIZE);
+
+   // XOR the blocks into the package key bits
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      const size_t left = std::min<size_t>(BLOCK_SIZE,
+                                           input_len - BLOCK_SIZE * (i+1));
+
+      zeroise(buf);
+      copy_mem(buf.data(), input + (BLOCK_SIZE * i), left);
+
+      for(size_t j = 0; j != sizeof(i); ++j)
+         buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i);
+
+      cipher->encrypt(buf.data());
+
+      xor_buf(package_key.data(), buf.data(), BLOCK_SIZE);
+      }
+
+   Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key));
+
+   pipe.process_msg(input, input_len - BLOCK_SIZE);
+
+   const size_t remaining = pipe.remaining();
+   BOTAN_ASSERT_EQUAL(remaining, pipe.read(output, remaining), "Expected read size");
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/aont/package.h b/third_party/botan/src/lib/misc/aont/package.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/aont/package.h
@@ -0,0 +1,49 @@
+/*
+* Rivest's Package Tranform
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AONT_PACKAGE_TRANSFORM_H_
+#define BOTAN_AONT_PACKAGE_TRANSFORM_H_
+
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Rivest's Package Tranform
+* @param rng the random number generator to use
+* @param cipher the block cipher to use (aont_package takes ownership)
+* @param input the input data buffer
+* @param input_len the length of the input data in bytes
+* @param output the output data buffer (must be at least
+*        input_len + cipher->BLOCK_SIZE bytes long)
+*/
+BOTAN_DEPRECATED("Possibly broken, avoid")
+void BOTAN_PUBLIC_API(2,0)
+aont_package(RandomNumberGenerator& rng,
+             BlockCipher* cipher,
+             const uint8_t input[], size_t input_len,
+             uint8_t output[]);
+
+/**
+* Rivest's Package Tranform (Inversion)
+* @param cipher the block cipher to use (aont_package takes ownership)
+* @param input the input data buffer
+* @param input_len the length of the input data in bytes
+* @param output the output data buffer (must be at least
+*        input_len - cipher->BLOCK_SIZE bytes long)
+*/
+BOTAN_DEPRECATED("Possibly broken, avoid")
+void BOTAN_PUBLIC_API(2,0)
+aont_unpackage(BlockCipher* cipher,
+               const uint8_t input[], size_t input_len,
+               uint8_t output[]);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp b/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp
@@ -0,0 +1,180 @@
+/*
+* Cryptobox Message Routines
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cryptobox.h>
+#include <botan/cipher_mode.h>
+#include <botan/mac.h>
+#include <botan/rng.h>
+#include <botan/pbkdf.h>
+#include <botan/data_src.h>
+#include <botan/pem.h>
+#include <botan/loadstor.h>
+#include <botan/mem_ops.h>
+
+namespace Botan {
+
+namespace CryptoBox {
+
+namespace {
+
+/*
+First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
+for later use as flags, etc if needed
+*/
+const uint32_t CRYPTOBOX_VERSION_CODE = 0xEFC22400;
+
+const size_t VERSION_CODE_LEN = 4;
+const size_t CIPHER_KEY_LEN = 32;
+const size_t CIPHER_IV_LEN = 16;
+const size_t MAC_KEY_LEN = 32;
+const size_t MAC_OUTPUT_LEN = 20;
+const size_t PBKDF_SALT_LEN = 10;
+const size_t PBKDF_ITERATIONS = 8 * 1024;
+
+const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN;
+const size_t CRYPTOBOX_HEADER_LEN = VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN;
+
+}
+
+std::string encrypt(const uint8_t input[], size_t input_len,
+                    const std::string& passphrase,
+                    RandomNumberGenerator& rng)
+   {
+   /*
+   Output format is:
+      version # (4 bytes)
+      salt (10 bytes)
+      mac (20 bytes)
+      ciphertext
+   */
+   secure_vector<uint8_t> out_buf(CRYPTOBOX_HEADER_LEN + input_len);
+   for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
+     out_buf[i] = get_byte(i, CRYPTOBOX_VERSION_CODE);
+   rng.randomize(&out_buf[VERSION_CODE_LEN], PBKDF_SALT_LEN);
+   // space left for MAC here
+   if(input_len > 0)
+      copy_mem(&out_buf[CRYPTOBOX_HEADER_LEN], input, input_len);
+
+   // Generate the keys and IV
+
+   std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
+
+   OctetString master_key = pbkdf->derive_key(
+      CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN,
+      passphrase,
+      &out_buf[VERSION_CODE_LEN],
+      PBKDF_SALT_LEN,
+      PBKDF_ITERATIONS);
+
+   const uint8_t* mk = master_key.begin();
+   const uint8_t* cipher_key = mk;
+   const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
+   const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
+
+   // Now encrypt and authenticate
+   std::unique_ptr<Cipher_Mode> ctr = Cipher_Mode::create_or_throw("Serpent/CTR-BE", ENCRYPTION);
+   ctr->set_key(cipher_key, CIPHER_KEY_LEN);
+   ctr->start(iv, CIPHER_IV_LEN);
+   ctr->finish(out_buf, CRYPTOBOX_HEADER_LEN);
+
+   std::unique_ptr<MessageAuthenticationCode> hmac =
+      MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   hmac->set_key(mac_key, MAC_KEY_LEN);
+   if(input_len > 0)
+      hmac->update(&out_buf[CRYPTOBOX_HEADER_LEN], input_len);
+
+   // Can't write directly because of MAC truncation
+   secure_vector<uint8_t> mac = hmac->final();
+   copy_mem(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN], mac.data(), MAC_OUTPUT_LEN);
+
+   return PEM_Code::encode(out_buf, "BOTAN CRYPTOBOX MESSAGE");
+   }
+
+secure_vector<uint8_t>
+decrypt_bin(const uint8_t input[], size_t input_len,
+            const std::string& passphrase)
+   {
+   DataSource_Memory input_src(input, input_len);
+   secure_vector<uint8_t> ciphertext =
+      PEM_Code::decode_check_label(input_src,
+                                   "BOTAN CRYPTOBOX MESSAGE");
+
+   if(ciphertext.size() < CRYPTOBOX_HEADER_LEN)
+      throw Decoding_Error("Invalid CryptoBox input");
+
+   for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
+      if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE))
+         throw Decoding_Error("Bad CryptoBox version");
+
+   const uint8_t* pbkdf_salt = &ciphertext[VERSION_CODE_LEN];
+   const uint8_t* box_mac = &ciphertext[VERSION_CODE_LEN + PBKDF_SALT_LEN];
+
+   std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
+
+   OctetString master_key = pbkdf->derive_key(
+      PBKDF_OUTPUT_LEN,
+      passphrase,
+      pbkdf_salt,
+      PBKDF_SALT_LEN,
+      PBKDF_ITERATIONS);
+
+   const uint8_t* mk = master_key.begin();
+   const uint8_t* cipher_key = mk;
+   const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
+   const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
+
+   // Now authenticate and decrypt
+   std::unique_ptr<MessageAuthenticationCode> hmac =
+      MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   hmac->set_key(mac_key, MAC_KEY_LEN);
+
+   if(ciphertext.size() > CRYPTOBOX_HEADER_LEN)
+      {
+      hmac->update(&ciphertext[CRYPTOBOX_HEADER_LEN],
+                   ciphertext.size() - CRYPTOBOX_HEADER_LEN);
+      }
+   secure_vector<uint8_t> computed_mac = hmac->final();
+
+   if(!constant_time_compare(computed_mac.data(), box_mac, MAC_OUTPUT_LEN))
+      throw Decoding_Error("CryptoBox integrity failure");
+
+   std::unique_ptr<Cipher_Mode> ctr(Cipher_Mode::create_or_throw("Serpent/CTR-BE", DECRYPTION));
+   ctr->set_key(cipher_key, CIPHER_KEY_LEN);
+   ctr->start(iv, CIPHER_IV_LEN);
+   ctr->finish(ciphertext, CRYPTOBOX_HEADER_LEN);
+
+   ciphertext.erase(ciphertext.begin(), ciphertext.begin() + CRYPTOBOX_HEADER_LEN);
+   return ciphertext;
+   }
+
+secure_vector<uint8_t> decrypt_bin(const std::string& input,
+                                   const std::string& passphrase)
+   {
+   return decrypt_bin(cast_char_ptr_to_uint8(input.data()),
+                      input.size(),
+                      passphrase);
+   }
+
+std::string decrypt(const uint8_t input[], size_t input_len,
+                    const std::string& passphrase)
+   {
+   const secure_vector<uint8_t> bin = decrypt_bin(input, input_len, passphrase);
+
+   return std::string(cast_uint8_ptr_to_char(&bin[0]),
+                      bin.size());
+   }
+
+std::string decrypt(const std::string& input,
+                    const std::string& passphrase)
+   {
+   return decrypt(cast_char_ptr_to_uint8(input.data()),
+                  input.size(), passphrase);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/misc/cryptobox/cryptobox.h b/third_party/botan/src/lib/misc/cryptobox/cryptobox.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/cryptobox/cryptobox.h
@@ -0,0 +1,79 @@
+/*
+* Cryptobox Message Routines
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRYPTOBOX_H_
+#define BOTAN_CRYPTOBOX_H_
+
+#include <string>
+#include <botan/symkey.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* This namespace holds various high-level crypto functions
+*/
+namespace CryptoBox {
+
+/**
+* Encrypt a message using a passphrase
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+* @param rng a ref to a random number generator, such as AutoSeeded_RNG
+*/
+BOTAN_PUBLIC_API(2,0) std::string encrypt(const uint8_t input[], size_t input_len,
+                              const std::string& passphrase,
+                              RandomNumberGenerator& rng);
+
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,3)
+secure_vector<uint8_t>
+decrypt_bin(const uint8_t input[], size_t input_len,
+            const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,3)
+secure_vector<uint8_t>
+decrypt_bin(const std::string& input,
+            const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string decrypt(const uint8_t input[], size_t input_len,
+                    const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string decrypt(const std::string& input,
+                    const std::string& passphrase);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp b/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp
@@ -0,0 +1,218 @@
+/*
+* Format Preserving Encryption (FE1 scheme)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/fpe_fe1.h>
+#include <botan/loadstor.h>
+#include <botan/numthry.h>
+#include <botan/divide.h>
+#include <botan/reducer.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+namespace {
+
+// Normally FPE is for SSNs, CC#s, etc, nothing too big
+const size_t MAX_N_BYTES = 128/8;
+
+/*
+* Factor n into a and b which are as close together as possible.
+* Assumes n is composed mostly of small factors which is the case for
+* typical uses of FPE (typically, n is a power of 10)
+*/
+void factor(BigInt n, BigInt& a, BigInt& b)
+   {
+   a = 1;
+   b = 1;
+
+   size_t n_low_zero = low_zero_bits(n);
+
+   a <<= (n_low_zero / 2);
+   b <<= n_low_zero - (n_low_zero / 2);
+   n >>= n_low_zero;
+
+   for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
+      {
+      while(n % PRIMES[i] == 0)
+         {
+         a *= PRIMES[i];
+         if(a > b)
+            std::swap(a, b);
+         n /= PRIMES[i];
+         }
+      }
+
+   if(a > b)
+      std::swap(a, b);
+   a *= n;
+
+   if(a <= 1 || b <= 1)
+      throw Internal_Error("Could not factor n for use in FPE");
+   }
+
+}
+
+FPE_FE1::FPE_FE1(const BigInt& n,
+                 size_t rounds,
+                 bool compat_mode,
+                 const std::string& mac_algo) :
+   m_rounds(rounds)
+   {
+   if(m_rounds < 3)
+      throw Invalid_Argument("FPE_FE1 rounds too small");
+
+   m_mac = MessageAuthenticationCode::create_or_throw(mac_algo);
+
+   m_n_bytes = BigInt::encode(n);
+
+   if(m_n_bytes.size() > MAX_N_BYTES)
+      throw Invalid_Argument("N is too large for FPE encryption");
+
+   factor(n, m_a, m_b);
+
+   if(compat_mode)
+      {
+      if(m_a < m_b)
+         std::swap(m_a, m_b);
+      }
+   else
+      {
+      if(m_a > m_b)
+         std::swap(m_a, m_b);
+      }
+
+   mod_a.reset(new Modular_Reducer(m_a));
+   }
+
+FPE_FE1::~FPE_FE1()
+   {
+   // for ~unique_ptr
+   }
+
+void FPE_FE1::clear()
+   {
+   m_mac->clear();
+   }
+
+std::string FPE_FE1::name() const
+   {
+   return "FPE_FE1(" + m_mac->name() + "," + std::to_string(m_rounds) + ")";
+   }
+
+Key_Length_Specification FPE_FE1::key_spec() const
+   {
+   return m_mac->key_spec();
+   }
+
+void FPE_FE1::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_mac->set_key(key, length);
+   }
+
+BigInt FPE_FE1::F(const BigInt& R, size_t round,
+                  const secure_vector<uint8_t>& tweak_mac,
+                  secure_vector<uint8_t>& tmp) const
+   {
+   tmp = BigInt::encode_locked(R);
+
+   m_mac->update(tweak_mac);
+   m_mac->update_be(static_cast<uint32_t>(round));
+
+   m_mac->update_be(static_cast<uint32_t>(tmp.size()));
+   m_mac->update(tmp.data(), tmp.size());
+
+   tmp = m_mac->final();
+   return BigInt(tmp.data(), tmp.size());
+   }
+
+secure_vector<uint8_t> FPE_FE1::compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const
+   {
+   m_mac->update_be(static_cast<uint32_t>(m_n_bytes.size()));
+   m_mac->update(m_n_bytes.data(), m_n_bytes.size());
+
+   m_mac->update_be(static_cast<uint32_t>(tweak_len));
+   if(tweak_len > 0)
+      m_mac->update(tweak, tweak_len);
+
+   return m_mac->final();
+   }
+
+BigInt FPE_FE1::encrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const
+   {
+   const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
+
+   BigInt X = input;
+
+   secure_vector<uint8_t> tmp;
+
+   BigInt L, R, Fi;
+   for(size_t i = 0; i != m_rounds; ++i)
+      {
+      ct_divide(X, m_b, L, R);
+      Fi = F(R, i, tweak_mac, tmp);
+      X = m_a * R + mod_a->reduce(L + Fi);
+      }
+
+   return X;
+   }
+
+BigInt FPE_FE1::decrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const
+   {
+   const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
+
+   BigInt X = input;
+   secure_vector<uint8_t> tmp;
+
+   BigInt W, R, Fi;
+   for(size_t i = 0; i != m_rounds; ++i)
+      {
+      ct_divide(X, m_a, R, W);
+
+      Fi = F(R, m_rounds-i-1, tweak_mac, tmp);
+      X = m_b * mod_a->reduce(W - Fi) + R;
+      }
+
+   return X;
+   }
+
+BigInt FPE_FE1::encrypt(const BigInt& x, uint64_t tweak) const
+   {
+   uint8_t tweak8[8];
+   store_be(tweak, tweak8);
+   return encrypt(x, tweak8, sizeof(tweak8));
+   }
+
+BigInt FPE_FE1::decrypt(const BigInt& x, uint64_t tweak) const
+   {
+   uint8_t tweak8[8];
+   store_be(tweak, tweak8);
+   return decrypt(x, tweak8, sizeof(tweak8));
+   }
+
+namespace FPE {
+
+BigInt fe1_encrypt(const BigInt& n, const BigInt& X,
+                   const SymmetricKey& key,
+                   const std::vector<uint8_t>& tweak)
+   {
+   FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
+   fpe.set_key(key);
+   return fpe.encrypt(X, tweak.data(), tweak.size());
+   }
+
+BigInt fe1_decrypt(const BigInt& n, const BigInt& X,
+                   const SymmetricKey& key,
+                   const std::vector<uint8_t>& tweak)
+   {
+   FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
+   fpe.set_key(key);
+   return fpe.decrypt(X, tweak.data(), tweak.size());
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h b/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h
@@ -0,0 +1,123 @@
+/*
+* Format Preserving Encryption (FE1 scheme)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FPE_FE1_H_
+#define BOTAN_FPE_FE1_H_
+
+#include <botan/sym_algo.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class Modular_Reducer;
+class MessageAuthenticationCode;
+
+/**
+* Format Preserving Encryption using the scheme FE1 from the paper
+* "Format-Preserving Encryption" by Bellare, Rogaway, et al
+* (https://eprint.iacr.org/2009/251)
+*/
+class BOTAN_PUBLIC_API(2,5) FPE_FE1 final : public SymmetricAlgorithm
+   {
+   public:
+
+      /**
+      * @param n the modulus. All plaintext and ciphertext values must be
+      *        less than this.
+      * @param rounds the number of rounds to use. Must be at least 3.
+      * @param compat_mode An error in versions before 2.5.0 chose incorrect
+      *        values for a and b. Set compat_mode to true to select this version.
+      * @param mac_algo the PRF to use as the encryption function
+      */
+      FPE_FE1(const BigInt& n,
+              size_t rounds = 5,
+              bool compat_mode = false,
+              const std::string& mac_algo = "HMAC(SHA-256)");
+
+      ~FPE_FE1();
+
+      Key_Length_Specification key_spec() const override;
+
+      std::string name() const override;
+
+      void clear() override;
+
+      /**
+      * Encrypt X from and onto the group Z_n using key and tweak
+      * @param x the plaintext to encrypt <= n
+      * @param tweak will modify the ciphertext
+      * @param tweak_len length of tweak
+      */
+      BigInt encrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const;
+
+      /**
+      * Decrypt X from and onto the group Z_n using key and tweak
+      * @param x the ciphertext to encrypt <= n
+      * @param tweak must match the value used to encrypt
+      * @param tweak_len length of tweak
+      */
+      BigInt decrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const;
+
+      BigInt encrypt(const BigInt& x, uint64_t tweak) const;
+
+      BigInt decrypt(const BigInt& x, uint64_t tweak) const;
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      BigInt F(const BigInt& R, size_t round,
+               const secure_vector<uint8_t>& tweak,
+               secure_vector<uint8_t>& tmp) const;
+
+      secure_vector<uint8_t> compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const;
+
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      std::unique_ptr<Modular_Reducer> mod_a;
+      std::vector<uint8_t> m_n_bytes;
+      BigInt m_a;
+      BigInt m_b;
+      size_t m_rounds;
+   };
+
+namespace FPE {
+
+/**
+* Format Preserving Encryption using the scheme FE1 from the paper
+* "Format-Preserving Encryption" by Bellare, Rogaway, et al
+* (https://eprint.iacr.org/2009/251)
+*
+* Encrypt X from and onto the group Z_n using key and tweak
+* @param n the modulus
+* @param X the plaintext as a BigInt
+* @param key a random key
+* @param tweak will modify the ciphertext (think of as an IV)
+*
+* @warning This function is hardcoded to use only 3 rounds which
+* may be insecure for some values of n. Prefer FPE_FE1 class
+*/
+BigInt BOTAN_PUBLIC_API(2,0) fe1_encrypt(const BigInt& n, const BigInt& X,
+                                         const SymmetricKey& key,
+                                         const std::vector<uint8_t>& tweak);
+
+/**
+* Decrypt X from and onto the group Z_n using key and tweak
+* @param n the modulus
+* @param X the ciphertext as a BigInt
+* @param key is the key used for encryption
+* @param tweak the same tweak used for encryption
+*
+* @warning This function is hardcoded to use only 3 rounds which
+* may be insecure for some values of n. Prefer FPE_FE1 class
+*/
+BigInt BOTAN_PUBLIC_API(2,0) fe1_decrypt(const BigInt& n, const BigInt& X,
+                                         const SymmetricKey& key,
+                                         const std::vector<uint8_t>& tweak);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/hotp/hotp.cpp b/third_party/botan/src/lib/misc/hotp/hotp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/hotp/hotp.cpp
@@ -0,0 +1,63 @@
+/*
+* HOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hotp.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+HOTP::HOTP(const uint8_t key[], size_t key_len,
+           const std::string& hash_algo, size_t digits)
+   {
+   BOTAN_ARG_CHECK(digits == 6 || digits == 7 || digits == 8, "Invalid HOTP digits");
+
+   if(digits == 6)
+      m_digit_mod = 1000000;
+   else if(digits == 7)
+      m_digit_mod = 10000000;
+   else if(digits == 8)
+      m_digit_mod = 100000000;
+
+   /*
+   RFC 4228 only supports SHA-1 but TOTP allows SHA-256 and SHA-512
+   and some HOTP libs support one or both as extensions
+   */
+   if(hash_algo == "SHA-1")
+      m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-1)");
+   else if(hash_algo == "SHA-256")
+      m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   else if(hash_algo == "SHA-512")
+      m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   else
+      throw Invalid_Argument("Unsupported HOTP hash function");
+
+   m_mac->set_key(key, key_len);
+   }
+
+uint32_t HOTP::generate_hotp(uint64_t counter)
+   {
+   m_mac->update_be(counter);
+   const secure_vector<uint8_t> mac = m_mac->final();
+
+   const size_t offset = mac[mac.size()-1] & 0x0F;
+   const uint32_t code = load_be<uint32_t>(mac.data() + offset, 0) & 0x7FFFFFFF;
+   return code % m_digit_mod;
+   }
+
+std::pair<bool,uint64_t> HOTP::verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range)
+   {
+   for(size_t i = 0; i <= resync_range; ++i)
+      {
+      if(generate_hotp(starting_counter + i) == otp)
+         return std::make_pair(true, starting_counter + i + 1);
+      }
+   return std::make_pair(false, starting_counter);
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/misc/hotp/hotp.h b/third_party/botan/src/lib/misc/hotp/hotp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/hotp/hotp.h
@@ -0,0 +1,63 @@
+/*
+* HOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HOTP_H_
+#define BOTAN_HOTP_H_
+
+#include <botan/mac.h>
+
+namespace Botan {
+
+/**
+* HOTP one time passwords (RFC 4226)
+*/
+class BOTAN_PUBLIC_API(2,2) HOTP final
+   {
+   public:
+      /**
+      * @param key the secret key shared between client and server
+      * @param hash_algo the hash algorithm to use, should be SHA-1 or SHA-256
+      * @param digits the number of digits in the OTP (must be 6, 7, or 8)
+      */
+      HOTP(const SymmetricKey& key, const std::string& hash_algo = "SHA-1", size_t digits = 6) :
+         HOTP(key.begin(), key.size(), hash_algo, digits) {}
+
+      /**
+      * @param key the secret key shared between client and server
+      * @param key_len length of key param
+      * @param hash_algo the hash algorithm to use, should be SHA-1 or SHA-256
+      * @param digits the number of digits in the OTP (must be 6, 7, or 8)
+      */
+      HOTP(const uint8_t key[], size_t key_len,
+           const std::string& hash_algo = "SHA-1",
+           size_t digits = 6);
+
+      /**
+      * Generate the HOTP for a particular counter value
+      * @warning if the counter value is repeated the OTP ceases to be one-time
+      */
+      uint32_t generate_hotp(uint64_t counter);
+
+      /**
+      * Check an OTP value using a starting counter and a resync range
+      * @param otp the client provided OTP
+      * @param starting_counter the server's guess as to the current counter state
+      * @param resync_range if 0 then only HOTP(starting_counter) is accepted
+      * If larger than 0, up to resync_range values after HOTP are also checked.
+      * @return (valid,next_counter). If the OTP does not validate, always
+      * returns (false,starting_counter). Otherwise returns (true,next_counter)
+      * where next_counter is at most starting_counter + resync_range + 1
+      */
+      std::pair<bool,uint64_t> verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range = 0);
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      uint32_t m_digit_mod;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/hotp/totp.cpp b/third_party/botan/src/lib/misc/hotp/totp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/hotp/totp.cpp
@@ -0,0 +1,63 @@
+/*
+* TOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/totp.h>
+#include <botan/calendar.h>
+
+namespace Botan {
+
+TOTP::TOTP(const uint8_t key[], size_t key_len,
+           const std::string& hash_algo,
+           size_t digits, size_t time_step)
+   : m_hotp(key, key_len, hash_algo, digits)
+   , m_time_step(time_step)
+   , m_unix_epoch(calendar_point(1970, 1, 1, 0, 0, 0).to_std_timepoint())
+   {
+   /*
+   * Technically any time step except 0 is valid, but 30 is typical
+   * and over 5 minutes seems unlikely.
+   */
+   BOTAN_ARG_CHECK(m_time_step > 0 && m_time_step < 300, "Invalid TOTP time step");
+   }
+
+uint32_t TOTP::generate_totp(std::chrono::system_clock::time_point current_time)
+   {
+   const uint64_t unix_time =
+      std::chrono::duration_cast<std::chrono::seconds>(current_time - m_unix_epoch).count();
+   return this->generate_totp(unix_time);
+   }
+
+uint32_t TOTP::generate_totp(uint64_t unix_time)
+   {
+   return m_hotp.generate_hotp(unix_time / m_time_step);
+   }
+
+bool TOTP::verify_totp(uint32_t otp, std::chrono::system_clock::time_point current_time,
+                       size_t clock_drift_accepted)
+   {
+   const uint64_t unix_time =
+      std::chrono::duration_cast<std::chrono::seconds>(current_time - m_unix_epoch).count();
+   return verify_totp(otp, unix_time, clock_drift_accepted);
+   }
+
+bool TOTP::verify_totp(uint32_t otp, uint64_t unix_time,
+                       size_t clock_drift_accepted)
+   {
+   uint64_t t = unix_time / m_time_step;
+
+   for(size_t i = 0; i <= clock_drift_accepted; ++i)
+      {
+      if(m_hotp.generate_hotp(t-i) == otp)
+         {
+         return true;
+         }
+      }
+
+   return false;
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/hotp/totp.h b/third_party/botan/src/lib/misc/hotp/totp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/hotp/totp.h
@@ -0,0 +1,70 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TOTP_H_
+#define BOTAN_TOTP_H_
+
+#include <botan/hotp.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* TOTP (time based) one time passwords (RFC 6238)
+*/
+class BOTAN_PUBLIC_API(2,2) TOTP final
+   {
+   public:
+      /**
+      * @param key the secret key shared between client and server
+      * @param hash_algo the hash algorithm to use, should be SHA-1, SHA-256 or SHA-512
+      * @param digits the number of digits in the OTP (must be 6, 7, or 8)
+      * @param time_step granularity of OTP in seconds
+      */
+      TOTP(const SymmetricKey& key,
+           const std::string& hash_algo = "SHA-1",
+           size_t digits = 6, size_t time_step = 30) :
+         TOTP(key.begin(), key.size(), hash_algo, digits, time_step) {}
+
+      /**
+      * @param key the secret key shared between client and server
+      * @param key_len length of key
+      * @param hash_algo the hash algorithm to use, should be SHA-1, SHA-256 or SHA-512
+      * @param digits the number of digits in the OTP (must be 6, 7, or 8)
+      * @param time_step granularity of OTP in seconds
+      */
+      TOTP(const uint8_t key[], size_t key_len,
+           const std::string& hash_algo = "SHA-1",
+           size_t digits = 6,
+           size_t time_step = 30);
+
+      /**
+      * Convert the provided time_point to a Unix timestamp and call generate_totp
+      */
+      uint32_t generate_totp(std::chrono::system_clock::time_point time_point);
+
+      /**
+      * Generate the OTP corresponding the the provided "Unix timestamp" (ie
+      * number of seconds since midnight Jan 1, 1970)
+      */
+      uint32_t generate_totp(uint64_t unix_time);
+
+      bool verify_totp(uint32_t otp,
+                       std::chrono::system_clock::time_point time,
+                       size_t clock_drift_accepted = 0);
+
+      bool verify_totp(uint32_t otp, uint64_t unix_time,
+                       size_t clock_drift_accepted = 0);
+
+   private:
+      HOTP m_hotp;
+      size_t m_time_step;
+      std::chrono::system_clock::time_point m_unix_epoch;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp b/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp
@@ -0,0 +1,209 @@
+/*
+* (C) 2011,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/nist_keywrap.h>
+#include <botan/block_cipher.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+std::vector<uint8_t>
+raw_nist_key_wrap(const uint8_t input[],
+                  size_t input_len,
+                  const BlockCipher& bc,
+                  uint64_t ICV)
+   {
+   const size_t n = (input_len + 7) / 8;
+
+   secure_vector<uint8_t> R((n + 1) * 8);
+   secure_vector<uint8_t> A(16);
+
+   store_be(ICV, A.data());
+
+   copy_mem(&R[8], input, input_len);
+
+   for(size_t j = 0; j <= 5; ++j)
+      {
+      for(size_t i = 1; i <= n; ++i)
+         {
+         const uint32_t t = static_cast<uint32_t>((n * j) + i);
+
+         copy_mem(&A[8], &R[8*i], 8);
+
+         bc.encrypt(A.data());
+         copy_mem(&R[8*i], &A[8], 8);
+
+         uint8_t t_buf[4] = { 0 };
+         store_be(t, t_buf);
+         xor_buf(&A[4], t_buf, 4);
+         }
+      }
+
+   copy_mem(R.data(), A.data(), 8);
+
+   return std::vector<uint8_t>(R.begin(), R.end());
+   }
+
+secure_vector<uint8_t>
+raw_nist_key_unwrap(const uint8_t input[],
+                    size_t input_len,
+                    const BlockCipher& bc,
+                    uint64_t& ICV_out)
+   {
+   if(input_len < 16 || input_len % 8 != 0)
+      throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+   const size_t n = (input_len - 8) / 8;
+
+   secure_vector<uint8_t> R(n * 8);
+   secure_vector<uint8_t> A(16);
+
+   for(size_t i = 0; i != 8; ++i)
+      A[i] = input[i];
+
+   copy_mem(R.data(), input + 8, input_len - 8);
+
+   for(size_t j = 0; j <= 5; ++j)
+      {
+      for(size_t i = n; i != 0; --i)
+         {
+         const uint32_t t = static_cast<uint32_t>((5 - j) * n + i);
+
+         uint8_t t_buf[4] = { 0 };
+         store_be(t, t_buf);
+
+         xor_buf(&A[4], t_buf, 4);
+
+         copy_mem(&A[8], &R[8*(i-1)], 8);
+
+         bc.decrypt(A.data());
+
+         copy_mem(&R[8*(i-1)], &A[8], 8);
+         }
+      }
+
+   ICV_out = load_be<uint64_t>(A.data(), 0);
+
+   return R;
+   }
+
+}
+
+std::vector<uint8_t>
+nist_key_wrap(const uint8_t input[],
+              size_t input_len,
+              const BlockCipher& bc)
+   {
+   if(bc.block_size() != 16)
+      throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+   if(input_len % 8 != 0)
+      throw Invalid_Argument("Bad input size for NIST key wrap");
+
+   return raw_nist_key_wrap(input, input_len, bc, 0xA6A6A6A6A6A6A6A6);
+   }
+
+secure_vector<uint8_t>
+nist_key_unwrap(const uint8_t input[],
+                size_t input_len,
+                const BlockCipher& bc)
+   {
+   if(bc.block_size() != 16)
+      throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+   if(input_len < 16 || input_len % 8 != 0)
+      throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+   uint64_t ICV_out = 0;
+
+   secure_vector<uint8_t> R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
+
+   if(ICV_out != 0xA6A6A6A6A6A6A6A6)
+      throw Invalid_Authentication_Tag("NIST key unwrap failed");
+
+   return R;
+   }
+
+std::vector<uint8_t>
+nist_key_wrap_padded(const uint8_t input[],
+                     size_t input_len,
+                     const BlockCipher& bc)
+   {
+   if(bc.block_size() != 16)
+      throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+   const uint64_t ICV = 0xA65959A600000000 | static_cast<uint32_t>(input_len);
+
+   if(input_len <= 8)
+      {
+      /*
+      * Special case for small inputs: if input <= 8 bytes just use ECB
+      */
+      std::vector<uint8_t> block(16);
+      store_be(ICV, block.data());
+      copy_mem(block.data() + 8, input, input_len);
+      bc.encrypt(block);
+      return block;
+      }
+   else
+      {
+      return raw_nist_key_wrap(input, input_len, bc, ICV);
+      }
+   }
+
+secure_vector<uint8_t>
+nist_key_unwrap_padded(const uint8_t input[],
+                       size_t input_len,
+                       const BlockCipher& bc)
+   {
+   if(bc.block_size() != 16)
+      throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+   if(input_len < 16 || input_len % 8 != 0)
+      throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+   uint64_t ICV_out = 0;
+   secure_vector<uint8_t> R;
+
+   if(input_len == 16)
+      {
+      secure_vector<uint8_t> block(input, input + input_len);
+      bc.decrypt(block);
+
+      ICV_out = load_be<uint64_t>(block.data(), 0);
+      R.resize(8);
+      copy_mem(R.data(), block.data() + 8, 8);
+      }
+   else
+      {
+      R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
+      }
+
+   if((ICV_out >> 32) != 0xA65959A6)
+      throw Invalid_Authentication_Tag("NIST key unwrap failed");
+
+   const size_t len = (ICV_out & 0xFFFFFFFF);
+
+   if(R.size() < 8 || len > R.size() || len < R.size() - 8)
+      throw Invalid_Authentication_Tag("NIST key unwrap failed");
+
+   const size_t padding = R.size() - len;
+
+   for(size_t i = 0; i != padding; ++i)
+      {
+      if(R[R.size() - i - 1] != 0)
+         throw Invalid_Authentication_Tag("NIST key unwrap failed");
+      }
+
+   R.resize(R.size() - padding);
+
+   return R;
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h b/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h
@@ -0,0 +1,67 @@
+/*
+* (C) 2011,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NIST_KEY_WRAP_H_
+#define BOTAN_NIST_KEY_WRAP_H_
+
+#include <botan/secmem.h>
+
+namespace Botan {
+
+class BlockCipher;
+
+/**
+* Key wrap. See RFC 3394 and NIST SP800-38F
+* @param input the value to be encrypted
+* @param input_len length of input, must be a multiple of 8
+* @param bc a keyed 128-bit block cipher that will be used to encrypt input
+* @return input encrypted under NIST key wrap algorithm
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_wrap(const uint8_t input[],
+              size_t input_len,
+              const BlockCipher& bc);
+
+/**
+* @param input the value to be decrypted, output of nist_key_wrap
+* @param input_len length of input
+* @param bc a keyed 128-bit block cipher that will be used to decrypt input
+* @return input decrypted under NIST key wrap algorithm
+* Throws an exception if decryption fails.
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_unwrap(const uint8_t input[],
+                size_t input_len,
+                const BlockCipher& bc);
+
+/**
+* KWP (key wrap with padding). See RFC 5649 and NIST SP800-38F
+* @param input the value to be encrypted
+* @param input_len length of input
+* @param bc a keyed 128-bit block cipher that will be used to encrypt input
+* @return input encrypted under NIST key wrap algorithm
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_wrap_padded(const uint8_t input[],
+                     size_t input_len,
+                     const BlockCipher& bc);
+
+/**
+* @param input the value to be decrypted, output of nist_key_wrap
+* @param input_len length of input
+* @param bc a keyed 128-bit block cipher that will be used to decrypt input
+* @return input decrypted under NIST key wrap algorithm
+* Throws an exception if decryption fails.
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_unwrap_padded(const uint8_t input[],
+                       size_t input_len,
+                       const BlockCipher& bc);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp b/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp
@@ -0,0 +1,44 @@
+/*
+* AES Key Wrap (RFC 3394)
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rfc3394.h>
+#include <botan/nist_keywrap.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> rfc3394_keywrap(const secure_vector<uint8_t>& key,
+                                       const SymmetricKey& kek)
+   {
+   BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32,
+                   "Invalid KEK length for NIST key wrap");
+
+   const std::string cipher_name = "AES-" + std::to_string(8*kek.size());
+   std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name));
+   aes->set_key(kek);
+
+   std::vector<uint8_t> wrapped = nist_key_wrap(key.data(), key.size(), *aes);
+   return secure_vector<uint8_t>(wrapped.begin(), wrapped.end());
+   }
+
+secure_vector<uint8_t> rfc3394_keyunwrap(const secure_vector<uint8_t>& key,
+                                         const SymmetricKey& kek)
+   {
+   BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32,
+                   "Invalid KEK length for NIST key wrap");
+
+   BOTAN_ARG_CHECK(key.size() >= 16 && key.size() % 8 == 0,
+                   "Bad input key size for NIST key unwrap");
+
+   const std::string cipher_name = "AES-" + std::to_string(8*kek.size());
+   std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name));
+   aes->set_key(kek);
+
+   return nist_key_unwrap(key.data(), key.size(), *aes);
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/rfc3394/rfc3394.h b/third_party/botan/src/lib/misc/rfc3394/rfc3394.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/rfc3394/rfc3394.h
@@ -0,0 +1,39 @@
+/*
+* AES Key Wrap (RFC 3394)
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC3394_H_
+#define BOTAN_RFC3394_H_
+
+#include <botan/symkey.h>
+
+namespace Botan {
+
+/**
+* Encrypt a key under a key encryption key using the algorithm
+* described in RFC 3394
+*
+* @param key the plaintext key to encrypt
+* @param kek the key encryption key
+* @return key encrypted under kek
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) rfc3394_keywrap(const secure_vector<uint8_t>& key,
+                                                             const SymmetricKey& kek);
+
+/**
+* Decrypt a key under a key encryption key using the algorithm
+* described in RFC 3394
+*
+* @param key the encrypted key to decrypt
+* @param kek the key encryption key
+* @return key decrypted under kek
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) rfc3394_keyunwrap(const secure_vector<uint8_t>& key,
+                                                               const SymmetricKey& kek);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/roughtime/roughtime.cpp b/third_party/botan/src/lib/misc/roughtime/roughtime.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/roughtime/roughtime.cpp
@@ -0,0 +1,466 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/roughtime.h>
+
+#include <botan/base64.h>
+#include <botan/hash.h>
+#include <botan/internal/socket_udp.h>
+#include <botan/pubkey.h>
+#include <botan/rng.h>
+
+#include <cmath>
+#include <map>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+// This exists to work around a LGTM false positive
+static_assert(Roughtime::request_min_size == 1024, "Expected minimum size");
+
+template< bool B, class T = void >
+using enable_if_t = typename std::enable_if<B,T>::type;
+
+template<class T>
+struct is_array : std::false_type {};
+
+template<class T, std::size_t N>
+struct is_array<std::array<T,N>>:std::true_type{};
+
+template<typename T>
+T impl_from_little_endian(const uint8_t* t, const size_t i)
+   {
+   static_assert(sizeof(T) <= sizeof(int64_t), "");
+   return T(static_cast<int64_t>(t[i]) << i * 8) + (i == 0 ? T(0) : impl_from_little_endian<T>(t, i - 1));
+   }
+
+template<typename T>
+T from_little_endian(const uint8_t* t)
+   {
+   return impl_from_little_endian<T>(t, sizeof(T) - 1);
+   }
+
+template<typename T, enable_if_t<is_array<T>::value>* = nullptr>
+T copy(const uint8_t* t)
+   {
+   return typecast_copy<T>(t); //arrays are endianess indepedent, so we do a memcpy
+   }
+
+template<typename T, enable_if_t<!is_array<T>::value>* = nullptr>
+T copy(const uint8_t* t)
+   {
+   return from_little_endian<T>(t); //other types are arithmetic, so we account that roughtime serializes as little endian
+   }
+
+template<typename T>
+std::map<std::string, std::vector<uint8_t>> unpack_roughtime_packet(T bytes)
+   {
+   if(bytes.size() < 8)
+      { throw Roughtime::Roughtime_Error("Map length is under minimum of 8 bytes"); }
+   const auto buf = bytes.data();
+   const uint32_t num_tags = buf[0];
+   const uint32_t start_content = num_tags * 8;
+   if(start_content > bytes.size())
+      { throw Roughtime::Roughtime_Error("Map length too small to contain all tags"); }
+   uint32_t start = start_content;
+   std::map<std::string, std::vector<uint8_t>> tags;
+   for(uint32_t i=0; i<num_tags; ++i)
+      {
+      const size_t end = ((i+1) == num_tags) ? bytes.size() : start_content + from_little_endian<uint32_t>(buf + 4 + i*4);
+      if(end > bytes.size())
+         { throw Roughtime::Roughtime_Error("Tag end index out of bounds"); }
+      if(end < start)
+         { throw Roughtime::Roughtime_Error("Tag offset must be more than previous tag offset"); }
+      const char* label_ptr = cast_uint8_ptr_to_char(buf) + (num_tags+i)*4;
+      const char label[] = {label_ptr[0], label_ptr[1], label_ptr[2], label_ptr[3], 0};
+      auto ret = tags.emplace(label, std::vector<uint8_t>(buf+start, buf+end));
+      if(!ret.second)
+         { throw Roughtime::Roughtime_Error(std::string("Map has duplicated tag: ") + label); }
+      start = static_cast<uint32_t>(end);
+      }
+   return tags;
+   }
+
+template<typename T>
+T get(const std::map<std::string, std::vector<uint8_t>>& map, const std::string& label)
+   {
+   const auto& tag = map.find(label);
+   if(tag == map.end())
+      { throw Roughtime::Roughtime_Error("Tag " + label + " not found"); }
+   if(tag->second.size() != sizeof(T))
+      { throw Roughtime::Roughtime_Error("Tag " + label + " has unexpected size"); }
+   return copy<T>(tag->second.data());
+   }
+
+const std::vector<uint8_t>& get_v(const std::map<std::string, std::vector<uint8_t>>& map, const std::string& label)
+   {
+   const auto& tag = map.find(label);
+   if(tag == map.end())
+      { throw Roughtime::Roughtime_Error("Tag " + label + " not found"); }
+   return tag->second;
+   }
+
+bool verify_signature(const std::array<uint8_t, 32>& pk, const std::vector<uint8_t>& payload,
+                      const std::array<uint8_t, 64>& signature)
+   {
+   const char context[] = "RoughTime v1 response signature";
+   Ed25519_PublicKey key(std::vector<uint8_t>(pk.data(), pk.data()+pk.size()));
+   PK_Verifier verifier(key, "Pure");
+   verifier.update(cast_char_ptr_to_uint8(context), sizeof(context)); //add context including \0
+   verifier.update(payload);
+   return verifier.check_signature(signature.data(), signature.size());
+   }
+
+std::array<uint8_t, 64> hashLeaf(const std::array<uint8_t, 64>& leaf)
+   {
+   std::array<uint8_t, 64> ret;
+   std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-512"));
+   hash->update(0);
+   hash->update(leaf.data(), leaf.size());
+   hash->final(ret.data());
+   return ret;
+   }
+
+void hashNode(std::array<uint8_t, 64>& hash, const std::array<uint8_t, 64>& node, bool reverse)
+   {
+   std::unique_ptr<HashFunction> h(HashFunction::create_or_throw("SHA-512"));
+   h->update(1);
+   if(reverse)
+      {
+      h->update(node.data(), node.size());
+      h->update(hash.data(), hash.size());
+      }
+   else
+      {
+      h->update(hash.data(), hash.size());
+      h->update(node.data(), node.size());
+      }
+   h->final(hash.data());
+   }
+
+template<size_t N, typename T>
+std::array<uint8_t, N> vector_to_array(std::vector<uint8_t,T> vec)
+   {
+   if(vec.size() != N)
+      { throw std::logic_error("Invalid vector size"); }
+   return typecast_copy<std::array<uint8_t, N>>(vec.data());
+   }
+}
+
+namespace Roughtime {
+
+Nonce::Nonce(const std::vector<uint8_t>& nonce)
+   {
+   if(nonce.size() != 64)
+      { throw Invalid_Argument("Nonce lenght must be 64"); }
+   m_nonce = typecast_copy<std::array<uint8_t, 64>>(nonce.data());
+   }
+Nonce::Nonce(RandomNumberGenerator& rng)
+   {
+   rng.randomize(m_nonce.data(), m_nonce.size());
+   }
+
+std::array<uint8_t, request_min_size> encode_request(const Nonce& nonce)
+   {
+   std::array<uint8_t, request_min_size> buf = {{2, 0, 0, 0, 64, 0, 0, 0, 'N', 'O', 'N', 'C', 'P', 'A', 'D', 0xff}};
+   std::memcpy(buf.data() + 16, nonce.get_nonce().data(), nonce.get_nonce().size());
+   std::memset(buf.data() + 16 + nonce.get_nonce().size(), 0, buf.size() - 16 - nonce.get_nonce().size());
+   return buf;
+   }
+
+Response Response::from_bits(const std::vector<uint8_t>& response,
+                             const Nonce& nonce)
+   {
+   const auto response_v = unpack_roughtime_packet(response);
+   const auto cert = unpack_roughtime_packet(get_v(response_v, "CERT"));
+   const auto cert_dele = get<std::array<uint8_t, 72>>(cert, "DELE");
+   const auto cert_sig =  get<std::array<uint8_t, 64>>(cert, "SIG");
+   const auto cert_dele_v = unpack_roughtime_packet(cert_dele);
+   const auto srep = get_v(response_v, "SREP");
+   const auto srep_v = unpack_roughtime_packet(srep);
+
+   const auto cert_dele_pubk = get<std::array<uint8_t, 32>>(cert_dele_v, "PUBK");
+   const auto sig  = get<std::array<uint8_t, 64>>(response_v, "SIG");
+   if(!verify_signature(cert_dele_pubk, srep, sig))
+      { throw Roughtime_Error("Response signature invalid"); }
+
+   const auto indx = get<uint32_t>(response_v, "INDX");
+   const auto path = get_v(response_v, "PATH");
+   const auto srep_root = get<std::array<uint8_t, 64>>(srep_v, "ROOT");
+   const auto size = path.size();
+   const auto levels = size/64;
+
+   if(size % 64)
+      { throw Roughtime_Error("Merkle tree path size must be multiple of 64 bytes"); }
+   if(indx >= (1u << levels))
+      { throw Roughtime_Error("Merkle tree path is too short"); }
+
+   auto hash = hashLeaf(nonce.get_nonce());
+   auto index = indx;
+   auto level = 0u;
+   while(level < levels)
+      {
+      hashNode(hash, typecast_copy<std::array<uint8_t, 64>>(path.data() + level*64), index&1);
+      ++level;
+      index>>=1;
+      }
+
+   if(srep_root != hash)
+      { throw Roughtime_Error("Nonce verification failed"); }
+
+   const auto cert_dele_maxt = sys_microseconds64(get<microseconds64>(cert_dele_v, "MAXT"));
+   const auto cert_dele_mint = sys_microseconds64(get<microseconds64>(cert_dele_v, "MINT"));
+   const auto srep_midp = sys_microseconds64(get<microseconds64>(srep_v, "MIDP"));
+   const auto srep_radi = get<microseconds32>(srep_v, "RADI");
+   if(srep_midp < cert_dele_mint)
+      { throw Roughtime_Error("Midpoint earlier than delegation start"); }
+   if(srep_midp > cert_dele_maxt)
+      { throw Roughtime_Error("Midpoint later than delegation end"); }
+   return {cert_dele, cert_sig, srep_midp, srep_radi};
+   }
+
+bool Response::validate(const Ed25519_PublicKey& pk) const
+   {
+   const char context[] = "RoughTime v1 delegation signature--";
+   PK_Verifier verifier(pk, "Pure");
+   verifier.update(cast_char_ptr_to_uint8(context), sizeof(context)); //add context including \0
+   verifier.update(m_cert_dele.data(), m_cert_dele.size());
+   return verifier.check_signature(m_cert_sig.data(), m_cert_sig.size());
+   }
+
+Nonce nonce_from_blind(const std::vector<uint8_t>& previous_response,
+                       const Nonce& blind)
+   {
+   std::array<uint8_t, 64> ret;
+   const auto blind_arr = blind.get_nonce();
+   std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create_or_throw("SHA-512"));
+   hash->update(previous_response);
+   hash->update(hash->final());
+   hash->update(blind_arr.data(), blind_arr.size());
+   hash->final(ret.data());
+
+   return ret;
+   }
+
+Chain::Chain(const std::string& str)
+   {
+   std::stringstream ss(str);
+   const std::string ERROR_MESSAGE = "Line does not have 4 space separated fields";
+   for(std::string s; std::getline(ss, s);)
+      {
+      size_t start = 0, end = 0;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto publicKeyType = s.substr(start, end-start);
+      if(publicKeyType != "ed25519")
+         { throw Not_Implemented("Only ed25519 publicKeyType is implemented"); }
+
+      start = end + 1;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto serverPublicKey = Botan::Ed25519_PublicKey(Botan::base64_decode(s.substr(start, end-start)));
+
+      start = end + 1;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      if((end - start) != 88)
+         {
+         throw Decoding_Error("Nonce has invalid length");
+         }
+      const auto vec = Botan::base64_decode(s.substr(start, end-start));
+      const auto nonceOrBlind = Nonce(vector_to_array<64>(Botan::base64_decode(s.substr(start, end-start))));
+
+      start = end + 1;
+      end = s.find(' ', start);
+      if(end != std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto response = Botan::unlock(Botan::base64_decode(s.substr(start)));
+
+      m_links.push_back({response, serverPublicKey, nonceOrBlind});
+      }
+   }
+std::vector<Response> Chain::responses() const
+   {
+   std::vector<Response> responses;
+   for(unsigned i = 0; i < m_links.size(); ++i)
+      {
+      const auto& l = m_links[i];
+      const auto nonce = i ? nonce_from_blind(m_links[i-1].response(), l.nonce_or_blind()) : l.nonce_or_blind();
+      const auto response = Response::from_bits(l.response(), nonce);
+      if(!response.validate(l.public_key()))
+         { throw Roughtime_Error("Invalid signature or public key"); }
+      responses.push_back(response);
+      }
+   return responses;
+   }
+Nonce Chain::next_nonce(const Nonce& blind) const
+   {
+   return m_links.empty()
+          ? blind
+          : nonce_from_blind(m_links.back().response(), blind);
+   }
+void Chain::append(const Link& new_link, size_t max_chain_size)
+   {
+   if(max_chain_size <= 0)
+      { throw Invalid_Argument("Max chain size must be positive"); }
+
+   while(m_links.size() >= max_chain_size)
+      {
+      if(m_links.size() == 1)
+         {
+         auto new_link_updated = new_link;
+         new_link_updated.nonce_or_blind() =
+            nonce_from_blind(m_links[0].response(), new_link.nonce_or_blind()); //we need to convert blind to nonce
+         m_links.clear();
+         m_links.push_back(new_link_updated);
+         return;
+         }
+      if(m_links.size() >= 2)
+         {
+         m_links[1].nonce_or_blind() =
+            nonce_from_blind(m_links[0].response(), m_links[1].nonce_or_blind()); //we need to convert blind to nonce
+         }
+      m_links.erase(m_links.begin());
+      }
+   m_links.push_back(new_link);
+   }
+
+std::string Chain::to_string() const
+   {
+   std::string s;
+   s.reserve((7+1 + 88+1 + 44+1 + 480)*m_links.size());
+   for(const auto& link : m_links)
+      {
+      s += "ed25519";
+      s += ' ';
+      s += Botan::base64_encode(link.public_key().get_public_key());
+      s += ' ';
+      s += Botan::base64_encode(link.nonce_or_blind().get_nonce().data(), link.nonce_or_blind().get_nonce().size());
+      s += ' ';
+      s += Botan::base64_encode(link.response());
+      s += '\n';
+      }
+   return s;
+   }
+
+std::vector<uint8_t> online_request(const std::string& uri,
+                                    const Nonce& nonce,
+                                    std::chrono::milliseconds timeout)
+   {
+   const std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
+   auto socket = OS::open_socket_udp(uri, timeout);
+   if(!socket)
+      { throw Not_Implemented("No socket support enabled in build"); }
+
+   const auto encoded = encode_request(nonce);
+   socket->write(encoded.data(), encoded.size());
+
+   if(std::chrono::system_clock::now() - start_time > timeout)
+      { throw System_Error("Timeout during socket write"); }
+
+   std::vector<uint8_t> buffer;
+   buffer.resize(360+64*10+1); //response basic size is 360 bytes + 64 bytes for each level of merkle tree
+                               //add one additional byte to be able to differentiate if datagram got truncated
+   const auto n = socket->read(buffer.data(), buffer.size());
+
+   if(!n || std::chrono::system_clock::now() - start_time > timeout)
+      { throw System_Error("Timeout waiting for response"); }
+
+   if(n == buffer.size())
+      { throw System_Error("Buffer too small"); }
+
+   buffer.resize(n);
+   return buffer;
+   }
+
+std::vector<Server_Information> servers_from_str(const std::string& str)
+   {
+   std::vector<Server_Information> servers;
+   std::stringstream ss(str);
+   const std::string ERROR_MESSAGE = "Line does not have at least 5 space separated fields";
+   for(std::string s; std::getline(ss, s);)
+      {
+      size_t start = 0, end = 0;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto name = s.substr(start, end-start);
+
+      start = end + 1;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto publicKeyType = s.substr(start, end-start);
+      if(publicKeyType != "ed25519")
+         { throw Not_Implemented("Only ed25519 publicKeyType is implemented"); }
+
+      start = end + 1;
+      end = s.find(' ', start);
+
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto publicKeyBase64 = s.substr(start, end-start);
+      const auto publicKey = Botan::Ed25519_PublicKey(Botan::base64_decode(publicKeyBase64));
+
+      start = end + 1;
+      end = s.find(' ', start);
+      if(end == std::string::npos)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+      const auto protocol = s.substr(start, end-start);
+      if(protocol != "udp")
+         { throw Not_Implemented("Only UDP protocol is implemented"); }
+
+      const auto addresses = [&]()
+         {
+         std::vector<std::string> addr;
+         for(;;)
+            {
+            start = end + 1;
+            end = s.find(' ', start);
+            const auto address = s.substr(start, (end == std::string::npos) ? std::string::npos : end-start);
+            if(address.empty())
+               { return addr; }
+            addr.push_back(address);
+            if(end == std::string::npos)
+               { return addr; }
+            }
+         }
+      ();
+      if(addresses.size() == 0)
+         {
+         throw Decoding_Error(ERROR_MESSAGE);
+         }
+
+      servers.push_back({name, publicKey, std::move(addresses)});
+      }
+   return servers;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/misc/roughtime/roughtime.h b/third_party/botan/src/lib/misc/roughtime/roughtime.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/roughtime/roughtime.h
@@ -0,0 +1,167 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ROUGHTIME_H_
+#define BOTAN_ROUGHTIME_H_
+
+#include <array>
+#include <chrono>
+#include <vector>
+
+#include <botan/ed25519.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace Roughtime {
+
+const unsigned request_min_size = 1024;
+
+class BOTAN_PUBLIC_API(2, 13) Roughtime_Error final : public Decoding_Error
+   {
+   public:
+      explicit Roughtime_Error(const std::string& s) : Decoding_Error("Roughtime " + s) {}
+      ErrorType error_type() const noexcept override { return ErrorType::RoughtimeError; }
+   };
+
+class BOTAN_PUBLIC_API(2, 13) Nonce final
+   {
+   public:
+      Nonce() = default;
+      Nonce(const std::vector<uint8_t>& nonce);
+      Nonce(RandomNumberGenerator& rng);
+      Nonce(const std::array<uint8_t, 64>& nonce)
+         {
+         m_nonce = nonce;
+         }
+      bool operator==(const Nonce& rhs) const { return m_nonce == rhs.m_nonce; }
+      const std::array<uint8_t, 64>& get_nonce() const { return m_nonce; }
+   private:
+      std::array<uint8_t, 64> m_nonce;
+   };
+
+
+/**
+* An Roughtime request.
+*/
+BOTAN_PUBLIC_API(2, 13)
+std::array<uint8_t, request_min_size> encode_request(const Nonce& nonce);
+
+/**
+* An Roughtime response.
+*/
+class BOTAN_PUBLIC_API(2, 13) Response final
+   {
+   public:
+      using microseconds32 = std::chrono::duration<uint32_t, std::micro>;
+      using microseconds64 = std::chrono::duration<uint64_t, std::micro>;
+      using sys_microseconds64 = std::chrono::time_point<std::chrono::system_clock, microseconds64>;
+
+      static Response from_bits(const std::vector<uint8_t>& response, const Nonce& nonce);
+
+      bool validate(const Ed25519_PublicKey& pk) const;
+
+      sys_microseconds64 utc_midpoint() const { return m_utc_midpoint; }
+
+      microseconds32 utc_radius() const { return m_utc_radius; }
+   private:
+      Response(const std::array<uint8_t, 72>& dele,
+               const std::array<uint8_t, 64>& sig,
+               sys_microseconds64 utc_midp,
+               microseconds32 utc_radius)
+         : m_cert_dele(dele)
+         , m_cert_sig(sig)
+         , m_utc_midpoint {utc_midp}
+         , m_utc_radius {utc_radius}
+         {}
+      const std::array<uint8_t, 72> m_cert_dele;
+      const std::array<uint8_t, 64> m_cert_sig;
+      const sys_microseconds64 m_utc_midpoint;
+      const microseconds32 m_utc_radius;
+   };
+
+class BOTAN_PUBLIC_API(2, 13) Link final
+   {
+   public:
+      Link(const std::vector<uint8_t>& response,
+           const Ed25519_PublicKey& public_key,
+           const Nonce& nonce_or_blind)
+         : m_response{response}
+         , m_public_key{public_key}
+         , m_nonce_or_blind{nonce_or_blind}
+         {}
+      const std::vector<uint8_t>& response() const { return m_response; }
+      const Ed25519_PublicKey& public_key() const { return m_public_key; }
+      const Nonce& nonce_or_blind() const { return m_nonce_or_blind; }
+      Nonce& nonce_or_blind() { return m_nonce_or_blind; }
+
+   private:
+      std::vector<uint8_t> m_response;
+      Ed25519_PublicKey m_public_key;
+      Nonce m_nonce_or_blind;
+   };
+
+class BOTAN_PUBLIC_API(2, 13) Chain final
+   {
+   public:
+      Chain() = default; //empty
+      Chain(const std::string& str);
+      const std::vector<Link>& links() const { return m_links; }
+      std::vector<Response> responses() const;
+      Nonce next_nonce(const Nonce& blind) const;
+      void append(const Link& new_link, size_t max_chain_size);
+      std::string to_string() const;
+   private:
+      std::vector<Link> m_links;
+   };
+
+/**
+*/
+BOTAN_PUBLIC_API(2, 13)
+Nonce nonce_from_blind(const std::vector<uint8_t>& previous_response,
+                       const Nonce& blind);
+
+/**
+* Makes an online Roughtime request via UDP and returns the Roughtime response.
+* @param url Roughtime server UDP endpoint (host:port)
+* @param nonce the nonce to send to the server
+* @param timeout a timeout on the UDP request
+* @return Roughtime response
+*/
+BOTAN_PUBLIC_API(2, 13)
+std::vector<uint8_t> online_request(const std::string& url,
+                                    const Nonce& nonce,
+                                    std::chrono::milliseconds timeout = std::chrono::seconds(3));
+
+struct BOTAN_PUBLIC_API(2, 13) Server_Information final
+   {
+public:
+   Server_Information(const std::string& name,
+                      const Botan::Ed25519_PublicKey& public_key,
+                      const std::vector<std::string>& addresses)
+      : m_name { name }
+      , m_public_key { public_key }
+      , m_addresses { addresses }
+      {}
+   const std::string& name() const {return m_name;}
+   const Botan::Ed25519_PublicKey& public_key() const {return m_public_key;}
+   const std::vector<std::string>& addresses() const {return m_addresses;}
+
+private:
+   std::string m_name;
+   Botan::Ed25519_PublicKey m_public_key;
+   std::vector<std::string> m_addresses;
+   };
+
+BOTAN_PUBLIC_API(2, 13)
+std::vector<Server_Information> servers_from_str(const std::string& str);
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/srp6/srp6.cpp b/third_party/botan/src/lib/misc/srp6/srp6.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/srp6/srp6.cpp
@@ -0,0 +1,193 @@
+/*
+* SRP-6a (RFC 5054 compatatible)
+* (C) 2011,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/srp6.h>
+#include <botan/hash.h>
+#include <botan/dl_group.h>
+#include <botan/numthry.h>
+
+namespace Botan {
+
+namespace {
+
+BigInt hash_seq(const std::string& hash_id,
+                size_t pad_to,
+                const BigInt& in1,
+                const BigInt& in2)
+   {
+   std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_id));
+
+   hash_fn->update(BigInt::encode_1363(in1, pad_to));
+   hash_fn->update(BigInt::encode_1363(in2, pad_to));
+
+   return BigInt::decode(hash_fn->final());
+   }
+
+BigInt compute_x(const std::string& hash_id,
+                 const std::string& identifier,
+                 const std::string& password,
+                 const std::vector<uint8_t>& salt)
+   {
+   std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_id));
+
+   hash_fn->update(identifier);
+   hash_fn->update(":");
+   hash_fn->update(password);
+
+   secure_vector<uint8_t> inner_h = hash_fn->final();
+
+   hash_fn->update(salt);
+   hash_fn->update(inner_h);
+
+   secure_vector<uint8_t> outer_h = hash_fn->final();
+
+   return BigInt::decode(outer_h);
+   }
+
+}
+
+std::string srp6_group_identifier(const BigInt& N, const BigInt& g)
+   {
+   /*
+   This function assumes that only one 'standard' SRP parameter set has
+   been defined for a particular bitsize. As of this writing that is the case.
+   */
+   try
+      {
+      const std::string group_name = "modp/srp/" + std::to_string(N.bits());
+
+      DL_Group group(group_name);
+
+      if(group.get_p() == N && group.get_g() == g)
+         return group_name;
+      }
+   catch(...)
+      {
+      }
+
+   // If we didn't return, the group was unknown or did not match
+   throw Invalid_Argument("Invalid or unknown SRP group parameters");
+   }
+
+std::pair<BigInt, SymmetricKey>
+srp6_client_agree(const std::string& identifier,
+                  const std::string& password,
+                  const std::string& group_id,
+                  const std::string& hash_id,
+                  const std::vector<uint8_t>& salt,
+                  const BigInt& B,
+                  RandomNumberGenerator& rng)
+   {
+   DL_Group group(group_id);
+   const size_t a_bits = group.exponent_bits();
+
+   return srp6_client_agree(identifier, password, group, hash_id, salt, B, a_bits, rng);
+   }
+
+std::pair<BigInt, SymmetricKey>
+srp6_client_agree(const std::string& identifier,
+                  const std::string& password,
+                  const DL_Group& group,
+                  const std::string& hash_id,
+                  const std::vector<uint8_t>& salt,
+                  const BigInt& B,
+                  const size_t a_bits,
+                  RandomNumberGenerator& rng)
+   {
+   const BigInt& g = group.get_g();
+   const BigInt& p = group.get_p();
+
+   const size_t p_bytes = group.p_bytes();
+
+   if(B <= 0 || B >= p)
+      throw Decoding_Error("Invalid SRP parameter from server");
+
+   const BigInt k = hash_seq(hash_id, p_bytes, p, g);
+
+   const BigInt a(rng, a_bits);
+
+   const BigInt A = group.power_g_p(a, a_bits);
+
+   const BigInt u = hash_seq(hash_id, p_bytes, A, B);
+
+   const BigInt x = compute_x(hash_id, identifier, password, salt);
+
+   const BigInt S = power_mod(group.mod_p(B - (k * power_mod(g, x, p))),
+                              group.mod_p(a + (u * x)), p);
+
+   const SymmetricKey Sk(BigInt::encode_1363(S, p_bytes));
+
+   return std::make_pair(A, Sk);
+   }
+
+BigInt generate_srp6_verifier(const std::string& identifier,
+                              const std::string& password,
+                              const std::vector<uint8_t>& salt,
+                              const std::string& group_id,
+                              const std::string& hash_id)
+   {
+   DL_Group group(group_id);
+   return generate_srp6_verifier(identifier, password, salt, group, hash_id);
+   }
+
+BigInt generate_srp6_verifier(const std::string& identifier,
+                              const std::string& password,
+                              const std::vector<uint8_t>& salt,
+                              const DL_Group& group,
+                              const std::string& hash_id)
+   {
+   const BigInt x = compute_x(hash_id, identifier, password, salt);
+   // FIXME: x should be size of hash fn so avoid computing x.bits() here
+   return group.power_g_p(x, x.bits());
+   }
+
+BigInt SRP6_Server_Session::step1(const BigInt& v,
+                                  const std::string& group_id,
+                                  const std::string& hash_id,
+                                  RandomNumberGenerator& rng)
+   {
+   DL_Group group(group_id);
+   const size_t b_bits = group.exponent_bits();
+
+   return this->step1(v, group, hash_id, b_bits, rng);
+   }
+
+BigInt SRP6_Server_Session::step1(const BigInt& v,
+                                  const DL_Group& group,
+                                  const std::string& hash_id,
+                                  size_t b_bits,
+                                  RandomNumberGenerator& rng)
+   {
+   const BigInt& g = group.get_g();
+   const BigInt& p = group.get_p();
+
+   m_p_bytes = p.bytes();
+   m_v = v;
+   m_b = BigInt(rng, b_bits);
+   m_p = p;
+   m_hash_id = hash_id;
+
+   const BigInt k = hash_seq(hash_id, m_p_bytes, p, g);
+
+   m_B = group.mod_p(v*k + group.power_g_p(m_b, b_bits));
+
+   return m_B;
+   }
+
+SymmetricKey SRP6_Server_Session::step2(const BigInt& A)
+   {
+   if(A <= 0 || A >= m_p)
+      throw Decoding_Error("Invalid SRP parameter from client");
+
+   const BigInt u = hash_seq(m_hash_id, m_p_bytes, A, m_B);
+
+   const BigInt S = power_mod(A * power_mod(m_v, u, m_p), m_b, m_p);
+
+   return BigInt::encode_1363(S, m_p_bytes);
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/srp6/srp6.h b/third_party/botan/src/lib/misc/srp6/srp6.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/srp6/srp6.h
@@ -0,0 +1,155 @@
+/*
+* SRP-6a (RFC 5054 compatatible)
+* (C) 2011,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC5054_SRP6_H_
+#define BOTAN_RFC5054_SRP6_H_
+
+#include <botan/bigint.h>
+#include <botan/symkey.h>
+#include <string>
+
+namespace Botan {
+
+class DL_Group;
+class RandomNumberGenerator;
+
+/**
+* SRP6a Client side
+* @param username the username we are attempting login for
+* @param password the password we are attempting to use
+* @param group_id specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+* @param salt is the salt value sent by the server
+* @param B is the server's public value
+* @param rng is a random number generator
+*
+* @return (A,K) the client public key and the shared secret key
+*/
+std::pair<BigInt,SymmetricKey>
+BOTAN_PUBLIC_API(2,0) srp6_client_agree(const std::string& username,
+                            const std::string& password,
+                            const std::string& group_id,
+                            const std::string& hash_id,
+                            const std::vector<uint8_t>& salt,
+                            const BigInt& B,
+                            RandomNumberGenerator& rng);
+
+
+/**
+* SRP6a Client side
+* @param username the username we are attempting login for
+* @param password the password we are attempting to use
+* @param group specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+* @param salt is the salt value sent by the server
+* @param B is the server's public value
+* @param a_bits size of secret exponent in bits
+* @param rng is a random number generator
+*
+* @return (A,K) the client public key and the shared secret key
+*/
+std::pair<BigInt,SymmetricKey> BOTAN_PUBLIC_API(2,11)
+   srp6_client_agree(const std::string& username,
+                     const std::string& password,
+                     const DL_Group& group,
+                     const std::string& hash_id,
+                     const std::vector<uint8_t>& salt,
+                     const BigInt& B,
+                     size_t a_bits,
+                     RandomNumberGenerator& rng);
+
+/**
+* Generate a new SRP-6 verifier
+* @param identifier a username or other client identifier
+* @param password the secret used to authenticate user
+* @param salt a randomly chosen value, at least 128 bits long
+* @param group_id specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+*/
+BigInt BOTAN_PUBLIC_API(2,0)
+   generate_srp6_verifier(const std::string& identifier,
+                          const std::string& password,
+                          const std::vector<uint8_t>& salt,
+                          const std::string& group_id,
+                          const std::string& hash_id);
+
+/**
+* Generate a new SRP-6 verifier
+* @param identifier a username or other client identifier
+* @param password the secret used to authenticate user
+* @param salt a randomly chosen value, at least 128 bits long
+* @param group specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+*/
+BigInt BOTAN_PUBLIC_API(2,11)
+   generate_srp6_verifier(const std::string& identifier,
+                          const std::string& password,
+                          const std::vector<uint8_t>& salt,
+                          const DL_Group& group,
+                          const std::string& hash_id);
+
+/**
+* Return the group id for this SRP param set, or else thrown an
+* exception
+* @param N the group modulus
+* @param g the group generator
+* @return group identifier
+*/
+std::string BOTAN_PUBLIC_API(2,0) srp6_group_identifier(const BigInt& N, const BigInt& g);
+
+/**
+* Represents a SRP-6a server session
+*/
+class BOTAN_PUBLIC_API(2,0) SRP6_Server_Session final
+   {
+   public:
+      /**
+      * Server side step 1
+      * @param v the verification value saved from client registration
+      * @param group_id the SRP group id
+      * @param hash_id the SRP hash in use
+      * @param rng a random number generator
+      * @return SRP-6 B value
+      */
+      BigInt step1(const BigInt& v,
+                   const std::string& group_id,
+                   const std::string& hash_id,
+                   RandomNumberGenerator& rng);
+
+      /**
+      * Server side step 1
+      * This version of step1 added in 2.11
+      *
+      * @param v the verification value saved from client registration
+      * @param group the SRP group
+      * @param hash_id the SRP hash in use
+      * @param rng a random number generator
+      * @param b_bits size of secret exponent in bits
+      * @return SRP-6 B value
+      */
+      BigInt step1(const BigInt& v,
+                   const DL_Group& group,
+                   const std::string& hash_id,
+                   const size_t b_bits,
+                   RandomNumberGenerator& rng);
+
+      /**
+      * Server side step 2
+      * @param A the client's value
+      * @return shared symmetric key
+      */
+      SymmetricKey step2(const BigInt& A);
+
+   private:
+      std::string m_hash_id;
+      BigInt m_B, m_b, m_v, m_S, m_p;
+      size_t m_p_bytes = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/misc/tss/tss.cpp b/third_party/botan/src/lib/misc/tss/tss.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/tss/tss.cpp
@@ -0,0 +1,333 @@
+/*
+* RTSS (threshold secret sharing)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tss.h>
+#include <botan/rng.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+namespace {
+
+const size_t RTSS_HEADER_SIZE = 20;
+
+/**
+Table for GF(2^8) arithmetic (exponentials)
+*/
+const uint8_t RTSS_EXP[256] = {
+0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72,
+0x96, 0xA1, 0xF8, 0x13, 0x35, 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73,
+0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, 0xE5,
+0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70,
+0x90, 0xAB, 0xE6, 0x31, 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44,
+0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, 0x4C, 0xD4,
+0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18,
+0x28, 0x78, 0x88, 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F,
+0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, 0xB5, 0xC4, 0x57,
+0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6,
+0x61, 0xA3, 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F,
+0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, 0xFB, 0x16, 0x3A, 0x4E,
+0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F,
+0x41, 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED,
+0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, 0x9F, 0xBA, 0xD5, 0x64, 0xAC,
+0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80,
+0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F,
+0xB1, 0xC8, 0x43, 0xC5, 0x54, 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4,
+0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, 0x45,
+0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42,
+0xC6, 0x51, 0xF3, 0x0E, 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D,
+0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, 0x39, 0x4B,
+0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7,
+0x52, 0xF6, 0x01 };
+
+/**
+Table for GF(2^8) arithmetic (logarithms)
+*/
+const uint8_t RTSS_LOG[] = {
+0x90, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B,
+0x68, 0x33, 0xEE, 0xDF, 0x03, 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D,
+0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, 0x7D,
+0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72,
+0x9A, 0xC9, 0x09, 0x78, 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1,
+0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, 0x96, 0x8F,
+0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40,
+0x46, 0x83, 0x38, 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62,
+0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, 0x7E, 0x6E, 0x48,
+0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85,
+0x3D, 0xBA, 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E,
+0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, 0xAF, 0x58, 0xA8, 0x50,
+0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD,
+0xE8, 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB,
+0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, 0x7F, 0x0C, 0xF6, 0x6F, 0x17,
+0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7,
+0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1,
+0x6C, 0xAA, 0x55, 0x29, 0x9D, 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE,
+0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, 0x53,
+0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D,
+0x56, 0xF2, 0xD3, 0xAB, 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E,
+0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, 0x67, 0x4A,
+0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0,
+0xF7, 0x70, 0x07 };
+
+uint8_t gfp_mul(uint8_t x, uint8_t y)
+   {
+   if(x == 0 || y == 0)
+      return 0;
+   return RTSS_EXP[(RTSS_LOG[x] + RTSS_LOG[y]) % 255];
+   }
+
+uint8_t rtss_hash_id(const std::string& hash_name)
+   {
+   if(hash_name == "None")
+      return 0;
+   else if(hash_name == "SHA-160" || hash_name == "SHA-1" || hash_name == "SHA1")
+      return 1;
+   else if(hash_name == "SHA-256")
+      return 2;
+   else
+      throw Invalid_Argument("RTSS only supports SHA-1 and SHA-256");
+   }
+
+std::unique_ptr<HashFunction> get_rtss_hash_by_id(uint8_t id)
+   {
+   if(id == 0)
+      return std::unique_ptr<HashFunction>();
+   if(id == 1)
+      return HashFunction::create_or_throw("SHA-1");
+   else if(id == 2)
+      return HashFunction::create_or_throw("SHA-256");
+   else
+      throw Decoding_Error("Unknown RTSS hash identifier");
+   }
+
+}
+
+RTSS_Share::RTSS_Share(const std::string& hex_input)
+   {
+   m_contents = hex_decode_locked(hex_input);
+   }
+
+RTSS_Share::RTSS_Share(const uint8_t bin[], size_t len)
+   {
+   m_contents.assign(bin, bin + len);
+   }
+
+uint8_t RTSS_Share::share_id() const
+   {
+   if(!initialized())
+      throw Invalid_State("RTSS_Share::share_id not initialized");
+
+   if(m_contents.size() < RTSS_HEADER_SIZE + 1)
+      throw Decoding_Error("RTSS_Share::share_id invalid share data");
+
+   return m_contents[20];
+   }
+
+std::string RTSS_Share::to_string() const
+   {
+   return hex_encode(m_contents.data(), m_contents.size());
+   }
+
+std::vector<RTSS_Share>
+RTSS_Share::split(uint8_t M, uint8_t N,
+                  const uint8_t S[], uint16_t S_len,
+                  const uint8_t identifier[16],
+                  RandomNumberGenerator& rng)
+   {
+   return RTSS_Share::split(M, N, S, S_len,
+                            std::vector<uint8_t>(identifier, identifier + 16),
+                            "SHA-256",
+                            rng);
+   }
+
+std::vector<RTSS_Share>
+RTSS_Share::split(uint8_t M, uint8_t N,
+                  const uint8_t S[], uint16_t S_len,
+                  const std::vector<uint8_t>& identifier,
+                  const std::string& hash_fn,
+                  RandomNumberGenerator& rng)
+   {
+   if(M <= 1 || N <= 1 || M > N || N >= 255)
+      throw Invalid_Argument("RTSS_Share::split: Invalid N or M");
+
+   if(identifier.size() > 16)
+      throw Invalid_Argument("RTSS_Share::split Invalid identifier size");
+
+   const uint8_t hash_id = rtss_hash_id(hash_fn);
+
+   std::unique_ptr<HashFunction> hash;
+   if(hash_id > 0)
+      hash = HashFunction::create_or_throw(hash_fn);
+
+   // secret = S || H(S)
+   secure_vector<uint8_t> secret(S, S + S_len);
+   if(hash)
+      secret += hash->process(S, S_len);
+
+   if(secret.size() >= 0xFFFE)
+      throw Encoding_Error("RTSS_Share::split secret too large for TSS format");
+
+   // +1 byte for the share ID
+   const uint16_t share_len = static_cast<uint16_t>(secret.size() + 1);
+
+   secure_vector<uint8_t> share_header(RTSS_HEADER_SIZE);
+   copy_mem(&share_header[0], identifier.data(), identifier.size());
+   share_header[16] = hash_id;
+   share_header[17] = M;
+   share_header[18] = get_byte(0, share_len);
+   share_header[19] = get_byte(1, share_len);
+
+   // Create RTSS header in each share
+   std::vector<RTSS_Share> shares(N);
+
+   for(uint8_t i = 0; i != N; ++i)
+      {
+      shares[i].m_contents.reserve(share_header.size() + share_len);
+      shares[i].m_contents = share_header;
+      }
+
+   // Choose sequential values for X starting from 1
+   for(uint8_t i = 0; i != N; ++i)
+      shares[i].m_contents.push_back(i+1);
+
+   for(size_t i = 0; i != secret.size(); ++i)
+      {
+      std::vector<uint8_t> coefficients(M-1);
+      rng.randomize(coefficients.data(), coefficients.size());
+
+      for(uint8_t j = 0; j != N; ++j)
+         {
+         const uint8_t X = j + 1;
+
+         uint8_t sum = secret[i];
+         uint8_t X_i = X;
+
+         for(size_t k = 0; k != coefficients.size(); ++k)
+            {
+            sum ^= gfp_mul(X_i, coefficients[k]);
+            X_i  = gfp_mul(X_i, X);
+            }
+
+         shares[j].m_contents.push_back(sum);
+         }
+      }
+
+   return shares;
+   }
+
+secure_vector<uint8_t>
+RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares)
+   {
+   if(shares.size() <= 1)
+      throw Decoding_Error("Insufficient shares to do TSS reconstruction");
+
+   for(size_t i = 0; i != shares.size(); ++i)
+      {
+      if(shares[i].size() < RTSS_HEADER_SIZE + 1)
+         throw Decoding_Error("Missing or malformed RTSS header");
+
+      if(shares[i].share_id() == 0)
+         throw Decoding_Error("Invalid (id = 0) RTSS share detected");
+
+      if(i > 0)
+         {
+         if(shares[i].size() != shares[0].size())
+            throw Decoding_Error("Different sized RTSS shares detected");
+
+         if(!same_mem(&shares[0].m_contents[0],
+                      &shares[i].m_contents[0], RTSS_HEADER_SIZE))
+            throw Decoding_Error("Different RTSS headers detected");
+         }
+      }
+
+   const uint8_t N = shares[0].m_contents[17];
+
+   if(shares.size() < N)
+      throw Decoding_Error("Insufficient shares to do TSS reconstruction");
+
+   const uint16_t share_len = make_uint16(shares[0].m_contents[18],
+                                          shares[0].m_contents[19]);
+
+   const uint8_t hash_id = shares[0].m_contents[16];
+   std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id));
+   const size_t hash_len = (hash ? hash->output_length() : 0);
+
+   if(shares[0].size() != RTSS_HEADER_SIZE + share_len)
+      {
+      /*
+      * This second (laxer) check accomodates a bug in TSS that was
+      * fixed in 2.9.0 - previous versions used the length of the
+      * *secret* here, instead of the length of the *share*, which is
+      * precisely 1 + hash_len longer.
+      */
+      if(shares[0].size() <= RTSS_HEADER_SIZE + 1 + hash_len)
+         throw Decoding_Error("Bad RTSS length field in header");
+      }
+
+   std::vector<uint8_t> V(shares.size());
+   secure_vector<uint8_t> recovered;
+
+   for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i)
+      {
+      for(size_t j = 0; j != V.size(); ++j)
+         V[j] = shares[j].m_contents[i];
+
+      uint8_t r = 0;
+      for(size_t k = 0; k != shares.size(); ++k)
+         {
+         // L_i function:
+         uint8_t r2 = 1;
+         for(size_t l = 0; l != shares.size(); ++l)
+            {
+            if(k == l)
+               continue;
+
+            uint8_t share_k = shares[k].share_id();
+            uint8_t share_l = shares[l].share_id();
+
+            if(share_k == share_l)
+               throw Decoding_Error("Duplicate shares found in RTSS recovery");
+
+            uint8_t div = RTSS_EXP[(255 +
+                                 RTSS_LOG[share_l] -
+                                 RTSS_LOG[share_k ^ share_l]) % 255];
+
+            r2 = gfp_mul(r2, div);
+            }
+
+         r ^= gfp_mul(V[k], r2);
+         }
+      recovered.push_back(r);
+      }
+
+   if(hash)
+      {
+      if(recovered.size() < hash->output_length())
+         throw Decoding_Error("RTSS recovered value too short to be valid");
+
+      const size_t secret_len = recovered.size() - hash->output_length();
+
+      hash->update(recovered.data(), secret_len);
+      secure_vector<uint8_t> hash_check = hash->final();
+
+      if(!constant_time_compare(hash_check.data(),
+                                &recovered[secret_len],
+                                hash->output_length()))
+         {
+         throw Decoding_Error("RTSS hash check failed");
+         }
+
+      // remove the trailing hash value
+      recovered.resize(secret_len);
+      }
+
+   return recovered;
+   }
+
+}
diff --git a/third_party/botan/src/lib/misc/tss/tss.h b/third_party/botan/src/lib/misc/tss/tss.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/misc/tss/tss.h
@@ -0,0 +1,104 @@
+/*
+* RTSS (threshold secret sharing)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RTSS_H_
+#define BOTAN_RTSS_H_
+
+#include <botan/secmem.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* A split secret, using the format from draft-mcgrew-tss-03
+*/
+class BOTAN_PUBLIC_API(2,0) RTSS_Share final
+   {
+   public:
+      /**
+      * @param M the number of shares needed to reconstruct
+      * @param N the number of shares generated
+      * @param secret the secret to split
+      * @param secret_len the length of the secret
+      * @param identifier the 16 byte share identifier
+      * @param rng the random number generator to use
+      */
+      static std::vector<RTSS_Share>
+         split(uint8_t M, uint8_t N,
+               const uint8_t secret[], uint16_t secret_len,
+               const uint8_t identifier[16],
+               RandomNumberGenerator& rng);
+
+      /**
+      * @param M the number of shares needed to reconstruct
+      * @param N the number of shares generated
+      * @param secret the secret to split
+      * @param secret_len the length of the secret
+      * @param identifier the share identifier
+      * @param hash_fn the hash function to use for a checksum ("None", "SHA-1", "SHA-256")
+      * @param rng the random number generator to use
+      */
+      static std::vector<RTSS_Share>
+         split(uint8_t M, uint8_t N,
+               const uint8_t secret[], uint16_t secret_len,
+               const std::vector<uint8_t>& identifier,
+               const std::string& hash_fn,
+               RandomNumberGenerator& rng);
+
+      /**
+      * @param shares the list of shares
+      */
+      static secure_vector<uint8_t>
+        reconstruct(const std::vector<RTSS_Share>& shares);
+
+      RTSS_Share() = default;
+
+      /**
+      * @param hex_input the share encoded in hexadecimal
+      */
+      explicit RTSS_Share(const std::string& hex_input);
+
+      /**
+      * @param data the shared data
+      * @param len the length of data
+      */
+      RTSS_Share(const uint8_t data[], size_t len);
+
+      /**
+      * @return binary representation
+      */
+      const secure_vector<uint8_t>& data() const { return m_contents; }
+
+      /**
+      * @return hex representation
+      */
+      std::string to_string() const;
+
+      /**
+      * @return share identifier
+      */
+      uint8_t share_id() const;
+
+      /**
+      * @return size of this share in bytes
+      */
+      size_t size() const { return m_contents.size(); }
+
+      /**
+      * @return if this TSS share was initialized or not
+      */
+      bool initialized() const { return (m_contents.size() > 0); }
+   private:
+      secure_vector<uint8_t> m_contents;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/aead.cpp b/third_party/botan/src/lib/modes/aead/aead.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/aead.cpp
@@ -0,0 +1,168 @@
+/*
+* (C) 2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aead.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+  #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+  #include <botan/ccm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+  #include <botan/chacha20poly1305.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+  #include <botan/eax.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+  #include <botan/gcm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+  #include <botan/ocb.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+  #include <botan/siv.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create_or_throw(const std::string& algo,
+                                                      Cipher_Dir dir,
+                                                      const std::string& provider)
+   {
+   if(auto aead = AEAD_Mode::create(algo, dir, provider))
+      return aead;
+
+   throw Lookup_Error("AEAD", algo, provider);
+   }
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create(const std::string& algo,
+                                             Cipher_Dir dir,
+                                             const std::string& provider)
+   {
+   BOTAN_UNUSED(provider);
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+   if(algo == "ChaCha20Poly1305")
+      {
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Encryption);
+      else
+         return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Decryption);
+
+      }
+#endif
+
+   if(algo.find('/') != std::string::npos)
+      {
+      const std::vector<std::string> algo_parts = split_on(algo, '/');
+      const std::string cipher_name = algo_parts[0];
+      const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+      if(mode_info.empty())
+         return std::unique_ptr<AEAD_Mode>();
+
+      std::ostringstream alg_args;
+
+      alg_args << '(' << cipher_name;
+      for(size_t i = 1; i < mode_info.size(); ++i)
+         alg_args << ',' << mode_info[i];
+      for(size_t i = 2; i < algo_parts.size(); ++i)
+         alg_args << ',' << algo_parts[i];
+      alg_args << ')';
+
+      const std::string mode_name = mode_info[0] + alg_args.str();
+      return AEAD_Mode::create(mode_name, dir);
+      }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+   SCAN_Name req(algo);
+
+   if(req.arg_count() == 0)
+      {
+      return std::unique_ptr<AEAD_Mode>();
+      }
+
+   std::unique_ptr<BlockCipher> bc(BlockCipher::create(req.arg(0), provider));
+
+   if(!bc)
+      {
+      return std::unique_ptr<AEAD_Mode>();
+      }
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+   if(req.algo_name() == "CCM")
+      {
+      size_t tag_len = req.arg_as_integer(1, 16);
+      size_t L_len = req.arg_as_integer(2, 3);
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new CCM_Encryption(bc.release(), tag_len, L_len));
+      else
+         return std::unique_ptr<AEAD_Mode>(new CCM_Decryption(bc.release(), tag_len, L_len));
+      }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+   if(req.algo_name() == "GCM")
+      {
+      size_t tag_len = req.arg_as_integer(1, 16);
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new GCM_Encryption(bc.release(), tag_len));
+      else
+         return std::unique_ptr<AEAD_Mode>(new GCM_Decryption(bc.release(), tag_len));
+      }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+   if(req.algo_name() == "OCB")
+      {
+      size_t tag_len = req.arg_as_integer(1, 16);
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new OCB_Encryption(bc.release(), tag_len));
+      else
+         return std::unique_ptr<AEAD_Mode>(new OCB_Decryption(bc.release(), tag_len));
+      }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+   if(req.algo_name() == "EAX")
+      {
+      size_t tag_len = req.arg_as_integer(1, bc->block_size());
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new EAX_Encryption(bc.release(), tag_len));
+      else
+         return std::unique_ptr<AEAD_Mode>(new EAX_Decryption(bc.release(), tag_len));
+      }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+   if(req.algo_name() == "SIV")
+      {
+      if(dir == ENCRYPTION)
+         return std::unique_ptr<AEAD_Mode>(new SIV_Encryption(bc.release()));
+      else
+         return std::unique_ptr<AEAD_Mode>(new SIV_Decryption(bc.release()));
+      }
+#endif
+
+#endif
+
+   return std::unique_ptr<AEAD_Mode>();
+   }
+
+
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/aead.h b/third_party/botan/src/lib/modes/aead/aead.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/aead.h
@@ -0,0 +1,121 @@
+/*
+* Interface for AEAD modes
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_MODE_H_
+#define BOTAN_AEAD_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+namespace Botan {
+
+/**
+* Interface for AEAD (Authenticated Encryption with Associated Data)
+* modes. These modes provide both encryption and message
+* authentication, and can authenticate additional per-message data
+* which is not included in the ciphertext (for instance a sequence
+* number).
+*/
+class BOTAN_PUBLIC_API(2,0) AEAD_Mode : public Cipher_Mode
+   {
+   public:
+      /**
+      * Create an AEAD mode
+      * @param algo the algorithm to create
+      * @param direction specify if this should be an encryption or decryption AEAD
+      * @param provider optional specification for provider to use
+      * @return an AEAD mode or a null pointer if not available
+      */
+      static std::unique_ptr<AEAD_Mode> create(const std::string& algo,
+                                               Cipher_Dir direction,
+                                               const std::string& provider = "");
+
+      /**
+      * Create an AEAD mode, or throw
+      * @param algo the algorithm to create
+      * @param direction specify if this should be an encryption or decryption AEAD
+      * @param provider optional specification for provider to use
+      * @return an AEAD mode, or throw an exception
+      */
+      static std::unique_ptr<AEAD_Mode> create_or_throw(const std::string& algo,
+                                                        Cipher_Dir direction,
+                                                        const std::string& provider = "");
+
+      bool authenticated() const override { return true; }
+
+      /**
+      * Set associated data that is not included in the ciphertext but
+      * that should be authenticated. Must be called after set_key and
+      * before start.
+      *
+      * Unless reset by another call, the associated data is kept
+      * between messages. Thus, if the AD does not change, calling
+      * once (after set_key) is the optimum.
+      *
+      * @param ad the associated data
+      * @param ad_len length of add in bytes
+      */
+      virtual void set_associated_data(const uint8_t ad[], size_t ad_len) = 0;
+
+      /**
+      * Most AEADs require the key to be set prior to setting the AD
+      * A few allow the AD to be set even before the cipher is keyed.
+      * Such ciphers would return false from this function.
+      */
+      virtual bool associated_data_requires_key() const { return true; }
+
+      /**
+      * Set associated data that is not included in the ciphertext but
+      * that should be authenticated. Must be called after set_key and
+      * before start.
+      *
+      * See @ref set_associated_data().
+      *
+      * @param ad the associated data
+      */
+      template<typename Alloc>
+      void set_associated_data_vec(const std::vector<uint8_t, Alloc>& ad)
+         {
+         set_associated_data(ad.data(), ad.size());
+         }
+
+      /**
+      * Set associated data that is not included in the ciphertext but
+      * that should be authenticated. Must be called after set_key and
+      * before start.
+      *
+      * See @ref set_associated_data().
+      *
+      * @param ad the associated data
+      */
+      template<typename Alloc>
+      void set_ad(const std::vector<uint8_t, Alloc>& ad)
+         {
+         set_associated_data(ad.data(), ad.size());
+         }
+
+      /**
+      * @return default AEAD nonce size (a commonly supported value among AEAD
+      * modes, and large enough that random collisions are unlikely)
+      */
+      size_t default_nonce_length() const override { return 12; }
+
+      virtual ~AEAD_Mode() = default;
+   };
+
+/**
+* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX")
+* @param name AEAD name
+* @param direction ENCRYPTION or DECRYPTION
+*/
+inline AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction)
+   {
+   return AEAD_Mode::create(name, direction, "").release();
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp b/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
@@ -0,0 +1,279 @@
+/*
+* CCM Mode Encryption
+* (C) 2013,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ccm.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+// 128-bit cipher is intrinsic to CCM definition
+static const size_t CCM_BS = 16;
+
+/*
+* CCM_Mode Constructor
+*/
+CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) :
+   m_tag_size(tag_size),
+   m_L(L),
+   m_cipher(cipher)
+   {
+   if(m_cipher->block_size() != CCM_BS)
+      throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
+
+   if(L < 2 || L > 8)
+      throw Invalid_Argument("Invalid CCM L value " + std::to_string(L));
+
+   if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0)
+      throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size));
+   }
+
+void CCM_Mode::clear()
+   {
+   m_cipher->clear();
+   reset();
+   }
+
+void CCM_Mode::reset()
+   {
+   m_nonce.clear();
+   m_msg_buf.clear();
+   m_ad_buf.clear();
+   }
+
+std::string CCM_Mode::name() const
+   {
+   return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")";
+   }
+
+bool CCM_Mode::valid_nonce_length(size_t n) const
+   {
+   return (n == (15-L()));
+   }
+
+size_t CCM_Mode::default_nonce_length() const
+   {
+   return (15-L());
+   }
+
+size_t CCM_Mode::update_granularity() const
+   {
+   /*
+   This value does not particularly matter as regardless CCM_Mode::update
+   buffers all input, so in theory this could be 1. However as for instance
+   Transform_Filter creates update_granularity() uint8_t buffers, use a
+   somewhat large size to avoid bouncing on a tiny buffer.
+   */
+   return m_cipher->parallel_bytes();
+   }
+
+Key_Length_Specification CCM_Mode::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+void CCM_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_cipher->set_key(key, length);
+   }
+
+void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length)
+   {
+   m_ad_buf.clear();
+
+   if(length)
+      {
+      // FIXME: support larger AD using length encoding rules
+      BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length");
+
+      m_ad_buf.push_back(get_byte(0, static_cast<uint16_t>(length)));
+      m_ad_buf.push_back(get_byte(1, static_cast<uint16_t>(length)));
+      m_ad_buf += std::make_pair(ad, length);
+      while(m_ad_buf.size() % CCM_BS)
+         m_ad_buf.push_back(0); // pad with zeros to full block size
+      }
+   }
+
+void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   m_nonce.assign(nonce, nonce + nonce_len);
+   m_msg_buf.clear();
+   }
+
+size_t CCM_Mode::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(m_nonce.size() > 0);
+   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+   return 0; // no output until finished
+   }
+
+void CCM_Mode::encode_length(uint64_t len, uint8_t out[])
+   {
+   const size_t len_bytes = L();
+
+   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
+
+   for(size_t i = 0; i != len_bytes; ++i)
+      out[len_bytes-1-i] = get_byte(sizeof(uint64_t)-1-i, len);
+
+   if(len_bytes < 8 && (len >> (len_bytes*8)) > 0)
+      throw Encoding_Error("CCM message length too long to encode in L field");
+   }
+
+void CCM_Mode::inc(secure_vector<uint8_t>& C)
+   {
+   for(size_t i = 0; i != C.size(); ++i)
+      if(++C[C.size()-i-1])
+         break;
+   }
+
+secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz)
+   {
+   if(m_nonce.size() != 15-L())
+      throw Invalid_State("CCM mode must set nonce");
+   secure_vector<uint8_t> B0(CCM_BS);
+
+   const uint8_t b_flags =
+      static_cast<uint8_t>((m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1));
+
+   B0[0] = b_flags;
+   copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
+   encode_length(sz, &B0[m_nonce.size()+1]);
+
+   return B0;
+   }
+
+secure_vector<uint8_t> CCM_Mode::format_c0()
+   {
+   if(m_nonce.size() != 15-L())
+      throw Invalid_State("CCM mode must set nonce");
+   secure_vector<uint8_t> C(CCM_BS);
+
+   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
+
+   C[0] = a_flags;
+   copy_mem(&C[1], m_nonce.data(), m_nonce.size());
+
+   return C;
+   }
+
+void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   const secure_vector<uint8_t>& ad = ad_buf();
+   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+   const BlockCipher& E = cipher();
+
+   secure_vector<uint8_t> T(CCM_BS);
+   E.encrypt(format_b0(sz), T);
+
+   for(size_t i = 0; i != ad.size(); i += CCM_BS)
+      {
+      xor_buf(T.data(), &ad[i], CCM_BS);
+      E.encrypt(T);
+      }
+
+   secure_vector<uint8_t> C = format_c0();
+   secure_vector<uint8_t> S0(CCM_BS);
+   E.encrypt(C, S0);
+   inc(C);
+
+   secure_vector<uint8_t> X(CCM_BS);
+
+   const uint8_t* buf_end = &buf[sz];
+
+   while(buf != buf_end)
+      {
+      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+      xor_buf(T.data(), buf, to_proc);
+      E.encrypt(T);
+
+      E.encrypt(C, X);
+      xor_buf(buf, X.data(), to_proc);
+      inc(C);
+
+      buf += to_proc;
+      }
+
+   T ^= S0;
+
+   buffer += std::make_pair(T.data(), tag_size());
+
+   reset();
+   }
+
+void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+   const secure_vector<uint8_t>& ad = ad_buf();
+   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+   const BlockCipher& E = cipher();
+
+   secure_vector<uint8_t> T(CCM_BS);
+   E.encrypt(format_b0(sz - tag_size()), T);
+
+   for(size_t i = 0; i != ad.size(); i += CCM_BS)
+      {
+      xor_buf(T.data(), &ad[i], CCM_BS);
+      E.encrypt(T);
+      }
+
+   secure_vector<uint8_t> C = format_c0();
+
+   secure_vector<uint8_t> S0(CCM_BS);
+   E.encrypt(C, S0);
+   inc(C);
+
+   secure_vector<uint8_t> X(CCM_BS);
+
+   const uint8_t* buf_end = &buf[sz - tag_size()];
+
+   while(buf != buf_end)
+      {
+      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+      E.encrypt(C, X);
+      xor_buf(buf, X.data(), to_proc);
+      inc(C);
+
+      xor_buf(T.data(), buf, to_proc);
+      E.encrypt(T);
+
+      buf += to_proc;
+      }
+
+   T ^= S0;
+
+   if(!constant_time_compare(T.data(), buf_end, tag_size()))
+      throw Invalid_Authentication_Tag("CCM tag check failed");
+
+   buffer.resize(buffer.size() - tag_size());
+
+   reset();
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/ccm/ccm.h b/third_party/botan/src/lib/modes/aead/ccm/ccm.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/ccm/ccm.h
@@ -0,0 +1,130 @@
+/*
+* CCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CCM_H_
+#define BOTAN_AEAD_CCM_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ccm.h)
+
+namespace Botan {
+
+/**
+* Base class for CCM encryption and decryption
+* @see RFC 3610
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Mode : public AEAD_Mode
+   {
+   public:
+      size_t process(uint8_t buf[], size_t sz) override;
+
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      bool associated_data_requires_key() const override { return false; }
+
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      bool valid_nonce_length(size_t) const override;
+
+      size_t default_nonce_length() const override;
+
+      void clear() override;
+
+      void reset() override;
+
+      size_t tag_size() const override { return m_tag_size; }
+
+   protected:
+      CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L);
+
+      size_t L() const { return m_L; }
+
+      const BlockCipher& cipher() const { return *m_cipher; }
+
+      void encode_length(uint64_t len, uint8_t out[]);
+
+      void inc(secure_vector<uint8_t>& C);
+
+      const secure_vector<uint8_t>& ad_buf() const { return m_ad_buf; }
+
+      secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+      secure_vector<uint8_t> format_b0(size_t msg_size);
+      secure_vector<uint8_t> format_c0();
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      const size_t m_tag_size;
+      const size_t m_L;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      secure_vector<uint8_t> m_nonce, m_msg_buf, m_ad_buf;
+   };
+
+/**
+* CCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Encryption final : public CCM_Mode
+   {
+   public:
+      /**
+      * @param cipher a 128-bit block cipher
+      * @param tag_size is how big the auth tag will be (even values
+      *                 between 4 and 16 are accepted)
+      * @param L length of L parameter. The total message length
+      *           must be less than 2**L bytes, and the nonce is 15-L bytes.
+      */
+      CCM_Encryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+         CCM_Mode(cipher, tag_size, L) {}
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+   };
+
+/**
+* CCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Decryption final : public CCM_Mode
+   {
+   public:
+      /**
+      * @param cipher a 128-bit block cipher
+      * @param tag_size is how big the auth tag will be (even values
+      *                 between 4 and 16 are accepted)
+      * @param L length of L parameter. The total message length
+      *           must be less than 2**L bytes, and the nonce is 15-L bytes.
+      */
+      CCM_Decryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+         CCM_Mode(cipher, tag_size, L) {}
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -0,0 +1,171 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014,2016,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha20poly1305.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+ChaCha20Poly1305_Mode::ChaCha20Poly1305_Mode() :
+   m_chacha(StreamCipher::create("ChaCha")),
+   m_poly1305(MessageAuthenticationCode::create("Poly1305"))
+   {
+   if(!m_chacha || !m_poly1305)
+      throw Algorithm_Not_Found("ChaCha20Poly1305");
+   }
+
+bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const
+   {
+   return (n == 8 || n == 12 || n == 24);
+   }
+
+void ChaCha20Poly1305_Mode::clear()
+   {
+   m_chacha->clear();
+   m_poly1305->clear();
+   reset();
+   }
+
+void ChaCha20Poly1305_Mode::reset()
+   {
+   m_ad.clear();
+   m_ctext_len = 0;
+   m_nonce_len = 0;
+   }
+
+void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_chacha->set_key(key, length);
+   }
+
+void ChaCha20Poly1305_Mode::set_associated_data(const uint8_t ad[], size_t length)
+   {
+   if(m_ctext_len > 0 || m_nonce_len > 0)
+      throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
+   m_ad.assign(ad, ad + length);
+   }
+
+void ChaCha20Poly1305_Mode::update_len(size_t len)
+   {
+   uint8_t len8[8] = { 0 };
+   store_le(static_cast<uint64_t>(len), len8);
+   m_poly1305->update(len8, 8);
+   }
+
+void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   m_ctext_len = 0;
+   m_nonce_len = nonce_len;
+
+   m_chacha->set_iv(nonce, nonce_len);
+
+   uint8_t first_block[64];
+   m_chacha->write_keystream(first_block, sizeof(first_block));
+
+   m_poly1305->set_key(first_block, 32);
+   // Remainder of first block is discarded
+   secure_scrub_memory(first_block, sizeof(first_block));
+
+   m_poly1305->update(m_ad);
+
+   if(cfrg_version())
+      {
+      if(m_ad.size() % 16)
+         {
+         const uint8_t zeros[16] = { 0 };
+         m_poly1305->update(zeros, 16 - m_ad.size() % 16);
+         }
+      }
+   else
+      {
+      update_len(m_ad.size());
+      }
+   }
+
+size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   m_chacha->cipher1(buf, sz);
+   m_poly1305->update(buf, sz); // poly1305 of ciphertext
+   m_ctext_len += sz;
+   return sz;
+   }
+
+void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   update(buffer, offset);
+   if(cfrg_version())
+      {
+      if(m_ctext_len % 16)
+         {
+         const uint8_t zeros[16] = { 0 };
+         m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+         }
+      update_len(m_ad.size());
+      }
+   update_len(m_ctext_len);
+
+   buffer.resize(buffer.size() + tag_size());
+   m_poly1305->final(&buffer[buffer.size() - tag_size()]);
+   m_ctext_len = 0;
+   m_nonce_len = 0;
+   }
+
+size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   m_poly1305->update(buf, sz); // poly1305 of ciphertext
+   m_chacha->cipher1(buf, sz);
+   m_ctext_len += sz;
+   return sz;
+   }
+
+void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+   const size_t remaining = sz - tag_size();
+
+   if(remaining)
+      {
+      m_poly1305->update(buf, remaining); // poly1305 of ciphertext
+      m_chacha->cipher1(buf, remaining);
+      m_ctext_len += remaining;
+      }
+
+   if(cfrg_version())
+      {
+      if(m_ctext_len % 16)
+         {
+         const uint8_t zeros[16] = { 0 };
+         m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+         }
+      update_len(m_ad.size());
+      }
+
+   update_len(m_ctext_len);
+
+   uint8_t mac[16];
+   m_poly1305->final(mac);
+
+   const uint8_t* included_tag = &buf[remaining];
+
+   m_ctext_len = 0;
+   m_nonce_len = 0;
+
+   if(!constant_time_compare(mac, included_tag, tag_size()))
+      throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
+   buffer.resize(offset + remaining);
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
@@ -0,0 +1,104 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CHACHA20_POLY1305_H_
+#define BOTAN_AEAD_CHACHA20_POLY1305_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(chacha20poly1305.h)
+
+namespace Botan {
+
+/**
+* Base class
+* See draft-irtf-cfrg-chacha20-poly1305-03 for specification
+* If a nonce of 64 bits is used the older version described in
+* draft-agl-tls-chacha20poly1305-04 is used instead.
+* If a nonce of 192 bits is used, XChaCha20Poly1305 is selected.
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Mode : public AEAD_Mode
+   {
+   public:
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      bool associated_data_requires_key() const override { return false; }
+
+      std::string name() const override { return "ChaCha20Poly1305"; }
+
+      size_t update_granularity() const override { return 64; }
+
+      Key_Length_Specification key_spec() const override
+         { return Key_Length_Specification(32); }
+
+      bool valid_nonce_length(size_t n) const override;
+
+      size_t tag_size() const override { return 16; }
+
+      void clear() override;
+
+      void reset() override;
+
+   protected:
+      std::unique_ptr<StreamCipher> m_chacha;
+      std::unique_ptr<MessageAuthenticationCode> m_poly1305;
+
+      ChaCha20Poly1305_Mode();
+
+      secure_vector<uint8_t> m_ad;
+      size_t m_nonce_len = 0;
+      size_t m_ctext_len = 0;
+
+      bool cfrg_version() const { return m_nonce_len == 12 || m_nonce_len == 24; }
+      void update_len(size_t len);
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+   };
+
+/**
+* ChaCha20Poly1305 Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Encryption final : public ChaCha20Poly1305_Mode
+   {
+   public:
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+/**
+* ChaCha20Poly1305 Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Decryption final : public ChaCha20Poly1305_Mode
+   {
+   public:
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/eax/eax.cpp b/third_party/botan/src/lib/modes/aead/eax/eax.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/eax/eax.cpp
@@ -0,0 +1,194 @@
+/*
+* EAX Mode Encryption
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eax.h>
+#include <botan/cmac.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* EAX MAC-based PRF
+*/
+secure_vector<uint8_t> eax_prf(uint8_t tag, size_t block_size,
+                           MessageAuthenticationCode& mac,
+                           const uint8_t in[], size_t length)
+   {
+   for(size_t i = 0; i != block_size - 1; ++i)
+      {
+      mac.update(0);
+      }
+   mac.update(tag);
+   mac.update(in, length);
+   return mac.final();
+   }
+
+}
+
+/*
+* EAX_Mode Constructor
+*/
+EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size) :
+   m_tag_size(tag_size),
+   m_cipher(cipher),
+   m_ctr(new CTR_BE(m_cipher->clone())),
+   m_cmac(new CMAC(m_cipher->clone()))
+   {
+   if(m_tag_size < 8 || m_tag_size > m_cmac->output_length())
+      throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size));
+   }
+
+void EAX_Mode::clear()
+   {
+   m_cipher->clear();
+   m_ctr->clear();
+   m_cmac->clear();
+   reset();
+   }
+
+void EAX_Mode::reset()
+   {
+   m_ad_mac.clear();
+   m_nonce_mac.clear();
+
+   // Clear out any data added to the CMAC calculation
+   try {
+      m_cmac->final();
+   }
+   catch(Key_Not_Set&) {}
+   }
+
+std::string EAX_Mode::name() const
+   {
+   return (m_cipher->name() + "/EAX");
+   }
+
+size_t EAX_Mode::update_granularity() const
+   {
+   /*
+   * For EAX this actually can be as low as 1 but that causes problems
+   * for applications which use update_granularity as the buffer size.
+   */
+   return m_cipher->parallel_bytes();
+   }
+
+Key_Length_Specification EAX_Mode::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+/*
+* Set the EAX key
+*/
+void EAX_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   /*
+   * These could share the key schedule, which is one nice part of EAX,
+   * but it's much easier to ignore that here...
+   */
+   m_ctr->set_key(key, length);
+   m_cmac->set_key(key, length);
+   }
+
+/*
+* Set the EAX associated data
+*/
+void EAX_Mode::set_associated_data(const uint8_t ad[], size_t length)
+   {
+   if(m_nonce_mac.empty() == false)
+      throw Invalid_State("Cannot set AD for EAX while processing a message");
+   m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad, length);
+   }
+
+void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   m_nonce_mac = eax_prf(0, block_size(), *m_cmac, nonce, nonce_len);
+
+   m_ctr->set_iv(m_nonce_mac.data(), m_nonce_mac.size());
+
+   for(size_t i = 0; i != block_size() - 1; ++i)
+      m_cmac->update(0);
+   m_cmac->update(2);
+   }
+
+size_t EAX_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+   m_ctr->cipher(buf, buf, sz);
+   m_cmac->update(buf, sz);
+   return sz;
+   }
+
+void EAX_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT_NOMSG(m_nonce_mac.empty() == false);
+   update(buffer, offset);
+
+   secure_vector<uint8_t> data_mac = m_cmac->final();
+   xor_buf(data_mac, m_nonce_mac, data_mac.size());
+
+   if(m_ad_mac.empty())
+      {
+      m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+      }
+
+   xor_buf(data_mac, m_ad_mac, data_mac.size());
+
+   buffer += std::make_pair(data_mac.data(), tag_size());
+   }
+
+size_t EAX_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+   m_cmac->update(buf, sz);
+   m_ctr->cipher(buf, buf, sz);
+   return sz;
+   }
+
+void EAX_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+   const size_t remaining = sz - tag_size();
+
+   if(remaining)
+      {
+      m_cmac->update(buf, remaining);
+      m_ctr->cipher(buf, buf, remaining);
+      }
+
+   const uint8_t* included_tag = &buf[remaining];
+
+   secure_vector<uint8_t> mac = m_cmac->final();
+   mac ^= m_nonce_mac;
+
+   if(m_ad_mac.empty())
+      {
+      m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+      }
+
+   mac ^= m_ad_mac;
+
+   if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+      throw Invalid_Authentication_Tag("EAX tag check failed");
+
+   buffer.resize(offset + remaining);
+
+   m_nonce_mac.clear();
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/eax/eax.h b/third_party/botan/src/lib/modes/aead/eax/eax.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/eax/eax.h
@@ -0,0 +1,119 @@
+/*
+* EAX Mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_EAX_H_
+#define BOTAN_AEAD_EAX_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eax.h)
+
+namespace Botan {
+
+/**
+* EAX base class
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Mode : public AEAD_Mode
+   {
+   public:
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      // EAX supports arbitrary nonce lengths
+      bool valid_nonce_length(size_t) const override { return true; }
+
+      size_t tag_size() const override { return m_tag_size; }
+
+      void clear() override;
+
+      void reset() override;
+
+   protected:
+      /**
+      * @param cipher the cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      EAX_Mode(BlockCipher* cipher, size_t tag_size);
+
+      size_t block_size() const { return m_cipher->block_size(); }
+
+      size_t m_tag_size;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<StreamCipher> m_ctr;
+      std::unique_ptr<MessageAuthenticationCode> m_cmac;
+
+      secure_vector<uint8_t> m_ad_mac;
+
+      secure_vector<uint8_t> m_nonce_mac;
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+   };
+
+/**
+* EAX Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Encryption final : public EAX_Mode
+   {
+   public:
+      /**
+      * @param cipher a 128-bit block cipher
+      * @param tag_size is how big the auth tag will be
+      */
+      EAX_Encryption(BlockCipher* cipher, size_t tag_size = 0) :
+         EAX_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+/**
+* EAX Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Decryption final : public EAX_Mode
+   {
+   public:
+      /**
+      * @param cipher a 128-bit block cipher
+      * @param tag_size is how big the auth tag will be
+      */
+      EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) :
+         EAX_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp b/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
@@ -0,0 +1,179 @@
+/*
+* GCM Mode Encryption
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gcm.h>
+#include <botan/ghash.h>
+#include <botan/block_cipher.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+/*
+* GCM_Mode Constructor
+*/
+GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size) :
+   m_tag_size(tag_size),
+   m_cipher_name(cipher->name()),
+   m_ctr(new CTR_BE(cipher, 4)),
+   m_ghash(new GHASH)
+   {
+   if(cipher->block_size() != GCM_BS)
+      throw Invalid_Argument("Invalid block cipher for GCM");
+
+   /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */
+   /* 64 bit tag is still supported but deprecated and will be removed in the future */
+   if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16))
+      throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size));
+   }
+
+GCM_Mode::~GCM_Mode() { /* for unique_ptr */ }
+
+void GCM_Mode::clear()
+   {
+   m_ctr->clear();
+   m_ghash->clear();
+   reset();
+   }
+
+void GCM_Mode::reset()
+   {
+   m_ghash->reset();
+   }
+
+std::string GCM_Mode::name() const
+   {
+   return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")");
+   }
+
+std::string GCM_Mode::provider() const
+   {
+   return m_ghash->provider();
+   }
+
+size_t GCM_Mode::update_granularity() const
+   {
+   return GCM_BS * std::max<size_t>(2, BOTAN_BLOCK_CIPHER_PAR_MULT);
+   }
+
+bool GCM_Mode::valid_nonce_length(size_t len) const
+   {
+   // GCM does not support empty nonces
+   return (len > 0);
+   }
+
+Key_Length_Specification GCM_Mode::key_spec() const
+   {
+   return m_ctr->key_spec();
+   }
+
+void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen)
+   {
+   m_ctr->set_key(key, keylen);
+
+   const std::vector<uint8_t> zeros(GCM_BS);
+   m_ctr->set_iv(zeros.data(), zeros.size());
+
+   secure_vector<uint8_t> H(GCM_BS);
+   m_ctr->encipher(H);
+   m_ghash->set_key(H);
+   }
+
+void GCM_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+   {
+   m_ghash->set_associated_data(ad, ad_len);
+   }
+
+void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   if(m_y0.size() != GCM_BS)
+      m_y0.resize(GCM_BS);
+
+   clear_mem(m_y0.data(), m_y0.size());
+
+   if(nonce_len == 12)
+      {
+      copy_mem(m_y0.data(), nonce, nonce_len);
+      m_y0[15] = 1;
+      }
+   else
+      {
+      m_ghash->nonce_hash(m_y0, nonce, nonce_len);
+      }
+
+   m_ctr->set_iv(m_y0.data(), m_y0.size());
+
+   clear_mem(m_y0.data(), m_y0.size());
+   m_ctr->encipher(m_y0);
+
+   m_ghash->start(m_y0.data(), m_y0.size());
+   clear_mem(m_y0.data(), m_y0.size());
+   }
+
+size_t GCM_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+   m_ctr->cipher(buf, buf, sz);
+   m_ghash->update(buf, sz);
+   return sz;
+   }
+
+void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   m_ctr->cipher(buf, buf, sz);
+   m_ghash->update(buf, sz);
+
+   uint8_t mac[16] = { 0 };
+   m_ghash->final(mac, tag_size());
+   buffer += std::make_pair(mac, tag_size());
+   }
+
+size_t GCM_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+   m_ghash->update(buf, sz);
+   m_ctr->cipher(buf, buf, sz);
+   return sz;
+   }
+
+void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   if(sz < tag_size())
+      throw Decoding_Error("Insufficient input for GCM decryption, tag missing");
+
+   const size_t remaining = sz - tag_size();
+
+   // handle any final input before the tag
+   if(remaining)
+      {
+      m_ghash->update(buf, remaining);
+      m_ctr->cipher(buf, buf, remaining);
+      }
+
+   uint8_t mac[16] = { 0 };
+   m_ghash->final(mac, tag_size());
+
+   const uint8_t* included_tag = &buffer[remaining+offset];
+
+   if(!constant_time_compare(mac, included_tag, tag_size()))
+      throw Invalid_Authentication_Tag("GCM tag check failed");
+
+   buffer.resize(offset + remaining);
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/gcm/gcm.h b/third_party/botan/src/lib/modes/aead/gcm/gcm.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/gcm/gcm.h
@@ -0,0 +1,117 @@
+/*
+* GCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_GCM_H_
+#define BOTAN_AEAD_GCM_H_
+
+#include <botan/aead.h>
+#include <botan/sym_algo.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gcm.h)
+
+namespace Botan {
+
+class BlockCipher;
+class StreamCipher;
+class GHASH;
+
+/**
+* GCM Mode
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Mode : public AEAD_Mode
+   {
+   public:
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      bool valid_nonce_length(size_t len) const override;
+
+      size_t tag_size() const override { return m_tag_size; }
+
+      void clear() override;
+
+      void reset() override;
+
+      std::string provider() const override;
+   protected:
+      GCM_Mode(BlockCipher* cipher, size_t tag_size);
+
+      ~GCM_Mode();
+
+      static const size_t GCM_BS = 16;
+
+      const size_t m_tag_size;
+      const std::string m_cipher_name;
+
+      std::unique_ptr<StreamCipher> m_ctr;
+      std::unique_ptr<GHASH> m_ghash;
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      secure_vector<uint8_t> m_y0;
+   };
+
+/**
+* GCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Encryption final : public GCM_Mode
+   {
+   public:
+      /**
+      * @param cipher the 128 bit block cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      GCM_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+         GCM_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+/**
+* GCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Decryption final : public GCM_Mode
+   {
+   public:
+      /**
+      * @param cipher the 128 bit block cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      GCM_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+         GCM_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp b/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
@@ -0,0 +1,533 @@
+/*
+* OCB Mode
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocb.h>
+#include <botan/block_cipher.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+// Has to be in Botan namespace so unique_ptr can reference it
+class L_computer final
+   {
+   public:
+      explicit L_computer(const BlockCipher& cipher) :
+         m_BS(cipher.block_size()),
+         m_max_blocks(cipher.parallel_bytes() / m_BS)
+         {
+         m_L_star.resize(m_BS);
+         cipher.encrypt(m_L_star);
+         m_L_dollar = poly_double(star());
+         m_L.push_back(poly_double(dollar()));
+
+         while(m_L.size() < 8)
+            m_L.push_back(poly_double(m_L.back()));
+
+         m_offset_buf.resize(m_BS * m_max_blocks);
+         }
+
+      void init(const secure_vector<uint8_t>& offset)
+         {
+         m_offset = offset;
+         }
+
+      bool initialized() const { return m_offset.empty() == false; }
+
+      const secure_vector<uint8_t>& star() const { return m_L_star; }
+      const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
+      const secure_vector<uint8_t>& offset() const { return m_offset; }
+
+      const secure_vector<uint8_t>& get(size_t i) const
+         {
+         while(m_L.size() <= i)
+            m_L.push_back(poly_double(m_L.back()));
+
+         return m_L[i];
+         }
+
+      const uint8_t*
+      compute_offsets(size_t block_index, size_t blocks)
+         {
+         BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
+
+         uint8_t* offsets = m_offset_buf.data();
+
+         if(block_index % 4 == 0)
+            {
+            const secure_vector<uint8_t>& L0 = get(0);
+            const secure_vector<uint8_t>& L1 = get(1);
+
+            while(blocks >= 4)
+               {
+               // ntz(4*i+1) == 0
+               // ntz(4*i+2) == 1
+               // ntz(4*i+3) == 0
+               block_index += 4;
+               const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
+
+               xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
+               offsets += m_BS;
+
+               xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
+               offsets += m_BS;
+
+               xor_buf(m_offset.data(), L1.data(), m_BS);
+               copy_mem(offsets, m_offset.data(), m_BS);
+               offsets += m_BS;
+
+               xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
+               copy_mem(offsets, m_offset.data(), m_BS);
+               offsets += m_BS;
+
+               blocks -= 4;
+               }
+            }
+
+         for(size_t i = 0; i != blocks; ++i)
+            { // could be done in parallel
+            const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1));
+            xor_buf(m_offset.data(), get(ntz).data(), m_BS);
+            copy_mem(offsets, m_offset.data(), m_BS);
+            offsets += m_BS;
+            }
+
+         return m_offset_buf.data();
+         }
+
+   private:
+      secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) const
+         {
+         secure_vector<uint8_t> out(in.size());
+         poly_double_n(out.data(), in.data(), out.size());
+         return out;
+         }
+
+      const size_t m_BS, m_max_blocks;
+      secure_vector<uint8_t> m_L_dollar, m_L_star;
+      secure_vector<uint8_t> m_offset;
+      mutable std::vector<secure_vector<uint8_t>> m_L;
+      secure_vector<uint8_t> m_offset_buf;
+   };
+
+namespace {
+
+/*
+* OCB's HASH
+*/
+secure_vector<uint8_t> ocb_hash(const L_computer& L,
+                                const BlockCipher& cipher,
+                                const uint8_t ad[], size_t ad_len)
+   {
+   const size_t BS = cipher.block_size();
+   secure_vector<uint8_t> sum(BS);
+   secure_vector<uint8_t> offset(BS);
+
+   secure_vector<uint8_t> buf(BS);
+
+   const size_t ad_blocks = (ad_len / BS);
+   const size_t ad_remainder = (ad_len % BS);
+
+   for(size_t i = 0; i != ad_blocks; ++i)
+      {
+      // this loop could run in parallel
+      offset ^= L.get(var_ctz32(static_cast<uint32_t>(i+1)));
+      buf = offset;
+      xor_buf(buf.data(), &ad[BS*i], BS);
+      cipher.encrypt(buf);
+      sum ^= buf;
+      }
+
+   if(ad_remainder)
+      {
+      offset ^= L.star();
+      buf = offset;
+      xor_buf(buf.data(), &ad[BS*ad_blocks], ad_remainder);
+      buf[ad_remainder] ^= 0x80;
+      cipher.encrypt(buf);
+      sum ^= buf;
+      }
+
+   return sum;
+   }
+
+}
+
+OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size) :
+   m_cipher(cipher),
+   m_checksum(m_cipher->parallel_bytes()),
+   m_ad_hash(m_cipher->block_size()),
+   m_tag_size(tag_size),
+   m_block_size(m_cipher->block_size()),
+   m_par_blocks(m_cipher->parallel_bytes() / m_block_size)
+   {
+   const size_t BS = block_size();
+
+   /*
+   * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
+   * sizes but only 128, 192, 256 and 512 bit are currently supported
+   * by this implementation.
+   */
+   BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+                   "Invalid block size for OCB");
+
+   BOTAN_ARG_CHECK(m_tag_size % 4 == 0 &&
+                   m_tag_size >= 8 && m_tag_size <= BS &&
+                   m_tag_size <= 32,
+                   "Invalid OCB tag length");
+   }
+
+OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ }
+
+void OCB_Mode::clear()
+   {
+   m_cipher->clear();
+   m_L.reset(); // add clear here?
+   reset();
+   }
+
+void OCB_Mode::reset()
+   {
+   m_block_index = 0;
+   zeroise(m_ad_hash);
+   zeroise(m_checksum);
+   m_last_nonce.clear();
+   m_stretch.clear();
+   }
+
+bool OCB_Mode::valid_nonce_length(size_t length) const
+   {
+   if(length == 0)
+      return false;
+   if(block_size() == 16)
+      return length < 16;
+   else
+      return length < (block_size() - 1);
+   }
+
+std::string OCB_Mode::name() const
+   {
+   return m_cipher->name() + "/OCB"; // include tag size?
+   }
+
+size_t OCB_Mode::update_granularity() const
+   {
+   return (m_par_blocks * block_size());
+   }
+
+Key_Length_Specification OCB_Mode::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+void OCB_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_cipher->set_key(key, length);
+   m_L.reset(new L_computer(*m_cipher));
+   }
+
+void OCB_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+   {
+   verify_key_set(m_L != nullptr);
+   m_ad_hash = ocb_hash(*m_L, *m_cipher, ad, ad_len);
+   }
+
+const secure_vector<uint8_t>&
+OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len)
+   {
+   const size_t BS = block_size();
+
+   BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+                "OCB block size is supported");
+
+   const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
+
+   const uint8_t BOTTOM_MASK =
+      static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
+
+   m_nonce_buf.resize(BS);
+   clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
+
+   copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
+   m_nonce_buf[0] = static_cast<uint8_t>(((tag_size()*8) % (BS*8)) << (BS <= 16 ? 1 : 0));
+
+   m_nonce_buf[BS - nonce_len - 1] ^= 1;
+
+   const uint8_t bottom = m_nonce_buf[BS-1] & BOTTOM_MASK;
+   m_nonce_buf[BS-1] &= ~BOTTOM_MASK;
+
+   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
+
+   if(need_new_stretch)
+      {
+      m_last_nonce = m_nonce_buf;
+
+      m_cipher->encrypt(m_nonce_buf);
+
+      /*
+      The loop bounds (BS vs BS/2) are derived from the relation
+      between the block size and the MASKLEN. Using the terminology
+      of draft-krovetz-ocb-wide, we have to derive enough bits in
+      ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
+
+                 +----------+---------+-------+---------+
+                 | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
+                 +----------+---------+-------+---------+
+                 |       32 |     141 |    17 |    4    |
+                 |       64 |      27 |    25 |    5    |
+                 |       96 |    1601 |    33 |    6    |
+                 |      128 |     135 |     8 |    6    |
+                 |      192 |     135 |    40 |    7    |
+                 |      256 |    1061 |     1 |    8    |
+                 |      384 |    4109 |    80 |    8    |
+                 |      512 |     293 |   176 |    8    |
+                 |     1024 |  524355 |   352 |    9    |
+                 +----------+---------+-------+---------+
+      */
+      if(BS == 16)
+         {
+         for(size_t i = 0; i != BS / 2; ++i)
+            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+1]);
+         }
+      else if(BS == 24)
+         {
+         for(size_t i = 0; i != 16; ++i)
+            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+5]);
+         }
+      else if(BS == 32)
+         {
+         for(size_t i = 0; i != BS; ++i)
+            m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i+1] >> 7));
+         }
+      else if(BS == 64)
+         {
+         for(size_t i = 0; i != BS / 2; ++i)
+            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+22]);
+         }
+
+      m_stretch = m_nonce_buf;
+      }
+
+   // now set the offset from stretch and bottom
+   const size_t shift_bytes = bottom / 8;
+   const size_t shift_bits  = bottom % 8;
+
+   BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
+
+   m_offset.resize(BS);
+   for(size_t i = 0; i != BS; ++i)
+      {
+      m_offset[i]  = (m_stretch[i+shift_bytes] << shift_bits);
+      m_offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits));
+      }
+
+   return m_offset;
+   }
+
+void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   verify_key_set(m_L != nullptr);
+
+   m_L->init(update_nonce(nonce, nonce_len));
+   zeroise(m_checksum);
+   m_block_index = 0;
+   }
+
+void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
+   {
+   verify_key_set(m_L != nullptr);
+   BOTAN_STATE_CHECK(m_L->initialized());
+
+   const size_t BS = block_size();
+
+   while(blocks)
+      {
+      const size_t proc_blocks = std::min(blocks, par_blocks());
+      const size_t proc_bytes = proc_blocks * BS;
+
+      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
+
+      xor_buf(m_checksum.data(), buffer, proc_bytes);
+
+      m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
+
+      buffer += proc_bytes;
+      blocks -= proc_blocks;
+      m_block_index += proc_blocks;
+      }
+   }
+
+size_t OCB_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+   encrypt(buf, sz / block_size());
+   return sz;
+   }
+
+void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   verify_key_set(m_L != nullptr);
+
+   const size_t BS = block_size();
+
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   secure_vector<uint8_t> mac(BS);
+
+   if(sz)
+      {
+      const size_t final_full_blocks = sz / BS;
+      const size_t remainder_bytes = sz - (final_full_blocks * BS);
+
+      encrypt(buf, final_full_blocks);
+      mac = m_L->offset();
+
+      if(remainder_bytes)
+         {
+         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
+         uint8_t* remainder = &buf[sz - remainder_bytes];
+
+         xor_buf(m_checksum.data(), remainder, remainder_bytes);
+         m_checksum[remainder_bytes] ^= 0x80;
+
+         // Offset_*
+         mac ^= m_L->star();
+
+         secure_vector<uint8_t> pad(BS);
+         m_cipher->encrypt(mac, pad);
+         xor_buf(remainder, pad.data(), remainder_bytes);
+         }
+      }
+   else
+      {
+      mac = m_L->offset();
+      }
+
+   // now compute the tag
+
+   // fold checksum
+   for(size_t i = 0; i != m_checksum.size(); i += BS)
+      {
+      xor_buf(mac.data(), m_checksum.data() + i, BS);
+      }
+
+   xor_buf(mac.data(), m_L->dollar().data(), BS);
+   m_cipher->encrypt(mac);
+   xor_buf(mac.data(), m_ad_hash.data(), BS);
+
+   buffer += std::make_pair(mac.data(), tag_size());
+
+   zeroise(m_checksum);
+   m_block_index = 0;
+   }
+
+void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
+   {
+   verify_key_set(m_L != nullptr);
+   BOTAN_STATE_CHECK(m_L->initialized());
+
+   const size_t BS = block_size();
+
+   while(blocks)
+      {
+      const size_t proc_blocks = std::min(blocks, par_blocks());
+      const size_t proc_bytes = proc_blocks * BS;
+
+      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
+
+      m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
+
+      xor_buf(m_checksum.data(), buffer, proc_bytes);
+
+      buffer += proc_bytes;
+      blocks -= proc_blocks;
+      m_block_index += proc_blocks;
+      }
+   }
+
+size_t OCB_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+   decrypt(buf, sz / block_size());
+   return sz;
+   }
+
+void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   verify_key_set(m_L != nullptr);
+
+   const size_t BS = block_size();
+
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+   const size_t remaining = sz - tag_size();
+
+   secure_vector<uint8_t> mac(BS);
+
+   if(remaining)
+      {
+      const size_t final_full_blocks = remaining / BS;
+      const size_t final_bytes = remaining - (final_full_blocks * BS);
+
+      decrypt(buf, final_full_blocks);
+      mac ^= m_L->offset();
+
+      if(final_bytes)
+         {
+         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
+
+         uint8_t* remainder = &buf[remaining - final_bytes];
+
+         mac ^= m_L->star();
+         secure_vector<uint8_t> pad(BS);
+         m_cipher->encrypt(mac, pad); // P_*
+         xor_buf(remainder, pad.data(), final_bytes);
+
+         xor_buf(m_checksum.data(), remainder, final_bytes);
+         m_checksum[final_bytes] ^= 0x80;
+         }
+      }
+   else
+      mac = m_L->offset();
+
+   // compute the mac
+
+   // fold checksum
+   for(size_t i = 0; i != m_checksum.size(); i += BS)
+      {
+      xor_buf(mac.data(), m_checksum.data() + i, BS);
+      }
+
+   mac ^= m_L->dollar();
+   m_cipher->encrypt(mac);
+   mac ^= m_ad_hash;
+
+   // reset state
+   zeroise(m_checksum);
+   m_block_index = 0;
+
+   // compare mac
+   const uint8_t* included_tag = &buf[remaining];
+
+   if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+      throw Invalid_Authentication_Tag("OCB tag check failed");
+
+   // remove tag from end of message
+   buffer.resize(remaining + offset);
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/ocb/ocb.h b/third_party/botan/src/lib/modes/aead/ocb/ocb.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/ocb/ocb.h
@@ -0,0 +1,137 @@
+/*
+* OCB Mode
+* (C) 2013,2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_OCB_H_
+#define BOTAN_AEAD_OCB_H_
+
+#include <botan/aead.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ocb.h)
+
+namespace Botan {
+
+class BlockCipher;
+class L_computer;
+
+/**
+* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note
+* that OCB is patented, but is freely licensed in some circumstances.
+*
+* @see "The OCB Authenticated-Encryption Algorithm" RFC 7253
+*      https://tools.ietf.org/html/rfc7253
+* @see "OCB For Block Ciphers Without 128-Bit Blocks"
+*      (draft-krovetz-ocb-wide-d3) for the extension of OCB to
+*      block ciphers with larger block sizes.
+* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
+* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb
+*/
+class BOTAN_PUBLIC_API(2,0) OCB_Mode : public AEAD_Mode
+   {
+   public:
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      bool valid_nonce_length(size_t) const override;
+
+      size_t tag_size() const override { return m_tag_size; }
+
+      void clear() override;
+
+      void reset() override;
+
+      ~OCB_Mode();
+   protected:
+      /**
+      * @param cipher the block cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      OCB_Mode(BlockCipher* cipher, size_t tag_size);
+
+      size_t block_size() const { return m_block_size; }
+      size_t par_blocks() const { return m_par_blocks; }
+      size_t par_bytes() const { return m_checksum.size(); }
+
+      // fixme make these private
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<L_computer> m_L;
+
+      size_t m_block_index = 0;
+
+      secure_vector<uint8_t> m_checksum;
+      secure_vector<uint8_t> m_ad_hash;
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      const secure_vector<uint8_t>& update_nonce(const uint8_t nonce[], size_t nonce_len);
+
+      const size_t m_tag_size;
+      const size_t m_block_size;
+      const size_t m_par_blocks;
+      secure_vector<uint8_t> m_last_nonce;
+      secure_vector<uint8_t> m_stretch;
+      secure_vector<uint8_t> m_nonce_buf;
+      secure_vector<uint8_t> m_offset;
+   };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Encryption final : public OCB_Mode
+   {
+   public:
+      /**
+      * @param cipher the block cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+         OCB_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   private:
+      void encrypt(uint8_t input[], size_t blocks);
+   };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Decryption final : public OCB_Mode
+   {
+   public:
+      /**
+      * @param cipher the block cipher to use
+      * @param tag_size is how big the auth tag will be
+      */
+      OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+         OCB_Mode(cipher, tag_size) {}
+
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   private:
+      void decrypt(uint8_t input[], size_t blocks);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/aead/siv/siv.cpp b/third_party/botan/src/lib/modes/aead/siv/siv.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/siv/siv.cpp
@@ -0,0 +1,206 @@
+/*
+* SIV Mode Encryption
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/siv.h>
+#include <botan/block_cipher.h>
+#include <botan/cmac.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+SIV_Mode::SIV_Mode(BlockCipher* cipher) :
+   m_name(cipher->name() + "/SIV"),
+   m_ctr(new CTR_BE(cipher->clone(), 8)),
+   m_mac(new CMAC(cipher)),
+   m_bs(cipher->block_size())
+   {
+   // Not really true but only 128 bit allowed at the moment
+   if(m_bs != 16)
+      throw Invalid_Argument("SIV requires a 128 bit block cipher");
+   }
+
+SIV_Mode::~SIV_Mode()
+   {
+   // for ~unique_ptr
+   }
+
+void SIV_Mode::clear()
+   {
+   m_ctr->clear();
+   m_mac->clear();
+   reset();
+   }
+
+void SIV_Mode::reset()
+   {
+   m_nonce.clear();
+   m_msg_buf.clear();
+   m_ad_macs.clear();
+   }
+
+std::string SIV_Mode::name() const
+   {
+   return m_name;
+   }
+
+bool SIV_Mode::valid_nonce_length(size_t) const
+   {
+   return true;
+   }
+
+size_t SIV_Mode::update_granularity() const
+   {
+   /*
+   This value does not particularly matter as regardless SIV_Mode::update
+   buffers all input, so in theory this could be 1. However as for instance
+   Transform_Filter creates update_granularity() uint8_t buffers, use a
+   somewhat large size to avoid bouncing on a tiny buffer.
+   */
+   return 128;
+   }
+
+Key_Length_Specification SIV_Mode::key_spec() const
+   {
+   return m_mac->key_spec().multiple(2);
+   }
+
+void SIV_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   const size_t keylen = length / 2;
+   m_mac->set_key(key, keylen);
+   m_ctr->set_key(key + keylen, keylen);
+   m_ad_macs.clear();
+   }
+
+void SIV_Mode::set_associated_data_n(size_t n, const uint8_t ad[], size_t length)
+   {
+   const size_t max_ads = block_size() * 8 - 2;
+   if(n > max_ads)
+      throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
+
+   if(n >= m_ad_macs.size())
+      m_ad_macs.resize(n+1);
+
+   m_ad_macs[n] = m_mac->process(ad, length);
+   }
+
+void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   if(nonce_len)
+      m_nonce = m_mac->process(nonce, nonce_len);
+   else
+      m_nonce.clear();
+
+   m_msg_buf.clear();
+   }
+
+size_t SIV_Mode::process(uint8_t buf[], size_t sz)
+   {
+   // all output is saved for processing in finish
+   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+   return 0;
+   }
+
+secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len)
+   {
+   const std::vector<uint8_t> zeros(block_size());
+
+   secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size());
+
+   for(size_t i = 0; i != m_ad_macs.size(); ++i)
+      {
+      poly_double_n(V.data(), V.size());
+      V ^= m_ad_macs[i];
+      }
+
+   if(m_nonce.size())
+      {
+      poly_double_n(V.data(), V.size());
+      V ^= m_nonce;
+      }
+
+   if(text_len < block_size())
+      {
+      poly_double_n(V.data(), V.size());
+      xor_buf(V.data(), text, text_len);
+      V[text_len] ^= 0x80;
+      return m_mac->process(V);
+      }
+
+   m_mac->update(text, text_len - block_size());
+   xor_buf(V.data(), &text[text_len - block_size()], block_size());
+   m_mac->update(V);
+
+   return m_mac->final();
+   }
+
+void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V)
+   {
+   V[m_bs-8] &= 0x7F;
+   V[m_bs-4] &= 0x7F;
+
+   ctr().set_iv(V.data(), V.size());
+   }
+
+void SIV_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+   msg_buf().clear();
+
+   const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset);
+
+   buffer.insert(buffer.begin() + offset, V.begin(), V.end());
+
+   if(buffer.size() != offset + V.size())
+      {
+      set_ctr_iv(V);
+      ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size());
+      }
+   }
+
+void SIV_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+   if(msg_buf().size() > 0)
+      {
+      buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+      msg_buf().clear();
+      }
+
+   const size_t sz = buffer.size() - offset;
+
+   BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+   secure_vector<uint8_t> V(buffer.data() + offset,
+                            buffer.data() + offset + block_size());
+
+   if(buffer.size() != offset + V.size())
+      {
+      set_ctr_iv(V);
+
+      ctr().cipher(buffer.data() + offset + V.size(),
+                   buffer.data() + offset,
+                   buffer.size() - offset - V.size());
+      }
+
+   const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
+
+   if(!constant_time_compare(T.data(), V.data(), T.size()))
+      throw Invalid_Authentication_Tag("SIV tag check failed");
+
+   buffer.resize(buffer.size() - tag_size());
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/aead/siv/siv.h b/third_party/botan/src/lib/modes/aead/siv/siv.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/aead/siv/siv.h
@@ -0,0 +1,127 @@
+/*
+* SIV Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_SIV_H_
+#define BOTAN_AEAD_SIV_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(siv.h)
+
+namespace Botan {
+
+class BlockCipher;
+class MessageAuthenticationCode;
+
+/**
+* Base class for SIV encryption and decryption (@see RFC 5297)
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Mode : public AEAD_Mode
+   {
+   public:
+      size_t process(uint8_t buf[], size_t size) override;
+
+      /**
+      * Sets the nth element of the vector of associated data
+      * @param n index into the AD vector
+      * @param ad associated data
+      * @param ad_len length of associated data in bytes
+      */
+      void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len);
+
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override
+         {
+         set_associated_data_n(0, ad, ad_len);
+         }
+
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      bool valid_nonce_length(size_t) const override;
+
+      void clear() override;
+
+      void reset() override;
+
+      size_t tag_size() const override { return 16; }
+
+      ~SIV_Mode();
+
+   protected:
+      explicit SIV_Mode(BlockCipher* cipher);
+
+      size_t block_size() const { return m_bs; }
+
+      StreamCipher& ctr() { return *m_ctr; }
+
+      void set_ctr_iv(secure_vector<uint8_t> V);
+
+      secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+      secure_vector<uint8_t> S2V(const uint8_t text[], size_t text_len);
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      const std::string m_name;
+      std::unique_ptr<StreamCipher> m_ctr;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      secure_vector<uint8_t> m_nonce, m_msg_buf;
+      std::vector<secure_vector<uint8_t>> m_ad_macs;
+      const size_t m_bs;
+   };
+
+/**
+* SIV Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Encryption final : public SIV_Mode
+   {
+   public:
+      /**
+      * @param cipher a block cipher
+      */
+      explicit SIV_Encryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override
+         { return input_length + tag_size(); }
+
+      size_t minimum_final_size() const override { return 0; }
+   };
+
+/**
+* SIV Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Decryption final : public SIV_Mode
+   {
+   public:
+      /**
+      * @param cipher a 128-bit block cipher
+      */
+      explicit SIV_Decryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override
+         {
+         BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+         return input_length - tag_size();
+         }
+
+      size_t minimum_final_size() const override { return tag_size(); }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/cbc/cbc.cpp b/third_party/botan/src/lib/modes/cbc/cbc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cbc/cbc.cpp
@@ -0,0 +1,323 @@
+/*
+* CBC Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cbc.h>
+#include <botan/mode_pad.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+   m_cipher(cipher),
+   m_padding(padding),
+   m_block_size(cipher->block_size())
+   {
+   if(m_padding && !m_padding->valid_blocksize(m_block_size))
+      throw Invalid_Argument("Padding " + m_padding->name() +
+                             " cannot be used with " +
+                             cipher->name() + "/CBC");
+   }
+
+void CBC_Mode::clear()
+   {
+   m_cipher->clear();
+   reset();
+   }
+
+void CBC_Mode::reset()
+   {
+   m_state.clear();
+   }
+
+std::string CBC_Mode::name() const
+   {
+   if(m_padding)
+      return cipher().name() + "/CBC/" + padding().name();
+   else
+      return cipher().name() + "/CBC/CTS";
+   }
+
+size_t CBC_Mode::update_granularity() const
+   {
+   return cipher().parallel_bytes();
+   }
+
+Key_Length_Specification CBC_Mode::key_spec() const
+   {
+   return cipher().key_spec();
+   }
+
+size_t CBC_Mode::default_nonce_length() const
+   {
+   return block_size();
+   }
+
+bool CBC_Mode::valid_nonce_length(size_t n) const
+   {
+   return (n == 0 || n == block_size());
+   }
+
+void CBC_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_cipher->set_key(key, length);
+   m_state.clear();
+   }
+
+void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   /*
+   * A nonce of zero length means carry the last ciphertext value over
+   * as the new IV, as unfortunately some protocols require this. If
+   * this is the first message then we use an IV of all zeros.
+   */
+   if(nonce_len)
+      m_state.assign(nonce, nonce + nonce_len);
+   else if(m_state.empty())
+      m_state.resize(m_cipher->block_size());
+   // else leave the state alone
+   }
+
+size_t CBC_Encryption::minimum_final_size() const
+   {
+   return 0;
+   }
+
+size_t CBC_Encryption::output_length(size_t input_length) const
+   {
+   if(input_length == 0)
+      return block_size();
+   else
+      return round_up(input_length, block_size());
+   }
+
+size_t CBC_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+   const size_t BS = block_size();
+
+   BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks");
+   const size_t blocks = sz / BS;
+
+   if(blocks > 0)
+      {
+      xor_buf(&buf[0], state_ptr(), BS);
+      cipher().encrypt(&buf[0]);
+
+      for(size_t i = 1; i != blocks; ++i)
+         {
+         xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS);
+         cipher().encrypt(&buf[BS*i]);
+         }
+
+      state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]);
+      }
+
+   return sz;
+   }
+
+void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+   const size_t BS = block_size();
+
+   const size_t bytes_in_final_block = (buffer.size()-offset) % BS;
+
+   padding().add_padding(buffer, bytes_in_final_block, BS);
+
+   BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary");
+
+   update(buffer, offset);
+   }
+
+bool CTS_Encryption::valid_nonce_length(size_t n) const
+   {
+   return (n == block_size());
+   }
+
+size_t CTS_Encryption::minimum_final_size() const
+   {
+   return block_size() + 1;
+   }
+
+size_t CTS_Encryption::output_length(size_t input_length) const
+   {
+   return input_length; // no ciphertext expansion in CTS
+   }
+
+void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   uint8_t* buf = buffer.data() + offset;
+   const size_t sz = buffer.size() - offset;
+
+   const size_t BS = block_size();
+
+   if(sz < BS + 1)
+      throw Encoding_Error(name() + ": insufficient data to encrypt");
+
+   if(sz % BS == 0)
+      {
+      update(buffer, offset);
+
+      // swap last two blocks
+      for(size_t i = 0; i != BS; ++i)
+         std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+      }
+   else
+      {
+      const size_t full_blocks = ((sz / BS) - 1) * BS;
+      const size_t final_bytes = sz - full_blocks;
+      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+      buffer.resize(full_blocks + offset);
+      update(buffer, offset);
+
+      xor_buf(last.data(), state_ptr(), BS);
+      cipher().encrypt(last.data());
+
+      for(size_t i = 0; i != final_bytes - BS; ++i)
+         {
+         last[i] ^= last[i + BS];
+         last[i + BS] ^= last[i];
+         }
+
+      cipher().encrypt(last.data());
+
+      buffer += last;
+      }
+   }
+
+size_t CBC_Decryption::output_length(size_t input_length) const
+   {
+   return input_length; // precise for CTS, worst case otherwise
+   }
+
+size_t CBC_Decryption::minimum_final_size() const
+   {
+   return block_size();
+   }
+
+size_t CBC_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+
+   const size_t BS = block_size();
+
+   BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+   size_t blocks = sz / BS;
+
+   while(blocks)
+      {
+      const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
+
+      cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);
+
+      xor_buf(m_tempbuf.data(), state_ptr(), BS);
+      xor_buf(&m_tempbuf[BS], buf, to_proc - BS);
+      copy_mem(state_ptr(), buf + (to_proc - BS), BS);
+
+      copy_mem(buf, m_tempbuf.data(), to_proc);
+
+      buf += to_proc;
+      blocks -= to_proc / BS;
+      }
+
+   return sz;
+   }
+
+void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+
+   const size_t BS = block_size();
+
+   if(sz == 0 || sz % BS)
+      throw Decoding_Error(name() + ": Ciphertext not a multiple of block size");
+
+   update(buffer, offset);
+
+   const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS);
+   buffer.resize(buffer.size() - pad_bytes); // remove padding
+   if(pad_bytes == 0 && padding().name() != "NoPadding")
+      {
+      throw Decoding_Error("Invalid CBC padding");
+      }
+   }
+
+void CBC_Decryption::reset()
+   {
+   CBC_Mode::reset();
+   zeroise(m_tempbuf);
+   }
+
+bool CTS_Decryption::valid_nonce_length(size_t n) const
+   {
+   return (n == block_size());
+   }
+
+size_t CTS_Decryption::minimum_final_size() const
+   {
+   return block_size() + 1;
+   }
+
+void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_STATE_CHECK(state().empty() == false);
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   const size_t BS = block_size();
+
+   if(sz < BS + 1)
+      throw Encoding_Error(name() + ": insufficient data to decrypt");
+
+   if(sz % BS == 0)
+      {
+      // swap last two blocks
+
+      for(size_t i = 0; i != BS; ++i)
+         std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+
+      update(buffer, offset);
+      }
+   else
+      {
+      const size_t full_blocks = ((sz / BS) - 1) * BS;
+      const size_t final_bytes = sz - full_blocks;
+      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+      buffer.resize(full_blocks + offset);
+      update(buffer, offset);
+
+      cipher().decrypt(last.data());
+
+      xor_buf(last.data(), &last[BS], final_bytes - BS);
+
+      for(size_t i = 0; i != final_bytes - BS; ++i)
+         std::swap(last[i], last[i + BS]);
+
+      cipher().decrypt(last.data());
+      xor_buf(last.data(), state_ptr(), BS);
+
+      buffer += last;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/cbc/cbc.h b/third_party/botan/src/lib/modes/cbc/cbc.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cbc/cbc.h
@@ -0,0 +1,157 @@
+/*
+* CBC mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CBC_H_
+#define BOTAN_MODE_CBC_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+#include <botan/mode_pad.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cbc.h)
+
+namespace Botan {
+
+/**
+* CBC Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Mode : public Cipher_Mode
+   {
+   public:
+      std::string name() const override;
+
+      size_t update_granularity() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      size_t default_nonce_length() const override;
+
+      bool valid_nonce_length(size_t n) const override;
+
+      void clear() override;
+
+      void reset() override;
+
+   protected:
+      CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding);
+
+      const BlockCipher& cipher() const { return *m_cipher; }
+
+      const BlockCipherModePaddingMethod& padding() const
+         {
+         BOTAN_ASSERT_NONNULL(m_padding);
+         return *m_padding;
+         }
+
+      size_t block_size() const { return m_block_size; }
+
+      secure_vector<uint8_t>& state() { return m_state; }
+
+      uint8_t* state_ptr() { return m_state.data(); }
+
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<BlockCipherModePaddingMethod> m_padding;
+      secure_vector<uint8_t> m_state;
+      size_t m_block_size;
+   };
+
+/**
+* CBC Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Encryption : public CBC_Mode
+   {
+   public:
+      /**
+      * @param cipher block cipher to use
+      * @param padding padding method to use
+      */
+      CBC_Encryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+         CBC_Mode(cipher, padding) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
+
+      size_t minimum_final_size() const override;
+   };
+
+/**
+* CBC Encryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Encryption final : public CBC_Encryption
+   {
+   public:
+      /**
+      * @param cipher block cipher to use
+      */
+      explicit CTS_Encryption(BlockCipher* cipher) : CBC_Encryption(cipher, nullptr) {}
+
+      size_t output_length(size_t input_length) const override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t minimum_final_size() const override;
+
+      bool valid_nonce_length(size_t n) const override;
+   };
+
+/**
+* CBC Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Decryption : public CBC_Mode
+   {
+   public:
+      /**
+      * @param cipher block cipher to use
+      * @param padding padding method to use
+      */
+      CBC_Decryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+         CBC_Mode(cipher, padding), m_tempbuf(update_granularity()) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
+
+      size_t minimum_final_size() const override;
+
+      void reset() override;
+
+   private:
+      secure_vector<uint8_t> m_tempbuf;
+   };
+
+/**
+* CBC Decryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Decryption final : public CBC_Decryption
+   {
+   public:
+      /**
+      * @param cipher block cipher to use
+      */
+      explicit CTS_Decryption(BlockCipher* cipher) : CBC_Decryption(cipher, nullptr) {}
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t minimum_final_size() const override;
+
+      bool valid_nonce_length(size_t n) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/cfb/cfb.cpp b/third_party/botan/src/lib/modes/cfb/cfb.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cfb/cfb.cpp
@@ -0,0 +1,229 @@
+/*
+* CFB Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cfb.h>
+
+namespace Botan {
+
+CFB_Mode::CFB_Mode(BlockCipher* cipher, size_t feedback_bits) :
+   m_cipher(cipher),
+   m_block_size(m_cipher->block_size()),
+   m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size)
+   {
+   if(feedback_bits % 8 || feedback() > cipher->block_size())
+      throw Invalid_Argument(name() + ": feedback bits " +
+                                  std::to_string(feedback_bits) + " not supported");
+   }
+
+void CFB_Mode::clear()
+   {
+   m_cipher->clear();
+   m_keystream.clear();
+   reset();
+   }
+
+void CFB_Mode::reset()
+   {
+   m_state.clear();
+   zeroise(m_keystream);
+   }
+
+std::string CFB_Mode::name() const
+   {
+   if(feedback() == cipher().block_size())
+      return cipher().name() + "/CFB";
+   else
+      return cipher().name() + "/CFB(" + std::to_string(feedback()*8) + ")";
+   }
+
+size_t CFB_Mode::output_length(size_t input_length) const
+   {
+   return input_length;
+   }
+
+size_t CFB_Mode::update_granularity() const
+   {
+   return feedback();
+   }
+
+size_t CFB_Mode::minimum_final_size() const
+   {
+   return 0;
+   }
+
+Key_Length_Specification CFB_Mode::key_spec() const
+   {
+   return cipher().key_spec();
+   }
+
+size_t CFB_Mode::default_nonce_length() const
+   {
+   return block_size();
+   }
+
+bool CFB_Mode::valid_nonce_length(size_t n) const
+   {
+   return (n == 0 || n == block_size());
+   }
+
+void CFB_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_cipher->set_key(key, length);
+   m_keystream.resize(m_cipher->block_size());
+   }
+
+void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   verify_key_set(!m_keystream.empty());
+
+   if(nonce_len == 0)
+      {
+      if(m_state.empty())
+         {
+         throw Invalid_State("CFB requires a non-empty initial nonce");
+         }
+      // No reason to encrypt state->keystream_buf, because no change
+      }
+   else
+      {
+      m_state.assign(nonce, nonce + nonce_len);
+      cipher().encrypt(m_state, m_keystream);
+      m_keystream_pos = 0;
+      }
+   }
+
+void CFB_Mode::shift_register()
+   {
+   const size_t shift = feedback();
+   const size_t carryover = block_size() - shift;
+
+   if(carryover > 0)
+      {
+      copy_mem(m_state.data(), &m_state[shift], carryover);
+      }
+   copy_mem(&m_state[carryover], m_keystream.data(), shift);
+   cipher().encrypt(m_state, m_keystream);
+   m_keystream_pos = 0;
+   }
+
+size_t CFB_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   verify_key_set(!m_keystream.empty());
+   BOTAN_STATE_CHECK(m_state.empty() == false);
+
+   const size_t shift = feedback();
+
+   size_t left = sz;
+
+   if(m_keystream_pos != 0)
+      {
+      const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+      xor_buf(m_keystream.data() + m_keystream_pos, buf, take);
+      copy_mem(buf, m_keystream.data() + m_keystream_pos, take);
+
+      m_keystream_pos += take;
+      left -= take;
+      buf += take;
+
+      if(m_keystream_pos == shift)
+         {
+         shift_register();
+         }
+      }
+
+   while(left >= shift)
+      {
+      xor_buf(m_keystream.data(), buf, shift);
+      copy_mem(buf, m_keystream.data(), shift);
+
+      left -= shift;
+      buf += shift;
+      shift_register();
+      }
+
+   if(left > 0)
+      {
+      xor_buf(m_keystream.data(), buf, left);
+      copy_mem(buf, m_keystream.data(), left);
+      m_keystream_pos += left;
+      }
+
+   return sz;
+   }
+
+void CFB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   update(buffer, offset);
+   }
+
+namespace {
+
+inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len)
+   {
+   for(size_t i = 0; i != len; ++i)
+      {
+      uint8_t k = key_buf[i];
+      key_buf[i] = buf[i];
+      buf[i] ^= k;
+      }
+   }
+
+}
+
+size_t CFB_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   verify_key_set(!m_keystream.empty());
+   BOTAN_STATE_CHECK(m_state.empty() == false);
+
+   const size_t shift = feedback();
+
+   size_t left = sz;
+
+   if(m_keystream_pos != 0)
+      {
+      const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+      xor_copy(buf, m_keystream.data() + m_keystream_pos, take);
+
+      m_keystream_pos += take;
+      left -= take;
+      buf += take;
+
+      if(m_keystream_pos == shift)
+         {
+         shift_register();
+         }
+      }
+
+   while(left >= shift)
+      {
+      xor_copy(buf, m_keystream.data(), shift);
+      left -= shift;
+      buf += shift;
+      shift_register();
+      }
+
+   if(left > 0)
+      {
+      xor_copy(buf, m_keystream.data(), left);
+      m_keystream_pos += left;
+      }
+
+   return sz;
+   }
+
+void CFB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   update(buffer, offset);
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/cfb/cfb.h b/third_party/botan/src/lib/modes/cfb/cfb.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cfb/cfb.h
@@ -0,0 +1,106 @@
+/*
+* CFB mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CFB_H_
+#define BOTAN_MODE_CFB_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cfb.h)
+
+namespace Botan {
+
+/**
+* CFB Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Mode : public Cipher_Mode
+   {
+   public:
+      std::string name() const override final;
+
+      size_t update_granularity() const override final;
+
+      size_t minimum_final_size() const override final;
+
+      Key_Length_Specification key_spec() const override final;
+
+      size_t output_length(size_t input_length) const override final;
+
+      size_t default_nonce_length() const override final;
+
+      bool valid_nonce_length(size_t n) const override final;
+
+      void clear() override final;
+
+      void reset() override final;
+   protected:
+      CFB_Mode(BlockCipher* cipher, size_t feedback_bits);
+
+      void shift_register();
+
+      size_t feedback() const { return m_feedback_bytes; }
+      const BlockCipher& cipher() const { return *m_cipher; }
+      size_t block_size() const { return m_block_size; }
+
+      secure_vector<uint8_t> m_state;
+      secure_vector<uint8_t> m_keystream;
+      size_t m_keystream_pos = 0;
+
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      const size_t m_block_size;
+      const size_t m_feedback_bytes;
+   };
+
+/**
+* CFB Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Encryption final : public CFB_Mode
+   {
+   public:
+      /**
+      * If feedback_bits is zero, cipher->block_size() bytes will be used.
+      * @param cipher block cipher to use
+      * @param feedback_bits number of bits fed back into the shift register,
+      * must be a multiple of 8
+      */
+      CFB_Encryption(BlockCipher* cipher, size_t feedback_bits) :
+         CFB_Mode(cipher, feedback_bits) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+/**
+* CFB Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Decryption final : public CFB_Mode
+   {
+   public:
+      /**
+      * If feedback_bits is zero, cipher->block_size() bytes will be used.
+      * @param cipher block cipher to use
+      * @param feedback_bits number of bits fed back into the shift register,
+      * must be a multiple of 8
+      */
+      CFB_Decryption(BlockCipher* cipher, size_t feedback_bits) :
+         CFB_Mode(cipher, feedback_bits) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/cipher_mode.cpp b/third_party/botan/src/lib/modes/cipher_mode.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cipher_mode.cpp
@@ -0,0 +1,205 @@
+/*
+* Cipher Modes
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/stream_mode.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+  #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+  #include <botan/aead.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CBC)
+  #include <botan/cbc.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+  #include <botan/cfb.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+  #include <botan/xts.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+  #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create_or_throw(const std::string& algo,
+                                                          Cipher_Dir direction,
+                                                          const std::string& provider)
+   {
+   if(auto mode = Cipher_Mode::create(algo, direction, provider))
+      return mode;
+
+   throw Lookup_Error("Cipher mode", algo, provider);
+   }
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo,
+                                                 Cipher_Dir direction,
+                                                 const std::string& provider)
+   {
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+   if(provider.empty() || provider == "commoncrypto")
+      {
+      std::unique_ptr<Cipher_Mode> commoncrypto_cipher(make_commoncrypto_cipher_mode(algo, direction));
+
+      if(commoncrypto_cipher)
+         return commoncrypto_cipher;
+
+      if(!provider.empty())
+         return std::unique_ptr<Cipher_Mode>();
+      }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider.empty() || provider == "openssl")
+      {
+      std::unique_ptr<Cipher_Mode> openssl_cipher(make_openssl_cipher_mode(algo, direction));
+
+      if(openssl_cipher)
+         return openssl_cipher;
+
+      if(!provider.empty())
+         return std::unique_ptr<Cipher_Mode>();
+      }
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   if(auto sc = StreamCipher::create(algo))
+      {
+      return std::unique_ptr<Cipher_Mode>(new Stream_Cipher_Mode(sc.release()));
+      }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+   if(auto aead = AEAD_Mode::create(algo, direction))
+      {
+      return std::unique_ptr<Cipher_Mode>(aead.release());
+      }
+#endif
+
+   if(algo.find('/') != std::string::npos)
+      {
+      const std::vector<std::string> algo_parts = split_on(algo, '/');
+      const std::string cipher_name = algo_parts[0];
+      const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+      if(mode_info.empty())
+         return std::unique_ptr<Cipher_Mode>();
+
+      std::ostringstream alg_args;
+
+      alg_args << '(' << cipher_name;
+      for(size_t i = 1; i < mode_info.size(); ++i)
+         alg_args << ',' << mode_info[i];
+      for(size_t i = 2; i < algo_parts.size(); ++i)
+         alg_args << ',' << algo_parts[i];
+      alg_args << ')';
+
+      const std::string mode_name = mode_info[0] + alg_args.str();
+      return Cipher_Mode::create(mode_name, direction, provider);
+      }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+   SCAN_Name spec(algo);
+
+   if(spec.arg_count() == 0)
+      {
+      return std::unique_ptr<Cipher_Mode>();
+      }
+
+   std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider));
+
+   if(!bc)
+      {
+      return std::unique_ptr<Cipher_Mode>();
+      }
+
+#if defined(BOTAN_HAS_MODE_CBC)
+   if(spec.algo_name() == "CBC")
+      {
+      const std::string padding = spec.arg(1, "PKCS7");
+
+      if(padding == "CTS")
+         {
+         if(direction == ENCRYPTION)
+            return std::unique_ptr<Cipher_Mode>(new CTS_Encryption(bc.release()));
+         else
+            return std::unique_ptr<Cipher_Mode>(new CTS_Decryption(bc.release()));
+         }
+      else
+         {
+         std::unique_ptr<BlockCipherModePaddingMethod> pad(get_bc_pad(padding));
+
+         if(pad)
+            {
+            if(direction == ENCRYPTION)
+               return std::unique_ptr<Cipher_Mode>(new CBC_Encryption(bc.release(), pad.release()));
+            else
+               return std::unique_ptr<Cipher_Mode>(new CBC_Decryption(bc.release(), pad.release()));
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+   if(spec.algo_name() == "XTS")
+      {
+      if(direction == ENCRYPTION)
+         return std::unique_ptr<Cipher_Mode>(new XTS_Encryption(bc.release()));
+      else
+         return std::unique_ptr<Cipher_Mode>(new XTS_Decryption(bc.release()));
+      }
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+   if(spec.algo_name() == "CFB")
+      {
+      const size_t feedback_bits = spec.arg_as_integer(1, 8*bc->block_size());
+      if(direction == ENCRYPTION)
+         return std::unique_ptr<Cipher_Mode>(new CFB_Encryption(bc.release(), feedback_bits));
+      else
+         return std::unique_ptr<Cipher_Mode>(new CFB_Decryption(bc.release(), feedback_bits));
+      }
+#endif
+
+#endif
+
+   return std::unique_ptr<Cipher_Mode>();
+   }
+
+//static
+std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec)
+   {
+   const std::vector<std::string>& possible = { "base", "openssl", "commoncrypto" };
+   std::vector<std::string> providers;
+   for(auto&& prov : possible)
+      {
+      std::unique_ptr<Cipher_Mode> mode = Cipher_Mode::create(algo_spec, ENCRYPTION, prov);
+      if(mode)
+         {
+         providers.push_back(prov); // available
+         }
+      }
+   return providers;
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/cipher_mode.h b/third_party/botan/src/lib/modes/cipher_mode.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/cipher_mode.h
@@ -0,0 +1,198 @@
+/*
+* Cipher Modes
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CIPHER_MODE_H_
+#define BOTAN_CIPHER_MODE_H_
+
+#include <botan/secmem.h>
+#include <botan/sym_algo.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* The two possible directions for cipher filters, determining whether they
+* actually perform encryption or decryption.
+*/
+enum Cipher_Dir : int { ENCRYPTION, DECRYPTION };
+
+/**
+* Interface for cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
+   {
+   public:
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      * @param algo_spec algorithm name
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * Create an AEAD mode
+      * @param algo the algorithm to create
+      * @param direction specify if this should be an encryption or decryption AEAD
+      * @param provider optional specification for provider to use
+      * @return an AEAD mode or a null pointer if not available
+      */
+      static std::unique_ptr<Cipher_Mode> create(const std::string& algo,
+                                                 Cipher_Dir direction,
+                                                 const std::string& provider = "");
+
+      /**
+      * Create an AEAD mode, or throw
+      * @param algo the algorithm to create
+      * @param direction specify if this should be an encryption or decryption AEAD
+      * @param provider optional specification for provider to use
+      * @return an AEAD mode, or throw an exception
+      */
+      static std::unique_ptr<Cipher_Mode> create_or_throw(const std::string& algo,
+                                                          Cipher_Dir direction,
+                                                          const std::string& provider = "");
+
+      /*
+      * Prepare for processing a message under the specified nonce
+      */
+      virtual void start_msg(const uint8_t nonce[], size_t nonce_len) = 0;
+
+      /**
+      * Begin processing a message.
+      * @param nonce the per message nonce
+      */
+      template<typename Alloc>
+      void start(const std::vector<uint8_t, Alloc>& nonce)
+         {
+         start_msg(nonce.data(), nonce.size());
+         }
+
+      /**
+      * Begin processing a message.
+      * @param nonce the per message nonce
+      * @param nonce_len length of nonce
+      */
+      void start(const uint8_t nonce[], size_t nonce_len)
+         {
+         start_msg(nonce, nonce_len);
+         }
+
+      /**
+      * Begin processing a message.
+      */
+      void start()
+         {
+         return start_msg(nullptr, 0);
+         }
+
+      /**
+      * Process message blocks
+      *
+      * Input must be a multiple of update_granularity
+      *
+      * Processes msg in place and returns bytes written. Normally
+      * this will be either msg_len (indicating the entire message was
+      * processed) or for certain AEAD modes zero (indicating that the
+      * mode requires the entire message be processed in one pass).
+      *
+      * @param msg the message to be processed
+      * @param msg_len length of the message in bytes
+      */
+      virtual size_t process(uint8_t msg[], size_t msg_len) = 0;
+
+      /**
+      * Process some data. Input must be in size update_granularity() uint8_t blocks.
+      * @param buffer in/out parameter which will possibly be resized
+      * @param offset an offset into blocks to begin processing
+      */
+      void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
+         {
+         BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+         uint8_t* buf = buffer.data() + offset;
+         const size_t buf_size = buffer.size() - offset;
+
+         const size_t written = process(buf, buf_size);
+         buffer.resize(offset + written);
+         }
+
+      /**
+      * Complete processing of a message.
+      *
+      * @param final_block in/out parameter which must be at least
+      *        minimum_final_size() bytes, and will be set to any final output
+      * @param offset an offset into final_block to begin processing
+      */
+      virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+      /**
+      * Returns the size of the output if this transform is used to process a
+      * message with input_length bytes. In most cases the answer is precise.
+      * If it is not possible to precise (namely for CBC decryption) instead a
+      * lower bound is returned.
+      */
+      virtual size_t output_length(size_t input_length) const = 0;
+
+      /**
+      * @return size of required blocks to update
+      */
+      virtual size_t update_granularity() const = 0;
+
+      /**
+      * @return required minimium size to finalize() - may be any
+      *         length larger than this.
+      */
+      virtual size_t minimum_final_size() const = 0;
+
+      /**
+      * @return the default size for a nonce
+      */
+      virtual size_t default_nonce_length() const = 0;
+
+      /**
+      * @return true iff nonce_len is a valid length for the nonce
+      */
+      virtual bool valid_nonce_length(size_t nonce_len) const = 0;
+
+      /**
+      * Resets just the message specific state and allows encrypting again under the existing key
+      */
+      virtual void reset() = 0;
+
+      /**
+      * @return true iff this mode provides authentication as well as
+      * confidentiality.
+      */
+      virtual bool authenticated() const { return false; }
+
+      /**
+      * @return the size of the authentication tag used (in bytes)
+      */
+      virtual size_t tag_size() const { return 0; }
+
+      /**
+      * @return provider information about this implementation. Default is "base",
+      * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+      */
+      virtual std::string provider() const { return "base"; }
+   };
+
+/**
+* Get a cipher mode by name (eg "AES-128/CBC" or "Serpent/XTS")
+* @param algo_spec cipher name
+* @param direction ENCRYPTION or DECRYPTION
+* @param provider provider implementation to choose
+*/
+inline Cipher_Mode* get_cipher_mode(const std::string& algo_spec,
+                                    Cipher_Dir direction,
+                                    const std::string& provider = "")
+   {
+   return Cipher_Mode::create(algo_spec, direction, provider).release();
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp b/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
@@ -0,0 +1,333 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2007,2013,2018,2020 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mode_pad.h>
+#include <botan/exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/**
+* Get a block cipher padding method by name
+*/
+BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec)
+   {
+   if(algo_spec == "NoPadding")
+      return new Null_Padding;
+
+   if(algo_spec == "PKCS7")
+      return new PKCS7_Padding;
+
+   if(algo_spec == "OneAndZeros")
+      return new OneAndZeros_Padding;
+
+   if(algo_spec == "X9.23")
+      return new ANSI_X923_Padding;
+
+   if(algo_spec == "ESP")
+      return new ESP_Padding;
+
+   return nullptr;
+   }
+
+/*
+* Pad with PKCS #7 Method
+*/
+void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer,
+                                size_t last_byte_pos,
+                                size_t BS) const
+   {
+   /*
+   Padding format is
+   01
+   0202
+   030303
+   ...
+   */
+   BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+   const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+   buffer.resize(buffer.size() + padding_len);
+
+   CT::poison(&last_byte_pos, 1);
+   CT::poison(buffer.data(), buffer.size());
+
+   BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+   BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+   const size_t start_of_last_block = buffer.size() - BS;
+   const size_t end_of_last_block = buffer.size();
+   const size_t start_of_padding = buffer.size() - padding_len;
+
+   for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+      {
+      auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+      buffer[i] = needs_padding.select(padding_len, buffer[i]);
+      }
+
+   CT::unpoison(buffer.data(), buffer.size());
+   CT::unpoison(last_byte_pos);
+   }
+
+/*
+* Unpad with PKCS #7 Method
+*/
+size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const
+   {
+   if(!valid_blocksize(input_length))
+      return input_length;
+
+   CT::poison(input, input_length);
+
+   const uint8_t last_byte = input[input_length-1];
+
+   /*
+   The input should == the block size so if the last byte exceeds
+   that then the padding is certainly invalid
+   */
+   auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length);
+
+   const size_t pad_pos = input_length - last_byte;
+
+   for(size_t i = 0; i != input_length - 1; ++i)
+      {
+      // Does this byte equal the expected pad byte?
+      const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte);
+
+      // Ignore values that are not part of the padding
+      const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+      bad_input |= in_range & (~pad_eq);
+      }
+
+   CT::unpoison(input, input_length);
+
+   return bad_input.select_and_unpoison(input_length, pad_pos);
+   }
+
+/*
+* Pad with ANSI X9.23 Method
+*/
+void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer,
+                                    size_t last_byte_pos,
+                                    size_t BS) const
+   {
+   /*
+   Padding format is
+   01
+   0002
+   000003
+   ...
+   */
+   BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+   const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+   buffer.resize(buffer.size() + padding_len);
+
+   CT::poison(&last_byte_pos, 1);
+   CT::poison(buffer.data(), buffer.size());
+
+   BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+   BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+   const size_t start_of_last_block = buffer.size() - BS;
+   const size_t end_of_zero_padding = buffer.size() - 1;
+   const size_t start_of_padding = buffer.size() - padding_len;
+
+   for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i)
+      {
+      auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+      buffer[i] = needs_padding.select(0, buffer[i]);
+      }
+
+   buffer[buffer.size()-1] = padding_len;
+   CT::unpoison(buffer.data(), buffer.size());
+   CT::unpoison(last_byte_pos);
+   }
+
+/*
+* Unpad with ANSI X9.23 Method
+*/
+size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const
+   {
+   if(!valid_blocksize(input_length))
+      return input_length;
+
+   CT::poison(input, input_length);
+
+   const size_t last_byte = input[input_length-1];
+
+   auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length);
+
+   const size_t pad_pos = input_length - last_byte;
+
+   for(size_t i = 0; i != input_length - 1; ++i)
+      {
+      // Ignore values that are not part of the padding
+      const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+      const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]);
+      bad_input |= pad_is_nonzero & in_range;
+      }
+
+   CT::unpoison(input, input_length);
+
+   return bad_input.select_and_unpoison(input_length, pad_pos);
+   }
+
+/*
+* Pad with One and Zeros Method
+*/
+void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer,
+                                      size_t last_byte_pos,
+                                      size_t BS) const
+   {
+   /*
+   Padding format is
+   80
+   8000
+   800000
+   ...
+   */
+
+   BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+   const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+   buffer.resize(buffer.size() + padding_len);
+
+   CT::poison(&last_byte_pos, 1);
+   CT::poison(buffer.data(), buffer.size());
+
+   BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+   BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+   const size_t start_of_last_block = buffer.size() - BS;
+   const size_t end_of_last_block = buffer.size();
+   const size_t start_of_padding = buffer.size() - padding_len;
+
+   for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+      {
+      auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding));
+      auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding));
+      buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i]));
+      }
+
+   CT::unpoison(buffer.data(), buffer.size());
+   CT::unpoison(last_byte_pos);
+   }
+
+/*
+* Unpad with One and Zeros Method
+*/
+size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const
+   {
+   if(!valid_blocksize(input_length))
+      return input_length;
+
+   CT::poison(input, input_length);
+
+   auto bad_input = CT::Mask<uint8_t>::cleared();
+   auto seen_0x80 = CT::Mask<uint8_t>::cleared();
+
+   size_t pad_pos = input_length - 1;
+   size_t i = input_length;
+
+   while(i)
+      {
+      const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80);
+      const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]);
+
+      seen_0x80 |= is_0x80;
+      pad_pos -= seen_0x80.if_not_set_return(1);
+      bad_input |= ~seen_0x80 & ~is_zero;
+      i--;
+      }
+   bad_input |= ~seen_0x80;
+
+   CT::unpoison(input, input_length);
+
+   return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos);
+   }
+
+/*
+* Pad with ESP Padding Method
+*/
+void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer,
+                              size_t last_byte_pos,
+                              size_t BS) const
+   {
+   /*
+   Padding format is
+   01
+   0102
+   010203
+   ...
+   */
+   BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+   const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos);
+
+   buffer.resize(buffer.size() + padding_len);
+
+   CT::poison(&last_byte_pos, 1);
+   CT::poison(buffer.data(), buffer.size());
+
+   BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0);
+   BOTAN_DEBUG_ASSERT(buffer.size() >= BS);
+
+   const size_t start_of_last_block = buffer.size() - BS;
+   const size_t end_of_last_block = buffer.size();
+   const size_t start_of_padding = buffer.size() - padding_len;
+
+   uint8_t pad_ctr = 0x01;
+
+   for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+      {
+      auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+      buffer[i] = needs_padding.select(pad_ctr, buffer[i]);
+      pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr);
+      }
+
+   CT::unpoison(buffer.data(), buffer.size());
+   CT::unpoison(last_byte_pos);
+   }
+
+/*
+* Unpad with ESP Padding Method
+*/
+size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const
+   {
+   if(!valid_blocksize(input_length))
+      return input_length;
+
+   CT::poison(input, input_length);
+
+   const uint8_t input_length_8 = static_cast<uint8_t>(input_length);
+   const uint8_t last_byte = input[input_length-1];
+
+   auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) |
+      CT::Mask<uint8_t>::is_gt(last_byte, input_length_8);
+
+   const uint8_t pad_pos = input_length_8 - last_byte;
+   size_t i = input_length_8 - 1;
+   while(i)
+      {
+      const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos);
+      const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1);
+
+      bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing;
+      --i;
+      }
+
+   CT::unpoison(input, input_length);
+   return bad_input.select_and_unpoison(input_length_8, pad_pos);
+   }
+
+
+}
diff --git a/third_party/botan/src/lib/modes/mode_pad/mode_pad.h b/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
@@ -0,0 +1,160 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2008,2013 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_PADDING_H_
+#define BOTAN_MODE_PADDING_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mode_pad.h)
+
+namespace Botan {
+
+/**
+* Block Cipher Mode Padding Method
+* This class is pretty limited, it cannot deal well with
+* randomized padding methods, or any padding method that
+* wants to add more than one block. For instance, it should
+* be possible to define cipher text stealing mode as simply
+* a padding mode for CBC, which happens to consume the last
+* two block (and requires use of the block cipher).
+*/
+class BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod
+   {
+   public:
+      /**
+      * Add padding bytes to buffer.
+      * @param buffer data to pad
+      * @param final_block_bytes size of the final block in bytes
+      * @param block_size size of each block in bytes
+      */
+      virtual void add_padding(secure_vector<uint8_t>& buffer,
+                               size_t final_block_bytes,
+                               size_t block_size) const = 0;
+
+      /**
+      * Remove padding bytes from block
+      * @param block the last block
+      * @param len the size of the block in bytes
+      * @return number of data bytes, or if the padding is invalid returns len
+      */
+      virtual size_t unpad(const uint8_t block[], size_t len) const = 0;
+
+      /**
+      * @param block_size of the cipher
+      * @return valid block size for this padding mode
+      */
+      virtual bool valid_blocksize(size_t block_size) const = 0;
+
+      /**
+      * @return name of the mode
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * virtual destructor
+      */
+      virtual ~BlockCipherModePaddingMethod() = default;
+   };
+
+/**
+* PKCS#7 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS7_Padding final : public BlockCipherModePaddingMethod
+   {
+   public:
+      void add_padding(secure_vector<uint8_t>& buffer,
+                       size_t final_block_bytes,
+                       size_t block_size) const override;
+
+      size_t unpad(const uint8_t[], size_t) const override;
+
+      bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+      std::string name() const override { return "PKCS7"; }
+   };
+
+/**
+* ANSI X9.23 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) ANSI_X923_Padding final : public BlockCipherModePaddingMethod
+   {
+   public:
+      void add_padding(secure_vector<uint8_t>& buffer,
+                       size_t final_block_bytes,
+                       size_t block_size) const override;
+
+      size_t unpad(const uint8_t[], size_t) const override;
+
+      bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+      std::string name() const override { return "X9.23"; }
+   };
+
+/**
+* One And Zeros Padding (ISO/IEC 9797-1, padding method 2)
+*/
+class BOTAN_PUBLIC_API(2,0) OneAndZeros_Padding final : public BlockCipherModePaddingMethod
+   {
+   public:
+      void add_padding(secure_vector<uint8_t>& buffer,
+                       size_t final_block_bytes,
+                       size_t block_size) const override;
+
+      size_t unpad(const uint8_t[], size_t) const override;
+
+      bool valid_blocksize(size_t bs) const override { return (bs > 2); }
+
+      std::string name() const override { return "OneAndZeros"; }
+   };
+
+/**
+* ESP Padding (RFC 4304)
+*/
+class BOTAN_PUBLIC_API(2,0) ESP_Padding final : public BlockCipherModePaddingMethod
+   {
+   public:
+      void add_padding(secure_vector<uint8_t>& buffer,
+                       size_t final_block_bytes,
+                       size_t block_size) const override;
+
+      size_t unpad(const uint8_t[], size_t) const override;
+
+      bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+      std::string name() const override { return "ESP"; }
+   };
+
+/**
+* Null Padding
+*/
+class BOTAN_PUBLIC_API(2,0) Null_Padding final : public BlockCipherModePaddingMethod
+   {
+   public:
+      void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override
+         {
+         /* no padding */
+         }
+
+      size_t unpad(const uint8_t[], size_t size) const override { return size; }
+
+      bool valid_blocksize(size_t) const override { return true; }
+
+      std::string name() const override { return "NoPadding"; }
+   };
+
+/**
+* Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7")
+* @param algo_spec block cipher padding mode name
+*/
+BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/stream_mode.h b/third_party/botan/src/lib/modes/stream_mode.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/stream_mode.h
@@ -0,0 +1,84 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREAM_MODE_H_
+#define BOTAN_STREAM_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   #include <botan/stream_cipher.h>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(stream_mode.h)
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+class BOTAN_PUBLIC_API(2,0) Stream_Cipher_Mode final : public Cipher_Mode
+   {
+   public:
+      /**
+      * @param cipher underyling stream cipher
+      */
+      explicit Stream_Cipher_Mode(StreamCipher* cipher) : m_cipher(cipher) {}
+
+      size_t process(uint8_t buf[], size_t sz) override
+         {
+         m_cipher->cipher1(buf, sz);
+         return sz;
+         }
+
+      void finish(secure_vector<uint8_t>& buf, size_t offset) override
+         { return update(buf, offset); }
+
+      size_t output_length(size_t input_length) const override { return input_length; }
+
+      size_t update_granularity() const override { return 1; }
+
+      size_t minimum_final_size() const override { return 0; }
+
+      size_t default_nonce_length() const override { return 0; }
+
+      bool valid_nonce_length(size_t nonce_len) const override
+         { return m_cipher->valid_iv_length(nonce_len); }
+
+      Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); }
+
+      std::string name() const override { return m_cipher->name(); }
+
+      void clear() override
+         {
+         m_cipher->clear();
+         reset();
+         }
+
+      void reset() override { /* no msg state */ }
+
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override
+         {
+         if(nonce_len > 0)
+            {
+            m_cipher->set_iv(nonce, nonce_len);
+            }
+         }
+
+      void key_schedule(const uint8_t key[], size_t length) override
+         {
+         m_cipher->set_key(key, length);
+         }
+
+      std::unique_ptr<StreamCipher> m_cipher;
+   };
+
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/modes/xts/xts.cpp b/third_party/botan/src/lib/modes/xts/xts.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/xts/xts.cpp
@@ -0,0 +1,248 @@
+/*
+* XTS Mode
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/xts.h>
+#include <botan/internal/poly_dbl.h>
+
+namespace Botan {
+
+XTS_Mode::XTS_Mode(BlockCipher* cipher) :
+   m_cipher(cipher),
+   m_cipher_block_size(m_cipher->block_size()),
+   m_cipher_parallelism(m_cipher->parallel_bytes())
+   {
+   if(poly_double_supported_size(m_cipher_block_size) == false)
+      {
+      throw Invalid_Argument("Cannot use " + cipher->name() + " with XTS");
+      }
+
+   m_tweak_cipher.reset(m_cipher->clone());
+   }
+
+void XTS_Mode::clear()
+   {
+   m_cipher->clear();
+   m_tweak_cipher->clear();
+   reset();
+   }
+
+void XTS_Mode::reset()
+   {
+   m_tweak.clear();
+   }
+
+std::string XTS_Mode::name() const
+   {
+   return cipher().name() + "/XTS";
+   }
+
+size_t XTS_Mode::minimum_final_size() const
+   {
+   return cipher_block_size();
+   }
+
+Key_Length_Specification XTS_Mode::key_spec() const
+   {
+   return cipher().key_spec().multiple(2);
+   }
+
+size_t XTS_Mode::default_nonce_length() const
+   {
+   return cipher_block_size();
+   }
+
+bool XTS_Mode::valid_nonce_length(size_t n) const
+   {
+   return cipher_block_size() == n;
+   }
+
+void XTS_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   const size_t key_half = length / 2;
+
+   if(length % 2 == 1 || !m_cipher->valid_keylength(key_half))
+      throw Invalid_Key_Length(name(), length);
+
+   m_cipher->set_key(key, key_half);
+   m_tweak_cipher->set_key(&key[key_half], key_half);
+   }
+
+void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   m_tweak.resize(update_granularity());
+   copy_mem(m_tweak.data(), nonce, nonce_len);
+   m_tweak_cipher->encrypt(m_tweak.data());
+
+   update_tweak(0);
+   }
+
+void XTS_Mode::update_tweak(size_t which)
+   {
+   const size_t BS = m_tweak_cipher->block_size();
+
+   if(which > 0)
+      poly_double_n_le(m_tweak.data(), &m_tweak[(which-1)*BS], BS);
+
+   const size_t blocks_in_tweak = update_granularity() / BS;
+
+   for(size_t i = 1; i < blocks_in_tweak; ++i)
+      poly_double_n_le(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
+   }
+
+size_t XTS_Encryption::output_length(size_t input_length) const
+   {
+   return input_length;
+   }
+
+size_t XTS_Encryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(tweak_set());
+   const size_t BS = cipher_block_size();
+
+   BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+   size_t blocks = sz / BS;
+
+   const size_t blocks_in_tweak = update_granularity() / BS;
+
+   while(blocks)
+      {
+      const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+      cipher().encrypt_n_xex(buf, tweak(), to_proc);
+
+      buf += to_proc * BS;
+      blocks -= to_proc;
+
+      update_tweak(to_proc);
+      }
+
+   return sz;
+   }
+
+void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS encrypt");
+
+   const size_t BS = cipher_block_size();
+
+   if(sz % BS == 0)
+      {
+      update(buffer, offset);
+      }
+   else
+      {
+      // steal ciphertext
+      const size_t full_blocks = ((sz / BS) - 1) * BS;
+      const size_t final_bytes = sz - full_blocks;
+      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+      buffer.resize(full_blocks + offset);
+      update(buffer, offset);
+
+      xor_buf(last, tweak(), BS);
+      cipher().encrypt(last);
+      xor_buf(last, tweak(), BS);
+
+      for(size_t i = 0; i != final_bytes - BS; ++i)
+         {
+         last[i] ^= last[i + BS];
+         last[i + BS] ^= last[i];
+         last[i] ^= last[i + BS];
+         }
+
+      xor_buf(last, tweak() + BS, BS);
+      cipher().encrypt(last);
+      xor_buf(last, tweak() + BS, BS);
+
+      buffer += last;
+      }
+   }
+
+size_t XTS_Decryption::output_length(size_t input_length) const
+   {
+   return input_length;
+   }
+
+size_t XTS_Decryption::process(uint8_t buf[], size_t sz)
+   {
+   BOTAN_STATE_CHECK(tweak_set());
+   const size_t BS = cipher_block_size();
+
+   BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+   size_t blocks = sz / BS;
+
+   const size_t blocks_in_tweak = update_granularity() / BS;
+
+   while(blocks)
+      {
+      const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+      cipher().decrypt_n_xex(buf, tweak(), to_proc);
+
+      buf += to_proc * BS;
+      blocks -= to_proc;
+
+      update_tweak(to_proc);
+      }
+
+   return sz;
+   }
+
+void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+   const size_t sz = buffer.size() - offset;
+   uint8_t* buf = buffer.data() + offset;
+
+   BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt");
+
+   const size_t BS = cipher_block_size();
+
+   if(sz % BS == 0)
+      {
+      update(buffer, offset);
+      }
+   else
+      {
+      // steal ciphertext
+      const size_t full_blocks = ((sz / BS) - 1) * BS;
+      const size_t final_bytes = sz - full_blocks;
+      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+      buffer.resize(full_blocks + offset);
+      update(buffer, offset);
+
+      xor_buf(last, tweak() + BS, BS);
+      cipher().decrypt(last);
+      xor_buf(last, tweak() + BS, BS);
+
+      for(size_t i = 0; i != final_bytes - BS; ++i)
+         {
+         last[i] ^= last[i + BS];
+         last[i + BS] ^= last[i];
+         last[i] ^= last[i + BS];
+         }
+
+      xor_buf(last, tweak(), BS);
+      cipher().decrypt(last);
+      xor_buf(last, tweak(), BS);
+
+      buffer += last;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/modes/xts/xts.h b/third_party/botan/src/lib/modes/xts/xts.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/modes/xts/xts.h
@@ -0,0 +1,103 @@
+/*
+* XTS mode, from IEEE P1619
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_XTS_H_
+#define BOTAN_MODE_XTS_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xts.h)
+
+namespace Botan {
+
+/**
+* IEEE P1619 XTS Mode
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Mode : public Cipher_Mode
+   {
+   public:
+      std::string name() const override;
+
+      size_t update_granularity() const override { return m_cipher_parallelism; }
+
+      size_t minimum_final_size() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      size_t default_nonce_length() const override;
+
+      bool valid_nonce_length(size_t n) const override;
+
+      void clear() override;
+
+      void reset() override;
+
+   protected:
+      explicit XTS_Mode(BlockCipher* cipher);
+
+      const uint8_t* tweak() const { return m_tweak.data(); }
+
+      bool tweak_set() const { return m_tweak.empty() == false; }
+
+      const BlockCipher& cipher() const { return *m_cipher; }
+
+      void update_tweak(size_t last_used);
+
+      size_t cipher_block_size() const { return m_cipher_block_size; }
+
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<BlockCipher> m_tweak_cipher;
+      secure_vector<uint8_t> m_tweak;
+      const size_t m_cipher_block_size;
+      const size_t m_cipher_parallelism;
+   };
+
+/**
+* IEEE P1619 XTS Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Encryption final : public XTS_Mode
+   {
+   public:
+      /**
+      * @param cipher underlying block cipher
+      */
+      explicit XTS_Encryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
+   };
+
+/**
+* IEEE P1619 XTS Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Decryption final : public XTS_Mode
+   {
+   public:
+      /**
+      * @param cipher underlying block cipher
+      */
+      explicit XTS_Decryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp b/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp
@@ -0,0 +1,181 @@
+/*
+* Bcrypt Password Hashing
+* (C) 2010,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bcrypt.h>
+#include <botan/rng.h>
+#include <botan/blowfish.h>
+#include <botan/base64.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+namespace {
+
+std::string bcrypt_base64_encode(const uint8_t input[], size_t length)
+   {
+   // Bcrypt uses a non-standard base64 alphabet
+   const uint8_t OPENBSD_BASE64_SUB[256] = {
+      0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x38, 0x80, 0x80, 0x80, 0x39,
+      0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x2E, 0x2F, 0x41, 0x42, 0x43, 0x44, 0x45,
+      0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
+      0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+      0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+      0x76, 0x77, 0x78, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80
+   };
+
+   std::string b64 = base64_encode(input, length);
+
+   while(b64.size() && b64[b64.size()-1] == '=')
+      b64 = b64.substr(0, b64.size() - 1);
+
+   for(size_t i = 0; i != b64.size(); ++i)
+      b64[i] = OPENBSD_BASE64_SUB[static_cast<uint8_t>(b64[i])];
+
+   return b64;
+   }
+
+std::vector<uint8_t> bcrypt_base64_decode(std::string input)
+   {
+   const uint8_t OPENBSD_BASE64_SUB[256] = {
+      0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x41, 0x42,
+      0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+      0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+      0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+      0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+      0x7A, 0x30, 0x31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+      0x80, 0x80, 0x80, 0x80
+   };
+
+   for(size_t i = 0; i != input.size(); ++i)
+      input[i] = OPENBSD_BASE64_SUB[static_cast<uint8_t>(input[i])];
+
+   return unlock(base64_decode(input));
+   }
+
+std::string make_bcrypt(const std::string& pass,
+                        const std::vector<uint8_t>& salt,
+                        uint16_t work_factor,
+                        char version)
+   {
+   /*
+   * On a 4 GHz Skylake, workfactor == 18 takes about 15 seconds to
+   * hash a password. This seems like a reasonable upper bound for the
+   * time being.
+   * Bcrypt allows up to work factor 31 (2^31 iterations)
+   */
+   BOTAN_ARG_CHECK(work_factor >= 4 && work_factor <= 18,
+                   "Invalid bcrypt work factor");
+
+   static const uint8_t BCRYPT_MAGIC[8*3] = {
+      0x4F, 0x72, 0x70, 0x68, 0x65, 0x61, 0x6E, 0x42,
+      0x65, 0x68, 0x6F, 0x6C, 0x64, 0x65, 0x72, 0x53,
+      0x63, 0x72, 0x79, 0x44, 0x6F, 0x75, 0x62, 0x74
+   };
+
+   Blowfish blowfish;
+
+   // Include the trailing NULL byte, so we need c_str() not data()
+   blowfish.salted_set_key(cast_char_ptr_to_uint8(pass.c_str()),
+                           pass.length() + 1,
+                           salt.data(),
+                           salt.size(),
+                           work_factor);
+
+   std::vector<uint8_t> ctext(BCRYPT_MAGIC, BCRYPT_MAGIC + 8*3);
+
+   for(size_t i = 0; i != 64; ++i)
+      blowfish.encrypt_n(ctext.data(), ctext.data(), 3);
+
+   std::string salt_b64 = bcrypt_base64_encode(salt.data(), salt.size());
+
+   std::string work_factor_str = std::to_string(work_factor);
+   if(work_factor_str.length() == 1)
+      work_factor_str = "0" + work_factor_str;
+
+   return "$2" + std::string(1, version) + "$" + work_factor_str +
+          "$" + salt_b64.substr(0, 22) +
+          bcrypt_base64_encode(ctext.data(), ctext.size() - 1);
+   }
+
+}
+
+std::string generate_bcrypt(const std::string& pass,
+                            RandomNumberGenerator& rng,
+                            uint16_t work_factor,
+                            char version)
+   {
+   /*
+   2a, 2b and 2y are identical for our purposes because our implementation of 2a
+   never had the truncation or signed char bugs in the first place.
+   */
+
+   if(version != 'a' && version != 'b' && version != 'y')
+      throw Invalid_Argument("Unknown bcrypt version '" + std::string(1, version) + "'");
+
+   std::vector<uint8_t> salt;
+   rng.random_vec(salt, 16);
+   return make_bcrypt(pass, salt, work_factor, version);
+   }
+
+bool check_bcrypt(const std::string& pass, const std::string& hash)
+   {
+   if(hash.size() != 60 ||
+      hash[0] != '$' || hash[1] != '2' || hash[3] != '$' || hash[6] != '$')
+      {
+      return false;
+      }
+
+   const char bcrypt_version = hash[2];
+
+   if(bcrypt_version != 'a' && bcrypt_version != 'b' && bcrypt_version != 'y')
+      {
+      return false;
+      }
+
+   const uint16_t workfactor = to_uint16(hash.substr(4, 2));
+
+   const std::vector<uint8_t> salt = bcrypt_base64_decode(hash.substr(7, 22));
+   if(salt.size() != 16)
+      return false;
+
+   const std::string compare = make_bcrypt(pass, salt, workfactor, bcrypt_version);
+
+   return same_mem(hash.data(), compare.data(), compare.size());
+   }
+
+}
diff --git a/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h b/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h
@@ -0,0 +1,49 @@
+/*
+* Bcrypt Password Hashing
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BCRYPT_H_
+#define BOTAN_BCRYPT_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Create a password hash using Bcrypt
+*
+* @warning The password is truncated at at most 72 characters; characters after
+*          that do not have any effect on the resulting hash. To support longer
+*          passwords, consider pre-hashing the password, for example by using
+*          the hex encoding of SHA-256 of the password as the input to bcrypt.
+*
+* @param password the password.
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param version which version to emit (may be 'a', 'b', or 'y' all of which
+*        have identical behavior in this implementation).
+*
+* @see https://www.usenix.org/events/usenix99/provos/provos_html/
+*/
+std::string BOTAN_PUBLIC_API(2,0) generate_bcrypt(const std::string& password,
+                                                  RandomNumberGenerator& rng,
+                                                  uint16_t work_factor = 12,
+                                                  char version = 'a');
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,0) check_bcrypt(const std::string& password,
+                                        const std::string& hash);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp b/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
@@ -0,0 +1,142 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/passhash9.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <botan/pbkdf2.h>
+#include <botan/base64.h>
+
+namespace Botan {
+
+namespace {
+
+const std::string MAGIC_PREFIX = "$9$";
+
+const size_t WORKFACTOR_BYTES = 2;
+const size_t ALGID_BYTES = 1;
+const size_t SALT_BYTES = 12; // 96 bits of salt
+const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
+
+const size_t WORK_FACTOR_SCALE = 10000;
+
+std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id)
+   {
+   if(alg_id == 0)
+      return MessageAuthenticationCode::create("HMAC(SHA-1)");
+   else if(alg_id == 1)
+      return MessageAuthenticationCode::create("HMAC(SHA-256)");
+   else if(alg_id == 2)
+      return MessageAuthenticationCode::create("CMAC(Blowfish)");
+   else if(alg_id == 3)
+      return MessageAuthenticationCode::create("HMAC(SHA-384)");
+   else if(alg_id == 4)
+      return MessageAuthenticationCode::create("HMAC(SHA-512)");
+   return nullptr;
+   }
+
+}
+
+std::string generate_passhash9(const std::string& pass,
+                               RandomNumberGenerator& rng,
+                               uint16_t work_factor,
+                               uint8_t alg_id)
+   {
+   BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512, "Invalid Passhash9 work factor");
+
+   std::unique_ptr<MessageAuthenticationCode> prf = get_pbkdf_prf(alg_id);
+
+   if(!prf)
+      throw Invalid_Argument("Passhash9: Algorithm id " +
+                             std::to_string(alg_id) +
+                             " is not defined");
+
+   PKCS5_PBKDF2 kdf(prf.release()); // takes ownership of pointer
+
+   secure_vector<uint8_t> salt(SALT_BYTES);
+   rng.randomize(salt.data(), salt.size());
+
+   const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+   secure_vector<uint8_t> blob;
+   blob.push_back(alg_id);
+   blob.push_back(get_byte(0, work_factor));
+   blob.push_back(get_byte(1, work_factor));
+   blob += salt;
+   blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN,
+                          pass,
+                          salt.data(), salt.size(),
+                          kdf_iterations).bits_of();
+
+   return MAGIC_PREFIX + base64_encode(blob);
+   }
+
+bool check_passhash9(const std::string& pass, const std::string& hash)
+   {
+   const size_t BINARY_LENGTH =
+     ALGID_BYTES +
+     WORKFACTOR_BYTES +
+     PASSHASH9_PBKDF_OUTPUT_LEN +
+     SALT_BYTES;
+
+   const size_t BASE64_LENGTH =
+      MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
+
+   if(hash.size() != BASE64_LENGTH)
+      return false;
+
+   for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
+      if(hash[i] != MAGIC_PREFIX[i])
+         return false;
+
+   secure_vector<uint8_t> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size());
+
+   if(bin.size() != BINARY_LENGTH)
+      return false;
+
+   uint8_t alg_id = bin[0];
+
+   const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
+
+   // Bug in the format, bad states shouldn't be representable, but are...
+   if(work_factor == 0)
+      return false;
+
+   if(work_factor > 512)
+      throw Invalid_Argument("Requested passhash9 work factor " +
+                             std::to_string(work_factor) + " is too large");
+
+   const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+   std::unique_ptr<MessageAuthenticationCode> pbkdf_prf = get_pbkdf_prf(alg_id);
+
+   if(!pbkdf_prf)
+      return false; // unknown algorithm, reject
+
+   PKCS5_PBKDF2 kdf(pbkdf_prf.release()); // takes ownership of pointer
+
+   secure_vector<uint8_t> cmp = kdf.derive_key(
+      PASSHASH9_PBKDF_OUTPUT_LEN,
+      pass,
+      &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
+      kdf_iterations).bits_of();
+
+   return constant_time_compare(cmp.data(),
+                   &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
+                   PASSHASH9_PBKDF_OUTPUT_LEN);
+   }
+
+bool is_passhash9_alg_supported(uint8_t alg_id)
+   {
+   if (get_pbkdf_prf(alg_id))
+      {
+      return true;
+      }
+   return false;
+   }
+
+}
diff --git a/third_party/botan/src/lib/passhash/passhash9/passhash9.h b/third_party/botan/src/lib/passhash/passhash9/passhash9.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/passhash/passhash9/passhash9.h
@@ -0,0 +1,52 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PASSHASH9_H_
+#define BOTAN_PASSHASH9_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Create a password hash using PBKDF2
+* @param password the password
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param alg_id specifies which PRF to use with PBKDF2
+*        0 is HMAC(SHA-1)
+*        1 is HMAC(SHA-256)
+*        2 is CMAC(Blowfish)
+*        3 is HMAC(SHA-384)
+*        4 is HMAC(SHA-512)
+*        all other values are currently undefined
+*/
+std::string BOTAN_PUBLIC_API(2,0) generate_passhash9(const std::string& password,
+                                         RandomNumberGenerator& rng,
+                                         uint16_t work_factor = 15,
+                                         uint8_t alg_id = 4);
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,0) check_passhash9(const std::string& password,
+                               const std::string& hash);
+
+/**
+* Check if the PRF used with PBKDF2 is supported
+* @param alg_id alg_id used in generate_passhash9()
+*/
+bool BOTAN_PUBLIC_API(2,3) is_passhash9_alg_supported(uint8_t alg_id);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp b/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp
@@ -0,0 +1,443 @@
+/**
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/loadstor.h>
+#include <botan/hash.h>
+#include <botan/mem_ops.h>
+#include <botan/rotate.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+static const size_t SYNC_POINTS = 4;
+
+secure_vector<uint8_t> argon2_H0(HashFunction& blake2b,
+                                 size_t output_len,
+                                 const char* password, size_t password_len,
+                                 const uint8_t salt[], size_t salt_len,
+                                 const uint8_t key[], size_t key_len,
+                                 const uint8_t ad[], size_t ad_len,
+                                 size_t y, size_t p, size_t M, size_t t)
+   {
+   const uint8_t v = 19; // Argon2 version code
+
+   blake2b.update_le(static_cast<uint32_t>(p));
+   blake2b.update_le(static_cast<uint32_t>(output_len));
+   blake2b.update_le(static_cast<uint32_t>(M));
+   blake2b.update_le(static_cast<uint32_t>(t));
+   blake2b.update_le(static_cast<uint32_t>(v));
+   blake2b.update_le(static_cast<uint32_t>(y));
+
+   blake2b.update_le(static_cast<uint32_t>(password_len));
+   blake2b.update(cast_char_ptr_to_uint8(password), password_len);
+
+   blake2b.update_le(static_cast<uint32_t>(salt_len));
+   blake2b.update(salt, salt_len);
+
+   blake2b.update_le(static_cast<uint32_t>(key_len));
+   blake2b.update(key, key_len);
+
+   blake2b.update_le(static_cast<uint32_t>(ad_len));
+   blake2b.update(ad, ad_len);
+
+   return blake2b.final();
+   }
+
+void Htick(secure_vector<uint8_t>& T,
+           uint8_t output[],
+           size_t output_len,
+           HashFunction& blake2b,
+           const secure_vector<uint8_t>& H0,
+           size_t p0, size_t p1)
+   {
+   BOTAN_ASSERT_NOMSG(output_len % 64 == 0);
+
+   blake2b.update_le(static_cast<uint32_t>(output_len));
+   blake2b.update(H0);
+   blake2b.update_le(static_cast<uint32_t>(p0));
+   blake2b.update_le(static_cast<uint32_t>(p1));
+
+   blake2b.final(&T[0]);
+
+   while(output_len > 64)
+      {
+      copy_mem(output, &T[0], 32);
+      output_len -= 32;
+      output += 32;
+
+      blake2b.update(T);
+      blake2b.final(&T[0]);
+      }
+
+   if(output_len > 0)
+      copy_mem(output, &T[0], output_len);
+   }
+
+void extract_key(uint8_t output[], size_t output_len,
+                 const secure_vector<uint64_t>& B,
+                 size_t memory, size_t threads)
+   {
+   const size_t lanes = memory / threads;
+
+   secure_vector<uint64_t> sum(128);
+
+   for(size_t lane = 0; lane != threads; ++lane)
+      {
+      size_t start = 128*(lane * lanes + lanes - 1);
+      size_t end = 128*(lane * lanes + lanes);
+
+      for(size_t j = start; j != end; ++j)
+         {
+         sum[j % 128] ^= B[j];
+         }
+      }
+
+   secure_vector<uint8_t> sum8(1024);
+   copy_out_le(sum8.data(), 1024, sum.data());
+
+   if(output_len <= 64)
+      {
+      std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")");
+      blake2b->update_le(static_cast<uint32_t>(output_len));
+      blake2b->update(sum8.data(), sum8.size());
+      blake2b->final(output);
+      }
+   else
+      {
+      secure_vector<uint8_t> T(64);
+
+      std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(512)");
+      blake2b->update_le(static_cast<uint32_t>(output_len));
+      blake2b->update(sum8.data(), sum8.size());
+      blake2b->final(&T[0]);
+
+      while(output_len > 64)
+         {
+         copy_mem(output, &T[0], 32);
+         output_len -= 32;
+         output += 32;
+
+         if(output_len > 64)
+            {
+            blake2b->update(T);
+            blake2b->final(&T[0]);
+            }
+         }
+
+      if(output_len == 64)
+         {
+         blake2b->update(T);
+         blake2b->final(output);
+         }
+      else
+         {
+         std::unique_ptr<HashFunction> blake2b_f = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")");
+         blake2b_f->update(T);
+         blake2b_f->final(output);
+         }
+      }
+   }
+
+void init_blocks(secure_vector<uint64_t>& B,
+                 HashFunction& blake2b,
+                 const secure_vector<uint8_t>& H0,
+                 size_t memory,
+                 size_t threads)
+   {
+   BOTAN_ASSERT_NOMSG(B.size() >= threads*256);
+
+   secure_vector<uint8_t> H(1024);
+   secure_vector<uint8_t> T(blake2b.output_length());
+
+   for(size_t i = 0; i != threads; ++i)
+      {
+      const size_t B_off = i * (memory / threads);
+
+      BOTAN_ASSERT_NOMSG(B.size() >= 128*(B_off+2));
+
+      Htick(T, &H[0], H.size(), blake2b, H0, 0, i);
+
+      for(size_t j = 0; j != 128; ++j)
+         {
+         B[128*B_off+j] = load_le<uint64_t>(H.data(), j);
+         }
+
+      Htick(T, &H[0], H.size(), blake2b, H0, 1, i);
+
+      for(size_t j = 0; j != 128; ++j)
+         {
+         B[128*(B_off+1)+j] = load_le<uint64_t>(H.data(), j);
+         }
+      }
+   }
+
+inline void blamka_G(uint64_t& A, uint64_t& B, uint64_t& C, uint64_t& D)
+   {
+   A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B);
+   D = rotr<32>(A ^ D);
+
+   C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D);
+   B = rotr<24>(B ^ C);
+
+   A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B);
+   D = rotr<16>(A ^ D);
+
+   C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D);
+   B = rotr<63>(B ^ C);
+   }
+
+inline void blamka(uint64_t& V0, uint64_t& V1, uint64_t& V2, uint64_t& V3,
+                   uint64_t& V4, uint64_t& V5, uint64_t& V6, uint64_t& V7,
+                   uint64_t& V8, uint64_t& V9, uint64_t& VA, uint64_t& VB,
+                   uint64_t& VC, uint64_t& VD, uint64_t& VE, uint64_t& VF)
+   {
+   blamka_G(V0, V4, V8, VC);
+   blamka_G(V1, V5, V9, VD);
+   blamka_G(V2, V6, VA, VE);
+   blamka_G(V3, V7, VB, VF);
+
+   blamka_G(V0, V5, VA, VF);
+   blamka_G(V1, V6, VB, VC);
+   blamka_G(V2, V7, V8, VD);
+   blamka_G(V3, V4, V9, VE);
+   }
+
+void process_block_xor(secure_vector<uint64_t>& T,
+                       secure_vector<uint64_t>& B,
+                       size_t offset,
+                       size_t prev,
+                       size_t new_offset)
+   {
+   for(size_t i = 0; i != 128; ++i)
+      T[i] = B[128*prev+i] ^ B[128*new_offset+i];
+
+   for(size_t i = 0; i != 128; i += 16)
+      {
+      blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3],
+             T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7],
+             T[i+ 8], T[i+ 9], T[i+10], T[i+11],
+             T[i+12], T[i+13], T[i+14], T[i+15]);
+      }
+
+   for(size_t i = 0; i != 128 / 8; i += 2)
+      {
+      blamka(T[    i], T[    i+1], T[ 16+i], T[ 16+i+1],
+             T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1],
+             T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1],
+             T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]);
+      }
+
+   for(size_t i = 0; i != 128; ++i)
+      B[128*offset + i] ^= T[i] ^ B[128*prev+i] ^ B[128*new_offset+i];
+   }
+
+void gen_2i_addresses(secure_vector<uint64_t>& T, secure_vector<uint64_t>& B,
+                      size_t n, size_t lane, size_t slice, size_t memory,
+                      size_t time, size_t mode, size_t cnt)
+   {
+   BOTAN_ASSERT_NOMSG(B.size() == 128);
+   BOTAN_ASSERT_NOMSG(T.size() == 128);
+
+   clear_mem(B.data(), B.size());
+   B[0] = n;
+   B[1] = lane;
+   B[2] = slice;
+   B[3] = memory;
+   B[4] = time;
+   B[5] = mode;
+   B[6] = cnt;
+
+   for(size_t r = 0; r != 2; ++r)
+      {
+      copy_mem(T.data(), B.data(), B.size());
+
+      for(size_t i = 0; i != 128; i += 16)
+         {
+         blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3],
+                T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7],
+                T[i+ 8], T[i+ 9], T[i+10], T[i+11],
+                T[i+12], T[i+13], T[i+14], T[i+15]);
+         }
+      for(size_t i = 0; i != 128 / 8; i += 2)
+         {
+         blamka(T[    i], T[    i+1], T[ 16+i], T[ 16+i+1],
+                T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1],
+                T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1],
+                T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]);
+         }
+
+      for(size_t i = 0; i != 128; ++i)
+         B[i] ^= T[i];
+      }
+   }
+
+uint32_t index_alpha(uint64_t random,
+                     size_t lanes,
+                     size_t segments,
+                     size_t threads,
+                     size_t n,
+                     size_t slice,
+                     size_t lane,
+                     size_t index)
+   {
+   size_t ref_lane = static_cast<uint32_t>(random >> 32) % threads;
+
+   if(n == 0 && slice == 0)
+      ref_lane = lane;
+
+   size_t m = 3*segments;
+   size_t s = ((slice+1) % 4)*segments;
+
+   if(lane == ref_lane)
+      m += index;
+
+   if(n == 0) {
+         m = slice*segments;
+         s = 0;
+         if(slice == 0 || lane == ref_lane)
+            m += index;
+   }
+
+   if(index == 0 || lane == ref_lane)
+      m -= 1;
+
+   uint64_t p = static_cast<uint32_t>(random);
+   p = (p * p) >> 32;
+   p = (p * m) >> 32;
+
+   return static_cast<uint32_t>(ref_lane*lanes + (s + m - (p+1)) % lanes);
+   }
+
+void process_block_argon2d(secure_vector<uint64_t>& T,
+                           secure_vector<uint64_t>& B,
+                           size_t n, size_t slice, size_t lane,
+                           size_t lanes, size_t segments, size_t threads)
+   {
+   size_t index = 0;
+   if(n == 0 && slice == 0)
+      index = 2;
+
+   while(index < segments)
+      {
+      const size_t offset = lane*lanes + slice*segments + index;
+
+      size_t prev = offset - 1;
+      if(index == 0 && slice == 0)
+         prev += lanes;
+
+      const uint64_t random = B.at(128*prev);
+      const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index);
+
+      process_block_xor(T, B, offset, prev, new_offset);
+
+      index += 1;
+      }
+   }
+
+void process_block_argon2i(secure_vector<uint64_t>& T,
+                           secure_vector<uint64_t>& B,
+                           size_t n, size_t slice, size_t lane,
+                           size_t lanes, size_t segments, size_t threads, uint8_t mode,
+                           size_t memory, size_t time)
+   {
+   size_t index = 0;
+   if(n == 0 && slice == 0)
+      index = 2;
+
+   secure_vector<uint64_t> addresses(128);
+   size_t address_counter = 1;
+
+   gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter);
+
+   while(index < segments)
+      {
+      const size_t offset = lane*lanes + slice*segments + index;
+
+      size_t prev = offset - 1;
+      if(index == 0 && slice == 0)
+         prev += lanes;
+
+      if(index > 0 && index % 128 == 0)
+         {
+         address_counter += 1;
+         gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter);
+         }
+
+      const uint64_t random = addresses[index % 128];
+      const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index);
+
+      process_block_xor(T, B, offset, prev, new_offset);
+
+      index += 1;
+      }
+   }
+
+void process_blocks(secure_vector<uint64_t>& B,
+                    size_t t,
+                    size_t memory,
+                    size_t threads,
+                    uint8_t mode)
+   {
+   const size_t lanes = memory / threads;
+   const size_t segments = lanes / SYNC_POINTS;
+
+   secure_vector<uint64_t> T(128);
+   for(size_t n = 0; n != t; ++n)
+      {
+      for(size_t slice = 0; slice != SYNC_POINTS; ++slice)
+         {
+         // TODO can run this in Thread_Pool
+         for(size_t lane = 0; lane != threads; ++lane)
+            {
+            if(mode == 1 || (mode == 2 && n == 0 && slice < SYNC_POINTS/2))
+               process_block_argon2i(T, B, n, slice, lane, lanes, segments, threads, mode, memory, t);
+            else
+               process_block_argon2d(T, B, n, slice, lane, lanes, segments, threads);
+            }
+         }
+      }
+
+   }
+
+}
+
+void argon2(uint8_t output[], size_t output_len,
+            const char* password, size_t password_len,
+            const uint8_t salt[], size_t salt_len,
+            const uint8_t key[], size_t key_len,
+            const uint8_t ad[], size_t ad_len,
+            uint8_t mode, size_t threads, size_t M, size_t t)
+   {
+   BOTAN_ARG_CHECK(mode == 0 || mode == 1 || mode == 2, "Unknown Argon2 mode parameter");
+   BOTAN_ARG_CHECK(output_len >= 4, "Invalid Argon2 output length");
+   BOTAN_ARG_CHECK(threads >= 1 && threads <= 128, "Invalid Argon2 threads parameter");
+   BOTAN_ARG_CHECK(M >= 8*threads && M <= 8192*1024, "Invalid Argon2 M parameter");
+   BOTAN_ARG_CHECK(t >= 1, "Invalid Argon2 t parameter");
+
+   std::unique_ptr<HashFunction> blake2 = HashFunction::create_or_throw("BLAKE2b");
+
+   const auto H0 = argon2_H0(*blake2, output_len,
+                             password, password_len,
+                             salt, salt_len,
+                             key, key_len,
+                             ad, ad_len,
+                             mode, threads, M, t);
+
+   const size_t memory = (M / (SYNC_POINTS*threads)) * (SYNC_POINTS*threads);
+
+   secure_vector<uint64_t> B(memory * 1024/8);
+
+   init_blocks(B, *blake2, H0, memory, threads);
+   process_blocks(B, t, memory, threads, mode);
+
+   clear_mem(output, output_len);
+   extract_key(output, output_len, B, memory, threads);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/argon2/argon2.h b/third_party/botan/src/lib/pbkdf/argon2/argon2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/argon2/argon2.h
@@ -0,0 +1,118 @@
+/**
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ARGON2_H_
+#define BOTAN_ARGON2_H_
+
+#include <botan/pwdhash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(argon2.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Argon2 key derivation function
+*/
+class BOTAN_PUBLIC_API(2,11) Argon2 final : public PasswordHash
+   {
+   public:
+      Argon2(uint8_t family, size_t M, size_t t, size_t p);
+
+      Argon2(const Argon2& other) = default;
+      Argon2& operator=(const Argon2&) = default;
+
+      /**
+      * Derive a new key under the current Argon2 parameter set
+      */
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len) const override;
+
+      std::string to_string() const override;
+
+      size_t M() const { return m_M; }
+      size_t t() const { return m_t; }
+      size_t p() const { return m_p; }
+
+      size_t iterations() const override { return t(); }
+
+      size_t parallelism() const override { return p(); }
+
+      size_t memory_param() const override { return M(); }
+
+      size_t total_memory_usage() const override { return M() * 1024; }
+
+   private:
+      uint8_t m_family;
+      size_t m_M, m_t, m_p;
+   };
+
+class BOTAN_PUBLIC_API(2,11) Argon2_Family final : public PasswordHashFamily
+   {
+   public:
+      Argon2_Family(uint8_t family);
+
+      std::string name() const override;
+
+      std::unique_ptr<PasswordHash> tune(size_t output_length,
+                                         std::chrono::milliseconds msec,
+                                         size_t max_memory) const override;
+
+      std::unique_ptr<PasswordHash> default_params() const override;
+
+      std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+      std::unique_ptr<PasswordHash> from_params(
+         size_t M, size_t t, size_t p) const override;
+   private:
+      const uint8_t m_family;
+   };
+
+/**
+* Argon2 key derivation function
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param password_len the length of password
+* @param salt the salt
+* @param salt_len length of salt
+* @param key an optional secret key
+* @param key_len the length of key
+* @param ad an optional additional input
+* @param ad_len the length of ad
+* @param y the Argon2 variant (0 = Argon2d, 1 = Argon2i, 2 = Argon2id)
+* @param p the parallelization parameter
+* @param M the amount of memory to use in Kb
+* @param t the number of iterations to use
+*/
+void BOTAN_PUBLIC_API(2,11) argon2(uint8_t output[], size_t output_len,
+                                   const char* password, size_t password_len,
+                                   const uint8_t salt[], size_t salt_len,
+                                   const uint8_t key[], size_t key_len,
+                                   const uint8_t ad[], size_t ad_len,
+                                   uint8_t y, size_t p, size_t M, size_t t);
+
+std::string BOTAN_PUBLIC_API(2,11)
+   argon2_generate_pwhash(const char* password, size_t password_len,
+                          RandomNumberGenerator& rng,
+                          size_t p, size_t M, size_t t,
+                          uint8_t y = 2, size_t salt_len = 16, size_t output_len = 32);
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param password_len the length of password
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,11) argon2_check_pwhash(const char* password, size_t password_len,
+                                                const std::string& hash);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp b/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp
@@ -0,0 +1,125 @@
+/**
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/rng.h>
+#include <botan/base64.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+std::string strip_padding(std::string s)
+   {
+   while(s.size() > 0 && s[s.size()-1] == '=')
+      s.resize(s.size() - 1);
+   return s;
+   }
+
+}
+
+std::string argon2_generate_pwhash(const char* password, size_t password_len,
+                                   RandomNumberGenerator& rng,
+                                   size_t p, size_t M, size_t t,
+                                   uint8_t y, size_t salt_len, size_t output_len)
+   {
+   std::vector<uint8_t> salt(salt_len);
+   rng.randomize(salt.data(), salt.size());
+
+   std::vector<uint8_t> output(output_len);
+   argon2(output.data(), output.size(),
+          password, password_len,
+          salt.data(), salt.size(),
+          nullptr, 0,
+          nullptr, 0,
+          y, p, M, t);
+
+   std::ostringstream oss;
+
+   if(y == 0)
+      oss << "$argon2d$";
+   else if(y == 1)
+      oss << "$argon2i$";
+   else
+      oss << "$argon2id$";
+
+   oss << "v=19$m=" << M << ",t=" << t << ",p=" << p << "$";
+   oss << strip_padding(base64_encode(salt)) << "$" << strip_padding(base64_encode(output));
+
+   return oss.str();
+   }
+
+bool argon2_check_pwhash(const char* password, size_t password_len,
+                         const std::string& input_hash)
+   {
+   const std::vector<std::string> parts = split_on(input_hash, '$');
+
+   if(parts.size() != 5)
+      return false;
+
+   uint8_t family = 0;
+
+   if(parts[0] == "argon2d")
+      family = 0;
+   else if(parts[0] == "argon2i")
+      family = 1;
+   else if(parts[0] == "argon2id")
+      family = 2;
+   else
+      return false;
+
+   if(parts[1] != "v=19")
+      return false;
+
+   const std::vector<std::string> params = split_on(parts[2], ',');
+
+   if(params.size() != 3)
+      return false;
+
+   size_t M = 0, t = 0, p = 0;
+
+   for(auto param_str : params)
+      {
+      const std::vector<std::string> param = split_on(param_str, '=');
+
+      if(param.size() != 2)
+         return false;
+
+      const std::string key = param[0];
+      const size_t val = to_u32bit(param[1]);
+      if(key == "m")
+         M = val;
+      else if(key == "t")
+         t = val;
+      else if(key == "p")
+         p = val;
+      else
+         return false;
+      }
+
+   std::vector<uint8_t> salt(base64_decode_max_output(parts[3].size()));
+   salt.resize(base64_decode(salt.data(), parts[3], false));
+
+   std::vector<uint8_t> hash(base64_decode_max_output(parts[4].size()));
+   hash.resize(base64_decode(hash.data(), parts[4], false));
+
+   if(hash.size() < 4)
+      return false;
+
+   std::vector<uint8_t> generated(hash.size());
+   argon2(generated.data(), generated.size(),
+          password, password_len,
+          salt.data(), salt.size(),
+          nullptr, 0,
+          nullptr, 0,
+          family, p, M, t);
+
+   return constant_time_compare(generated.data(), hash.data(), generated.size());
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp b/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
@@ -0,0 +1,154 @@
+/**
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/timer.h>
+#include <algorithm>
+
+namespace Botan {
+
+Argon2::Argon2(uint8_t family, size_t M, size_t t, size_t p) :
+   m_family(family),
+   m_M(M),
+   m_t(t),
+   m_p(p)
+   {}
+
+void Argon2::derive_key(uint8_t output[], size_t output_len,
+                        const char* password, size_t password_len,
+                        const uint8_t salt[], size_t salt_len) const
+   {
+   argon2(output, output_len,
+          password, password_len,
+          salt, salt_len,
+          nullptr, 0,
+          nullptr, 0,
+          m_family, m_p, m_M, m_t);
+   }
+
+namespace {
+
+std::string argon2_family_name(uint8_t f)
+   {
+   switch(f)
+      {
+      case 0:
+         return "Argon2d";
+      case 1:
+         return "Argon2i";
+      case 2:
+         return "Argon2id";
+      default:
+         throw Invalid_Argument("Unknown Argon2 parameter");
+      }
+   }
+
+}
+
+std::string Argon2::to_string() const
+   {
+   return argon2_family_name(m_family) + "(" +
+      std::to_string(m_M) + "," +
+      std::to_string(m_t) + "," +
+      std::to_string(m_p) + ")";
+  }
+
+Argon2_Family::Argon2_Family(uint8_t family) : m_family(family)
+   {
+   if(m_family != 0 && m_family != 1 && m_family != 2)
+      throw Invalid_Argument("Unknown Argon2 family identifier");
+   }
+
+std::string Argon2_Family::name() const
+   {
+   return argon2_family_name(m_family);
+   }
+
+std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
+                                                  std::chrono::milliseconds msec,
+                                                  size_t max_memory) const
+   {
+   const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024;
+
+   // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
+   // costs for larger params. Default is 36 MiB, or use 128 for long times.
+   const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024;
+   const size_t p = 1;
+   size_t t = 1;
+
+   Timer timer("Argon2");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+   timer.run_until_elapsed(tune_time, [&]() {
+      uint8_t output[64] = { 0 };
+      argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, tune_M, t);
+      });
+
+   if(timer.events() == 0 || timer.value() == 0)
+      return default_params();
+
+   size_t M = 4*1024;
+
+   const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M));
+
+   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+   /*
+   * Argon2 scaling rules:
+   * k*M, k*t, k*p all increase cost by about k
+   *
+   * Since we don't even take advantage of p > 1, we prefer increasing
+   * t or M instead.
+   *
+   * If possible to increase M, prefer that.
+   */
+
+   uint64_t est_nsec = measured_time;
+
+   if(est_nsec < target_nsec && M < max_kib)
+      {
+      const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
+      const uint64_t mem_headroom = max_kib / M;
+
+      const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom);
+      M *= static_cast<size_t>(M_mult);
+      est_nsec *= M_mult;
+      }
+
+   if(est_nsec < target_nsec)
+      {
+      const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
+      t *= static_cast<size_t>(desired_cost_increase);
+      }
+
+   return this->from_params(M, t, p);
+   }
+
+std::unique_ptr<PasswordHash> Argon2_Family::default_params() const
+   {
+   return this->from_params(128*1024, 1, 1);
+   }
+
+std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const
+   {
+   /*
+   These choices are arbitrary, but should not change in future
+   releases since they will break applications expecting deterministic
+   mapping from iteration count to params
+   */
+   const size_t M = iter;
+   const size_t t = 1;
+   const size_t p = 1;
+   return this->from_params(M, t, p);
+   }
+
+std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const
+   {
+   return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp b/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bcrypt_pbkdf.h>
+#include <botan/loadstor.h>
+#include <botan/blowfish.h>
+#include <botan/hash.h>
+#include <botan/internal/timer.h>
+
+namespace Botan {
+
+void Bcrypt_PBKDF::derive_key(uint8_t output[], size_t output_len,
+                              const char* password, size_t password_len,
+                              const uint8_t salt[], size_t salt_len) const
+   {
+   bcrypt_pbkdf(output, output_len,
+                password, password_len,
+                salt, salt_len,
+                m_iterations);
+   }
+
+std::string Bcrypt_PBKDF::to_string() const
+   {
+   return "Bcrypt-PBKDF(" + std::to_string(m_iterations) + ")";
+   }
+
+std::string Bcrypt_PBKDF_Family::name() const
+   {
+   return "Bcrypt-PBKDF";
+   }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length,
+                                                        std::chrono::milliseconds msec,
+                                                        size_t /*max_memory*/) const
+   {
+   Timer timer("Bcrypt_PBKDF");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+   const size_t blocks = (output_length + 32 - 1) / 32;
+
+   if(blocks == 0)
+      return default_params();
+
+   const size_t starting_iter = 2;
+
+   timer.run_until_elapsed(tune_time, [&]() {
+      uint8_t output[32] = { 0 };
+      bcrypt_pbkdf(output, sizeof(output), "test", 4, nullptr, 0, starting_iter);
+      });
+
+   if(timer.events() < blocks || timer.value() == 0)
+      return default_params();
+
+   const uint64_t measured_time = timer.value() / (timer.events() / blocks);
+
+   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+   const uint64_t desired_increase = target_nsec / measured_time;
+
+   if(desired_increase == 0)
+      return this->from_iterations(starting_iter);
+
+   return this->from_iterations(static_cast<size_t>(desired_increase * starting_iter));
+   }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::default_params() const
+   {
+   return this->from_iterations(32); // About 100 ms on fast machine
+   }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_iterations(size_t iter) const
+   {
+   return std::unique_ptr<PasswordHash>(new Bcrypt_PBKDF(iter));
+   }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_params(size_t iter, size_t /*t*/, size_t /*p*/) const
+   {
+   return this->from_iterations(iter);
+   }
+
+namespace {
+
+void bcrypt_round(Blowfish& blowfish,
+                  const secure_vector<uint8_t>& pass_hash,
+                  const secure_vector<uint8_t>& salt_hash,
+                  secure_vector<uint8_t>& out,
+                  secure_vector<uint8_t>& tmp)
+   {
+   const size_t BCRYPT_PBKDF_OUTPUT = 32;
+
+   // "OxychromaticBlowfishSwatDynamite"
+   static const uint8_t BCRYPT_PBKDF_MAGIC[BCRYPT_PBKDF_OUTPUT] = {
+      0x4F, 0x78, 0x79, 0x63, 0x68, 0x72, 0x6F, 0x6D,
+      0x61, 0x74, 0x69, 0x63, 0x42, 0x6C, 0x6F, 0x77,
+      0x66, 0x69, 0x73, 0x68, 0x53, 0x77, 0x61, 0x74,
+      0x44, 0x79, 0x6E, 0x61, 0x6D, 0x69, 0x74, 0x65
+   };
+
+   const size_t BCRYPT_PBKDF_WORKFACTOR = 6;
+   const size_t BCRYPT_PBKDF_ROUNDS = 64;
+
+   blowfish.salted_set_key(pass_hash.data(), pass_hash.size(),
+                           salt_hash.data(), salt_hash.size(),
+                           BCRYPT_PBKDF_WORKFACTOR, true);
+
+   copy_mem(tmp.data(), BCRYPT_PBKDF_MAGIC, BCRYPT_PBKDF_OUTPUT);
+   for(size_t i = 0; i != BCRYPT_PBKDF_ROUNDS; ++i)
+      blowfish.encrypt(tmp);
+
+   /*
+   Bcrypt PBKDF loads the Blowfish output as big endian for no reason
+   in particular. We can't just swap everything once at the end
+   because the (big-endian) values are fed into SHA-512 to generate
+   the salt for the next round
+   */
+   for(size_t i = 0; i != 32/4; ++i)
+      {
+      const uint32_t w = load_le<uint32_t>(tmp.data(), i);
+      store_be(w, &tmp[sizeof(uint32_t)*i]);
+      }
+
+   xor_buf(out.data(), tmp.data(), BCRYPT_PBKDF_OUTPUT);
+   }
+
+}
+
+void bcrypt_pbkdf(uint8_t output[], size_t output_len,
+                  const char* pass, size_t pass_len,
+                  const uint8_t salt[], size_t salt_len,
+                  size_t rounds)
+   {
+   BOTAN_ARG_CHECK(rounds >= 1, "Invalid rounds for Bcrypt PBKDF");
+
+   // No output desired, so we are all done already...
+   if(output_len == 0)
+      return;
+
+   BOTAN_ARG_CHECK(output_len <= 10*1024*1024, "Too much output for Bcrypt PBKDF");
+
+   const size_t BCRYPT_BLOCK_SIZE = 32;
+   const size_t blocks = (output_len + BCRYPT_BLOCK_SIZE - 1) / BCRYPT_BLOCK_SIZE;
+
+   std::unique_ptr<HashFunction> sha512 = HashFunction::create_or_throw("SHA-512");
+   const secure_vector<uint8_t> pass_hash = sha512->process(reinterpret_cast<const uint8_t*>(pass), pass_len);
+
+   secure_vector<uint8_t> salt_hash(sha512->output_length());
+
+   Blowfish blowfish;
+   secure_vector<uint8_t> out(BCRYPT_BLOCK_SIZE);
+   secure_vector<uint8_t> tmp(BCRYPT_BLOCK_SIZE);
+
+   for(size_t block = 0; block != blocks; ++block)
+      {
+      clear_mem(out.data(), out.size());
+
+      sha512->update(salt, salt_len);
+      sha512->update_be(static_cast<uint32_t>(block + 1));
+      sha512->final(salt_hash.data());
+
+      bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp);
+
+      for(size_t r = 1; r != rounds; ++r)
+         {
+         // Next salt is H(prev_output)
+         sha512->update(tmp);
+         sha512->final(salt_hash.data());
+
+         bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp);
+         }
+
+      for(size_t i = 0; i != BCRYPT_BLOCK_SIZE; ++i)
+         {
+         const size_t dest = i * blocks + block;
+         if(dest < output_len)
+            output[dest] = out[i];
+         }
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h b/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
@@ -0,0 +1,77 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF_BCRYPT_H_
+#define BOTAN_PBKDF_BCRYPT_H_
+
+#include <botan/pwdhash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(bcrypt_pbkdf.h)
+
+namespace Botan {
+
+/**
+* Bcrypt-PBKDF key derivation function
+*/
+class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF final : public PasswordHash
+   {
+   public:
+      Bcrypt_PBKDF(size_t iterations) : m_iterations(iterations) {}
+
+      Bcrypt_PBKDF(const Bcrypt_PBKDF& other) = default;
+      Bcrypt_PBKDF& operator=(const Bcrypt_PBKDF&) = default;
+
+      /**
+      * Derive a new key under the current Bcrypt-PBKDF parameter set
+      */
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len) const override;
+
+      std::string to_string() const override;
+
+      size_t iterations() const override { return m_iterations; }
+
+      size_t parallelism() const override { return 0; }
+
+      size_t memory_param() const override { return 0; }
+
+      size_t total_memory_usage() const override { return 4096; }
+
+   private:
+      size_t m_iterations;
+   };
+
+class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF_Family final : public PasswordHashFamily
+   {
+   public:
+      Bcrypt_PBKDF_Family() {}
+
+      std::string name() const override;
+
+      std::unique_ptr<PasswordHash> tune(size_t output_length,
+                                         std::chrono::milliseconds msec,
+                                         size_t max_memory) const override;
+
+      std::unique_ptr<PasswordHash> default_params() const override;
+
+      std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+      std::unique_ptr<PasswordHash> from_params(
+         size_t i, size_t, size_t) const override;
+   };
+
+/**
+* Bcrypt PBKDF compatible with OpenBSD bcrypt_pbkdf
+*/
+void BOTAN_UNSTABLE_API bcrypt_pbkdf(uint8_t output[], size_t output_len,
+                                     const char* pass, size_t pass_len,
+                                     const uint8_t salt[], size_t salt_len,
+                                     size_t rounds);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf.cpp b/third_party/botan/src/lib/pbkdf/pbkdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf.cpp
@@ -0,0 +1,133 @@
+/*
+* PBKDF
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+
+#if defined(BOTAN_HAS_PBKDF1)
+#include <botan/pbkdf1.h>
+#endif
+
+#if defined(BOTAN_HAS_PBKDF2)
+#include <botan/pbkdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+#include <botan/pgp_s2k.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec,
+                                     const std::string& provider)
+   {
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_PBKDF2)
+   if(req.algo_name() == "PBKDF2")
+      {
+      // TODO OpenSSL
+
+      if(provider.empty() || provider == "base")
+         {
+         if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+            return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release()));
+
+         if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+            return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release()));
+         }
+
+      return nullptr;
+      }
+#endif
+
+#if defined(BOTAN_HAS_PBKDF1)
+   if(req.algo_name() == "PBKDF1" && req.arg_count() == 1)
+      {
+      if(auto hash = HashFunction::create(req.arg(0)))
+         return std::unique_ptr<PBKDF>(new PKCS5_PBKDF1(hash.release()));
+
+      }
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+   if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1)
+      {
+      if(auto hash = HashFunction::create(req.arg(0)))
+         return std::unique_ptr<PBKDF>(new OpenPGP_S2K(hash.release()));
+      }
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<PBKDF>
+PBKDF::create_or_throw(const std::string& algo,
+                             const std::string& provider)
+   {
+   if(auto pbkdf = PBKDF::create(algo, provider))
+      {
+      return pbkdf;
+      }
+   throw Lookup_Error("PBKDF", algo, provider);
+   }
+
+std::vector<std::string> PBKDF::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<PBKDF>(algo_spec, { "base", "openssl" });
+   }
+
+void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len,
+                        const std::string& passphrase,
+                        const uint8_t salt[], size_t salt_len,
+                        std::chrono::milliseconds msec,
+                        size_t& iterations) const
+   {
+   iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec);
+   }
+
+void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len,
+                             const std::string& passphrase,
+                             const uint8_t salt[], size_t salt_len,
+                             size_t iterations) const
+   {
+   if(iterations == 0)
+      throw Invalid_Argument(name() + ": Invalid iteration count");
+
+   const size_t iterations_run = pbkdf(out, out_len, passphrase,
+                                       salt, salt_len, iterations,
+                                       std::chrono::milliseconds(0));
+   BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations");
+   }
+
+secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len,
+                                            const std::string& passphrase,
+                                            const uint8_t salt[], size_t salt_len,
+                                            size_t iterations) const
+   {
+   secure_vector<uint8_t> out(out_len);
+   pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations);
+   return out;
+   }
+
+secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len,
+                                       const std::string& passphrase,
+                                       const uint8_t salt[], size_t salt_len,
+                                       std::chrono::milliseconds msec,
+                                       size_t& iterations) const
+   {
+   secure_vector<uint8_t> out(out_len);
+   pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations);
+   return out;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf.h b/third_party/botan/src/lib/pbkdf/pbkdf.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf.h
@@ -0,0 +1,246 @@
+/*
+* PBKDF
+* (C) 1999-2007,2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF_H_
+#define BOTAN_PBKDF_H_
+
+#include <botan/symkey.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* Base class for PBKDF (password based key derivation function)
+* implementations. Converts a password into a key using a salt
+* and iterated hashing to make brute force attacks harder.
+*
+* Starting in 2.8 this functionality is also offered by PasswordHash.
+* The PBKDF interface may be removed in a future release.
+*/
+class BOTAN_PUBLIC_API(2,0) PBKDF
+   {
+   public:
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to choose
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<PBKDF> create(const std::string& algo_spec,
+                                           const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name, or throw if the
+      * algo/provider combination cannot be found. If provider is
+      * empty then best available is chosen.
+      */
+      static std::unique_ptr<PBKDF>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * @return new instance of this same algorithm
+      */
+      virtual PBKDF* clone() const = 0;
+
+      /**
+      * @return name of this PBKDF
+      */
+      virtual std::string name() const = 0;
+
+      virtual ~PBKDF() = default;
+
+      /**
+      * Derive a key from a passphrase for a number of iterations
+      * specified by either iterations or if iterations == 0 then
+      * running until msec time has elapsed.
+      *
+      * @param out buffer to store the derived key, must be of out_len bytes
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param iterations the number of iterations to use (use 10K or more)
+      * @param msec if iterations is zero, then instead the PBKDF is
+      *        run until msec milliseconds has passed.
+      * @return the number of iterations performed
+      */
+      virtual size_t pbkdf(uint8_t out[], size_t out_len,
+                           const std::string& passphrase,
+                           const uint8_t salt[], size_t salt_len,
+                           size_t iterations,
+                           std::chrono::milliseconds msec) const = 0;
+
+      /**
+      * Derive a key from a passphrase for a number of iterations.
+      *
+      * @param out buffer to store the derived key, must be of out_len bytes
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param iterations the number of iterations to use (use 10K or more)
+      */
+      void pbkdf_iterations(uint8_t out[], size_t out_len,
+                            const std::string& passphrase,
+                            const uint8_t salt[], size_t salt_len,
+                            size_t iterations) const;
+
+      /**
+      * Derive a key from a passphrase, running until msec time has elapsed.
+      *
+      * @param out buffer to store the derived key, must be of out_len bytes
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param msec if iterations is zero, then instead the PBKDF is
+      *        run until msec milliseconds has passed.
+      * @param iterations set to the number iterations executed
+      */
+      void pbkdf_timed(uint8_t out[], size_t out_len,
+                         const std::string& passphrase,
+                         const uint8_t salt[], size_t salt_len,
+                         std::chrono::milliseconds msec,
+                         size_t& iterations) const;
+
+      /**
+      * Derive a key from a passphrase for a number of iterations.
+      *
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param iterations the number of iterations to use (use 10K or more)
+      * @return the derived key
+      */
+      secure_vector<uint8_t> pbkdf_iterations(size_t out_len,
+                                           const std::string& passphrase,
+                                           const uint8_t salt[], size_t salt_len,
+                                           size_t iterations) const;
+
+      /**
+      * Derive a key from a passphrase, running until msec time has elapsed.
+      *
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param msec if iterations is zero, then instead the PBKDF is
+      *        run until msec milliseconds has passed.
+      * @param iterations set to the number iterations executed
+      * @return the derived key
+      */
+      secure_vector<uint8_t> pbkdf_timed(size_t out_len,
+                                      const std::string& passphrase,
+                                      const uint8_t salt[], size_t salt_len,
+                                      std::chrono::milliseconds msec,
+                                      size_t& iterations) const;
+
+      // Following kept for compat with 1.10:
+
+      /**
+      * Derive a key from a passphrase
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param iterations the number of iterations to use (use 10K or more)
+      */
+      OctetString derive_key(size_t out_len,
+                             const std::string& passphrase,
+                             const uint8_t salt[], size_t salt_len,
+                             size_t iterations) const
+         {
+         return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations);
+         }
+
+      /**
+      * Derive a key from a passphrase
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param iterations the number of iterations to use (use 10K or more)
+      */
+      template<typename Alloc>
+      OctetString derive_key(size_t out_len,
+                             const std::string& passphrase,
+                             const std::vector<uint8_t, Alloc>& salt,
+                             size_t iterations) const
+         {
+         return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations);
+         }
+
+      /**
+      * Derive a key from a passphrase
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param msec is how long to run the PBKDF
+      * @param iterations is set to the number of iterations used
+      */
+      OctetString derive_key(size_t out_len,
+                             const std::string& passphrase,
+                             const uint8_t salt[], size_t salt_len,
+                             std::chrono::milliseconds msec,
+                             size_t& iterations) const
+         {
+         return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations);
+         }
+
+      /**
+      * Derive a key from a passphrase using a certain amount of time
+      * @param out_len the desired length of the key to produce
+      * @param passphrase the password to derive the key from
+      * @param salt a randomly chosen salt
+      * @param msec is how long to run the PBKDF
+      * @param iterations is set to the number of iterations used
+      */
+      template<typename Alloc>
+      OctetString derive_key(size_t out_len,
+                             const std::string& passphrase,
+                             const std::vector<uint8_t, Alloc>& salt,
+                             std::chrono::milliseconds msec,
+                             size_t& iterations) const
+         {
+         return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations);
+         }
+   };
+
+/*
+* Compatibility typedef
+*/
+typedef PBKDF S2K;
+
+/**
+* Password based key derivation function factory method
+* @param algo_spec the name of the desired PBKDF algorithm
+* @param provider the provider to use
+* @return pointer to newly allocated object of that type
+*/
+inline PBKDF* get_pbkdf(const std::string& algo_spec,
+                        const std::string& provider = "")
+   {
+   return PBKDF::create_or_throw(algo_spec, provider).release();
+   }
+
+inline PBKDF* get_s2k(const std::string& algo_spec)
+   {
+   return get_pbkdf(algo_spec);
+   }
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp b/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp
@@ -0,0 +1,54 @@
+/*
+* PBKDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf1.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+size_t PKCS5_PBKDF1::pbkdf(uint8_t output_buf[], size_t output_len,
+                           const std::string& passphrase,
+                           const uint8_t salt[], size_t salt_len,
+                           size_t iterations,
+                           std::chrono::milliseconds msec) const
+   {
+   if(output_len > m_hash->output_length())
+      throw Invalid_Argument("PKCS5_PBKDF1: Requested output length too long");
+
+   m_hash->update(passphrase);
+   m_hash->update(salt, salt_len);
+   secure_vector<uint8_t> key = m_hash->final();
+
+   const auto start = std::chrono::high_resolution_clock::now();
+   size_t iterations_performed = 1;
+
+   while(true)
+      {
+      if(iterations == 0)
+         {
+         if(iterations_performed % 10000 == 0)
+            {
+            auto time_taken = std::chrono::high_resolution_clock::now() - start;
+            auto msec_taken = std::chrono::duration_cast<std::chrono::milliseconds>(time_taken);
+            if(msec_taken > msec)
+               break;
+            }
+         }
+      else if(iterations_performed == iterations)
+         break;
+
+      m_hash->update(key);
+      m_hash->final(key.data());
+
+      ++iterations_performed;
+      }
+
+   copy_mem(output_buf, key.data(), output_len);
+   return iterations_performed;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h b/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h
@@ -0,0 +1,53 @@
+/*
+* PBKDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF1_H_
+#define BOTAN_PBKDF1_H_
+
+#include <botan/pbkdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbkdf1.h)
+
+namespace Botan {
+
+/**
+* PKCS #5 v1 PBKDF, aka PBKDF1
+* Can only generate a key up to the size of the hash output.
+* Unless needed for backwards compatibility, use PKCS5_PBKDF2
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF1 final : public PBKDF
+   {
+   public:
+      /**
+      * Create a PKCS #5 instance using the specified hash function.
+      * @param hash pointer to a hash function object to use
+      */
+      explicit PKCS5_PBKDF1(HashFunction* hash) : m_hash(hash) {}
+
+      std::string name() const override
+         {
+         return "PBKDF1(" + m_hash->name() + ")";
+         }
+
+      PBKDF* clone() const override
+         {
+         return new PKCS5_PBKDF1(m_hash->clone());
+         }
+
+      size_t pbkdf(uint8_t output_buf[], size_t output_len,
+                           const std::string& passphrase,
+                           const uint8_t salt[], size_t salt_len,
+                           size_t iterations,
+                           std::chrono::milliseconds msec) const override;
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp b/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
@@ -0,0 +1,228 @@
+/*
+* PBKDF2
+* (C) 1999-2007 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/timer.h>
+
+namespace Botan {
+
+namespace {
+
+void pbkdf2_set_key(MessageAuthenticationCode& prf,
+                    const char* password,
+                    size_t password_len)
+   {
+   try
+      {
+      prf.set_key(cast_char_ptr_to_uint8(password), password_len);
+      }
+   catch(Invalid_Key_Length&)
+      {
+      throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size");
+      }
+   }
+
+}
+
+size_t
+pbkdf2(MessageAuthenticationCode& prf,
+       uint8_t out[],
+       size_t out_len,
+       const std::string& password,
+       const uint8_t salt[], size_t salt_len,
+       size_t iterations,
+       std::chrono::milliseconds msec)
+   {
+   if(iterations == 0)
+      {
+      iterations = PBKDF2(prf, out_len, msec).iterations();
+      }
+
+   PBKDF2 pbkdf2(prf, iterations);
+
+   pbkdf2.derive_key(out, out_len,
+                     password.c_str(), password.size(),
+                     salt, salt_len);
+
+   return iterations;
+   }
+
+namespace {
+
+size_t tune_pbkdf2(MessageAuthenticationCode& prf,
+                   size_t output_length,
+                   uint32_t msec)
+   {
+   if(output_length == 0)
+      output_length = 1;
+
+   const size_t prf_sz = prf.output_length();
+   BOTAN_ASSERT_NOMSG(prf_sz > 0);
+   secure_vector<uint8_t> U(prf_sz);
+
+   const size_t trial_iterations = 2000;
+
+   // Short output ensures we only need a single PBKDF2 block
+
+   Timer timer("PBKDF2");
+
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+   prf.set_key(nullptr, 0);
+
+   timer.run_until_elapsed(tune_time, [&]() {
+      uint8_t out[12] = { 0 };
+      uint8_t salt[12] = { 0 };
+      pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations);
+      });
+
+   if(timer.events() == 0)
+      return trial_iterations;
+
+   const uint64_t duration_nsec = timer.value() / timer.events();
+
+   const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000;
+
+   if(duration_nsec > desired_nsec)
+      return trial_iterations;
+
+   const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz;
+
+   const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed);
+
+   if(multiplier == 0)
+      return trial_iterations;
+   else
+      return trial_iterations * multiplier;
+   }
+
+}
+
+void pbkdf2(MessageAuthenticationCode& prf,
+            uint8_t out[],
+            size_t out_len,
+            const uint8_t salt[],
+            size_t salt_len,
+            size_t iterations)
+   {
+   if(iterations == 0)
+      throw Invalid_Argument("PBKDF2: Invalid iteration count");
+
+   clear_mem(out, out_len);
+
+   if(out_len == 0)
+      return;
+
+   const size_t prf_sz = prf.output_length();
+   BOTAN_ASSERT_NOMSG(prf_sz > 0);
+
+   secure_vector<uint8_t> U(prf_sz);
+
+   uint32_t counter = 1;
+   while(out_len)
+      {
+      const size_t prf_output = std::min<size_t>(prf_sz, out_len);
+
+      prf.update(salt, salt_len);
+      prf.update_be(counter++);
+      prf.final(U.data());
+
+      xor_buf(out, U.data(), prf_output);
+
+      for(size_t i = 1; i != iterations; ++i)
+         {
+         prf.update(U);
+         prf.final(U.data());
+         xor_buf(out, U.data(), prf_output);
+         }
+
+      out_len -= prf_output;
+      out += prf_output;
+      }
+   }
+
+// PBKDF interface
+size_t
+PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len,
+                    const std::string& password,
+                    const uint8_t salt[], size_t salt_len,
+                    size_t iterations,
+                    std::chrono::milliseconds msec) const
+   {
+   if(iterations == 0)
+      {
+      iterations = PBKDF2(*m_mac, key_len, msec).iterations();
+      }
+
+   PBKDF2 pbkdf2(*m_mac, iterations);
+
+   pbkdf2.derive_key(key, key_len,
+                     password.c_str(), password.size(),
+                     salt, salt_len);
+
+   return iterations;
+   }
+
+std::string PKCS5_PBKDF2::name() const
+   {
+   return "PBKDF2(" + m_mac->name() + ")";
+   }
+
+PBKDF* PKCS5_PBKDF2::clone() const
+   {
+   return new PKCS5_PBKDF2(m_mac->clone());
+   }
+
+// PasswordHash interface
+
+PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) :
+   m_prf(prf.clone()),
+   m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count())))
+   {}
+
+std::string PBKDF2::to_string() const
+   {
+   return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")";
+   }
+
+void PBKDF2::derive_key(uint8_t out[], size_t out_len,
+                        const char* password, const size_t password_len,
+                        const uint8_t salt[], size_t salt_len) const
+   {
+   pbkdf2_set_key(*m_prf, password, password_len);
+   pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations);
+   }
+
+std::string PBKDF2_Family::name() const
+   {
+   return "PBKDF2(" + m_prf->name() + ")";
+   }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const
+   {
+   return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, output_len, msec));
+   }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const
+   {
+   return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, 150000));
+   }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t, size_t) const
+   {
+   return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter));
+   }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const
+   {
+   return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h b/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h
@@ -0,0 +1,117 @@
+/*
+* PBKDF2
+* (C) 1999-2007,2012 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF2_H_
+#define BOTAN_PBKDF2_H_
+
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h)
+
+namespace Botan {
+
+BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf,
+                        uint8_t out[],
+                        size_t out_len,
+                        const std::string& passphrase,
+                        const uint8_t salt[], size_t salt_len,
+                        size_t iterations,
+                        std::chrono::milliseconds msec);
+
+/**
+* Perform PBKDF2. The prf is assumed to be keyed already.
+*/
+BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf,
+                                  uint8_t out[], size_t out_len,
+                                  const uint8_t salt[], size_t salt_len,
+                                  size_t iterations);
+
+/**
+* PBKDF2
+*/
+class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash
+   {
+   public:
+      PBKDF2(const MessageAuthenticationCode& prf, size_t iter) :
+         m_prf(prf.clone()),
+         m_iterations(iter)
+         {}
+
+      PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec);
+
+      size_t iterations() const override { return m_iterations; }
+
+      std::string to_string() const override;
+
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len) const override;
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+      size_t m_iterations;
+   };
+
+/**
+* Family of PKCS #5 PBKDF2 operations
+*/
+class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily
+   {
+   public:
+      PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+      std::string name() const override;
+
+      std::unique_ptr<PasswordHash> tune(size_t output_len,
+                                         std::chrono::milliseconds msec,
+                                         size_t max_memory) const override;
+
+      /**
+      * Return some default parameter set for this PBKDF that should be good
+      * enough for most users. The value returned may change over time as
+      * processing power and attacks improve.
+      */
+      std::unique_ptr<PasswordHash> default_params() const override;
+
+      std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+      std::unique_ptr<PasswordHash> from_params(
+         size_t iter, size_t, size_t) const override;
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_prf;
+   };
+
+/**
+* PKCS #5 PBKDF2 (old interface)
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF
+   {
+   public:
+      std::string name() const override;
+
+      PBKDF* clone() const override;
+
+      size_t pbkdf(uint8_t output_buf[], size_t output_len,
+                   const std::string& passphrase,
+                   const uint8_t salt[], size_t salt_len,
+                   size_t iterations,
+                   std::chrono::milliseconds msec) const override;
+
+      /**
+      * Create a PKCS #5 instance using the specified message auth code
+      * @param mac_fn the MAC object to use as PRF
+      */
+      explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {}
+   private:
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp b/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
@@ -0,0 +1,219 @@
+/*
+* OpenPGP S2K
+* (C) 1999-2007,2017 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/pgp_s2k.h>
+#include <botan/exceptn.h>
+#include <botan/internal/timer.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+PGP stores the iteration count as a single byte
+Thus it can only actually take on one of 256 values, based on the
+formula in RFC 4880 section 3.6.1.3
+*/
+static const uint32_t OPENPGP_S2K_ITERS[256] = {
+   1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600,
+   1664, 1728, 1792, 1856, 1920, 1984, 2048, 2176, 2304, 2432,
+   2560, 2688, 2816, 2944, 3072, 3200, 3328, 3456, 3584, 3712,
+   3840, 3968, 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888,
+   6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, 8192, 8704,
+   9216, 9728, 10240, 10752, 11264, 11776, 12288, 12800, 13312,
+   13824, 14336, 14848, 15360, 15872, 16384, 17408, 18432, 19456,
+   20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672,
+   29696, 30720, 31744, 32768, 34816, 36864, 38912, 40960, 43008,
+   45056, 47104, 49152, 51200, 53248, 55296, 57344, 59392, 61440,
+   63488, 65536, 69632, 73728, 77824, 81920, 86016, 90112, 94208,
+   98304, 102400, 106496, 110592, 114688, 118784, 122880, 126976,
+   131072, 139264, 147456, 155648, 163840, 172032, 180224, 188416,
+   196608, 204800, 212992, 221184, 229376, 237568, 245760, 253952,
+   262144, 278528, 294912, 311296, 327680, 344064, 360448, 376832,
+   393216, 409600, 425984, 442368, 458752, 475136, 491520, 507904,
+   524288, 557056, 589824, 622592, 655360, 688128, 720896, 753664,
+   786432, 819200, 851968, 884736, 917504, 950272, 983040, 1015808,
+   1048576, 1114112, 1179648, 1245184, 1310720, 1376256, 1441792,
+   1507328, 1572864, 1638400, 1703936, 1769472, 1835008, 1900544,
+   1966080, 2031616, 2097152, 2228224, 2359296, 2490368, 2621440,
+   2752512, 2883584, 3014656, 3145728, 3276800, 3407872, 3538944,
+   3670016, 3801088, 3932160, 4063232, 4194304, 4456448, 4718592,
+   4980736, 5242880, 5505024, 5767168, 6029312, 6291456, 6553600,
+   6815744, 7077888, 7340032, 7602176, 7864320, 8126464, 8388608,
+   8912896, 9437184, 9961472, 10485760, 11010048, 11534336,
+   12058624, 12582912, 13107200, 13631488, 14155776, 14680064,
+   15204352, 15728640, 16252928, 16777216, 17825792, 18874368,
+   19922944, 20971520, 22020096, 23068672, 24117248, 25165824,
+   26214400, 27262976, 28311552, 29360128, 30408704, 31457280,
+   32505856, 33554432, 35651584, 37748736, 39845888, 41943040,
+   44040192, 46137344, 48234496, 50331648, 52428800, 54525952,
+   56623104, 58720256, 60817408, 62914560, 65011712 };
+
+uint8_t RFC4880_encode_count(size_t desired_iterations)
+   {
+   if(desired_iterations <= OPENPGP_S2K_ITERS[0])
+      return 0;
+
+   if(desired_iterations >= OPENPGP_S2K_ITERS[255])
+      return 255;
+
+   auto i = std::lower_bound(OPENPGP_S2K_ITERS, OPENPGP_S2K_ITERS + 256, desired_iterations);
+
+   return static_cast<uint8_t>(i - OPENPGP_S2K_ITERS);
+   }
+
+size_t RFC4880_decode_count(uint8_t iter)
+   {
+   return OPENPGP_S2K_ITERS[iter];
+   }
+
+namespace {
+
+void pgp_s2k(HashFunction& hash,
+             uint8_t output_buf[], size_t output_len,
+             const char* password, const size_t password_size,
+             const uint8_t salt[], size_t salt_len,
+             size_t iterations)
+   {
+   if(iterations > 1 && salt_len == 0)
+      throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode");
+
+   secure_vector<uint8_t> input_buf(salt_len + password_size);
+   if(salt_len > 0)
+      {
+      copy_mem(&input_buf[0], salt, salt_len);
+      }
+   if(password_size > 0)
+      {
+      copy_mem(&input_buf[salt_len],
+               cast_char_ptr_to_uint8(password),
+               password_size);
+      }
+
+   secure_vector<uint8_t> hash_buf(hash.output_length());
+
+   size_t pass = 0;
+   size_t generated = 0;
+
+   while(generated != output_len)
+      {
+      const size_t output_this_pass =
+         std::min(hash_buf.size(), output_len - generated);
+
+      // Preload some number of zero bytes (empty first iteration)
+      std::vector<uint8_t> zero_padding(pass);
+      hash.update(zero_padding);
+
+      // The input is always fully processed even if iterations is very small
+      if(input_buf.empty() == false)
+         {
+         size_t left = std::max(iterations, input_buf.size());
+         while(left > 0)
+            {
+            const size_t input_to_take = std::min(left, input_buf.size());
+            hash.update(input_buf.data(), input_to_take);
+            left -= input_to_take;
+            }
+         }
+
+      hash.final(hash_buf.data());
+      copy_mem(output_buf + generated, hash_buf.data(), output_this_pass);
+      generated += output_this_pass;
+      ++pass;
+      }
+   }
+
+}
+
+size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len,
+                          const std::string& password,
+                          const uint8_t salt[], size_t salt_len,
+                          size_t iterations,
+                          std::chrono::milliseconds msec) const
+   {
+   std::unique_ptr<PasswordHash> pwdhash;
+
+   if(iterations == 0)
+      {
+      RFC4880_S2K_Family s2k_params(m_hash->clone());
+      iterations = s2k_params.tune(output_len, msec, 0)->iterations();
+      }
+
+   pgp_s2k(*m_hash, output_buf, output_len,
+           password.c_str(), password.size(),
+           salt, salt_len,
+           iterations);
+
+   return iterations;
+   }
+
+std::string RFC4880_S2K_Family::name() const
+   {
+   return "OpenPGP-S2K(" + m_hash->name() + ")";
+   }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const
+   {
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+   const size_t buf_size = 1024;
+   std::vector<uint8_t> buffer(buf_size);
+
+   Timer timer("RFC4880_S2K", buf_size);
+   timer.run_until_elapsed(tune_time, [&]() {
+      m_hash->update(buffer);
+      });
+
+   const double hash_bytes_per_second = timer.bytes_per_second();
+   const uint64_t desired_nsec = msec.count() * 1000000;
+
+   const size_t hash_size = m_hash->output_length();
+   const size_t blocks_required = (output_len <= hash_size ? 1 : (output_len + hash_size - 1) / hash_size);
+
+   const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required;
+   const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed));
+
+   return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iterations));
+   }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iter, size_t, size_t) const
+   {
+   return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter));
+   }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const
+   {
+   return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), 50331648));
+   }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iter) const
+   {
+   return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter));
+   }
+
+RFC4880_S2K::RFC4880_S2K(HashFunction* hash, size_t iterations) :
+   m_hash(hash),
+   m_iterations(iterations)
+   {
+   }
+
+std::string RFC4880_S2K::to_string() const
+   {
+   return "OpenPGP-S2K(" + m_hash->name() + "," + std::to_string(m_iterations) + ")";
+   }
+
+void RFC4880_S2K::derive_key(uint8_t out[], size_t out_len,
+                             const char* password, const size_t password_len,
+                             const uint8_t salt[], size_t salt_len) const
+   {
+   pgp_s2k(*m_hash, out, out_len,
+           password, password_len,
+           salt, salt_len,
+           m_iterations);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h b/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
@@ -0,0 +1,164 @@
+/*
+* OpenPGP PBKDF
+* (C) 1999-2007,2017 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_OPENPGP_S2K_H_
+#define BOTAN_OPENPGP_S2K_H_
+
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/hash.h>
+
+/*
+This header will not be fully internal - the RFC4880 count
+encoding functions will remain here. But the definition of
+OpenPGP_S2K will be made internal
+*/
+
+//BOTAN_FUTURE_INTERNAL_HEADER(pgp_s2k.h)
+
+namespace Botan {
+
+/**
+* RFC 4880 encodes the iteration count to a single-byte value
+*/
+uint8_t BOTAN_PUBLIC_API(2,8) RFC4880_encode_count(size_t iterations);
+
+/**
+* Decode the iteration count from RFC 4880 encoding
+*/
+size_t BOTAN_PUBLIC_API(2,8) RFC4880_decode_count(uint8_t encoded_iter);
+
+/**
+* Round an arbitrary iteration count to next largest iteration count
+* supported by RFC4880 encoding.
+*/
+inline size_t RFC4880_round_iterations(size_t iterations)
+   {
+   return RFC4880_decode_count(RFC4880_encode_count(iterations));
+   }
+
+/**
+* OpenPGP's S2K
+*
+* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3
+* If the salt is empty and iterations == 1, "simple" S2K is used
+* If the salt is non-empty and iterations == 1, "salted" S2K is used
+* If the salt is non-empty and iterations > 1, "iterated" S2K is used
+*
+* Due to complexities of the PGP S2K algorithm, time-based derivation
+* is not supported. So if iterations == 0 and msec.count() > 0, an
+* exception is thrown. In the future this may be supported, in which
+* case "iterated" S2K will be used and the number of iterations
+* performed is returned.
+*
+* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as
+* the number of bytes hashed.
+*/
+class BOTAN_PUBLIC_API(2,2) OpenPGP_S2K final : public PBKDF
+   {
+   public:
+      /**
+      * @param hash the hash function to use
+      */
+      explicit OpenPGP_S2K(HashFunction* hash) : m_hash(hash) {}
+
+      std::string name() const override
+         {
+         return "OpenPGP-S2K(" + m_hash->name() + ")";
+         }
+
+      PBKDF* clone() const override
+         {
+         return new OpenPGP_S2K(m_hash->clone());
+         }
+
+      size_t pbkdf(uint8_t output_buf[], size_t output_len,
+                   const std::string& passphrase,
+                   const uint8_t salt[], size_t salt_len,
+                   size_t iterations,
+                   std::chrono::milliseconds msec) const override;
+
+      /**
+      * RFC 4880 encodes the iteration count to a single-byte value
+      */
+      static uint8_t encode_count(size_t iterations)
+         {
+         return RFC4880_encode_count(iterations);
+         }
+
+      static size_t decode_count(uint8_t encoded_iter)
+         {
+         return RFC4880_decode_count(encoded_iter);
+         }
+
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+/**
+* OpenPGP's S2K
+*
+* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3
+* If the salt is empty and iterations == 1, "simple" S2K is used
+* If the salt is non-empty and iterations == 1, "salted" S2K is used
+* If the salt is non-empty and iterations > 1, "iterated" S2K is used
+*
+* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as
+* the number of bytes hashed.
+*/
+class BOTAN_PUBLIC_API(2,8) RFC4880_S2K final : public PasswordHash
+   {
+   public:
+      /**
+      * @param hash the hash function to use
+      * @param iterations is rounded due to PGP formatting
+      */
+      RFC4880_S2K(HashFunction* hash, size_t iterations);
+
+      std::string to_string() const override;
+
+      size_t iterations() const override { return m_iterations; }
+
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len) const override;
+
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+      size_t m_iterations;
+   };
+
+class BOTAN_PUBLIC_API(2,8) RFC4880_S2K_Family final : public PasswordHashFamily
+   {
+   public:
+      RFC4880_S2K_Family(HashFunction* hash) : m_hash(hash) {}
+
+      std::string name() const override;
+
+      std::unique_ptr<PasswordHash> tune(size_t output_len,
+                                         std::chrono::milliseconds msec,
+                                         size_t max_mem) const override;
+
+      /**
+      * Return some default parameter set for this PBKDF that should be good
+      * enough for most users. The value returned may change over time as
+      * processing power and attacks improve.
+      */
+      std::unique_ptr<PasswordHash> default_params() const override;
+
+      std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+      std::unique_ptr<PasswordHash> from_params(
+         size_t iter, size_t, size_t) const override;
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/pwdhash.cpp b/third_party/botan/src/lib/pbkdf/pwdhash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pwdhash.cpp
@@ -0,0 +1,118 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pwdhash.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+
+#if defined(BOTAN_HAS_PBKDF2)
+   #include <botan/pbkdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+   #include <botan/pgp_s2k.h>
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+   #include <botan/scrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+   #include <botan/argon2.h>
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+   #include <botan/bcrypt_pbkdf.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec,
+                                     const std::string& provider)
+   {
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_PBKDF2)
+   if(req.algo_name() == "PBKDF2")
+      {
+      // TODO OpenSSL
+
+      if(provider.empty() || provider == "base")
+         {
+         if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+            return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release()));
+
+         if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+            return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release()));
+         }
+
+      return nullptr;
+      }
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+   if(req.algo_name() == "Scrypt")
+      {
+      return std::unique_ptr<PasswordHashFamily>(new Scrypt_Family);
+      }
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+   if(req.algo_name() == "Argon2d")
+      {
+      return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(0));
+      }
+   else if(req.algo_name() == "Argon2i")
+      {
+      return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(1));
+      }
+   else if(req.algo_name() == "Argon2id")
+      {
+      return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(2));
+      }
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+   if(req.algo_name() == "Bcrypt-PBKDF")
+      {
+      return std::unique_ptr<PasswordHashFamily>(new Bcrypt_PBKDF_Family);
+      }
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+   if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1)
+      {
+      if(auto hash = HashFunction::create(req.arg(0)))
+         {
+         return std::unique_ptr<PasswordHashFamily>(new RFC4880_S2K_Family(hash.release()));
+         }
+      }
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<PasswordHashFamily>
+PasswordHashFamily::create_or_throw(const std::string& algo,
+                             const std::string& provider)
+   {
+   if(auto pbkdf = PasswordHashFamily::create(algo, provider))
+      {
+      return pbkdf;
+      }
+   throw Lookup_Error("PasswordHashFamily", algo, provider);
+   }
+
+std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<PasswordHashFamily>(algo_spec, { "base", "openssl" });
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/pwdhash.h b/third_party/botan/src/lib/pbkdf/pwdhash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/pwdhash.h
@@ -0,0 +1,162 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PWDHASH_H_
+#define BOTAN_PWDHASH_H_
+
+#include <botan/types.h>
+#include <string>
+#include <memory>
+#include <vector>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* Base class for password based key derivation functions.
+*
+* Converts a password into a key using a salt and iterated hashing to
+* make brute force attacks harder.
+*/
+class BOTAN_PUBLIC_API(2,8) PasswordHash
+   {
+   public:
+      virtual ~PasswordHash() = default;
+
+      virtual std::string to_string() const = 0;
+
+      /**
+      * Most password hashes have some notion of iterations.
+      */
+      virtual size_t iterations() const = 0;
+
+      /**
+      * Some password hashing algorithms have a parameter which controls how
+      * much memory is used. If not supported by some algorithm, returns 0.
+      */
+      virtual size_t memory_param() const { return 0; }
+
+      /**
+      * Some password hashing algorithms have a parallelism parameter.
+      * If the algorithm does not support this notion, then the
+      * function returns zero. This allows distinguishing between a
+      * password hash which just does not support parallel operation,
+      * vs one that does support parallel operation but which has been
+      * configured to use a single lane.
+      */
+      virtual size_t parallelism() const { return 0; }
+
+      /**
+      * Returns an estimate of the total memory usage required to perform this
+      * key derivation.
+      *
+      * If this algorithm uses a small and constant amount of memory, with no
+      * effort made towards being memory hard, this function returns 0.
+      */
+      virtual size_t total_memory_usage() const { return 0; }
+
+      /**
+      * Derive a key from a password
+      *
+      * @param out buffer to store the derived key, must be of out_len bytes
+      * @param out_len the desired length of the key to produce
+      * @param password the password to derive the key from
+      * @param password_len the length of password in bytes
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      *
+      * This function is const, but is not thread safe. Different threads should
+      * either use unique objects, or serialize all access.
+      */
+      virtual void derive_key(uint8_t out[], size_t out_len,
+                              const char* password, size_t password_len,
+                              const uint8_t salt[], size_t salt_len) const = 0;
+   };
+
+class BOTAN_PUBLIC_API(2,8) PasswordHashFamily
+   {
+   public:
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to choose
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec,
+                                                        const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name, or throw if the
+      * algo/provider combination cannot be found. If provider is
+      * empty then best available is chosen.
+      */
+      static std::unique_ptr<PasswordHashFamily>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      virtual ~PasswordHashFamily() = default;
+
+      /**
+      * @return name of this PasswordHash
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Return a new parameter set tuned for this machine
+      * @param output_length how long the output length will be
+      * @param msec the desired execution time in milliseconds
+      *
+      * @param max_memory_usage_mb some password hash functions can use a tunable
+      * amount of memory, in this case max_memory_usage limits the amount of RAM
+      * the returned parameters will require, in mebibytes (2**20 bytes). It may
+      * require some small amount above the request. Set to zero to place no
+      * limit at all.
+      */
+      virtual std::unique_ptr<PasswordHash> tune(size_t output_length,
+                                                 std::chrono::milliseconds msec,
+                                                 size_t max_memory_usage_mb = 0) const = 0;
+
+      /**
+      * Return some default parameter set for this PBKDF that should be good
+      * enough for most users. The value returned may change over time as
+      * processing power and attacks improve.
+      */
+      virtual std::unique_ptr<PasswordHash> default_params() const = 0;
+
+      /**
+      * Return a parameter chosen based on a rough approximation with the
+      * specified iteration count. The exact value this returns for a particular
+      * algorithm may change from over time. Think of it as an alternative to
+      * tune, where time is expressed in terms of PBKDF2 iterations rather than
+      * milliseconds.
+      */
+      virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0;
+
+      /**
+      * Create a password hash using some scheme specific format.
+      * Eg PBKDF2 and PGP-S2K set iterations in i1
+      * Scrypt uses N,r,p in i{1-3}
+      * Bcrypt-PBKDF just has iterations
+      * Argon2{i,d,id} would use iterations, memory, parallelism for i{1-3},
+      * and Argon2 type is part of the family.
+      *
+      * Values not needed should be set to 0
+      */
+      virtual std::unique_ptr<PasswordHash> from_params(
+         size_t i1,
+         size_t i2 = 0,
+         size_t i3 = 0) const = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp b/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp
@@ -0,0 +1,249 @@
+/**
+* (C) 2018 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/scrypt.h>
+#include <botan/pbkdf2.h>
+#include <botan/salsa20.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/timer.h>
+#include <sstream>
+
+namespace Botan {
+
+std::string Scrypt_Family::name() const
+   {
+   return "Scrypt";
+   }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const
+   {
+   return std::unique_ptr<PasswordHash>(new Scrypt(32768, 8, 1));
+   }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
+                                                  std::chrono::milliseconds msec,
+                                                  size_t max_memory_usage_mb) const
+   {
+   BOTAN_UNUSED(output_length);
+
+   /*
+   * Some rough relations between scrypt parameters and runtime.
+   * Denote here by stime(N,r,p) the msec it takes to run scrypt.
+   *
+   * Emperically for smaller sizes:
+   * stime(N,8*r,p) / stime(N,r,p) is ~ 6-7
+   * stime(N,r,8*p) / stime(N,r,8*p) is ~ 7
+   * stime(2*N,r,p) / stime(N,r,p) is ~ 2
+   *
+   * Compute stime(8192,1,1) as baseline and extrapolate
+   */
+
+   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024;
+   // Starting parameters
+   size_t N = 8192;
+   size_t r = 1;
+   size_t p = 1;
+
+   Timer timer("Scrypt");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+   timer.run_until_elapsed(tune_time, [&]() {
+      uint8_t output[32] = { 0 };
+      scrypt(output, sizeof(output), "test", 4, nullptr, 0, N, r, p);
+      });
+
+   // No timer events seems strange, perhaps something is wrong - give
+   // up on this and just return default params
+   if(timer.events() == 0)
+      return default_params();
+
+   // nsec per eval of scrypt with initial params
+   const uint64_t measured_time = timer.value() / timer.events();
+
+   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+   uint64_t est_nsec = measured_time;
+
+   // First move increase r by 8x if possible
+
+   if(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*8 < max_memory_usage)
+      {
+      if(target_nsec / est_nsec >= 5)
+         {
+         r *= 8;
+         est_nsec *= 5;
+         }
+      }
+
+   // Now double N as many times as we can
+
+   while(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*2 < max_memory_usage)
+      {
+      if(target_nsec / est_nsec >= 2)
+         {
+         N *= 2;
+         est_nsec *= 2;
+         }
+      else
+         break;
+      }
+
+   // If we have extra runtime budget, increment p
+
+   if(target_nsec / est_nsec > 2)
+      p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec));
+
+   return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+   }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const
+   {
+   return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+   }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const
+   {
+   const size_t r = 8;
+   const size_t p = 1;
+
+   size_t N = 8192;
+
+   if(iter > 50000)
+      N = 16384;
+   if(iter > 100000)
+      N = 32768;
+   if(iter > 150000)
+      N = 65536;
+
+   return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+   }
+
+Scrypt::Scrypt(size_t N, size_t r, size_t p) :
+   m_N(N), m_r(r), m_p(p)
+   {
+   if(!is_power_of_2(N))
+      throw Invalid_Argument("Scrypt N parameter must be a power of 2");
+
+   if(p == 0 || p > 1024)
+      throw Invalid_Argument("Invalid or unsupported scrypt p");
+   if(r == 0 || r > 256)
+      throw Invalid_Argument("Invalid or unsupported scrypt r");
+   if(N < 1 || N > 4194304)
+      throw Invalid_Argument("Invalid or unsupported scrypt N");
+   }
+
+std::string Scrypt::to_string() const
+   {
+   std::ostringstream oss;
+   oss << "Scrypt(" << m_N << "," << m_r << "," << m_p << ")";
+   return oss.str();
+   }
+
+size_t Scrypt::total_memory_usage() const
+   {
+   return scrypt_memory_usage(m_N, m_r, m_p);
+   }
+
+void Scrypt::derive_key(uint8_t output[], size_t output_len,
+                        const char* password, size_t password_len,
+                        const uint8_t salt[], size_t salt_len) const
+   {
+   scrypt(output, output_len,
+          password, password_len,
+          salt, salt_len,
+          N(), r(), p());
+   }
+
+namespace {
+
+void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y)
+   {
+   uint32_t B32[16];
+   secure_vector<uint8_t> X(64);
+   copy_mem(X.data(), &B[(2*r-1)*64], 64);
+
+   for(size_t i = 0; i != 2*r; i++)
+      {
+      xor_buf(X.data(), &B[64*i], 64);
+      load_le<uint32_t>(B32, X.data(), 16);
+      Salsa20::salsa_core(X.data(), B32, 8);
+      copy_mem(&Y[64*i], X.data(), 64);
+      }
+
+   for(size_t i = 0; i < r; ++i)
+      {
+      copy_mem(&B[i*64], &Y[(i * 2) * 64], 64);
+      }
+
+   for(size_t i = 0; i < r; ++i)
+      {
+      copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
+      }
+   }
+
+void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V)
+   {
+   const size_t S = 128 * r;
+
+   for(size_t i = 0; i != N; ++i)
+      {
+      copy_mem(&V[S*i], B, S);
+      scryptBlockMix(r, B, &V[N*S]);
+      }
+
+   for(size_t i = 0; i != N; ++i)
+      {
+      // compiler doesn't know here that N is power of 2
+      const size_t j = load_le<uint32_t>(&B[(2*r-1)*64], 0) & (N - 1);
+      xor_buf(B, &V[j*S], S);
+      scryptBlockMix(r, B, &V[N*S]);
+      }
+   }
+
+}
+
+void scrypt(uint8_t output[], size_t output_len,
+            const char* password, size_t password_len,
+            const uint8_t salt[], size_t salt_len,
+            size_t N, size_t r, size_t p)
+   {
+   const size_t S = 128 * r;
+   secure_vector<uint8_t> B(p * S);
+   // temp space
+   secure_vector<uint8_t> V((N+1) * S);
+
+   auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+
+   try
+      {
+      hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len);
+      }
+   catch(Invalid_Key_Length&)
+      {
+      throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length");
+      }
+
+   pbkdf2(*hmac_sha256.get(),
+          B.data(), B.size(),
+          salt, salt_len,
+          1);
+
+   // these can be parallel
+   for(size_t i = 0; i != p; ++i)
+      {
+      scryptROMmix(r, N, &B[128*r*i], V);
+      }
+
+   pbkdf2(*hmac_sha256.get(),
+          output, output_len,
+          B.data(), B.size(),
+          1);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h b/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h
@@ -0,0 +1,127 @@
+/**
+* (C) 2018 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SCRYPT_H_
+#define BOTAN_SCRYPT_H_
+
+#include <botan/pwdhash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(scrypt.h)
+
+namespace Botan {
+
+/**
+* Scrypt key derivation function (RFC 7914)
+*/
+class BOTAN_PUBLIC_API(2,8) Scrypt final : public PasswordHash
+   {
+   public:
+      Scrypt(size_t N, size_t r, size_t p);
+
+      Scrypt(const Scrypt& other) = default;
+      Scrypt& operator=(const Scrypt&) = default;
+
+      /**
+      * Derive a new key under the current Scrypt parameter set
+      */
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len) const override;
+
+      std::string to_string() const override;
+
+      size_t N() const { return m_N; }
+      size_t r() const { return m_r; }
+      size_t p() const { return m_p; }
+
+      size_t iterations() const override { return r(); }
+
+      size_t parallelism() const override { return p(); }
+
+      size_t memory_param() const override { return N(); }
+
+      size_t total_memory_usage() const override;
+
+   private:
+      size_t m_N, m_r, m_p;
+   };
+
+class BOTAN_PUBLIC_API(2,8) Scrypt_Family final : public PasswordHashFamily
+   {
+   public:
+      std::string name() const override;
+
+      std::unique_ptr<PasswordHash> tune(size_t output_length,
+                                         std::chrono::milliseconds msec,
+                                         size_t max_memory) const override;
+
+      std::unique_ptr<PasswordHash> default_params() const override;
+
+      std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+      std::unique_ptr<PasswordHash> from_params(
+         size_t N, size_t r, size_t p) const override;
+   };
+
+/**
+* Scrypt key derivation function (RFC 7914)
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param password_len length of password
+* @param salt the salt
+* @param salt_len length of salt
+* @param N the CPU/Memory cost parameter, must be power of 2
+* @param r the block size parameter
+* @param p the parallelization parameter
+*
+* Suitable parameters for most uses would be N = 32768, r = 8, p = 1
+*
+* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory
+*/
+void BOTAN_PUBLIC_API(2,8) scrypt(uint8_t output[], size_t output_len,
+                                  const char* password, size_t password_len,
+                                  const uint8_t salt[], size_t salt_len,
+                                  size_t N, size_t r, size_t p);
+
+/**
+* Scrypt key derivation function (RFC 7914)
+* Before 2.8 this function was the primary interface for scrypt
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param salt the salt
+* @param salt_len length of salt
+* @param N the CPU/Memory cost parameter, must be power of 2
+* @param r the block size parameter
+* @param p the parallelization parameter
+*
+* Suitable parameters for most uses would be N = 32768, r = 8, p = 1
+*
+* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory
+*/
+inline void scrypt(uint8_t output[], size_t output_len,
+                   const std::string& password,
+                   const uint8_t salt[], size_t salt_len,
+                   size_t N, size_t r, size_t p)
+   {
+   return scrypt(output, output_len,
+                 password.c_str(), password.size(),
+                 salt, salt_len,
+                 N, r, p);
+   }
+
+inline size_t scrypt_memory_usage(size_t N, size_t r, size_t p)
+   {
+   return 128 * r * (N + p);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/eme.cpp b/third_party/botan/src/lib/pk_pad/eme.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme.cpp
@@ -0,0 +1,94 @@
+/*
+* EME Base Class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_EME_OAEP)
+#include <botan/oaep.h>
+#endif
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+#include <botan/eme_pkcs.h>
+#endif
+
+#if defined(BOTAN_HAS_EME_RAW)
+#include <botan/eme_raw.h>
+#endif
+
+namespace Botan {
+
+EME* get_eme(const std::string& algo_spec)
+   {
+#if defined(BOTAN_HAS_EME_RAW)
+   if(algo_spec == "Raw")
+      return new EME_Raw;
+#endif
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+   if(algo_spec == "PKCS1v15" || algo_spec == "EME-PKCS1-v1_5")
+      return new EME_PKCS1v15;
+#endif
+
+#if defined(BOTAN_HAS_EME_OAEP)
+   SCAN_Name req(algo_spec);
+
+   if(req.algo_name() == "OAEP" ||
+      req.algo_name() == "EME-OAEP" ||
+      req.algo_name() == "EME1")
+      {
+      if(req.arg_count() == 1 ||
+         ((req.arg_count() == 2 || req.arg_count() == 3) && req.arg(1) == "MGF1"))
+         {
+         if(auto hash = HashFunction::create(req.arg(0)))
+            return new OAEP(hash.release(), req.arg(2, ""));
+         }
+      else if(req.arg_count() == 2 || req.arg_count() == 3)
+         {
+         auto mgf_params = parse_algorithm_name(req.arg(1));
+
+         if(mgf_params.size() == 2 && mgf_params[0] == "MGF1")
+            {
+            auto hash = HashFunction::create(req.arg(0));
+            auto mgf1_hash = HashFunction::create(mgf_params[1]);
+
+            if(hash && mgf1_hash)
+               {
+               return new OAEP(hash.release(), mgf1_hash.release(), req.arg(2, ""));
+               }
+            }
+         }
+      }
+#endif
+
+   throw Algorithm_Not_Found(algo_spec);
+   }
+
+/*
+* Encode a message
+*/
+secure_vector<uint8_t> EME::encode(const uint8_t msg[], size_t msg_len,
+                                size_t key_bits,
+                                RandomNumberGenerator& rng) const
+   {
+   return pad(msg, msg_len, key_bits, rng);
+   }
+
+/*
+* Encode a message
+*/
+secure_vector<uint8_t> EME::encode(const secure_vector<uint8_t>& msg,
+                                size_t key_bits,
+                                RandomNumberGenerator& rng) const
+   {
+   return pad(msg.data(), msg.size(), key_bits, rng);
+   }
+
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/eme.h b/third_party/botan/src/lib/pk_pad/eme.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme.h
@@ -0,0 +1,94 @@
+/*
+* EME Classes
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PUBKEY_EME_ENCRYPTION_PAD_H_
+#define BOTAN_PUBKEY_EME_ENCRYPTION_PAD_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Encoding Method for Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) EME
+   {
+   public:
+      virtual ~EME() = default;
+
+      /**
+      * Return the maximum input size in bytes we can support
+      * @param keybits the size of the key in bits
+      * @return upper bound of input in bytes
+      */
+      virtual size_t maximum_input_size(size_t keybits) const = 0;
+
+      /**
+      * Encode an input
+      * @param in the plaintext
+      * @param in_length length of plaintext in bytes
+      * @param key_length length of the key in bits
+      * @param rng a random number generator
+      * @return encoded plaintext
+      */
+      secure_vector<uint8_t> encode(const uint8_t in[],
+                                 size_t in_length,
+                                 size_t key_length,
+                                 RandomNumberGenerator& rng) const;
+
+      /**
+      * Encode an input
+      * @param in the plaintext
+      * @param key_length length of the key in bits
+      * @param rng a random number generator
+      * @return encoded plaintext
+      */
+      secure_vector<uint8_t> encode(const secure_vector<uint8_t>& in,
+                                 size_t key_length,
+                                 RandomNumberGenerator& rng) const;
+
+      /**
+      * Decode an input
+      * @param valid_mask written to specifies if output is valid
+      * @param in the encoded plaintext
+      * @param in_len length of encoded plaintext in bytes
+      * @return bytes of out[] written to along with
+      *         validity mask (0xFF if valid, else 0x00)
+      */
+      virtual secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+                                        const uint8_t in[],
+                                        size_t in_len) const = 0;
+
+      /**
+      * Encode an input
+      * @param in the plaintext
+      * @param in_length length of plaintext in bytes
+      * @param key_length length of the key in bits
+      * @param rng a random number generator
+      * @return encoded plaintext
+      */
+      virtual secure_vector<uint8_t> pad(const uint8_t in[],
+                                      size_t in_length,
+                                      size_t key_length,
+                                      RandomNumberGenerator& rng) const = 0;
+   };
+
+/**
+* Factory method for EME (message-encoding methods for encryption) objects
+* @param algo_spec the name of the EME to create
+* @return pointer to newly allocated object of that type
+*/
+BOTAN_PUBLIC_API(2,0) EME*  get_eme(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp b/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp
@@ -0,0 +1,166 @@
+/*
+* OAEP
+* (C) 1999-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/oaep.h>
+#include <botan/mgf1.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* OAEP Pad Operation
+*/
+secure_vector<uint8_t> OAEP::pad(const uint8_t in[], size_t in_length,
+                             size_t key_length,
+                             RandomNumberGenerator& rng) const
+   {
+   key_length /= 8;
+
+   if(in_length > maximum_input_size(key_length * 8))
+      {
+      throw Invalid_Argument("OAEP: Input is too large");
+      }
+
+   secure_vector<uint8_t> out(key_length);
+
+   rng.randomize(out.data(), m_Phash.size());
+
+   buffer_insert(out, m_Phash.size(), m_Phash.data(), m_Phash.size());
+   out[out.size() - in_length - 1] = 0x01;
+   buffer_insert(out, out.size() - in_length, in, in_length);
+
+   mgf1_mask(*m_mgf1_hash,
+             out.data(), m_Phash.size(),
+             &out[m_Phash.size()], out.size() - m_Phash.size());
+
+   mgf1_mask(*m_mgf1_hash,
+             &out[m_Phash.size()], out.size() - m_Phash.size(),
+             out.data(), m_Phash.size());
+
+   return out;
+   }
+
+/*
+* OAEP Unpad Operation
+*/
+secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask,
+                                   const uint8_t in[], size_t in_length) const
+   {
+   /*
+   Must be careful about error messages here; if an attacker can
+   distinguish them, it is easy to use the differences as an oracle to
+   find the secret key, as described in "A Chosen Ciphertext Attack on
+   RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in
+   PKCS #1 v2.0", James Manger, Crypto 2001
+
+   Also have to be careful about timing attacks! Pointed out by Falko
+   Strenzke.
+
+   According to the standard (Section 7.1.1), the encryptor always
+   creates a message as follows:
+      i. Concatenate a single octet with hexadecimal value 0x00,
+         maskedSeed, and maskedDB to form an encoded message EM of
+         length k octets as
+            EM = 0x00 || maskedSeed || maskedDB.
+   where k is the length of the modulus N.
+   Therefore, the first byte can always be skipped safely.
+   */
+
+   const uint8_t skip_first = CT::Mask<uint8_t>::is_zero(in[0]).if_set_return(1);
+
+   secure_vector<uint8_t> input(in + skip_first, in + in_length);
+
+   const size_t hlen = m_Phash.size();
+
+   mgf1_mask(*m_mgf1_hash,
+             &input[hlen], input.size() - hlen,
+             input.data(), hlen);
+
+   mgf1_mask(*m_mgf1_hash,
+             input.data(), hlen,
+             &input[hlen], input.size() - hlen);
+
+   return oaep_find_delim(valid_mask, input.data(), input.size(), m_Phash);
+   }
+
+secure_vector<uint8_t>
+oaep_find_delim(uint8_t& valid_mask,
+                const uint8_t input[], size_t input_len,
+                const secure_vector<uint8_t>& Phash)
+   {
+   const size_t hlen = Phash.size();
+
+   // Too short to be valid, reject immediately
+   if(input_len < 1 + 2*hlen)
+      {
+      return secure_vector<uint8_t>();
+      }
+
+   CT::poison(input, input_len);
+
+   size_t delim_idx = 2 * hlen;
+   CT::Mask<uint8_t> waiting_for_delim = CT::Mask<uint8_t>::set();
+   CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared();
+
+   for(size_t i = delim_idx; i < input_len; ++i)
+      {
+      const auto zero_m = CT::Mask<uint8_t>::is_zero(input[i]);
+      const auto one_m = CT::Mask<uint8_t>::is_equal(input[i], 1);
+
+      const auto add_m = waiting_for_delim & zero_m;
+
+      bad_input_m |= waiting_for_delim & ~(zero_m | one_m);
+
+      delim_idx += add_m.if_set_return(1);
+
+      waiting_for_delim &= zero_m;
+      }
+
+   // If we never saw any non-zero byte, then it's not valid input
+   bad_input_m |= waiting_for_delim;
+   bad_input_m |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(&input[hlen], Phash.data(), hlen));
+
+   delim_idx += 1;
+
+   valid_mask = (~bad_input_m).unpoisoned_value();
+   const secure_vector<uint8_t> output = CT::copy_output(bad_input_m, input, input_len, delim_idx);
+
+   CT::unpoison(input, input_len);
+
+   return output;
+   }
+
+/*
+* Return the max input size for a given key size
+*/
+size_t OAEP::maximum_input_size(size_t keybits) const
+   {
+   if(keybits / 8 > 2*m_Phash.size() + 1)
+      return ((keybits / 8) - 2*m_Phash.size() - 1);
+   else
+      return 0;
+   }
+
+/*
+* OAEP Constructor
+*/
+OAEP::OAEP(HashFunction* hash, const std::string& P) : m_mgf1_hash(hash)
+   {
+   m_Phash = m_mgf1_hash->process(P);
+   }
+
+OAEP::OAEP(HashFunction* hash,
+           HashFunction* mgf1_hash,
+           const std::string& P) : m_mgf1_hash(mgf1_hash)
+   {
+   std::unique_ptr<HashFunction> phash(hash); // takes ownership
+   m_Phash = phash->process(P);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h b/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h
@@ -0,0 +1,62 @@
+/*
+* OAEP
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OAEP_H_
+#define BOTAN_OAEP_H_
+
+#include <botan/eme.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(oaep.h)
+
+namespace Botan {
+
+/**
+* OAEP (called EME1 in IEEE 1363 and in earlier versions of the library)
+* as specified in PKCS#1 v2.0 (RFC 2437)
+*/
+class BOTAN_PUBLIC_API(2,0) OAEP final : public EME
+   {
+   public:
+      size_t maximum_input_size(size_t) const override;
+
+      /**
+      * @param hash function to use for hashing (takes ownership)
+      * @param P an optional label. Normally empty.
+      */
+      OAEP(HashFunction* hash, const std::string& P = "");
+
+      /**
+      * @param hash function to use for hashing (takes ownership)
+      * @param mgf1_hash function to use for MGF1 (takes ownership)
+      * @param P an optional label. Normally empty.
+      */
+      OAEP(HashFunction* hash,
+           HashFunction* mgf1_hash,
+           const std::string& P = "");
+   private:
+      secure_vector<uint8_t> pad(const uint8_t in[],
+                              size_t in_length,
+                              size_t key_length,
+                              RandomNumberGenerator& rng) const override;
+
+      secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+                                const uint8_t in[],
+                                size_t in_len) const override;
+
+      secure_vector<uint8_t> m_Phash;
+      std::unique_ptr<HashFunction> m_mgf1_hash;
+   };
+
+secure_vector<uint8_t>
+BOTAN_TEST_API oaep_find_delim(uint8_t& valid_mask,
+                               const uint8_t input[], size_t input_len,
+                               const secure_vector<uint8_t>& Phash);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp b/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
@@ -0,0 +1,109 @@
+/*
+* PKCS #1 v1.5 Type 2 (encryption) padding
+* (C) 1999-2007,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme_pkcs.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* PKCS1 Pad Operation
+*/
+secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[], size_t inlen,
+                                     size_t key_length,
+                                     RandomNumberGenerator& rng) const
+   {
+   key_length /= 8;
+
+   if(inlen > maximum_input_size(key_length * 8))
+      {
+      throw Invalid_Argument("PKCS1: Input is too large");
+      }
+
+   secure_vector<uint8_t> out(key_length);
+
+   out[0] = 0x02;
+   rng.randomize(out.data() + 1, (key_length - inlen - 2));
+
+   for(size_t j = 1; j != key_length - inlen - 1; ++j)
+      {
+      if(out[j] == 0)
+         {
+         out[j] = rng.next_nonzero_byte();
+         }
+      }
+
+   buffer_insert(out, key_length - inlen, in, inlen);
+
+   return out;
+   }
+
+/*
+* PKCS1 Unpad Operation
+*/
+secure_vector<uint8_t> EME_PKCS1v15::unpad(uint8_t& valid_mask,
+                                        const uint8_t in[], size_t inlen) const
+   {
+   /*
+   * RSA decryption pads the ciphertext up to the modulus size, so this only
+   * occurs with very (!) small keys, or when fuzzing.
+   *
+   * 11 bytes == 00,02 + 8 bytes mandatory padding + 00
+   */
+   if(inlen < 11)
+      {
+      valid_mask = false;
+      return secure_vector<uint8_t>();
+      }
+
+   CT::poison(in, inlen);
+
+   CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared();
+   CT::Mask<uint8_t> seen_zero_m = CT::Mask<uint8_t>::cleared();
+   size_t delim_idx = 2; // initial 0002
+
+   bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[0], 0);
+   bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[1], 2);
+
+   for(size_t i = 2; i < inlen; ++i)
+      {
+      const auto is_zero_m = CT::Mask<uint8_t>::is_zero(in[i]);
+      delim_idx += seen_zero_m.if_not_set_return(1);
+      seen_zero_m |= is_zero_m;
+      }
+
+   // no zero delim -> bad padding
+   bad_input_m |= ~seen_zero_m;
+   /*
+   delim indicates < 8 bytes padding -> bad padding
+
+   We require 11 here because we are counting also the 00 delim byte
+   */
+   bad_input_m |= CT::Mask<uint8_t>(CT::Mask<size_t>::is_lt(delim_idx, 11));
+
+   valid_mask = (~bad_input_m).unpoisoned_value();
+   const secure_vector<uint8_t> output = CT::copy_output(bad_input_m, in, inlen, delim_idx);
+
+   CT::unpoison(in, inlen);
+
+   return output;
+   }
+
+/*
+* Return the max input size for a given key size
+*/
+size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const
+   {
+   if(keybits / 8 > 10)
+      return ((keybits / 8) - 10);
+   else
+      return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h b/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
@@ -0,0 +1,35 @@
+/*
+* EME PKCS#1 v1.5
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EME_PKCS1_H_
+#define BOTAN_EME_PKCS1_H_
+
+#include <botan/eme.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme_pkcs.h)
+
+namespace Botan {
+
+/**
+* EME from PKCS #1 v1.5
+*/
+class BOTAN_PUBLIC_API(2,0) EME_PKCS1v15 final : public EME
+   {
+   public:
+      size_t maximum_input_size(size_t) const override;
+
+      secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t,
+                             RandomNumberGenerator&) const override;
+
+      secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+                                const uint8_t in[],
+                                size_t in_len) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp b/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme_raw.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> EME_Raw::pad(const uint8_t in[], size_t in_length,
+                                 size_t,
+                                 RandomNumberGenerator&) const
+   {
+   return secure_vector<uint8_t>(in, in + in_length);
+   }
+
+secure_vector<uint8_t> EME_Raw::unpad(uint8_t& valid_mask,
+                                   const uint8_t in[], size_t in_length) const
+   {
+   valid_mask = 0xFF;
+   return CT::strip_leading_zeros(in, in_length);
+   }
+
+size_t EME_Raw::maximum_input_size(size_t keybits) const
+   {
+   return keybits / 8;
+   }
+}
diff --git a/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h b/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h
@@ -0,0 +1,33 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EME_RAW_H_
+#define BOTAN_EME_RAW_H_
+
+#include <botan/eme.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme_raw.h)
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) EME_Raw final : public EME
+   {
+   public:
+      size_t maximum_input_size(size_t i) const override;
+
+      EME_Raw() = default;
+   private:
+      secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t,
+                             RandomNumberGenerator&) const override;
+
+      secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+                                const uint8_t in[],
+                                size_t in_len) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa.cpp b/third_party/botan/src/lib/pk_pad/emsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa.cpp
@@ -0,0 +1,207 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_EMSA1)
+   #include <botan/emsa1.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_X931)
+   #include <botan/emsa_x931.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PKCS1)
+   #include <botan/emsa_pkcs1.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PSSR)
+   #include <botan/pssr.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+   #include <botan/emsa_raw.h>
+#endif
+
+#if defined(BOTAN_HAS_ISO_9796)
+   #include <botan/iso9796.h>
+#endif
+
+namespace Botan {
+
+AlgorithmIdentifier EMSA::config_for_x509(const Private_Key&,
+                                          const std::string&) const
+   {
+   throw Not_Implemented("Encoding " + name() + " not supported for signing X509 objects");
+   }
+
+EMSA* get_emsa(const std::string& algo_spec)
+   {
+   SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_EMSA1)
+   if(req.algo_name() == "EMSA1" && req.arg_count() == 1)
+      {
+      if(auto hash = HashFunction::create(req.arg(0)))
+         return new EMSA1(hash.release());
+      }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PKCS1)
+   if(req.algo_name() == "EMSA_PKCS1" ||
+      req.algo_name() == "PKCS1v15" ||
+      req.algo_name() == "EMSA-PKCS1-v1_5" ||
+      req.algo_name() == "EMSA3")
+      {
+      if(req.arg_count() == 2 && req.arg(0) == "Raw")
+         {
+         return new EMSA_PKCS1v15_Raw(req.arg(1));
+         }
+      else if(req.arg_count() == 1)
+         {
+         if(req.arg(0) == "Raw")
+            {
+            return new EMSA_PKCS1v15_Raw;
+            }
+         else
+            {
+            if(auto hash = HashFunction::create(req.arg(0)))
+               {
+               return new EMSA_PKCS1v15(hash.release());
+               }
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PSSR)
+   if(req.algo_name() == "PSS_Raw" ||
+      req.algo_name() == "PSSR_Raw")
+      {
+      if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1")
+         {
+         if(auto h = HashFunction::create(req.arg(0)))
+            {
+            if(req.arg_count() == 3)
+               {
+               const size_t salt_size = req.arg_as_integer(2, 0);
+               return new PSSR_Raw(h.release(), salt_size);
+               }
+            else
+               {
+               return new PSSR_Raw(h.release());
+               }
+            }
+         }
+      }
+
+   if(req.algo_name() == "PSS" ||
+      req.algo_name() == "PSSR" ||
+      req.algo_name() == "EMSA-PSS" ||
+      req.algo_name() == "PSS-MGF1" ||
+      req.algo_name() == "EMSA4")
+      {
+      if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1")
+         {
+         if(auto h = HashFunction::create(req.arg(0)))
+            {
+            if(req.arg_count() == 3)
+               {
+               const size_t salt_size = req.arg_as_integer(2, 0);
+               return new PSSR(h.release(), salt_size);
+               }
+            else
+               {
+               return new PSSR(h.release());
+               }
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_ISO_9796)
+   if(req.algo_name() == "ISO_9796_DS2")
+      {
+      if(req.arg_count_between(1, 3))
+         {
+         if(auto h = HashFunction::create(req.arg(0)))
+            {
+            const size_t salt_size = req.arg_as_integer(2, h->output_length());
+            const bool implicit = req.arg(1, "exp") == "imp";
+            return new ISO_9796_DS2(h.release(), implicit, salt_size);
+            }
+         }
+      }
+   //ISO-9796-2 DS 3 is deterministic and DS2 without a salt
+   if(req.algo_name() == "ISO_9796_DS3")
+      {
+      if(req.arg_count_between(1, 2))
+         {
+         if(auto h = HashFunction::create(req.arg(0)))
+            {
+            const bool implicit = req.arg(1, "exp") == "imp";
+            return new ISO_9796_DS3(h.release(), implicit);
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_X931)
+   if(req.algo_name() == "EMSA_X931" ||
+         req.algo_name() == "EMSA2" ||
+         req.algo_name() == "X9.31")
+      {
+      if(req.arg_count() == 1)
+         {
+         if(auto hash = HashFunction::create(req.arg(0)))
+            {
+            return new EMSA_X931(hash.release());
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+   if(req.algo_name() == "Raw")
+      {
+      if(req.arg_count() == 0)
+         {
+         return new EMSA_Raw;
+         }
+      else
+         {
+         auto hash = HashFunction::create(req.arg(0));
+         if(hash)
+            return new EMSA_Raw(hash->output_length());
+         }
+      }
+#endif
+
+   throw Algorithm_Not_Found(algo_spec);
+   }
+
+std::string hash_for_emsa(const std::string& algo_spec)
+   {
+   SCAN_Name emsa_name(algo_spec);
+
+   if(emsa_name.arg_count() > 0)
+      {
+      const std::string pos_hash = emsa_name.arg(0);
+      return pos_hash;
+      }
+
+   // If we don't understand what this is return a safe default
+#if defined(BOTAN_HAS_SHA2_64)
+   return "SHA-512";
+#else
+   return "SHA-256";
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa.h b/third_party/botan/src/lib/pk_pad/emsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa.h
@@ -0,0 +1,107 @@
+/*
+* EMSA Classes
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PUBKEY_EMSA_H_
+#define BOTAN_PUBKEY_EMSA_H_
+
+#include <botan/secmem.h>
+#include <botan/alg_id.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa.h)
+
+namespace Botan {
+
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+* EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix
+*
+* Any way of encoding/padding signatures
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA
+   {
+   public:
+      virtual ~EMSA() = default;
+
+      /**
+      * Add more data to the signature computation
+      * @param input some data
+      * @param length length of input in bytes
+      */
+      virtual void update(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * @return raw hash
+      */
+      virtual secure_vector<uint8_t> raw_data() = 0;
+
+      /**
+      * Return the encoding of a message
+      * @param msg the result of raw_data()
+      * @param output_bits the desired output bit size
+      * @param rng a random number generator
+      * @return encoded signature
+      */
+      virtual secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                             size_t output_bits,
+                                             RandomNumberGenerator& rng) = 0;
+
+      /**
+      * Verify the encoding
+      * @param coded the received (coded) message representative
+      * @param raw the computed (local, uncoded) message representative
+      * @param key_bits the size of the key in bits
+      * @return true if coded is a valid encoding of raw, otherwise false
+      */
+      virtual bool verify(const secure_vector<uint8_t>& coded,
+                          const secure_vector<uint8_t>& raw,
+                          size_t key_bits) = 0;
+
+      /**
+      * Prepare sig_algo for use in choose_sig_format for x509 certs
+      *
+      * @param key used for checking compatibility with the encoding scheme
+      * @param cert_hash_name is checked to equal the hash for the encoding
+      * @return algorithm identifier to signatures created using this key,
+      *         padding method and hash.
+      */
+      virtual AlgorithmIdentifier config_for_x509(const Private_Key& key,
+                                                  const std::string& cert_hash_name) const;
+
+      /**
+      * @return a new object representing the same encoding method as *this
+      */
+      virtual EMSA* clone() = 0;
+
+      /**
+      * @return the SCAN name of the encoding/padding scheme
+      */
+      virtual std::string name() const = 0;
+   };
+
+/**
+* Factory method for EMSA (message-encoding methods for signatures
+* with appendix) objects
+* @param algo_spec the name of the EMSA to create
+* @return pointer to newly allocated object of that type
+*/
+BOTAN_PUBLIC_API(2,0) EMSA* get_emsa(const std::string& algo_spec);
+
+/**
+* Returns the hash function used in the given EMSA scheme
+* If the hash function is not specified or not understood,
+* returns "SHA-512"
+* @param algo_spec the name of the EMSA
+* @return hash function used in the given EMSA scheme
+*/
+BOTAN_PUBLIC_API(2,0) std::string hash_for_emsa(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp b/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp
@@ -0,0 +1,133 @@
+/*
+* EMSA1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa1.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa1_encoding(const secure_vector<uint8_t>& msg,
+                                  size_t output_bits)
+   {
+   if(8*msg.size() <= output_bits)
+      return msg;
+
+   size_t shift = 8*msg.size() - output_bits;
+
+   size_t byte_shift = shift / 8, bit_shift = shift % 8;
+   secure_vector<uint8_t> digest(msg.size() - byte_shift);
+
+   for(size_t j = 0; j != msg.size() - byte_shift; ++j)
+      digest[j] = msg[j];
+
+   if(bit_shift)
+      {
+      uint8_t carry = 0;
+      for(size_t j = 0; j != digest.size(); ++j)
+         {
+         uint8_t temp = digest[j];
+         digest[j] = (temp >> bit_shift) | carry;
+         carry = (temp << (8 - bit_shift));
+         }
+      }
+   return digest;
+   }
+
+}
+
+std::string EMSA1::name() const
+   {
+   return "EMSA1(" + m_hash->name() + ")";
+   }
+
+EMSA* EMSA1::clone()
+   {
+   return new EMSA1(m_hash->clone());
+   }
+
+void EMSA1::update(const uint8_t input[], size_t length)
+   {
+   m_hash->update(input, length);
+   }
+
+secure_vector<uint8_t> EMSA1::raw_data()
+   {
+   return m_hash->final();
+   }
+
+secure_vector<uint8_t> EMSA1::encoding_of(const secure_vector<uint8_t>& msg,
+                                       size_t output_bits,
+                                       RandomNumberGenerator&)
+   {
+   if(msg.size() != hash_output_length())
+      throw Encoding_Error("EMSA1::encoding_of: Invalid size for input");
+   return emsa1_encoding(msg, output_bits);
+   }
+
+bool EMSA1::verify(const secure_vector<uint8_t>& input,
+                   const secure_vector<uint8_t>& raw,
+                   size_t key_bits)
+   {
+   if(raw.size() != m_hash->output_length())
+      return false;
+
+   // Call emsa1_encoding to handle any required bit shifting
+   const secure_vector<uint8_t> our_coding = emsa1_encoding(raw, key_bits);
+
+   if(our_coding.size() < input.size())
+      return false;
+
+   const size_t offset = our_coding.size() - input.size(); // must be >= 0 per check above
+
+   // If our encoding is longer, all the bytes in it must be zero
+   for(size_t i = 0; i != offset; ++i)
+      if(our_coding[i] != 0)
+            return false;
+
+   return constant_time_compare(input.data(), &our_coding[offset], input.size());
+   }
+
+AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key,
+                                           const std::string& cert_hash_name) const
+   {
+   if(cert_hash_name != m_hash->name())
+      throw Invalid_Argument("Hash function from opts and hash_fn argument"
+         " need to be identical");
+   // check that the signature algorithm and the padding scheme fit
+   if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA1"))
+      {
+      throw Invalid_Argument("Encoding scheme with canonical name EMSA1"
+         " not supported for signature algorithm " + key.algo_name());
+      }
+
+   const OID oid = OID::from_string(key.algo_name() + "/" + name());
+
+   const std::string algo_name = key.algo_name();
+   std::vector<uint8_t> parameters;
+   if(algo_name == "DSA" ||
+      algo_name == "ECDSA" ||
+      algo_name == "ECGDSA" ||
+      algo_name == "ECKCDSA" ||
+      algo_name == "GOST-34.10" ||
+      algo_name == "GOST-34.10-2012-256" ||
+      algo_name == "GOST-34.10-2012-512")
+      {
+      // for DSA, ECDSA, GOST parameters "SHALL" be empty
+      }
+   else
+      {
+      parameters = key.algorithm_identifier().get_parameters();
+      }
+
+   return AlgorithmIdentifier(oid, parameters);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h b/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h
@@ -0,0 +1,55 @@
+/*
+* EMSA1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EMSA1_H_
+#define BOTAN_EMSA1_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa1.h)
+
+namespace Botan {
+
+/**
+* EMSA1 from IEEE 1363
+* Essentially, sign the hash directly
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA1 final : public EMSA
+   {
+   public:
+      /**
+      * @param hash the hash function to use
+      */
+      explicit EMSA1(HashFunction* hash) : m_hash(hash) {}
+
+      EMSA* clone() override;
+
+      std::string name() const override;
+
+      AlgorithmIdentifier config_for_x509(const Private_Key& key,
+                                          const std::string& cert_hash_name) const override;
+   private:
+      size_t hash_output_length() const { return m_hash->output_length(); }
+
+      void update(const uint8_t[], size_t) override;
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                         size_t output_bits,
+                                         RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
@@ -0,0 +1,166 @@
+/*
+* PKCS #1 v1.5 signature padding
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_pkcs1.h>
+#include <botan/hash_id.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg,
+                                   size_t output_bits,
+                                   const uint8_t hash_id[],
+                                   size_t hash_id_length)
+   {
+   size_t output_length = output_bits / 8;
+   if(output_length < hash_id_length + msg.size() + 10)
+      throw Encoding_Error("emsa3_encoding: Output length is too small");
+
+   secure_vector<uint8_t> T(output_length);
+   const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2;
+
+   T[0] = 0x01;
+   set_mem(&T[1], P_LENGTH, 0xFF);
+   T[P_LENGTH+1] = 0x00;
+
+   if(hash_id_length > 0)
+      {
+      BOTAN_ASSERT_NONNULL(hash_id);
+      buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
+      }
+
+   buffer_insert(T, output_length-msg.size(), msg.data(), msg.size());
+   return T;
+   }
+
+}
+
+void EMSA_PKCS1v15::update(const uint8_t input[], size_t length)
+   {
+   m_hash->update(input, length);
+   }
+
+secure_vector<uint8_t> EMSA_PKCS1v15::raw_data()
+   {
+   return m_hash->final();
+   }
+
+secure_vector<uint8_t>
+EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg,
+                           size_t output_bits,
+                           RandomNumberGenerator&)
+   {
+   if(msg.size() != m_hash->output_length())
+      throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length");
+
+   return emsa3_encoding(msg, output_bits,
+                         m_hash_id.data(), m_hash_id.size());
+   }
+
+bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded,
+                           const secure_vector<uint8_t>& raw,
+                           size_t key_bits)
+   {
+   if(raw.size() != m_hash->output_length())
+      return false;
+
+   try
+      {
+      return (coded == emsa3_encoding(raw, key_bits,
+                                      m_hash_id.data(), m_hash_id.size()));
+      }
+   catch(...)
+      {
+      return false;
+      }
+   }
+
+AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key,
+                                    const std::string& cert_hash_name) const
+   {
+   if(cert_hash_name != m_hash->name())
+      throw Invalid_Argument("Hash function from opts and hash_fn argument"
+         " need to be identical");
+   // check that the signature algorithm and the padding scheme fit
+   if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3"))
+      {
+      throw Invalid_Argument("Encoding scheme with canonical name EMSA3"
+         " not supported for signature algorithm " + key.algo_name());
+      }
+
+   // for RSA PKCSv1.5 parameters "SHALL" be NULL
+
+   const OID oid = OID::from_string(key.algo_name() + "/" + name());
+   return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_NULL_PARAM);
+   }
+
+EMSA_PKCS1v15::EMSA_PKCS1v15(HashFunction* hash) : m_hash(hash)
+   {
+   m_hash_id = pkcs_hash_id(m_hash->name());
+   }
+
+EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo)
+   {
+   if(!hash_algo.empty())
+      {
+      m_hash_id = pkcs_hash_id(hash_algo);
+      std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
+      m_hash_name = hash->name();
+      m_hash_output_len = hash->output_length();
+      }
+   else
+      {
+      m_hash_output_len = 0;
+      }
+   }
+
+void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length)
+   {
+   m_message += std::make_pair(input, length);
+   }
+
+secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data()
+   {
+   secure_vector<uint8_t> ret;
+   std::swap(ret, m_message);
+
+   if(m_hash_output_len > 0 && ret.size() != m_hash_output_len)
+      throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length");
+
+   return ret;
+   }
+
+secure_vector<uint8_t>
+EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+                               size_t output_bits,
+                               RandomNumberGenerator&)
+   {
+   return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
+   }
+
+bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded,
+                               const secure_vector<uint8_t>& raw,
+                               size_t key_bits)
+   {
+   if(m_hash_output_len > 0 && raw.size() != m_hash_output_len)
+      return false;
+
+   try
+      {
+      return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
+      }
+   catch(...)
+      {
+      return false;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h b/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
@@ -0,0 +1,94 @@
+/*
+* PKCS #1 v1.5 signature padding
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EMSA_PKCS1_H_
+#define BOTAN_EMSA_PKCS1_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_pkcs1.h)
+
+namespace Botan {
+
+/**
+* PKCS #1 v1.5 signature padding
+* aka PKCS #1 block type 1
+* aka EMSA3 from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15 final : public EMSA
+   {
+   public:
+      /**
+      * @param hash the hash function to use
+      */
+      explicit EMSA_PKCS1v15(HashFunction* hash);
+
+      EMSA* clone() override { return new EMSA_PKCS1v15(m_hash->clone()); }
+
+      void update(const uint8_t[], size_t) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+                                     RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+                  size_t) override;
+
+      std::string name() const override
+         { return "EMSA3(" + m_hash->name() + ")"; }
+
+      AlgorithmIdentifier config_for_x509(const Private_Key& key,
+                                          const std::string& cert_hash_name) const override;
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+      std::vector<uint8_t> m_hash_id;
+   };
+
+/**
+* EMSA_PKCS1v15_Raw which is EMSA_PKCS1v15 without a hash or digest id
+* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS
+* mechanism", something I have not confirmed)
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15_Raw final : public EMSA
+   {
+   public:
+      EMSA* clone() override { return new EMSA_PKCS1v15_Raw(); }
+
+      void update(const uint8_t[], size_t) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+                                     RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+                  size_t) override;
+
+      /**
+      * @param hash_algo if non-empty, the digest id for that hash is
+      * included in the signature.
+      */
+      EMSA_PKCS1v15_Raw(const std::string& hash_algo = "");
+
+      std::string name() const override
+         {
+         if(m_hash_name.empty()) return "EMSA3(Raw)";
+         else return "EMSA3(Raw," + m_hash_name + ")";
+         }
+
+   private:
+      size_t m_hash_output_len = 0;
+      std::string m_hash_name;
+      std::vector<uint8_t> m_hash_id;
+      secure_vector<uint8_t> m_message;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp b/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -0,0 +1,291 @@
+/*
+* PSSR
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pssr.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/mgf1.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/der_enc.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* PSSR Encode Operation
+*/
+secure_vector<uint8_t> pss_encode(HashFunction& hash,
+                                  const secure_vector<uint8_t>& msg,
+                                  const secure_vector<uint8_t>& salt,
+                                  size_t output_bits)
+   {
+   const size_t HASH_SIZE = hash.output_length();
+   const size_t SALT_SIZE = salt.size();
+
+   if(msg.size() != HASH_SIZE)
+      throw Encoding_Error("Cannot encode PSS string, input length invalid for hash");
+   if(output_bits < 8*HASH_SIZE + 8*SALT_SIZE + 9)
+      throw Encoding_Error("Cannot encode PSS string, output length too small");
+
+   const size_t output_length = (output_bits + 7) / 8;
+
+   for(size_t i = 0; i != 8; ++i)
+      hash.update(0);
+   hash.update(msg);
+   hash.update(salt);
+   secure_vector<uint8_t> H = hash.final();
+
+   secure_vector<uint8_t> EM(output_length);
+
+   EM[output_length - HASH_SIZE - SALT_SIZE - 2] = 0x01;
+   buffer_insert(EM, output_length - 1 - HASH_SIZE - SALT_SIZE, salt);
+   mgf1_mask(hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - 1);
+   EM[0] &= 0xFF >> (8 * ((output_bits + 7) / 8) - output_bits);
+   buffer_insert(EM, output_length - 1 - HASH_SIZE, H);
+   EM[output_length-1] = 0xBC;
+   return EM;
+   }
+
+bool pss_verify(HashFunction& hash,
+                const secure_vector<uint8_t>& pss_repr,
+                const secure_vector<uint8_t>& message_hash,
+                size_t key_bits,
+                size_t* out_salt_size)
+   {
+   const size_t HASH_SIZE = hash.output_length();
+   const size_t KEY_BYTES = (key_bits + 7) / 8;
+
+   if(key_bits < 8*HASH_SIZE + 9)
+      return false;
+
+   if(message_hash.size() != HASH_SIZE)
+      return false;
+
+   if(pss_repr.size() > KEY_BYTES || pss_repr.size() <= 1)
+      return false;
+
+   if(pss_repr[pss_repr.size()-1] != 0xBC)
+      return false;
+
+   secure_vector<uint8_t> coded = pss_repr;
+   if(coded.size() < KEY_BYTES)
+      {
+      secure_vector<uint8_t> temp(KEY_BYTES);
+      buffer_insert(temp, KEY_BYTES - coded.size(), coded);
+      coded = temp;
+      }
+
+   const size_t TOP_BITS = 8 * ((key_bits + 7) / 8) - key_bits;
+   if(TOP_BITS > 8 - high_bit(coded[0]))
+      return false;
+
+   uint8_t* DB = coded.data();
+   const size_t DB_size = coded.size() - HASH_SIZE - 1;
+
+   const uint8_t* H = &coded[DB_size];
+   const size_t H_size = HASH_SIZE;
+
+   mgf1_mask(hash, H, H_size, DB, DB_size);
+   DB[0] &= 0xFF >> TOP_BITS;
+
+   size_t salt_offset = 0;
+   for(size_t j = 0; j != DB_size; ++j)
+      {
+      if(DB[j] == 0x01)
+         { salt_offset = j + 1; break; }
+      if(DB[j])
+         return false;
+      }
+   if(salt_offset == 0)
+      return false;
+
+   const size_t salt_size = DB_size - salt_offset;
+
+   for(size_t j = 0; j != 8; ++j)
+      hash.update(0);
+   hash.update(message_hash);
+   hash.update(&DB[salt_offset], salt_size);
+
+   const secure_vector<uint8_t> H2 = hash.final();
+
+   const bool ok = constant_time_compare(H, H2.data(), HASH_SIZE);
+
+   if(out_salt_size && ok)
+      *out_salt_size = salt_size;
+
+   return ok;
+   }
+
+}
+
+PSSR::PSSR(HashFunction* h) :
+   m_hash(h),
+   m_salt_size(m_hash->output_length()),
+   m_required_salt_len(false)
+   {
+   }
+
+PSSR::PSSR(HashFunction* h, size_t salt_size) :
+   m_hash(h),
+   m_salt_size(salt_size),
+   m_required_salt_len(true)
+   {
+   }
+
+/*
+* PSSR Update Operation
+*/
+void PSSR::update(const uint8_t input[], size_t length)
+   {
+   m_hash->update(input, length);
+   }
+
+/*
+* Return the raw (unencoded) data
+*/
+secure_vector<uint8_t> PSSR::raw_data()
+   {
+   return m_hash->final();
+   }
+
+secure_vector<uint8_t> PSSR::encoding_of(const secure_vector<uint8_t>& msg,
+                                         size_t output_bits,
+                                         RandomNumberGenerator& rng)
+   {
+   const secure_vector<uint8_t> salt = rng.random_vec(m_salt_size);
+   return pss_encode(*m_hash, msg, salt, output_bits);
+   }
+
+/*
+* PSSR Decode/Verify Operation
+*/
+bool PSSR::verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits)
+   {
+   size_t salt_size = 0;
+   const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
+
+   if(m_required_salt_len && salt_size != m_salt_size)
+      return false;
+
+   return ok;
+   }
+
+EMSA* PSSR::clone()
+   {
+   return new PSSR(m_hash->clone(), m_salt_size);
+   }
+
+std::string PSSR::name() const
+   {
+   return "EMSA4(" + m_hash->name() + ",MGF1," + std::to_string(m_salt_size) + ")";
+   }
+
+AlgorithmIdentifier PSSR::config_for_x509(const Private_Key& key,
+                                          const std::string& cert_hash_name) const
+   {
+   if(cert_hash_name != m_hash->name())
+      throw Invalid_Argument("Hash function from opts and hash_fn argument"
+         " need to be identical");
+   // check that the signature algorithm and the padding scheme fit
+   if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA4"))
+      {
+      throw Invalid_Argument("Encoding scheme with canonical name EMSA4"
+         " not supported for signature algorithm " + key.algo_name());
+      }
+
+   const AlgorithmIdentifier hash_id(cert_hash_name, AlgorithmIdentifier::USE_NULL_PARAM);
+   const AlgorithmIdentifier mgf_id("MGF1", hash_id.BER_encode());
+
+   std::vector<uint8_t> parameters;
+   DER_Encoder(parameters)
+      .start_cons(SEQUENCE)
+      .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).encode(hash_id).end_cons()
+      .start_cons(ASN1_Tag(1), CONTEXT_SPECIFIC).encode(mgf_id).end_cons()
+      .start_cons(ASN1_Tag(2), CONTEXT_SPECIFIC).encode(m_salt_size).end_cons()
+      .start_cons(ASN1_Tag(3), CONTEXT_SPECIFIC).encode(size_t(1)).end_cons() // trailer field
+      .end_cons();
+
+   // hardcoded as RSA is the only valid algorithm for EMSA4 at the moment
+   return AlgorithmIdentifier("RSA/EMSA4", parameters);
+   }
+
+PSSR_Raw::PSSR_Raw(HashFunction* h) :
+   m_hash(h),
+   m_salt_size(m_hash->output_length()),
+   m_required_salt_len(false)
+   {
+   }
+
+PSSR_Raw::PSSR_Raw(HashFunction* h, size_t salt_size) :
+   m_hash(h),
+   m_salt_size(salt_size),
+   m_required_salt_len(true)
+   {
+   }
+
+/*
+* PSSR_Raw Update Operation
+*/
+void PSSR_Raw::update(const uint8_t input[], size_t length)
+   {
+   m_msg.insert(m_msg.end(), input, input + length);
+   }
+
+/*
+* Return the raw (unencoded) data
+*/
+secure_vector<uint8_t> PSSR_Raw::raw_data()
+   {
+   secure_vector<uint8_t> ret;
+   std::swap(ret, m_msg);
+
+   if(ret.size() != m_hash->output_length())
+      throw Encoding_Error("PSSR_Raw Bad input length, did not match hash");
+
+   return ret;
+   }
+
+secure_vector<uint8_t> PSSR_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+                                             size_t output_bits,
+                                             RandomNumberGenerator& rng)
+   {
+   secure_vector<uint8_t> salt = rng.random_vec(m_salt_size);
+   return pss_encode(*m_hash, msg, salt, output_bits);
+   }
+
+/*
+* PSSR_Raw Decode/Verify Operation
+*/
+bool PSSR_Raw::verify(const secure_vector<uint8_t>& coded,
+                      const secure_vector<uint8_t>& raw,
+                      size_t key_bits)
+   {
+   size_t salt_size = 0;
+   const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
+
+   if(m_required_salt_len && salt_size != m_salt_size)
+      return false;
+
+   return ok;
+   }
+
+EMSA* PSSR_Raw::clone()
+   {
+   return new PSSR_Raw(m_hash->clone(), m_salt_size);
+   }
+
+std::string PSSR_Raw::name() const
+   {
+   return "PSSR_Raw(" + m_hash->name() + ",MGF1," + std::to_string(m_salt_size) + ")";
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h b/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h
@@ -0,0 +1,103 @@
+/*
+* PSSR
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PSSR_H_
+#define BOTAN_PSSR_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pssr.h)
+
+namespace Botan {
+
+/**
+* PSSR (called EMSA4 in IEEE 1363 and in old versions of the library)
+*/
+class BOTAN_PUBLIC_API(2,0) PSSR final : public EMSA
+   {
+   public:
+
+      /**
+      * @param hash the hash function to use
+      */
+      explicit PSSR(HashFunction* hash);
+
+      /**
+      * @param hash the hash function to use
+      * @param salt_size the size of the salt to use in bytes
+      */
+      PSSR(HashFunction* hash, size_t salt_size);
+
+      EMSA* clone() override;
+
+      std::string name() const override;
+
+      AlgorithmIdentifier config_for_x509(const Private_Key& key,
+                                          const std::string& cert_hash_name) const override;
+   private:
+      void update(const uint8_t input[], size_t length) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                      size_t output_bits,
+                                      RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+      size_t m_salt_size;
+      bool m_required_salt_len;
+   };
+
+/**
+* PSSR_Raw
+* This accepts a pre-hashed buffer
+*/
+class BOTAN_PUBLIC_API(2,3) PSSR_Raw final : public EMSA
+   {
+   public:
+
+      /**
+      * @param hash the hash function to use
+      */
+      explicit PSSR_Raw(HashFunction* hash);
+
+      /**
+      * @param hash the hash function to use
+      * @param salt_size the size of the salt to use in bytes
+      */
+      PSSR_Raw(HashFunction* hash, size_t salt_size);
+
+      EMSA* clone() override;
+
+      std::string name() const override;
+   private:
+      void update(const uint8_t input[], size_t length) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                         size_t output_bits,
+                                         RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+      secure_vector<uint8_t> m_msg;
+      size_t m_salt_size;
+      bool m_required_salt_len;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp b/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
@@ -0,0 +1,92 @@
+/*
+* EMSA-Raw
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_raw.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string EMSA_Raw::name() const
+   {
+   if(m_expected_size > 0)
+      return "Raw(" + std::to_string(m_expected_size) + ")";
+   return "Raw";
+   }
+
+/*
+* EMSA-Raw Encode Operation
+*/
+void EMSA_Raw::update(const uint8_t input[], size_t length)
+   {
+   m_message += std::make_pair(input, length);
+   }
+
+/*
+* Return the raw (unencoded) data
+*/
+secure_vector<uint8_t> EMSA_Raw::raw_data()
+   {
+   if(m_expected_size && m_message.size() != m_expected_size)
+      throw Invalid_Argument("EMSA_Raw was configured to use a " +
+                             std::to_string(m_expected_size) +
+                             " byte hash but instead was used for a " +
+                             std::to_string(m_message.size()) + " hash");
+
+   secure_vector<uint8_t> output;
+   std::swap(m_message, output);
+   return output;
+   }
+
+/*
+* EMSA-Raw Encode Operation
+*/
+secure_vector<uint8_t>
+EMSA_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+                      size_t,
+                      RandomNumberGenerator&)
+   {
+   if(m_expected_size && msg.size() != m_expected_size)
+      throw Invalid_Argument("EMSA_Raw was configured to use a " +
+                             std::to_string(m_expected_size) +
+                             " byte hash but instead was used for a " +
+                             std::to_string(msg.size()) + " hash");
+
+   return msg;
+   }
+
+/*
+* EMSA-Raw Verify Operation
+*/
+bool EMSA_Raw::verify(const secure_vector<uint8_t>& coded,
+                      const secure_vector<uint8_t>& raw,
+                      size_t)
+   {
+   if(m_expected_size && raw.size() != m_expected_size)
+      return false;
+
+   if(coded.size() == raw.size())
+      return (coded == raw);
+
+   if(coded.size() > raw.size())
+      return false;
+
+   // handle zero padding differences
+   const size_t leading_zeros_expected = raw.size() - coded.size();
+
+   bool same_modulo_leading_zeros = true;
+
+   for(size_t i = 0; i != leading_zeros_expected; ++i)
+      if(raw[i])
+         same_modulo_leading_zeros = false;
+
+   if(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size()))
+      same_modulo_leading_zeros = false;
+
+   return same_modulo_leading_zeros;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h b/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h
@@ -0,0 +1,47 @@
+/*
+* EMSA-Raw
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EMSA_RAW_H_
+#define BOTAN_EMSA_RAW_H_
+
+#include <botan/emsa.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_raw.h)
+
+namespace Botan {
+
+/**
+* EMSA-Raw - sign inputs directly
+* Don't use this unless you know what you are doing.
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_Raw final : public EMSA
+   {
+   public:
+      EMSA* clone() override { return new EMSA_Raw(); }
+
+      explicit EMSA_Raw(size_t expected_hash_size = 0) :
+         m_expected_size(expected_hash_size) {}
+
+      std::string name() const override;
+   private:
+      void update(const uint8_t[], size_t) override;
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+                                         RandomNumberGenerator&) override;
+
+      bool verify(const secure_vector<uint8_t>&,
+                  const secure_vector<uint8_t>&,
+                  size_t) override;
+
+      const size_t m_expected_size;
+      secure_vector<uint8_t> m_message;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp b/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
@@ -0,0 +1,102 @@
+/*
+* EMSA_X931
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_x931.h>
+#include <botan/exceptn.h>
+#include <botan/hash_id.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa2_encoding(const secure_vector<uint8_t>& msg,
+                                   size_t output_bits,
+                                   const secure_vector<uint8_t>& empty_hash,
+                                   uint8_t hash_id)
+   {
+   const size_t HASH_SIZE = empty_hash.size();
+
+   size_t output_length = (output_bits + 1) / 8;
+
+   if(msg.size() != HASH_SIZE)
+      throw Encoding_Error("EMSA_X931::encoding_of: Bad input length");
+   if(output_length < HASH_SIZE + 4)
+      throw Encoding_Error("EMSA_X931::encoding_of: Output length is too small");
+
+   const bool empty_input = (msg == empty_hash);
+
+   secure_vector<uint8_t> output(output_length);
+
+   output[0] = (empty_input ? 0x4B : 0x6B);
+   output[output_length - 3 - HASH_SIZE] = 0xBA;
+   set_mem(&output[1], output_length - 4 - HASH_SIZE, 0xBB);
+   buffer_insert(output, output_length - (HASH_SIZE + 2), msg.data(), msg.size());
+   output[output_length-2] = hash_id;
+   output[output_length-1] = 0xCC;
+
+   return output;
+   }
+
+}
+
+std::string EMSA_X931::name() const
+   {
+   return "EMSA2(" + m_hash->name() + ")";
+   }
+
+void EMSA_X931::update(const uint8_t input[], size_t length)
+   {
+   m_hash->update(input, length);
+   }
+
+secure_vector<uint8_t> EMSA_X931::raw_data()
+   {
+   return m_hash->final();
+   }
+
+/*
+* EMSA_X931 Encode Operation
+*/
+secure_vector<uint8_t> EMSA_X931::encoding_of(const secure_vector<uint8_t>& msg,
+                                      size_t output_bits,
+                                      RandomNumberGenerator&)
+   {
+   return emsa2_encoding(msg, output_bits, m_empty_hash, m_hash_id);
+   }
+
+/*
+* EMSA_X931 Verify Operation
+*/
+bool EMSA_X931::verify(const secure_vector<uint8_t>& coded,
+                   const secure_vector<uint8_t>& raw,
+                   size_t key_bits)
+   {
+   try
+      {
+      return (coded == emsa2_encoding(raw, key_bits,
+                                      m_empty_hash, m_hash_id));
+      }
+   catch(...)
+      {
+      return false;
+      }
+   }
+
+/*
+* EMSA_X931 Constructor
+*/
+EMSA_X931::EMSA_X931(HashFunction* hash) : m_hash(hash)
+   {
+   m_empty_hash = m_hash->final();
+
+   m_hash_id = ieee1363_hash_id(hash->name());
+
+   if(!m_hash_id)
+      throw Encoding_Error("EMSA_X931 no hash identifier for " + hash->name());
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h b/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h
@@ -0,0 +1,52 @@
+/*
+* X9.31 EMSA
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EMSA_X931_H_
+#define BOTAN_EMSA_X931_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_x931.h)
+
+namespace Botan {
+
+/**
+* EMSA from X9.31 (EMSA2 in IEEE 1363)
+* Useful for Rabin-Williams, also sometimes used with RSA in
+* odd protocols.
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_X931 final : public EMSA
+   {
+   public:
+      /**
+      * @param hash the hash function to use
+      */
+      explicit EMSA_X931(HashFunction* hash);
+
+      EMSA* clone() override { return new EMSA_X931(m_hash->clone()); }
+
+      std::string name() const override;
+
+   private:
+      void update(const uint8_t[], size_t) override;
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+                                     RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+                  size_t) override;
+
+      secure_vector<uint8_t> m_empty_hash;
+      std::unique_ptr<HashFunction> m_hash;
+      uint8_t m_hash_id;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp b/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp
@@ -0,0 +1,163 @@
+/*
+* Hash Function Identification
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash_id.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+const uint8_t MD5_PKCS_ID[] = {
+0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
+0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 };
+
+const uint8_t RIPEMD_160_PKCS_ID[] = {
+0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02,
+0x01, 0x05, 0x00, 0x04, 0x14 };
+
+const uint8_t SHA_160_PKCS_ID[] = {
+0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02,
+0x1A, 0x05, 0x00, 0x04, 0x14 };
+
+const uint8_t SHA_224_PKCS_ID[] = {
+0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C };
+
+const uint8_t SHA_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA_384_PKCS_ID[] = {
+0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
+
+const uint8_t SHA_512_PKCS_ID[] = {
+0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
+
+const uint8_t SHA_512_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA3_224_PKCS_ID[] = {
+0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C };
+
+const uint8_t SHA3_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA3_384_PKCS_ID[] = {
+0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30 };
+
+const uint8_t SHA3_512_PKCS_ID[] = {
+0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x0A, 0x05, 0x00, 0x04, 0x40 };
+
+const uint8_t SM3_PKCS_ID[] = {
+0x30, 0x30, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x81, 0x1C, 0xCF,
+0x55, 0x01, 0x83, 0x11, 0x05, 0x00, 0x04, 0x20,
+};
+
+const uint8_t TIGER_PKCS_ID[] = {
+0x30, 0x29, 0x30, 0x0D, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04,
+0x01, 0xDA, 0x47, 0x0C, 0x02, 0x05, 0x00, 0x04, 0x18 };
+
+}
+
+/*
+* HashID as specified by PKCS
+*/
+std::vector<uint8_t> pkcs_hash_id(const std::string& name)
+   {
+   // Special case for SSL/TLS RSA signatures
+   if(name == "Parallel(MD5,SHA-160)")
+      return std::vector<uint8_t>();
+
+   // If you add a value to this function, also update test_hash_id.cpp
+
+   if(name == "MD5")
+      return std::vector<uint8_t>(MD5_PKCS_ID,
+                               MD5_PKCS_ID + sizeof(MD5_PKCS_ID));
+
+   if(name == "RIPEMD-160")
+      return std::vector<uint8_t>(RIPEMD_160_PKCS_ID,
+                               RIPEMD_160_PKCS_ID + sizeof(RIPEMD_160_PKCS_ID));
+
+   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+      return std::vector<uint8_t>(SHA_160_PKCS_ID,
+                               SHA_160_PKCS_ID + sizeof(SHA_160_PKCS_ID));
+
+   if(name == "SHA-224")
+      return std::vector<uint8_t>(SHA_224_PKCS_ID,
+                               SHA_224_PKCS_ID + sizeof(SHA_224_PKCS_ID));
+
+   if(name == "SHA-256")
+      return std::vector<uint8_t>(SHA_256_PKCS_ID,
+                               SHA_256_PKCS_ID + sizeof(SHA_256_PKCS_ID));
+
+   if(name == "SHA-384")
+      return std::vector<uint8_t>(SHA_384_PKCS_ID,
+                               SHA_384_PKCS_ID + sizeof(SHA_384_PKCS_ID));
+
+   if(name == "SHA-512")
+      return std::vector<uint8_t>(SHA_512_PKCS_ID,
+                               SHA_512_PKCS_ID + sizeof(SHA_512_PKCS_ID));
+
+   if(name == "SHA-512-256")
+      return std::vector<uint8_t>(SHA_512_256_PKCS_ID,
+                               SHA_512_256_PKCS_ID + sizeof(SHA_512_256_PKCS_ID));
+
+   if(name == "SHA-3(224)")
+      return std::vector<uint8_t>(SHA3_224_PKCS_ID,
+                                SHA3_224_PKCS_ID + sizeof(SHA3_224_PKCS_ID));
+
+   if(name == "SHA-3(256)")
+      return std::vector<uint8_t>(SHA3_256_PKCS_ID,
+                                SHA3_256_PKCS_ID + sizeof(SHA3_256_PKCS_ID));
+
+   if(name == "SHA-3(384)")
+      return std::vector<uint8_t>(SHA3_384_PKCS_ID,
+                                SHA3_384_PKCS_ID + sizeof(SHA3_384_PKCS_ID));
+
+   if(name == "SHA-3(512)")
+      return std::vector<uint8_t>(SHA3_512_PKCS_ID,
+                                SHA3_512_PKCS_ID + sizeof(SHA3_512_PKCS_ID));
+
+   if(name == "SM3")
+      return std::vector<uint8_t>(SM3_PKCS_ID, SM3_PKCS_ID + sizeof(SM3_PKCS_ID));
+
+   if(name == "Tiger(24,3)")
+      return std::vector<uint8_t>(TIGER_PKCS_ID,
+                               TIGER_PKCS_ID + sizeof(TIGER_PKCS_ID));
+
+   throw Invalid_Argument("No PKCS #1 identifier for " + name);
+   }
+
+/*
+* HashID as specified by IEEE 1363/X9.31
+*/
+uint8_t ieee1363_hash_id(const std::string& name)
+   {
+   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+      return 0x33;
+
+   if(name == "SHA-224")    return 0x38;
+   if(name == "SHA-256")    return 0x34;
+   if(name == "SHA-384")    return 0x36;
+   if(name == "SHA-512")    return 0x35;
+
+   if(name == "RIPEMD-160") return 0x31;
+
+   if(name == "Whirlpool")  return 0x37;
+
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h b/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h
@@ -0,0 +1,34 @@
+/*
+* Hash Function Identification
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HASHID_H_
+#define BOTAN_HASHID_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Return the PKCS #1 hash identifier
+* @see RFC 3447 section 9.2
+* @param hash_name the name of the hash function
+* @return uint8_t sequence identifying the hash
+* @throw Invalid_Argument if the hash has no known PKCS #1 hash id
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t> pkcs_hash_id(const std::string& hash_name);
+
+/**
+* Return the IEEE 1363 hash identifier
+* @param hash_name the name of the hash function
+* @return uint8_t code identifying the hash, or 0 if not known
+*/
+BOTAN_PUBLIC_API(2,0) uint8_t ieee1363_hash_id(const std::string& hash_name);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp b/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp
@@ -0,0 +1,322 @@
+/*
+ * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3
+ * (C) 2016 Tobias Niemann, Hackmanit GmbH
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#include <botan/iso9796.h>
+#include <botan/rng.h>
+#include <botan/exceptn.h>
+#include <botan/mgf1.h>
+#include <botan/hash_id.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> iso9796_encoding(const secure_vector<uint8_t>& msg,
+                                        size_t output_bits,
+                                        std::unique_ptr<HashFunction>& hash,
+                                        size_t SALT_SIZE,
+                                        bool implicit,
+                                        RandomNumberGenerator& rng)
+   {
+   const size_t output_length = (output_bits + 7) / 8;
+
+   //set trailer length
+   size_t tLength = 1;
+   if(!implicit)
+      {
+      tLength = 2;
+      }
+   const size_t HASH_SIZE = hash->output_length();
+
+   if(output_length <= HASH_SIZE + SALT_SIZE + tLength)
+      {
+      throw Encoding_Error("ISO9796-2::encoding_of: Output length is too small");
+      }
+
+   //calculate message capacity
+   const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - tLength - 1;
+
+   //msg1 is the recoverable and msg2 the unrecoverable message part.
+   secure_vector<uint8_t> msg1;
+   secure_vector<uint8_t> msg2;
+   if(msg.size() > capacity)
+      {
+      msg1 = secure_vector<uint8_t>(msg.begin(), msg.begin() + capacity);
+      msg2 = secure_vector<uint8_t>(msg.begin() + capacity, msg.end());
+      hash->update(msg2);
+      }
+   else
+      {
+      msg1 = msg;
+      }
+   msg2 = hash->final();
+
+   //compute H(C||msg1 ||H(msg2)||S)
+   const size_t msgLength = msg1.size();
+   secure_vector<uint8_t> salt = rng.random_vec(SALT_SIZE);
+   hash->update_be(static_cast<uint64_t>(msgLength) * 8);
+   hash->update(msg1);
+   hash->update(msg2);
+   hash->update(salt);
+   secure_vector<uint8_t> H = hash->final();
+
+   secure_vector<uint8_t> EM(output_length);
+
+   //compute message offset.
+   const size_t offset = output_length - HASH_SIZE - SALT_SIZE - tLength - msgLength - 1;
+
+   //insert message border (0x01), msg1 and salt into the output buffer
+   EM[offset] = 0x01;
+   buffer_insert(EM, offset + 1, msg1);
+   buffer_insert(EM, offset + 1 + msgLength, salt);
+
+   //apply mask
+   mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(),
+             output_length - HASH_SIZE - tLength);
+   buffer_insert(EM, output_length - HASH_SIZE - tLength, H);
+   //set implicit/ISO trailer
+   if(!implicit)
+      {
+      uint8_t hash_id = ieee1363_hash_id(hash->name());
+      if(!hash_id)
+         {
+         throw Encoding_Error("ISO9796-2::encoding_of: no hash identifier for " + hash->name());
+         }
+      EM[output_length - 1] = 0xCC;
+      EM[output_length - 2] = hash_id;
+
+      }
+   else
+      {
+      EM[output_length - 1] = 0xBC;
+      }
+   //clear the leftmost bit (confer bouncy castle)
+   EM[0] &= 0x7F;
+
+   return EM;
+   }
+
+bool iso9796_verification(const secure_vector<uint8_t>& const_coded,
+                          const secure_vector<uint8_t>& raw, size_t key_bits, std::unique_ptr<HashFunction>& hash, size_t SALT_SIZE)
+   {
+   const size_t HASH_SIZE = hash->output_length();
+   const size_t KEY_BYTES = (key_bits + 7) / 8;
+
+   if(const_coded.size() != KEY_BYTES)
+      {
+      return false;
+      }
+   //get trailer length
+   size_t tLength;
+   if(const_coded[const_coded.size() - 1] == 0xBC)
+      {
+      tLength = 1;
+      }
+   else
+      {
+      uint8_t hash_id = ieee1363_hash_id(hash->name());
+      if((!const_coded[const_coded.size() - 2]) || (const_coded[const_coded.size() - 2] != hash_id) ||
+            (const_coded[const_coded.size() - 1] != 0xCC))
+         {
+         return false; //in case of wrong ISO trailer.
+         }
+      tLength = 2;
+      }
+
+   secure_vector<uint8_t> coded = const_coded;
+
+   CT::poison(coded.data(), coded.size());
+   //remove mask
+   uint8_t* DB = coded.data();
+   const size_t DB_size = coded.size() - HASH_SIZE - tLength;
+
+   const uint8_t* H = &coded[DB_size];
+
+   mgf1_mask(*hash, H, HASH_SIZE, DB, DB_size);
+   //clear the leftmost bit (confer bouncy castle)
+   DB[0] &= 0x7F;
+
+   //recover msg1 and salt
+   size_t msg1_offset = 1;
+
+   auto waiting_for_delim = CT::Mask<uint8_t>::set();
+   auto bad_input = CT::Mask<uint8_t>::cleared();
+
+   for(size_t j = 0; j < DB_size; ++j)
+      {
+      const auto is_zero = CT::Mask<uint8_t>::is_zero(DB[j]);
+      const auto is_one = CT::Mask<uint8_t>::is_equal(DB[j], 0x01);
+
+      const auto add_m = waiting_for_delim & is_zero;
+
+      bad_input |= waiting_for_delim & ~(is_zero | is_one);
+      msg1_offset += add_m.if_set_return(1);
+
+      waiting_for_delim &= is_zero;
+      }
+
+   //invalid, if delimiter 0x01 was not found or msg1_offset is too big
+   bad_input |= waiting_for_delim;
+   bad_input |= CT::Mask<size_t>::is_lt(coded.size(), tLength + HASH_SIZE + msg1_offset + SALT_SIZE);
+
+   //in case that msg1_offset is too big, just continue with offset = 0.
+   msg1_offset = CT::Mask<size_t>::expand(bad_input.value()).if_not_set_return(msg1_offset);
+
+   CT::unpoison(coded.data(), coded.size());
+   CT::unpoison(msg1_offset);
+
+   secure_vector<uint8_t> msg1(coded.begin() + msg1_offset,
+                            coded.end() - tLength - HASH_SIZE - SALT_SIZE);
+   secure_vector<uint8_t> salt(coded.begin() + msg1_offset + msg1.size(),
+                            coded.end() - tLength - HASH_SIZE);
+
+   //compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value
+   const size_t capacity = (key_bits - 2 + 7) / 8 - HASH_SIZE - SALT_SIZE - tLength - 1;
+   secure_vector<uint8_t> msg1raw;
+   secure_vector<uint8_t> msg2;
+   if(raw.size() > capacity)
+      {
+      msg1raw = secure_vector<uint8_t> (raw.begin(), raw.begin() + capacity);
+      msg2 = secure_vector<uint8_t> (raw.begin() + capacity, raw.end());
+      hash->update(msg2);
+      }
+   else
+      {
+      msg1raw = raw;
+      }
+   msg2 = hash->final();
+
+   const uint64_t msg1rawLength = msg1raw.size();
+   hash->update_be(msg1rawLength * 8);
+   hash->update(msg1raw);
+   hash->update(msg2);
+   hash->update(salt);
+   secure_vector<uint8_t> H3 = hash->final();
+
+   //compute H3(C*||msg1*||H(msg2)||S*) * indicates a recovered value
+   const uint64_t msgLength = msg1.size();
+   hash->update_be(msgLength * 8);
+   hash->update(msg1);
+   hash->update(msg2);
+   hash->update(salt);
+   secure_vector<uint8_t> H2 = hash->final();
+
+   //check if H3 == H2
+   bad_input |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(H3.data(), H2.data(), HASH_SIZE));
+
+   CT::unpoison(bad_input);
+   return (bad_input.is_set() == false);
+   }
+
+}
+
+EMSA* ISO_9796_DS2::clone()
+   {
+   return new ISO_9796_DS2(m_hash->clone(), m_implicit, m_SALT_SIZE);
+   }
+
+/*
+ *  ISO-9796-2 signature scheme 2
+ *  DS 2 is probabilistic
+ */
+void ISO_9796_DS2::update(const uint8_t input[], size_t length)
+   {
+   //need to buffer message completely, before digest
+   m_msg_buffer.insert(m_msg_buffer.end(), input, input+length);
+   }
+
+/*
+ * Return the raw (unencoded) data
+ */
+secure_vector<uint8_t> ISO_9796_DS2::raw_data()
+   {
+   secure_vector<uint8_t> retbuffer = m_msg_buffer;
+   m_msg_buffer.clear();
+   return retbuffer;
+   }
+
+/*
+ *  ISO-9796-2 scheme 2 encode operation
+ */
+secure_vector<uint8_t> ISO_9796_DS2::encoding_of(const secure_vector<uint8_t>& msg,
+                                                 size_t output_bits,
+                                                 RandomNumberGenerator& rng)
+   {
+   return iso9796_encoding(msg, output_bits, m_hash, m_SALT_SIZE, m_implicit, rng);
+   }
+
+/*
+ * ISO-9796-2 scheme 2 verify operation
+ */
+bool ISO_9796_DS2::verify(const secure_vector<uint8_t>& const_coded,
+                          const secure_vector<uint8_t>& raw, size_t key_bits)
+   {
+   return iso9796_verification(const_coded, raw, key_bits, m_hash, m_SALT_SIZE);
+   }
+
+/*
+ * Return the SCAN name
+ */
+std::string ISO_9796_DS2::name() const
+   {
+   return "ISO_9796_DS2(" + m_hash->name() + ","
+         + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_SALT_SIZE) + ")";
+   }
+
+EMSA* ISO_9796_DS3::clone()
+   {
+   return new ISO_9796_DS3(m_hash->clone(), m_implicit);
+   }
+
+/*
+ *  ISO-9796-2 signature scheme 3
+ *  DS 3 is deterministic and equals DS2 without salt
+ */
+void ISO_9796_DS3::update(const uint8_t input[], size_t length)
+   {
+   //need to buffer message completely, before digest
+   m_msg_buffer.insert(m_msg_buffer.end(), input, input+length);
+   }
+
+/*
+ * Return the raw (unencoded) data
+ */
+secure_vector<uint8_t> ISO_9796_DS3::raw_data()
+   {
+   secure_vector<uint8_t> retbuffer = m_msg_buffer;
+   m_msg_buffer.clear();
+   return retbuffer;
+   }
+
+/*
+ *  ISO-9796-2 scheme 3 encode operation
+ */
+secure_vector<uint8_t> ISO_9796_DS3::encoding_of(const secure_vector<uint8_t>& msg,
+      size_t output_bits, RandomNumberGenerator& rng)
+   {
+   return iso9796_encoding(msg, output_bits, m_hash, 0, m_implicit, rng);
+   }
+
+/*
+ * ISO-9796-2 scheme 3 verify operation
+ */
+bool ISO_9796_DS3::verify(const secure_vector<uint8_t>& const_coded,
+                          const secure_vector<uint8_t>& raw, size_t key_bits)
+   {
+   return iso9796_verification(const_coded, raw, key_bits, m_hash, 0);
+   }
+/*
+ * Return the SCAN name
+ */
+std::string ISO_9796_DS3::name() const
+   {
+   return "ISO_9796_DS3(" + m_hash->name() + "," +
+      (m_implicit ? "imp" : "exp") + ")";
+   }
+}
diff --git a/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h b/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h
@@ -0,0 +1,98 @@
+/*
+ * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3
+ * (C) 2016 Tobias Niemann, Hackmanit GmbH
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#ifndef BOTAN_ISO9796_H_
+#define BOTAN_ISO9796_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(iso9796.h)
+
+namespace Botan {
+
+/**
+* ISO-9796-2 - Digital signature scheme 2 (probabilistic)
+*/
+class BOTAN_PUBLIC_API(2,0) ISO_9796_DS2 final : public EMSA
+   {
+   public:
+      /**
+       * @param hash function to use
+       * @param implicit whether or not the trailer is implicit
+       */
+      explicit ISO_9796_DS2(HashFunction* hash, bool implicit = false) : m_hash(hash), m_implicit(implicit),
+         m_SALT_SIZE(hash->output_length()) {}
+
+      /**
+       * @param hash function to use
+       * @param implicit whether or not the trailer is implicit
+       * @param salt_size size of the salt to use in bytes
+       */
+      ISO_9796_DS2(HashFunction* hash, bool implicit, size_t salt_size) : m_hash(hash), m_implicit(implicit),
+         m_SALT_SIZE(salt_size) {}
+
+      EMSA* clone() override;
+
+      std::string name() const override;
+   private:
+      void update(const uint8_t input[], size_t length) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                      size_t output_bits,
+                                      RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+      bool m_implicit;
+      size_t m_SALT_SIZE;
+      secure_vector<uint8_t> m_msg_buffer;
+   };
+
+/**
+* ISO-9796-2 - Digital signature scheme 3 (deterministic)
+*/
+class BOTAN_PUBLIC_API(2,0) ISO_9796_DS3 final : public EMSA
+   {
+   public:
+      /**
+       * @param hash function to use
+       * @param implicit whether or not the trailer is implicit
+       */
+      ISO_9796_DS3(HashFunction* hash, bool implicit = false) : m_hash(hash), m_implicit(implicit)
+         {}
+
+      EMSA* clone() override;
+
+      std::string name() const override;
+   private:
+      void update(const uint8_t input[], size_t length) override;
+
+      secure_vector<uint8_t> raw_data() override;
+
+      secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+                                      size_t output_bits,
+                                      RandomNumberGenerator& rng) override;
+
+      bool verify(const secure_vector<uint8_t>& coded,
+                  const secure_vector<uint8_t>& raw,
+                  size_t key_bits) override;
+
+      std::unique_ptr<HashFunction> m_hash;
+      bool m_implicit;
+      secure_vector<uint8_t> m_msg_buffer;
+   };
+
+}
+
+#endif
+
diff --git a/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp b/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp
@@ -0,0 +1,36 @@
+/*
+* MGF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mgf1.h>
+#include <botan/hash.h>
+#include <algorithm>
+
+namespace Botan {
+
+void mgf1_mask(HashFunction& hash,
+               const uint8_t in[], size_t in_len,
+               uint8_t out[], size_t out_len)
+   {
+   uint32_t counter = 0;
+
+   secure_vector<uint8_t> buffer(hash.output_length());
+   while(out_len)
+      {
+      hash.update(in, in_len);
+      hash.update_be(counter);
+      hash.final(buffer.data());
+
+      const size_t xored = std::min<size_t>(buffer.size(), out_len);
+      xor_buf(out, buffer.data(), xored);
+      out += xored;
+      out_len -= xored;
+
+      ++counter;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h b/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h
@@ -0,0 +1,31 @@
+/*
+* MGF1
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MGF1_H_
+#define BOTAN_MGF1_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class HashFunction;
+
+/**
+* MGF1 from PKCS #1 v2.0
+* @param hash hash function to use
+* @param in input buffer
+* @param in_len size of the input buffer in bytes
+* @param out output buffer
+* @param out_len size of the output buffer in bytes
+*/
+void BOTAN_PUBLIC_API(2,0) mgf1_mask(HashFunction& hash,
+                         const uint8_t in[], size_t in_len,
+                         uint8_t out[], size_t out_len);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pk_pad/padding.cpp b/third_party/botan/src/lib/pk_pad/padding.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/padding.cpp
@@ -0,0 +1,44 @@
+/*
+* Sets of allowed padding schemes for public key types
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2017-12-20
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/padding.h>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace Botan {
+
+const std::map<const std::string, std::vector<std::string>> allowed_signature_paddings =
+   {
+   { "DSA", {"EMSA1"} },
+   { "ECDSA", {"EMSA1"} },
+   { "ECGDSA", {"EMSA1"} },
+   { "ECKCDSA", {"EMSA1"} },
+   { "GOST-34.10", {"EMSA1"} },
+   { "GOST-34.10-2012-256", {"EMSA1"} },
+   { "GOST-34.10-2012-512", {"EMSA1"} },
+   { "RSA", {"EMSA4", "EMSA3"} },
+   };
+
+const std::vector<std::string> get_sig_paddings(const std::string algo)
+   {
+   if(allowed_signature_paddings.count(algo) > 0)
+      return allowed_signature_paddings.at(algo);
+   return {};
+   }
+
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding)
+   {
+   std::vector<std::string> pads = get_sig_paddings(algo);
+   return std::find(pads.begin(), pads.end(), padding) != pads.end();
+   }
+}
diff --git a/third_party/botan/src/lib/pk_pad/padding.h b/third_party/botan/src/lib/pk_pad/padding.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pk_pad/padding.h
@@ -0,0 +1,36 @@
+/*
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PADDING_H_
+#define BOTAN_PADDING_H_
+
+#include <botan/build.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Returns the allowed padding schemes when using the given
+* algorithm (key type) for creating digital signatures.
+*
+* @param algo the algorithm for which to look up supported padding schemes
+* @return a vector of supported padding schemes
+*/
+BOTAN_TEST_API const std::vector<std::string> get_sig_paddings(const std::string algo);
+
+/**
+* Returns true iff the given padding scheme is valid for the given
+* signature algorithm (key type).
+*
+* @param algo the signature algorithm to be used
+* @param padding the padding scheme to be used
+*/
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h
@@ -0,0 +1,62 @@
+/*
+* Utils for calling CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_COMMONCRYPTO_H_
+#define BOTAN_INTERNAL_COMMONCRYPTO_H_
+
+#include <botan/pk_ops_fwd.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <memory>
+#include <string>
+
+namespace Botan {
+
+class Cipher_Mode;
+class BlockCipher;
+class HashFunction;
+enum Cipher_Dir : int;
+typedef int32_t CCCryptorStatus;
+
+class BOTAN_PUBLIC_API(2, 0) CommonCrypto_Error final : public Exception
+   {
+   public:
+      CommonCrypto_Error(const std::string& what) :
+         Exception(what + " failed."),
+         m_rc(0) {}
+
+      CommonCrypto_Error(const std::string& what, int32_t status) :
+         Exception(what + std::string(" failed. Status: ") + ccryptorstatus_to_string(status)),
+         m_rc(status) {}
+
+      ErrorType error_type() const noexcept override { return ErrorType::CommonCryptoError; }
+
+      int error_code() const noexcept override { return m_rc; }
+
+   private:
+      std::string ccryptorstatus_to_string(CCCryptorStatus status);
+
+      int32_t m_rc;
+   };
+
+/* Cipher Modes */
+
+Cipher_Mode*
+make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction);
+
+/* Block Ciphers */
+
+std::unique_ptr<BlockCipher>
+make_commoncrypto_block_cipher(const std::string& name);
+
+/* Hash */
+
+std::unique_ptr<HashFunction> make_commoncrypto_hash(const std::string& name);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp
@@ -0,0 +1,164 @@
+/*
+* Block Ciphers via CommonCrypto
+* (C) 2018 Jose Luis Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/hex.h>
+#include <botan/block_cipher.h>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+namespace {
+
+class CommonCrypto_BlockCipher final : public BlockCipher
+   {
+   public:
+      CommonCrypto_BlockCipher(const std::string& name, const CommonCryptor_Opts& opts);
+
+      ~CommonCrypto_BlockCipher();
+
+      void clear() override;
+      std::string provider() const override { return "commoncrypto"; }
+      std::string name() const override { return m_cipher_name; }
+      BlockCipher* clone() const override;
+
+      size_t block_size() const override { return m_opts.block_size; }
+
+      Key_Length_Specification key_spec() const override { return m_opts.key_spec; }
+
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+         {
+         verify_key_set(m_key_set);
+         size_t total_len = blocks * m_opts.block_size;
+         size_t out_len = 0;
+
+         CCCryptorStatus status = CCCryptorUpdate(m_encrypt, in, total_len,
+                                  out, total_len, &out_len);
+         if(status != kCCSuccess)
+            {
+            throw CommonCrypto_Error("CCCryptorUpdate encrypt", status);
+            }
+         }
+
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+         {
+         verify_key_set(m_key_set);
+         size_t total_len = blocks * m_opts.block_size;
+         size_t out_len = 0;
+
+         CCCryptorStatus status = CCCryptorUpdate(m_decrypt, in, total_len,
+                                  out, total_len, &out_len);
+         if(status != kCCSuccess)
+            {
+            throw CommonCrypto_Error("CCCryptorUpdate decrypt", status);
+            }
+         }
+
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      std::string m_cipher_name;
+      CommonCryptor_Opts m_opts;
+
+      CCCryptorRef m_encrypt = nullptr;
+      CCCryptorRef m_decrypt = nullptr;
+      bool m_key_set;
+   };
+
+CommonCrypto_BlockCipher::CommonCrypto_BlockCipher(const std::string& algo_name,
+      const CommonCryptor_Opts& opts) :
+   m_cipher_name(algo_name),
+   m_opts(opts),
+   m_key_set(false)
+   {
+   }
+
+CommonCrypto_BlockCipher::~CommonCrypto_BlockCipher()
+   {
+   if(m_encrypt)
+      {
+      CCCryptorRelease(m_encrypt);
+      }
+   if(m_decrypt)
+      {
+      CCCryptorRelease(m_decrypt);
+      }
+   }
+
+/*
+* Set the key
+*/
+void CommonCrypto_BlockCipher::key_schedule(const uint8_t key[], size_t length)
+   {
+   secure_vector<uint8_t> full_key(key, key + length);
+
+   clear();
+   commoncrypto_adjust_key_size(key, length, m_opts, full_key);
+
+   CCCryptorStatus status;
+   status = CCCryptorCreate(kCCEncrypt, m_opts.algo, kCCOptionECBMode,
+                            full_key.data(), full_key.size(), nullptr, &m_encrypt);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorCreate encrypt", status);
+      }
+   status = CCCryptorCreate(kCCDecrypt, m_opts.algo, kCCOptionECBMode,
+                            full_key.data(), full_key.size(), nullptr, &m_decrypt);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorCreate decrypt", status);
+      }
+
+   m_key_set = true;
+   }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* CommonCrypto_BlockCipher::clone() const
+   {
+   return new CommonCrypto_BlockCipher(m_cipher_name, m_opts);
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void CommonCrypto_BlockCipher::clear()
+   {
+   m_key_set = false;
+
+   if(m_encrypt)
+      {
+      CCCryptorRelease(m_encrypt);
+      m_encrypt = nullptr;
+      }
+
+   if(m_decrypt)
+      {
+      CCCryptorRelease(m_decrypt);
+      m_decrypt = nullptr;
+      }
+   }
+}
+
+std::unique_ptr<BlockCipher>
+make_commoncrypto_block_cipher(const std::string& name)
+   {
+
+   try
+      {
+      CommonCryptor_Opts opts = commoncrypto_opts_from_algo_name(name);
+      return std::unique_ptr<BlockCipher>(new CommonCrypto_BlockCipher(name, opts));
+      }
+   catch(CommonCrypto_Error& e)
+      {
+      return nullptr;
+      }
+   }
+}
+
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
@@ -0,0 +1,146 @@
+/*
+* CommonCrypto Hash Functions
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/hash.h>
+#include <unordered_map>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+namespace {
+
+template <class CTX>
+class CommonCrypto_HashFunction final : public HashFunction
+   {
+   public:
+
+      struct digest_config_t {
+         std::string name;
+         size_t digestLength;
+         size_t blockSize;
+         int (*init)(CTX *);
+         int (*update)(CTX *, const void *, CC_LONG len);
+         int (*final)(unsigned char *, CTX*);
+         };
+
+      void clear() override
+         {
+         if(m_info.init(&m_ctx) != 1)
+            throw CommonCrypto_Error("CC_" + m_info.name + "_Init");
+         }
+
+      std::string provider() const override { return "commoncrypto"; }
+      std::string name() const override { return m_info.name; }
+
+      HashFunction* clone() const override
+         {
+         return new CommonCrypto_HashFunction(m_info);
+         }
+
+      std::unique_ptr<HashFunction> copy_state() const override
+         {
+         return std::unique_ptr<CommonCrypto_HashFunction>(
+            new CommonCrypto_HashFunction(m_info, m_ctx));
+         }
+
+      size_t output_length() const override
+         {
+         return m_info.digestLength;
+         }
+
+      size_t hash_block_size() const override
+         {
+         return m_info.blockSize;
+         }
+
+      CommonCrypto_HashFunction(const digest_config_t& info) :
+         m_info(info)
+         {
+         clear();
+         }
+
+      CommonCrypto_HashFunction(const digest_config_t& info, const CTX &ctx) :
+         m_ctx(ctx), m_info(info) {}
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override
+         {
+         /* update len parameter is 32 bit unsigned integer, feed input in parts */
+         while (length > 0)
+            {
+            CC_LONG update_len = (length > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(length);
+            m_info.update(&m_ctx, input, update_len);
+            input += update_len;
+            length -= update_len;
+            }
+         }
+
+      void final_result(uint8_t output[]) override
+         {
+         if(m_info.final(output, &m_ctx) != 1)
+            throw CommonCrypto_Error("CC_" + m_info.name + "_Final");
+         clear();
+         }
+
+      CTX m_ctx;
+      digest_config_t m_info;
+   };
+}
+
+std::unique_ptr<HashFunction>
+make_commoncrypto_hash(const std::string& name)
+   {
+#define MAKE_COMMONCRYPTO_HASH_3(name, hash, ctx)               \
+   std::unique_ptr<HashFunction>(                               \
+      new CommonCrypto_HashFunction<CC_ ## ctx ## _CTX >({      \
+            name,                                               \
+            CC_ ## hash ## _DIGEST_LENGTH,                      \
+            CC_ ## hash ## _BLOCK_BYTES,                        \
+            CC_ ## hash ## _Init,                               \
+            CC_ ## hash ## _Update,                             \
+            CC_ ## hash ## _Final                               \
+         }));
+
+#define MAKE_COMMONCRYPTO_HASH_2(name, id)      \
+   MAKE_COMMONCRYPTO_HASH_3(name, id, id)
+
+#define MAKE_COMMONCRYPTO_HASH_1(id)            \
+   MAKE_COMMONCRYPTO_HASH_2(#id, id)
+
+#if defined(BOTAN_HAS_SHA2_32)
+   if(name == "SHA-224")
+      return MAKE_COMMONCRYPTO_HASH_3(name, SHA224, SHA256);
+   if(name == "SHA-256")
+      return MAKE_COMMONCRYPTO_HASH_2(name, SHA256);
+#endif
+#if defined(BOTAN_HAS_SHA2_64)
+   if(name == "SHA-384")
+      return MAKE_COMMONCRYPTO_HASH_3(name, SHA384, SHA512);
+   if(name == "SHA-512")
+      return MAKE_COMMONCRYPTO_HASH_2(name, SHA512);
+#endif
+
+#if defined(BOTAN_HAS_SHA1)
+   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+      return MAKE_COMMONCRYPTO_HASH_2(name, SHA1);
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+   if(name == "MD5")
+      return MAKE_COMMONCRYPTO_HASH_1(MD5);
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+   if(name == "MD4")
+      return MAKE_COMMONCRYPTO_HASH_1(MD4);
+#endif
+   return nullptr;
+   }
+
+}
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
@@ -0,0 +1,247 @@
+/*
+* Cipher Modes via CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/cipher_mode.h>
+#include <botan/internal/rounding.h>
+
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+class CommonCrypto_Cipher_Mode final : public Cipher_Mode
+   {
+   public:
+      CommonCrypto_Cipher_Mode(const std::string& name,
+                               Cipher_Dir direction,
+                               const CommonCryptor_Opts& opts);
+
+      ~CommonCrypto_Cipher_Mode();
+
+      std::string provider() const override { return "commoncrypto"; }
+      std::string name() const override { return m_mode_name; }
+
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      size_t process(uint8_t msg[], size_t msg_len) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
+      size_t output_length(size_t input_length) const override;
+      size_t update_granularity() const override;
+      size_t minimum_final_size() const override;
+      size_t default_nonce_length() const override;
+      bool valid_nonce_length(size_t nonce_len) const override;
+      void clear() override;
+      void reset() override;
+      Key_Length_Specification key_spec() const override;
+
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      const std::string m_mode_name;
+      Cipher_Dir m_direction;
+      CommonCryptor_Opts m_opts;
+      CCCryptorRef m_cipher = nullptr;
+      bool m_key_set;
+      bool m_nonce_set;
+   };
+
+CommonCrypto_Cipher_Mode::CommonCrypto_Cipher_Mode(const std::string& name,
+      Cipher_Dir direction, const CommonCryptor_Opts& opts) :
+   m_mode_name(name),
+   m_direction(direction),
+   m_opts(opts),
+   m_key_set(false),
+   m_nonce_set(false)
+   {
+   }
+
+CommonCrypto_Cipher_Mode::~CommonCrypto_Cipher_Mode()
+   {
+   if(m_cipher)
+      {
+      CCCryptorRelease(m_cipher);
+      }
+   }
+
+void CommonCrypto_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   verify_key_set(m_key_set);
+
+   if(!valid_nonce_length(nonce_len))
+      { throw Invalid_IV_Length(name(), nonce_len); }
+   if(nonce_len)
+      {
+      CCCryptorStatus status = CCCryptorReset(m_cipher, nonce);
+      if(status != kCCSuccess)
+         {
+         throw CommonCrypto_Error("CCCryptorReset on start_msg", status);
+         }
+      }
+   m_nonce_set = true;
+   }
+
+size_t CommonCrypto_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
+   {
+   verify_key_set(m_key_set);
+   BOTAN_STATE_CHECK(m_nonce_set);
+
+   if(msg_len == 0)
+      { return 0; }
+   if(msg_len > INT_MAX)
+      { throw Internal_Error("msg_len overflow"); }
+   size_t outl = CCCryptorGetOutputLength(m_cipher, msg_len, false);
+
+   secure_vector<uint8_t> out(outl);
+
+   if(m_opts.padding == ccNoPadding && msg_len % m_opts.block_size)
+      {
+      msg_len = outl;
+      }
+
+   CCCryptorStatus status = CCCryptorUpdate(m_cipher, msg, msg_len,
+                            out.data(), outl, &outl);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorUpdate", status);
+      }
+   copy_mem(msg, out.data(), outl);
+
+   return outl;
+   }
+
+void CommonCrypto_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+                                      size_t offset)
+   {
+   verify_key_set(m_key_set);
+   BOTAN_STATE_CHECK(m_nonce_set);
+
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+   uint8_t* buf = buffer.data() + offset;
+   const size_t buf_size = buffer.size() - offset;
+
+   size_t written = process(buf, buf_size);
+
+   size_t outl = CCCryptorGetOutputLength(m_cipher, buf_size - written, true);
+   secure_vector<uint8_t> out(outl);
+
+   CCCryptorStatus status = CCCryptorFinal(
+                               m_cipher, out.data(), outl, &outl);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorFinal", status);
+      }
+
+   size_t new_len = offset + written + outl;
+   if(m_opts.padding != ccNoPadding || buffer.size() < new_len)
+      {
+      buffer.resize(new_len);
+      }
+   copy_mem(buffer.data() - offset + written, out.data(), outl);
+   written += outl;
+   }
+
+size_t CommonCrypto_Cipher_Mode::update_granularity() const
+   {
+   return m_opts.block_size * BOTAN_BLOCK_CIPHER_PAR_MULT;
+   }
+
+size_t CommonCrypto_Cipher_Mode::minimum_final_size() const
+   {
+   if(m_direction == ENCRYPTION)
+      return 0;
+   else
+      return m_opts.block_size;
+   }
+
+size_t CommonCrypto_Cipher_Mode::default_nonce_length() const
+   {
+   return m_opts.block_size;
+   }
+
+bool CommonCrypto_Cipher_Mode::valid_nonce_length(size_t nonce_len) const
+   {
+   return (nonce_len == 0 || nonce_len == m_opts.block_size);
+   }
+
+size_t CommonCrypto_Cipher_Mode::output_length(size_t input_length) const
+   {
+   if(input_length == 0)
+      { return m_opts.block_size; }
+   else
+      { return round_up(input_length, m_opts.block_size); }
+   }
+
+void CommonCrypto_Cipher_Mode::clear()
+   {
+   m_key_set = false;
+
+   if(m_cipher == nullptr)
+      {
+      return;
+      }
+
+   if(m_cipher)
+      {
+      CCCryptorRelease(m_cipher);
+      m_cipher = nullptr;
+      }
+   }
+
+void CommonCrypto_Cipher_Mode::reset()
+   {
+   if(m_cipher == nullptr)
+      {
+      return;
+      }
+
+   m_nonce_set = false;
+
+   CCCryptorStatus status = CCCryptorReset(m_cipher, nullptr);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorReset", status);
+      }
+   }
+
+Key_Length_Specification CommonCrypto_Cipher_Mode::key_spec() const
+   {
+   return m_opts.key_spec;
+   }
+
+void CommonCrypto_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   CCCryptorStatus status;
+   CCOperation op = m_direction == ENCRYPTION ? kCCEncrypt : kCCDecrypt;
+   status = CCCryptorCreateWithMode(op, m_opts.mode, m_opts.algo, m_opts.padding,
+                                    nullptr, key, length, nullptr, 0, 0, 0, &m_cipher);
+   if(status != kCCSuccess)
+      {
+      throw CommonCrypto_Error("CCCryptorCreate", status);
+      }
+
+   m_key_set = true;
+   m_nonce_set = false;
+   }
+}
+
+Cipher_Mode*
+make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction)
+   {
+
+   try
+      {
+      CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name);
+      return new CommonCrypto_Cipher_Mode(name, direction, opts);
+      }
+   catch(CommonCrypto_Error& e)
+      {
+      return nullptr;
+      }
+   }
+}
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
@@ -0,0 +1,196 @@
+/*
+* Cipher Modes via CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/cipher_mode.h>
+#include <botan/parsing.h>
+#include <botan/internal/rounding.h>
+#include <botan/scan_name.h>
+
+namespace Botan {
+
+std::string CommonCrypto_Error::ccryptorstatus_to_string(CCCryptorStatus status)
+   {
+   switch(status)
+      {
+      case kCCSuccess:
+         return "Success";
+      case kCCParamError:
+         return "ParamError";
+      case kCCBufferTooSmall:
+         return "BufferTooSmall";
+      case kCCMemoryFailure:
+         return "MemoryFailure";
+      case kCCAlignmentError:
+         return "AlignmentError";
+      case kCCDecodeError:
+         return "DecodeError";
+      case kCCUnimplemented:
+         return "Unimplemented";
+      case kCCOverflow:
+         return "Overflow";
+      case kCCRNGFailure:
+         return "RNGFailure";
+      case kCCUnspecifiedError:
+         return "UnspecifiedError";
+      case kCCCallSequenceError:
+         return "CallSequenceError";
+      case kCCKeySizeError:
+         return "KeySizeError";
+      default:
+         return "Unknown";
+      }
+   };
+
+
+CommonCryptor_Opts commoncrypto_opts_from_algo_name(const std::string& algo_name)
+   {
+   CommonCryptor_Opts opts;
+
+   if(algo_name.compare(0, 3, "AES") == 0)
+      {
+      opts.algo = kCCAlgorithmAES;
+      opts.block_size = kCCBlockSizeAES128;
+      if(algo_name == "AES-128")
+         {
+         opts.key_spec = Key_Length_Specification(kCCKeySizeAES128);
+         }
+      else if(algo_name == "AES-192")
+         {
+         opts.key_spec = Key_Length_Specification(kCCKeySizeAES192);
+         }
+      else if(algo_name == "AES-256")
+         {
+         opts.key_spec = Key_Length_Specification(kCCKeySizeAES256);
+         }
+      else
+         {
+         throw CommonCrypto_Error("Unknown AES algorithm");
+         }
+      }
+   else if(algo_name == "DES")
+      {
+      opts.algo = kCCAlgorithmDES;
+      opts.block_size = kCCBlockSizeDES;
+      opts.key_spec = Key_Length_Specification(kCCKeySizeDES);
+      }
+   else if(algo_name == "TripleDES")
+      {
+      opts.algo = kCCAlgorithm3DES;
+      opts.block_size = kCCBlockSize3DES;
+      opts.key_spec = Key_Length_Specification(16, kCCKeySize3DES, 8);
+      }
+   else if(algo_name == "Blowfish")
+      {
+      opts.algo = kCCAlgorithmBlowfish;
+      opts.block_size = kCCBlockSizeBlowfish;
+      opts.key_spec = Key_Length_Specification(1, kCCKeySizeMaxBlowfish, 1);
+      }
+   else if(algo_name == "CAST-128")
+      {
+      opts.algo = kCCAlgorithmCAST;
+      opts.block_size = kCCBlockSizeCAST;
+      // Botan's base implementation of CAST does not support shorter keys
+      // so we limit its minimum key size to 11 here.
+      opts.key_spec = Key_Length_Specification(11, kCCKeySizeMaxCAST, 1);
+      }
+   else
+      {
+      throw CommonCrypto_Error("Unsupported cipher");
+      }
+
+   return opts;
+   }
+
+
+CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo)
+   {
+   SCAN_Name spec(algo);
+
+   std::string algo_name = spec.algo_name();
+   std::string cipher_mode = spec.cipher_mode();
+   std::string cipher_mode_padding = spec.cipher_mode_pad();
+
+   CommonCryptor_Opts opts = commoncrypto_opts_from_algo_name(algo_name);
+
+   //TODO add CFB and XTS support
+   if(cipher_mode.empty() || cipher_mode == "ECB")
+      {
+      opts.mode = kCCModeECB;
+      }
+   else if(cipher_mode == "CBC")
+      {
+      opts.mode = kCCModeCBC;
+      }
+   else if(cipher_mode == "CTR")
+      {
+      opts.mode = kCCModeCTR;
+      }
+   else if(cipher_mode == "OFB")
+      {
+      opts.mode = kCCModeOFB;
+      }
+   else
+      {
+      throw CommonCrypto_Error("Unsupported cipher mode!");
+      }
+
+   if(cipher_mode_padding == "NoPadding")
+      {
+      opts.padding = ccNoPadding;
+      }
+   /*
+   else if(cipher_mode_padding.empty() || cipher_mode_padding == "PKCS7")
+      {
+      opts.padding = ccPKCS7Padding;
+      }
+   */
+   else
+      {
+      throw CommonCrypto_Error("Unsupported cipher mode padding!");
+      }
+
+   return opts;
+   }
+
+
+void commoncrypto_adjust_key_size(const uint8_t key[], size_t length,
+                                  const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key)
+   {
+
+   if(opts.algo == kCCAlgorithmBlowfish && length < 8)
+      {
+      size_t repeat;
+      switch(length)
+         {
+         case 1:
+            repeat = 8;
+            break;
+         case 2:
+            repeat = 4;
+            break;
+         case 3:
+            repeat = 3;
+            break;
+         default:
+            repeat = 2;
+            break;
+         }
+
+      full_key.resize(length * repeat);
+      for(size_t i = 0; i < repeat; i++)
+         {
+         copy_mem(full_key.data() + i * length, key, length);
+         }
+      }
+   else if(opts.algo == kCCAlgorithm3DES && length == 16)
+      {
+      full_key += std::make_pair(key, 8);
+      }
+   }
+}
diff --git a/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h
@@ -0,0 +1,35 @@
+/*
+* Utils for calling CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_
+#define BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_
+
+#include <botan/key_spec.h>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+struct CommonCryptor_Opts
+   {
+   CCAlgorithm algo;
+   CCMode mode;
+   CCPadding padding;
+   size_t block_size;
+   Key_Length_Specification key_spec{0};
+   };
+
+CommonCryptor_Opts commoncrypto_opts_from_algo_name(const std::string& algo_name);
+CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo);
+
+void commoncrypto_adjust_key_size(const uint8_t key[], size_t length,
+                                  const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/openssl/openssl.h b/third_party/botan/src/lib/prov/openssl/openssl.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl.h
@@ -0,0 +1,118 @@
+/*
+* Utils for calling OpenSSL
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_OPENSSL_H_
+#define BOTAN_INTERNAL_OPENSSL_H_
+
+#include <botan/pk_ops_fwd.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <memory>
+#include <string>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#if defined(BOTAN_HAS_RC4)
+#include <openssl/rc4.h>
+#endif
+
+namespace Botan {
+
+class BlockCipher;
+class Cipher_Mode;
+class StreamCipher;
+class HashFunction;
+class RandomNumberGenerator;
+enum Cipher_Dir : int;
+
+class BOTAN_PUBLIC_API(2,0) OpenSSL_Error final : public Exception
+   {
+   public:
+      OpenSSL_Error(const std::string& what, int err) :
+         Exception(what + " failed: " + ERR_error_string(err, nullptr)),
+         m_err(err) {}
+
+      ErrorType error_type() const noexcept override { return ErrorType::OpenSSLError; }
+
+      int error_code() const noexcept override { return m_err; }
+
+   private:
+      int m_err;
+   };
+
+/* Block Ciphers */
+
+std::unique_ptr<BlockCipher>
+make_openssl_block_cipher(const std::string& name);
+
+/* Cipher Modes */
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction);
+
+/* Hash */
+
+std::unique_ptr<HashFunction>
+make_openssl_hash(const std::string& name);
+
+/* RSA */
+
+#if defined(BOTAN_HAS_RSA)
+
+class RSA_PublicKey;
+class RSA_PrivateKey;
+
+std::unique_ptr<PK_Ops::Encryption>
+make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Decryption>
+make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params);
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params);
+std::unique_ptr<RSA_PrivateKey>
+make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits);
+
+#endif
+
+/* ECDSA */
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_PublicKey;
+class ECDSA_PrivateKey;
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params);
+
+#endif
+
+/* ECDH */
+
+#if defined(BOTAN_HAS_ECDH)
+
+class ECDH_PrivateKey;
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params);
+
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+
+std::unique_ptr<StreamCipher>
+make_openssl_rc4(size_t skip);
+
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_block.cpp b/third_party/botan/src/lib/prov/openssl/openssl_block.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_block.cpp
@@ -0,0 +1,251 @@
+/*
+* Block Ciphers via OpenSSL
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/block_cipher.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_BlockCipher final : public BlockCipher
+   {
+   public:
+      OpenSSL_BlockCipher(const std::string& name,
+                          const EVP_CIPHER* cipher);
+
+      OpenSSL_BlockCipher(const std::string& name,
+                          const EVP_CIPHER* cipher,
+                          size_t kl_min, size_t kl_max, size_t kl_mod);
+
+      ~OpenSSL_BlockCipher();
+
+      void clear() override;
+      std::string provider() const override { return "openssl"; }
+      std::string name() const override { return m_cipher_name; }
+      BlockCipher* clone() const override;
+
+      size_t block_size() const override { return m_block_sz; }
+
+      Key_Length_Specification key_spec() const override { return m_cipher_key_spec; }
+
+      void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+         {
+         verify_key_set(m_key_set);
+         int out_len = 0;
+         if(!EVP_EncryptUpdate(m_encrypt, out, &out_len, in, blocks * m_block_sz))
+            throw OpenSSL_Error("EVP_EncryptUpdate", ERR_get_error());
+         }
+
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+         {
+         verify_key_set(m_key_set);
+         int out_len = 0;
+         if(!EVP_DecryptUpdate(m_decrypt, out, &out_len, in, blocks * m_block_sz))
+            throw OpenSSL_Error("EVP_DecryptUpdate", ERR_get_error());
+         }
+
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      size_t m_block_sz;
+      Key_Length_Specification m_cipher_key_spec;
+      std::string m_cipher_name;
+      EVP_CIPHER_CTX *m_encrypt;
+      EVP_CIPHER_CTX *m_decrypt;
+      bool m_key_set;
+   };
+
+OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name,
+                                         const EVP_CIPHER* algo) :
+   m_block_sz(EVP_CIPHER_block_size(algo)),
+   m_cipher_key_spec(EVP_CIPHER_key_length(algo)),
+   m_cipher_name(algo_name),
+   m_key_set(false)
+   {
+   if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE)
+      throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in");
+
+   m_encrypt = EVP_CIPHER_CTX_new();
+   m_decrypt = EVP_CIPHER_CTX_new();
+   if (m_encrypt == nullptr || m_decrypt == nullptr)
+      throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+   EVP_CIPHER_CTX_init(m_encrypt);
+   EVP_CIPHER_CTX_init(m_decrypt);
+
+   if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+   if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+   if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+   }
+
+OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name,
+                                         const EVP_CIPHER* algo,
+                                         size_t key_min,
+                                         size_t key_max,
+                                         size_t key_mod) :
+   m_block_sz(EVP_CIPHER_block_size(algo)),
+   m_cipher_key_spec(key_min, key_max, key_mod),
+   m_cipher_name(algo_name),
+   m_key_set(false)
+   {
+   if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE)
+      throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in");
+
+   m_encrypt = EVP_CIPHER_CTX_new();
+   m_decrypt = EVP_CIPHER_CTX_new();
+   if (m_encrypt == nullptr || m_decrypt == nullptr)
+      throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+   EVP_CIPHER_CTX_init(m_encrypt);
+   EVP_CIPHER_CTX_init(m_decrypt);
+
+   if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+   if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+   if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+   }
+
+OpenSSL_BlockCipher::~OpenSSL_BlockCipher()
+   {
+   EVP_CIPHER_CTX_cleanup(m_encrypt);
+   EVP_CIPHER_CTX_cleanup(m_decrypt);
+
+   EVP_CIPHER_CTX_free(m_encrypt);
+   EVP_CIPHER_CTX_free(m_decrypt);
+   }
+
+/*
+* Set the key
+*/
+void OpenSSL_BlockCipher::key_schedule(const uint8_t key[], size_t length)
+   {
+   secure_vector<uint8_t> full_key(key, key + length);
+
+   if(m_cipher_name == "TripleDES" && length == 16)
+      {
+      full_key += std::make_pair(key, 8);
+      }
+   else
+      {
+      if(EVP_CIPHER_CTX_set_key_length(m_encrypt, length) == 0 ||
+         EVP_CIPHER_CTX_set_key_length(m_decrypt, length) == 0)
+         throw Invalid_Argument("OpenSSL_BlockCipher: Bad key length for " +
+                                m_cipher_name);
+      }
+
+   if(!EVP_EncryptInit_ex(m_encrypt, nullptr, nullptr, full_key.data(), nullptr))
+      throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+   if(!EVP_DecryptInit_ex(m_decrypt, nullptr, nullptr, full_key.data(), nullptr))
+      throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+   m_key_set = true;
+   }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* OpenSSL_BlockCipher::clone() const
+   {
+   return new OpenSSL_BlockCipher(m_cipher_name,
+                                  EVP_CIPHER_CTX_cipher(m_encrypt),
+                                  m_cipher_key_spec.minimum_keylength(),
+                                  m_cipher_key_spec.maximum_keylength(),
+                                  m_cipher_key_spec.keylength_multiple());
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void OpenSSL_BlockCipher::clear()
+   {
+   const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_encrypt);
+
+   m_key_set = false;
+
+   if(!EVP_CIPHER_CTX_cleanup(m_encrypt))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup encrypt", ERR_get_error());
+   if(!EVP_CIPHER_CTX_cleanup(m_decrypt))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup decrypt", ERR_get_error());
+   EVP_CIPHER_CTX_init(m_encrypt);
+   EVP_CIPHER_CTX_init(m_decrypt);
+   if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+   if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+      throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+   }
+
+}
+
+std::unique_ptr<BlockCipher>
+make_openssl_block_cipher(const std::string& name)
+   {
+#define MAKE_OPENSSL_BLOCK(evp_fn) \
+   std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn()))
+#define MAKE_OPENSSL_BLOCK_KEYLEN(evp_fn, kl_min, kl_max, kl_mod)       \
+   std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn(), kl_min, kl_max, kl_mod))
+
+#if defined(BOTAN_HAS_AES) && !defined(OPENSSL_NO_AES)
+   if(name == "AES-128")
+      return MAKE_OPENSSL_BLOCK(EVP_aes_128_ecb);
+   if(name == "AES-192")
+      return MAKE_OPENSSL_BLOCK(EVP_aes_192_ecb);
+   if(name == "AES-256")
+      return MAKE_OPENSSL_BLOCK(EVP_aes_256_ecb);
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA) && !defined(OPENSSL_NO_CAMELLIA)
+   if(name == "Camellia-128")
+      return MAKE_OPENSSL_BLOCK(EVP_camellia_128_ecb);
+   if(name == "Camellia-192")
+      return MAKE_OPENSSL_BLOCK(EVP_camellia_192_ecb);
+   if(name == "Camellia-256")
+      return MAKE_OPENSSL_BLOCK(EVP_camellia_256_ecb);
+#endif
+
+#if defined(BOTAN_HAS_DES) && !defined(OPENSSL_NO_DES)
+   if(name == "DES")
+      return MAKE_OPENSSL_BLOCK(EVP_des_ecb);
+   if(name == "TripleDES")
+      return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_des_ede3_ecb, 16, 24, 8);
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH) && !defined(OPENSSL_NO_BF)
+   if(name == "Blowfish")
+      return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_bf_ecb, 1, 56, 1);
+#endif
+
+#if defined(BOTAN_HAS_CAST_128) && !defined(OPENSSL_NO_CAST)
+   if(name == "CAST-128")
+      return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_cast5_ecb, 1, 16, 1);
+#endif
+
+#if defined(BOTAN_HAS_SEED) && !defined(OPENSSL_NO_SEED)
+   if(name == "SEED")
+      return MAKE_OPENSSL_BLOCK(EVP_seed_ecb);
+#endif
+
+   return nullptr;
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp b/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp
@@ -0,0 +1,383 @@
+/*
+* ECDSA and ECDH via OpenSSL
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/openssl.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+  #include <botan/der_enc.h>
+  #include <botan/pkcs8.h>
+  #include <botan/internal/pk_ops_impl.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+  #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+  #include <botan/ecdh.h>
+#endif
+
+#include <openssl/x509.h>
+#include <openssl/objects.h>
+
+#if !defined(OPENSSL_NO_EC)
+  #include <openssl/ec.h>
+#endif
+
+#if !defined(OPENSSL_NO_ECDSA)
+  #include <openssl/ecdsa.h>
+#endif
+
+#if !defined(OPENSSL_NO_ECDH)
+  #include <openssl/ecdh.h>
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+namespace {
+
+secure_vector<uint8_t> PKCS8_for_openssl(const EC_PrivateKey& ec)
+   {
+   const PointGFp& pub_key = ec.public_point();
+   const BigInt& priv_key = ec.private_value();
+
+   return DER_Encoder()
+     .start_cons(SEQUENCE)
+        .encode(static_cast<size_t>(1))
+        .encode(BigInt::encode_1363(priv_key, priv_key.bytes()), OCTET_STRING)
+      .start_cons(ASN1_Tag(0), PRIVATE)
+      .raw_bytes(ec.domain().DER_encode(EC_DOMPAR_ENC_OID))
+      .end_cons()
+      .start_cons(ASN1_Tag(1), PRIVATE)
+      .encode(pub_key.encode(PointGFp::UNCOMPRESSED), BIT_STRING)
+      .end_cons()
+      .end_cons()
+      .get_contents();
+   }
+
+int OpenSSL_EC_curve_builtin(int nid)
+   {
+   // the NID macro is still defined even though the curve may not be
+   // supported, so we need to check the list of builtin curves at runtime
+   EC_builtin_curve builtin_curves[100];
+   size_t num = 0;
+
+   if (!(num = EC_get_builtin_curves(builtin_curves, sizeof(builtin_curves))))
+      {
+      return -1;
+      }
+
+   for(size_t i = 0; i < num; ++i)
+      {
+      if(builtin_curves[i].nid == nid)
+         {
+         return nid;
+         }
+      }
+
+   return -1;
+   }
+
+int OpenSSL_EC_nid_for(const OID& oid)
+   {
+   if(oid.empty())
+      return -1;
+
+   const std::string name = oid.to_formatted_string();
+
+   if(name == "secp192r1")
+      return OpenSSL_EC_curve_builtin(NID_X9_62_prime192v1);
+   if(name == "secp224r1")
+      return OpenSSL_EC_curve_builtin(NID_secp224r1);
+   if(name == "secp256r1")
+      return OpenSSL_EC_curve_builtin(NID_X9_62_prime256v1);
+   if(name == "secp384r1")
+      return OpenSSL_EC_curve_builtin(NID_secp384r1);
+   if(name == "secp521r1")
+      return OpenSSL_EC_curve_builtin(NID_secp521r1);
+
+   // OpenSSL 1.0.2 added brainpool curves
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+   if(name == "brainpool160r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP160r1);
+   if(name == "brainpool192r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP192r1);
+   if(name == "brainpool224r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP224r1);
+   if(name == "brainpool256r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP256r1);
+   if(name == "brainpool320r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP320r1);
+   if(name == "brainpool384r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP384r1);
+   if(name == "brainpool512r1")
+      return OpenSSL_EC_curve_builtin(NID_brainpoolP512r1);
+#endif
+
+   return -1;
+   }
+
+}
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA) && !defined(OPENSSL_NO_ECDSA)
+
+namespace {
+
+class OpenSSL_ECDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+      OpenSSL_ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const std::string& emsa, int nid) :
+         PK_Ops::Verification_with_EMSA(emsa), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free)
+         {
+         std::unique_ptr<::EC_GROUP, std::function<void (::EC_GROUP*)>> grp(::EC_GROUP_new_by_curve_name(nid),
+                                                                            ::EC_GROUP_free);
+
+         if(!grp)
+            throw OpenSSL_Error("EC_GROUP_new_by_curve_name", ERR_get_error());
+
+         if(!::EC_KEY_set_group(m_ossl_ec.get(), grp.get()))
+            throw OpenSSL_Error("EC_KEY_set_group", ERR_get_error());
+
+         const std::vector<uint8_t> enc = ecdsa.public_point().encode(PointGFp::UNCOMPRESSED);
+         const uint8_t* enc_ptr = enc.data();
+         EC_KEY* key_ptr = m_ossl_ec.get();
+         if(!::o2i_ECPublicKey(&key_ptr, &enc_ptr, enc.size()))
+            throw OpenSSL_Error("o2i_ECPublicKey", ERR_get_error());
+
+         const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+         m_order_bits = ::EC_GROUP_get_degree(group);
+         }
+
+      size_t max_input_bits() const override { return m_order_bits; }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig_bytes[], size_t sig_len) override
+         {
+         const size_t order_bytes = (m_order_bits + 7) / 8;
+         if(sig_len != 2 * order_bytes)
+            return false;
+
+         std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+         sig.reset(::ECDSA_SIG_new());
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         sig->r = BN_bin2bn(sig_bytes              , sig_len / 2, sig->r);
+         sig->s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, sig->s);
+#else
+         BIGNUM* r = BN_bin2bn(sig_bytes              , sig_len / 2, nullptr);
+         BIGNUM* s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, nullptr);
+         if(r == nullptr || s == nullptr)
+            throw OpenSSL_Error("BN_bin2bn sig s", ERR_get_error());
+
+         ECDSA_SIG_set0(sig.get(), r, s);
+#endif
+
+         const int res = ECDSA_do_verify(msg, msg_len, sig.get(), m_ossl_ec.get());
+         if(res < 0)
+            {
+            int err = ERR_get_error();
+
+            bool hard_error = true;
+
+#if defined(EC_R_BAD_SIGNATURE)
+            if(ERR_GET_REASON(err) == EC_R_BAD_SIGNATURE)
+               hard_error = false;
+#endif
+#if defined(EC_R_POINT_AT_INFINITY)
+            if(ERR_GET_REASON(err) == EC_R_POINT_AT_INFINITY)
+               hard_error = false;
+#endif
+#if defined(ECDSA_R_BAD_SIGNATURE)
+            if(ERR_GET_REASON(err) == ECDSA_R_BAD_SIGNATURE)
+               hard_error = false;
+#endif
+
+            if(hard_error)
+               throw OpenSSL_Error("ECDSA_do_verify", err);
+            }
+         return (res == 1);
+         }
+
+   private:
+      std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+      size_t m_order_bits = 0;
+   };
+
+class OpenSSL_ECDSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+      OpenSSL_ECDSA_Signing_Operation(const ECDSA_PrivateKey& ecdsa, const std::string& emsa) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_ossl_ec(nullptr, ::EC_KEY_free)
+         {
+         const secure_vector<uint8_t> der = PKCS8_for_openssl(ecdsa);
+         const uint8_t* der_ptr = der.data();
+         m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size()));
+         if(!m_ossl_ec)
+            throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error());
+
+         const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+         m_order_bits = ::EC_GROUP_get_degree(group);
+         m_order_bytes = (m_order_bits + 7) / 8;
+         }
+
+      size_t signature_length() const override { return 2*m_order_bytes; }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                   RandomNumberGenerator&) override
+         {
+         std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+         sig.reset(::ECDSA_do_sign(msg, msg_len, m_ossl_ec.get()));
+
+         if(!sig)
+            throw OpenSSL_Error("ECDSA_do_sign", ERR_get_error());
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         const BIGNUM* r = sig->r;
+         const BIGNUM* s = sig->s;
+#else
+         const BIGNUM* r;
+         const BIGNUM* s;
+         ECDSA_SIG_get0(sig.get(), &r, &s);
+#endif
+
+         const size_t r_bytes = BN_num_bytes(r);
+         const size_t s_bytes = BN_num_bytes(s);
+         secure_vector<uint8_t> sigval(2*m_order_bytes);
+         BN_bn2bin(r, &sigval[m_order_bytes - r_bytes]);
+         BN_bn2bin(s, &sigval[2*m_order_bytes - s_bytes]);
+         return sigval;
+         }
+
+      size_t max_input_bits() const override { return m_order_bits; }
+
+   private:
+      std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+      size_t m_order_bits;
+      size_t m_order_bytes;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params)
+   {
+   const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+   if(nid < 0)
+      {
+      throw Lookup_Error("OpenSSL ECDSA does not support this curve");
+      }
+
+   try
+      {
+      return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_ECDSA_Verification_Operation(key, params, nid));
+      }
+   catch(OpenSSL_Error&)
+      {
+      throw Lookup_Error("OpenSSL ECDSA does not support this key");
+      }
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params)
+   {
+   const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+   if(nid < 0)
+      {
+      throw Lookup_Error("OpenSSL ECDSA does not support this curve");
+      }
+   return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_ECDSA_Signing_Operation(key, params));
+   }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDH) && !defined(OPENSSL_NO_ECDH)
+
+namespace {
+
+class OpenSSL_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+   {
+   public:
+
+      OpenSSL_ECDH_KA_Operation(const ECDH_PrivateKey& ecdh, const std::string& kdf) :
+         PK_Ops::Key_Agreement_with_KDF(kdf), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free)
+         {
+         m_value_size = ecdh.domain().get_p_bytes();
+         const secure_vector<uint8_t> der = PKCS8_for_openssl(ecdh);
+         const uint8_t* der_ptr = der.data();
+         m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size()));
+         if(!m_ossl_ec)
+            throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error());
+         }
+
+      size_t agreed_value_size() const override { return m_value_size; }
+
+      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+         {
+         const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+         const size_t out_len = (::EC_GROUP_get_degree(group) + 7) / 8;
+         secure_vector<uint8_t> out(out_len);
+
+         std::unique_ptr<EC_POINT, std::function<void (EC_POINT*)>> pub_key(
+            ::EC_POINT_new(group), ::EC_POINT_free);
+
+         if(!pub_key)
+            throw OpenSSL_Error("EC_POINT_new", ERR_get_error());
+
+         const int os2ecp_rc =
+            ::EC_POINT_oct2point(group, pub_key.get(), w, w_len, nullptr);
+
+         if(os2ecp_rc != 1)
+            throw OpenSSL_Error("EC_POINT_oct2point", ERR_get_error());
+
+         const int ecdh_rc = ::ECDH_compute_key(out.data(),
+                                                out.size(),
+                                                pub_key.get(),
+                                                m_ossl_ec.get(),
+                                                /*KDF*/nullptr);
+
+         if(ecdh_rc <= 0)
+            throw OpenSSL_Error("ECDH_compute_key", ERR_get_error());
+
+         const size_t ecdh_sz = static_cast<size_t>(ecdh_rc);
+
+         if(ecdh_sz > out.size())
+            throw Internal_Error("OpenSSL ECDH returned more than requested");
+
+         out.resize(ecdh_sz);
+         return out;
+         }
+
+   private:
+      std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+      size_t m_value_size;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params)
+   {
+   const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+   if(nid < 0)
+      {
+      throw Lookup_Error("OpenSSL ECDH does not support this curve");
+      }
+
+   return std::unique_ptr<PK_Ops::Key_Agreement>(new OpenSSL_ECDH_KA_Operation(key, params));
+   }
+
+#endif
+
+}
+
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp b/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp
@@ -0,0 +1,151 @@
+/*
+* OpenSSL Hash Functions
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+#include <unordered_map>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_HashFunction final : public HashFunction
+   {
+   public:
+      void clear() override
+         {
+         const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+         if(!EVP_DigestInit_ex(m_md, algo, nullptr))
+            throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+         }
+
+      std::string provider() const override { return "openssl"; }
+      std::string name() const override { return m_name; }
+
+      HashFunction* clone() const override
+         {
+         const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+         return new OpenSSL_HashFunction(name(), algo);
+         }
+
+      std::unique_ptr<HashFunction> copy_state() const override
+         {
+         std::unique_ptr<OpenSSL_HashFunction> copy(new OpenSSL_HashFunction(m_name, nullptr));
+         EVP_MD_CTX_copy(copy->m_md, m_md);
+         return std::unique_ptr<HashFunction>(copy.release());
+         }
+
+      size_t output_length() const override
+         {
+         return EVP_MD_size(EVP_MD_CTX_md(m_md));
+         }
+
+      size_t hash_block_size() const override
+         {
+         return EVP_MD_block_size(EVP_MD_CTX_md(m_md));
+         }
+
+      OpenSSL_HashFunction(const std::string& name, const EVP_MD* md) : m_name(name)
+         {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         m_md = EVP_MD_CTX_create();
+#else
+         m_md = EVP_MD_CTX_new();
+#endif
+
+         if(m_md == nullptr)
+            throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+         EVP_MD_CTX_init(m_md);
+         if(md && !EVP_DigestInit_ex(m_md, md, nullptr))
+            throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+         }
+
+      OpenSSL_HashFunction(EVP_MD_CTX* ctx) : m_md(ctx)
+         {
+         }
+
+      ~OpenSSL_HashFunction()
+         {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         EVP_MD_CTX_destroy(m_md);
+#else
+         EVP_MD_CTX_free(m_md);
+#endif
+         }
+
+   private:
+      void add_data(const uint8_t input[], size_t length) override
+         {
+         if(!EVP_DigestUpdate(m_md, input, length))
+            throw OpenSSL_Error("EVP_DigestUpdate", ERR_get_error());
+         }
+
+      void final_result(uint8_t output[]) override
+         {
+         if(!EVP_DigestFinal_ex(m_md, output, nullptr))
+            throw OpenSSL_Error("EVP_DigestFinal_ex", ERR_get_error());
+         const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+         if(!EVP_DigestInit_ex(m_md, algo, nullptr))
+            throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+         }
+
+      std::string m_name;
+      EVP_MD_CTX* m_md;
+   };
+
+}
+
+std::unique_ptr<HashFunction>
+make_openssl_hash(const std::string& name)
+   {
+#define MAKE_OPENSSL_HASH(fn)                                       \
+   std::unique_ptr<HashFunction>(new OpenSSL_HashFunction(name, fn ()))
+
+#if defined(BOTAN_HAS_SHA2_32) && !defined(OPENSSL_NO_SHA256)
+   if(name == "SHA-224")
+      return MAKE_OPENSSL_HASH(EVP_sha224);
+   if(name == "SHA-256")
+      return MAKE_OPENSSL_HASH(EVP_sha256);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64) && !defined(OPENSSL_NO_SHA512)
+   if(name == "SHA-384")
+      return MAKE_OPENSSL_HASH(EVP_sha384);
+   if(name == "SHA-512")
+      return MAKE_OPENSSL_HASH(EVP_sha512);
+#endif
+
+#if defined(BOTAN_HAS_SHA1) && !defined(OPENSSL_NO_SHA)
+   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+      return MAKE_OPENSSL_HASH(EVP_sha1);
+#endif
+
+#if defined(BOTAN_HAS_RIPEMD_160) && !defined(OPENSSL_NO_RIPEMD)
+   if(name == "RIPEMD-160")
+      return MAKE_OPENSSL_HASH(EVP_ripemd160);
+#endif
+
+#if defined(BOTAN_HAS_MD5) && !defined(OPENSSL_NO_MD5)
+   if(name == "MD5")
+      return MAKE_OPENSSL_HASH(EVP_md5);
+   #endif
+
+#if defined(BOTAN_HAS_MD4) && !defined(OPENSSL_NO_MD4)
+   if(name == "MD4")
+      return MAKE_OPENSSL_HASH(EVP_md4);
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL) && !defined(OPENSSL_NO_WHIRLPOOL)
+   if(name == "Whirlpool")
+      return MAKE_OPENSSL_HASH(EVP_whirlpool);
+#endif
+
+   return nullptr;
+   }
+
+}
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp b/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp
@@ -0,0 +1,233 @@
+/*
+* Cipher Modes via OpenSSL
+* (C) 1999-2010,2015 Jack Lloyd
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_Cipher_Mode final : public Cipher_Mode
+   {
+   public:
+      OpenSSL_Cipher_Mode(const std::string& name,
+                          const EVP_CIPHER* cipher,
+                          Cipher_Dir direction);
+      ~OpenSSL_Cipher_Mode();
+
+      std::string provider() const override { return "openssl"; }
+      std::string name() const override { return m_mode_name; }
+
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      size_t process(uint8_t msg[], size_t msg_len) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
+      size_t output_length(size_t input_length) const override;
+      size_t update_granularity() const override;
+      size_t minimum_final_size() const override;
+      size_t default_nonce_length() const override;
+      bool valid_nonce_length(size_t nonce_len) const override;
+      void clear() override;
+      void reset() override;
+      Key_Length_Specification key_spec() const override;
+
+   private:
+      void key_schedule(const uint8_t key[], size_t length) override;
+
+      const std::string m_mode_name;
+      const Cipher_Dir m_direction;
+      size_t m_block_size;
+      EVP_CIPHER_CTX* m_cipher;
+      bool m_key_set;
+      bool m_nonce_set;
+   };
+
+OpenSSL_Cipher_Mode::OpenSSL_Cipher_Mode(const std::string& name,
+                                         const EVP_CIPHER* algo,
+                                         Cipher_Dir direction) :
+   m_mode_name(name),
+   m_direction(direction),
+   m_key_set(false),
+   m_nonce_set(false)
+   {
+   m_block_size = EVP_CIPHER_block_size(algo);
+
+   if(EVP_CIPHER_mode(algo) != EVP_CIPH_CBC_MODE)
+      throw Invalid_Argument("OpenSSL_BlockCipher: Non-CBC EVP was passed in");
+
+   m_cipher = EVP_CIPHER_CTX_new();
+   if (m_cipher == nullptr)
+      throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+   EVP_CIPHER_CTX_init(m_cipher);
+   if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr,
+                         m_direction == ENCRYPTION ? 1 : 0))
+      throw OpenSSL_Error("EVP_CipherInit_ex", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding", ERR_get_error());
+   }
+
+OpenSSL_Cipher_Mode::~OpenSSL_Cipher_Mode()
+   {
+   EVP_CIPHER_CTX_free(m_cipher);
+   }
+
+void OpenSSL_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   verify_key_set(m_key_set);
+
+   if(!valid_nonce_length(nonce_len))
+      throw Invalid_IV_Length(name(), nonce_len);
+
+   if(nonce_len)
+      {
+      if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nonce, -1))
+         throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error());
+      }
+   else if(m_nonce_set == false)
+      {
+      const std::vector<uint8_t> zeros(m_block_size);
+      if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, zeros.data(), -1))
+         throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error());
+      }
+   // otherwise existing CBC state left unchanged
+
+   m_nonce_set = true;
+   }
+
+size_t OpenSSL_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
+   {
+   verify_key_set(m_key_set);
+   BOTAN_STATE_CHECK(m_nonce_set);
+
+   if(msg_len == 0)
+      return 0;
+   if(msg_len > INT_MAX)
+      throw Internal_Error("msg_len overflow");
+   int outl = msg_len;
+   secure_vector<uint8_t> out(outl);
+
+   if(!EVP_CipherUpdate(m_cipher, out.data(), &outl, msg, msg_len))
+      throw OpenSSL_Error("EVP_CipherUpdate", ERR_get_error());
+   copy_mem(msg, out.data(), outl);
+   return outl;
+   }
+
+void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+                                 size_t offset)
+   {
+   verify_key_set(m_key_set);
+   BOTAN_STATE_CHECK(m_nonce_set);
+
+   BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+   uint8_t* buf = buffer.data() + offset;
+   const size_t buf_size = buffer.size() - offset;
+
+   size_t written = process(buf, buf_size);
+   int outl = buf_size - written;
+   secure_vector<uint8_t> out(outl);
+
+   if(!EVP_CipherFinal_ex(m_cipher, out.data(), &outl))
+      throw OpenSSL_Error("EVP_CipherFinal_ex", ERR_get_error());
+   copy_mem(buf + written, out.data(), outl);
+   written += outl;
+   buffer.resize(offset + written);
+   }
+
+size_t OpenSSL_Cipher_Mode::update_granularity() const
+   {
+   return m_block_size * BOTAN_BLOCK_CIPHER_PAR_MULT;
+   }
+
+size_t OpenSSL_Cipher_Mode::minimum_final_size() const
+   {
+   return 0; // no padding
+   }
+
+size_t OpenSSL_Cipher_Mode::default_nonce_length() const
+   {
+   return m_block_size;
+   }
+
+bool OpenSSL_Cipher_Mode::valid_nonce_length(size_t nonce_len) const
+   {
+   return (nonce_len == 0 || nonce_len == m_block_size);
+   }
+
+size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const
+   {
+   if(input_length == 0)
+      return m_block_size;
+   else
+      return round_up(input_length, m_block_size);
+   }
+
+void OpenSSL_Cipher_Mode::clear()
+   {
+   m_key_set = false;
+   m_nonce_set = false;
+
+   const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_cipher);
+
+   if(!EVP_CIPHER_CTX_cleanup(m_cipher))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup", ERR_get_error());
+   EVP_CIPHER_CTX_init(m_cipher);
+   if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr,
+                         m_direction == ENCRYPTION ? 1 : 0))
+      throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error());
+   if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding clear", ERR_get_error());
+   }
+
+void OpenSSL_Cipher_Mode::reset()
+   {
+   if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nullptr, -1))
+      throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error());
+   m_nonce_set = false;
+   }
+
+Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const
+   {
+   return Key_Length_Specification(EVP_CIPHER_CTX_key_length(m_cipher));
+   }
+
+void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
+   {
+   if(!EVP_CIPHER_CTX_set_key_length(m_cipher, length))
+      throw OpenSSL_Error("EVP_CIPHER_CTX_set_key_length", ERR_get_error());
+   if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, key, nullptr, -1))
+      throw OpenSSL_Error("EVP_CipherInit_ex key", ERR_get_error());
+   m_key_set = true;
+   m_nonce_set = false;
+   }
+
+}
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction)
+   {
+#define MAKE_OPENSSL_MODE(evp_fn) \
+   new OpenSSL_Cipher_Mode(name, (evp_fn)(), direction)
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && !defined(OPENSSL_NO_AES)
+   if(name == "AES-128/CBC/NoPadding")
+      return MAKE_OPENSSL_MODE(EVP_aes_128_cbc);
+   if(name == "AES-192/CBC/NoPadding")
+      return MAKE_OPENSSL_MODE(EVP_aes_192_cbc);
+   if(name == "AES-256/CBC/NoPadding")
+      return MAKE_OPENSSL_MODE(EVP_aes_256_cbc);
+#endif
+
+#undef MAKE_OPENSSL_MODE
+   return nullptr;
+   }
+
+}
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp b/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp
@@ -0,0 +1,93 @@
+/*
+* OpenSSL RC4
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stream_cipher.h>
+
+#if defined(BOTAN_HAS_OPENSSL) && defined(BOTAN_HAS_RC4)
+
+#include <botan/internal/openssl.h>
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+#include <openssl/rc4.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_RC4 final : public StreamCipher
+   {
+   public:
+      void clear() override { clear_mem(&m_rc4, 1); m_key_set = false; }
+
+      std::string provider() const override { return "openssl"; }
+
+      std::string name() const override
+         {
+         switch(m_skip)
+            {
+            case 0:
+               return "RC4";
+            case 256:
+               return "MARK-4";
+            default:
+               return "RC4(" + std::to_string(m_skip) + ")";
+            }
+         }
+
+      StreamCipher* clone() const override { return new OpenSSL_RC4(m_skip); }
+
+      Key_Length_Specification key_spec() const override
+         {
+         return Key_Length_Specification(1, 32);
+         }
+
+      explicit OpenSSL_RC4(size_t skip = 0) : m_skip(skip) { clear(); }
+      ~OpenSSL_RC4() { clear(); }
+
+      void set_iv(const uint8_t*, size_t len) override
+         {
+         if(len > 0)
+            throw Invalid_IV_Length("RC4", len);
+         }
+
+      void seek(uint64_t) override
+         {
+         throw Not_Implemented("RC4 does not support seeking");
+         }
+   private:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override
+         {
+         verify_key_set(m_key_set);
+         ::RC4(&m_rc4, length, in, out);
+         }
+
+      void key_schedule(const uint8_t key[], size_t length) override
+         {
+         ::RC4_set_key(&m_rc4, length, key);
+         uint8_t d = 0;
+         for(size_t i = 0; i != m_skip; ++i)
+            ::RC4(&m_rc4, 1, &d, &d);
+         m_key_set = true;
+         }
+
+      size_t m_skip;
+      RC4_KEY m_rc4;
+      bool m_key_set;
+   };
+
+}
+
+std::unique_ptr<StreamCipher>
+make_openssl_rc4(size_t skip)
+   {
+   return std::unique_ptr<StreamCipher>(new OpenSSL_RC4(skip));
+   }
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp b/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp
@@ -0,0 +1,312 @@
+/*
+* RSA operations provided by OpenSSL
+* (C) 2015 Jack Lloyd
+* (C) 2017 Alexander Bluhm
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/openssl.h>
+
+#if defined(BOTAN_HAS_RSA)
+
+#include <botan/rsa.h>
+#include <botan/rng.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/ct_utils.h>
+
+#include <functional>
+#include <memory>
+#include <cstdlib>
+
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+std::pair<int, size_t> get_openssl_enc_pad(const std::string& eme)
+   {
+   if(eme == "Raw")
+      return std::make_pair(RSA_NO_PADDING, 0);
+   else if(eme == "EME-PKCS1-v1_5")
+      return std::make_pair(RSA_PKCS1_PADDING, 11);
+   else if(eme == "OAEP(SHA-1)" || eme == "EME1(SHA-1)")
+      return std::make_pair(RSA_PKCS1_OAEP_PADDING, 41);
+   else
+      throw Lookup_Error("OpenSSL RSA does not support EME " + eme);
+   }
+
+class OpenSSL_RSA_Encryption_Operation final : public PK_Ops::Encryption
+   {
+   public:
+
+      OpenSSL_RSA_Encryption_Operation(const RSA_PublicKey& rsa, int pad, size_t pad_overhead) :
+         m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+         {
+         const std::vector<uint8_t> der = rsa.public_key_bits();
+         const uint8_t* der_ptr = der.data();
+         m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+         if(!m_openssl_rsa)
+            throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error());
+
+         m_bits = 8 * (n_size() - pad_overhead) - 1;
+         }
+
+      size_t ciphertext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+      size_t max_input_bits() const override { return m_bits; };
+
+      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len,
+                                  RandomNumberGenerator&) override
+         {
+         const size_t mod_sz = n_size();
+
+         if(msg_len > mod_sz)
+            throw Invalid_Argument("Input too large for RSA key");
+
+         secure_vector<uint8_t> outbuf(mod_sz);
+
+         secure_vector<uint8_t> inbuf;
+
+         if(m_padding == RSA_NO_PADDING)
+            {
+            inbuf.resize(mod_sz);
+            copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+            }
+         else
+            {
+            inbuf.assign(msg, msg + msg_len);
+            }
+
+         int rc = ::RSA_public_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+                                       m_openssl_rsa.get(), m_padding);
+         if(rc < 0)
+            throw OpenSSL_Error("RSA_public_encrypt", ERR_get_error());
+
+         return outbuf;
+         }
+
+   private:
+      size_t n_size() const { return ::RSA_size(m_openssl_rsa.get()); }
+      std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+      size_t m_bits = 0;
+      int m_padding = 0;
+   };
+
+class OpenSSL_RSA_Decryption_Operation final : public PK_Ops::Decryption
+   {
+   public:
+
+      OpenSSL_RSA_Decryption_Operation(const RSA_PrivateKey& rsa, int pad) :
+         m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+         {
+         const secure_vector<uint8_t> der = rsa.private_key_bits();
+         const uint8_t* der_ptr = der.data();
+         m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+         if(!m_openssl_rsa)
+            throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error());
+         }
+
+      size_t plaintext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+                                  const uint8_t msg[], size_t msg_len) override
+         {
+         secure_vector<uint8_t> buf(::RSA_size(m_openssl_rsa.get()));
+         int rc = ::RSA_private_decrypt(msg_len, msg, buf.data(), m_openssl_rsa.get(), m_padding);
+         if(rc < 0 || static_cast<size_t>(rc) > buf.size())
+            {
+            valid_mask = 0;
+            buf.resize(0);
+            }
+         else
+            {
+            valid_mask = 0xFF;
+            buf.resize(rc);
+            }
+
+         if(m_padding == RSA_NO_PADDING)
+            {
+            return CT::strip_leading_zeros(buf);
+            }
+
+         return buf;
+         }
+
+   private:
+      std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+      int m_padding = 0;
+   };
+
+class OpenSSL_RSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+
+      OpenSSL_RSA_Verification_Operation(const RSA_PublicKey& rsa, const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_openssl_rsa(nullptr, ::RSA_free)
+         {
+         const std::vector<uint8_t> der = rsa.public_key_bits();
+         const uint8_t* der_ptr = der.data();
+         m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+         if(!m_openssl_rsa)
+            throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error());
+         }
+
+      size_t max_input_bits() const override
+         {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         return ::BN_num_bits(m_openssl_rsa->n) - 1;
+#else
+         return ::RSA_bits(m_openssl_rsa.get()) - 1;
+#endif
+         }
+
+      bool with_recovery() const override { return true; }
+
+      secure_vector<uint8_t> verify_mr(const uint8_t msg[], size_t msg_len) override
+         {
+         const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+         if(msg_len > mod_sz)
+            throw Invalid_Argument("OpenSSL RSA verify input too large");
+
+         secure_vector<uint8_t> inbuf(mod_sz);
+
+         if(msg_len > 0)
+            copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+         secure_vector<uint8_t> outbuf(mod_sz);
+
+         int rc = ::RSA_public_decrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+                                       m_openssl_rsa.get(), RSA_NO_PADDING);
+         if(rc < 0)
+            throw Invalid_Argument("RSA_public_decrypt");
+
+         return CT::strip_leading_zeros(outbuf);
+         }
+   private:
+      std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+   };
+
+class OpenSSL_RSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+
+      OpenSSL_RSA_Signing_Operation(const RSA_PrivateKey& rsa, const std::string& emsa) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_openssl_rsa(nullptr, ::RSA_free)
+         {
+         const secure_vector<uint8_t> der = rsa.private_key_bits();
+         const uint8_t* der_ptr = der.data();
+         m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+         if(!m_openssl_rsa)
+            throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error());
+         }
+
+      size_t signature_length() const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                   RandomNumberGenerator&) override
+         {
+         const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+         if(msg_len > mod_sz)
+            throw Invalid_Argument("OpenSSL RSA sign input too large");
+
+         secure_vector<uint8_t> inbuf(mod_sz);
+         copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+         secure_vector<uint8_t> outbuf(mod_sz);
+
+         int rc = ::RSA_private_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+                                        m_openssl_rsa.get(), RSA_NO_PADDING);
+         if(rc < 0)
+            throw OpenSSL_Error("RSA_private_encrypt", ERR_get_error());
+
+         return outbuf;
+         }
+
+      size_t max_input_bits() const override
+         {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+         return ::BN_num_bits(m_openssl_rsa->n) - 1;
+#else
+         return ::RSA_bits(m_openssl_rsa.get()) - 1;
+#endif
+         }
+
+   private:
+      std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params)
+   {
+   auto pad_info = get_openssl_enc_pad(params);
+   return std::unique_ptr<PK_Ops::Encryption>(
+      new OpenSSL_RSA_Encryption_Operation(key, pad_info.first, pad_info.second));
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params)
+   {
+   auto pad_info = get_openssl_enc_pad(params);
+   return std::unique_ptr<PK_Ops::Decryption>(new OpenSSL_RSA_Decryption_Operation(key, pad_info.first));
+   }
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params)
+   {
+   return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_RSA_Verification_Operation(key, params));
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params)
+   {
+   return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_RSA_Signing_Operation(key, params));
+   }
+
+std::unique_ptr<RSA_PrivateKey>
+make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits)
+   {
+   if (rsa_bits > INT_MAX)
+      throw Internal_Error("rsa_bits overflow");
+
+   secure_vector<uint8_t> seed(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+   rng.randomize(seed.data(), seed.size());
+   RAND_seed(seed.data(), seed.size());
+
+   std::unique_ptr<BIGNUM, std::function<void (BIGNUM*)>> bn(BN_new(), BN_free);
+   if(!bn)
+      throw OpenSSL_Error("BN_new", ERR_get_error());
+   if(!BN_set_word(bn.get(), RSA_F4))
+      throw OpenSSL_Error("BN_set_word", ERR_get_error());
+
+   std::unique_ptr<RSA, std::function<void (RSA*)>> rsa(RSA_new(), RSA_free);
+   if(!rsa)
+      throw OpenSSL_Error("RSA_new", ERR_get_error());
+   if(!RSA_generate_key_ex(rsa.get(), rsa_bits, bn.get(), nullptr))
+      throw OpenSSL_Error("RSA_generate_key_ex", ERR_get_error());
+
+   uint8_t* der = nullptr;
+   int bytes = i2d_RSAPrivateKey(rsa.get(), &der);
+   if(bytes < 0)
+      throw OpenSSL_Error("i2d_RSAPrivateKey", ERR_get_error());
+
+   const secure_vector<uint8_t> keydata(der, der + bytes);
+   secure_scrub_memory(der, bytes);
+   std::free(der);
+   return std::unique_ptr<Botan::RSA_PrivateKey>
+      (new RSA_PrivateKey(AlgorithmIdentifier(), keydata));
+   }
+}
+
+#endif // BOTAN_HAS_RSA
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11.cpp b/third_party/botan/src/lib/prov/pkcs11/p11.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11.cpp
@@ -0,0 +1,766 @@
+/*
+* PKCS#11
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11.h>
+#include <botan/p11_session.h>
+
+#include <cstdint>
+#include <string>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+ReturnValue* ThrowException = reinterpret_cast< ReturnValue* >(-1);
+
+namespace {
+/// @param function_result Return value of the PKCS11 module function
+/// @param return_value if (`ThrowException`) is passed the function throws an exception, otherwise if a non-NULL pointer is passed:
+/// return_value receives the return value of the PKCS#11 function and no exception is thrown.
+/// @return true if function call was successful, false otherwise
+bool handle_return_value(const CK_RV function_result, ReturnValue* return_value)
+   {
+   if(return_value == ThrowException)
+      {
+      if(static_cast< ReturnValue >(function_result) != ReturnValue::OK)
+         {
+         // caller wants exception
+         throw PKCS11_ReturnError(static_cast< ReturnValue >(function_result));
+         }
+      }
+   else if(return_value != nullptr)
+      {
+      // caller wants return value
+      *return_value = static_cast< ReturnValue >(function_result);
+      }
+
+   return static_cast< ReturnValue >(function_result) == ReturnValue::OK;
+   }
+}
+
+void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin, const secure_string& pin)
+   {
+   slot.initialize(label, so_pin);
+   set_pin(slot, so_pin, pin);
+   }
+
+void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin)
+   {
+   Session session(slot, false);
+   session.login(UserType::User, old_pin);
+   session.set_pin(old_pin, new_pin);
+   }
+
+void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin)
+   {
+   Session session(slot, false);
+   session.login(UserType::SO, old_so_pin);
+   session.set_pin(old_so_pin, new_so_pin);
+   }
+
+void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin)
+   {
+   Session session(slot, false);
+   session.login(UserType::SO, so_pin);
+   session.init_pin(pin);
+   }
+
+LowLevel::LowLevel(FunctionListPtr ptr) :
+   m_func_list_ptr(ptr)
+   {
+   if(m_func_list_ptr == nullptr)
+      {
+      throw Invalid_Argument("Invalid PKCS#11 function list ptr");
+      }
+   }
+
+/****************************** General purpose functions ******************************/
+
+bool LowLevel::C_Initialize(VoidPtr init_args,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Initialize(init_args), return_value);
+   }
+
+bool LowLevel::C_Finalize(VoidPtr reserved,
+                          ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Finalize(reserved), return_value);
+   }
+
+bool LowLevel::C_GetInfo(Info* info_ptr,
+                         ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetInfo(info_ptr), return_value);
+   }
+
+bool LowLevel::C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr,
+                                 ReturnValue* return_value)
+   {
+   using get_function_list = CK_RV(*)(FunctionListPtr*);
+
+   get_function_list get_function_list_ptr = pkcs11_module.resolve<get_function_list>("C_GetFunctionList");
+
+   return handle_return_value(get_function_list_ptr(function_list_ptr_ptr), return_value);
+   }
+
+/****************************** Slot and token management functions ******************************/
+
+bool LowLevel::C_GetSlotList(Bbool token_present,
+                             SlotId* slot_list_ptr,
+                             Ulong* count_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetSlotList(token_present, slot_list_ptr, count_ptr), return_value);
+   }
+
+bool LowLevel::C_GetSlotList(bool token_present,
+                             std::vector<SlotId>& slot_ids,
+                             ReturnValue* return_value) const
+   {
+   slot_ids.clear();
+
+   // first get available slots
+   Ulong number_slots = 0;
+
+   bool success = C_GetSlotList(token_present, nullptr, &number_slots, return_value);
+
+   if(!success || !number_slots)
+      {
+      return success;
+      }
+
+   // get actual slot ids
+   slot_ids.resize(number_slots);
+   return C_GetSlotList(token_present, slot_ids.data(), &number_slots, return_value);
+   }
+
+bool LowLevel::C_GetSlotInfo(SlotId slot_id,
+                             SlotInfo* info_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetSlotInfo(slot_id, info_ptr), return_value);
+   }
+
+bool LowLevel::C_GetTokenInfo(SlotId slot_id,
+                              TokenInfo* info_ptr,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetTokenInfo(slot_id, info_ptr), return_value);
+   }
+
+bool LowLevel::C_WaitForSlotEvent(Flags flags,
+                                  SlotId* slot_ptr,
+                                  VoidPtr reserved,
+                                  ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_WaitForSlotEvent(flags, slot_ptr, reserved), return_value);
+   }
+
+bool LowLevel::C_GetMechanismList(SlotId slot_id,
+                                  MechanismType* mechanism_list_ptr,
+                                  Ulong* count_ptr,
+                                  ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetMechanismList(slot_id,
+                              reinterpret_cast< CK_MECHANISM_TYPE_PTR >(mechanism_list_ptr), count_ptr), return_value);
+   }
+
+bool LowLevel::C_GetMechanismList(SlotId slot_id,
+                                  std::vector<MechanismType>& mechanisms,
+                                  ReturnValue* return_value) const
+   {
+   mechanisms.clear();
+
+   // first get number of mechanisms
+   Ulong number_mechanisms = 0;
+
+   bool success = C_GetMechanismList(slot_id, nullptr, &number_mechanisms, return_value);
+
+   if(!success || !number_mechanisms)
+      {
+      return success;
+      }
+
+   // get actual mechanisms
+   mechanisms.resize(number_mechanisms);
+   return C_GetMechanismList(slot_id, reinterpret_cast< MechanismType* >(mechanisms.data()), &number_mechanisms,
+                             return_value);
+   }
+
+bool LowLevel::C_GetMechanismInfo(SlotId slot_id,
+                                  MechanismType type,
+                                  MechanismInfo* info_ptr,
+                                  ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetMechanismInfo(slot_id, static_cast< CK_MECHANISM_TYPE >(type),
+                              info_ptr), return_value);
+   }
+
+bool LowLevel::C_InitToken(SlotId slot_id,
+                           Utf8Char* so_pin_ptr,
+                           Ulong so_pin_len,
+                           Utf8Char* label_ptr,
+                           ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_InitToken(slot_id, so_pin_ptr, so_pin_len, label_ptr), return_value);
+   }
+
+bool LowLevel::C_InitPIN(SessionHandle session,
+                         Utf8Char* pin_ptr,
+                         Ulong pin_len,
+                         ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_InitPIN(session, pin_ptr, pin_len), return_value);
+   }
+
+bool LowLevel::C_SetPIN(SessionHandle session,
+                        Utf8Char* old_pin_ptr,
+                        Ulong old_len,
+                        Utf8Char* new_pin_ptr,
+                        Ulong new_len,
+                        ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SetPIN(session, old_pin_ptr, old_len, new_pin_ptr, new_len),
+                              return_value);
+   }
+
+/****************************** Session management ******************************/
+
+bool LowLevel::C_OpenSession(SlotId slot_id,
+                             Flags flags,
+                             VoidPtr application,
+                             Notify notify,
+                             SessionHandle* session_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_OpenSession(slot_id, flags, application, notify, session_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_CloseSession(SessionHandle session,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_CloseSession(session), return_value);
+   }
+
+bool LowLevel::C_CloseAllSessions(SlotId slot_id,
+                                  ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_CloseAllSessions(slot_id), return_value);
+   }
+
+bool LowLevel::C_GetSessionInfo(SessionHandle session,
+                                SessionInfo* info_ptr,
+                                ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetSessionInfo(session, info_ptr), return_value);
+   }
+
+bool LowLevel::C_GetOperationState(SessionHandle session,
+                                   Byte* operation_state_ptr,
+                                   Ulong* operation_state_len_ptr,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetOperationState(session, operation_state_ptr, operation_state_len_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_SetOperationState(SessionHandle session,
+                                   Byte* operation_state_ptr,
+                                   Ulong operation_state_len,
+                                   ObjectHandle encryption_key,
+                                   ObjectHandle authentication_key,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SetOperationState(session, operation_state_ptr, operation_state_len,
+                              encryption_key, authentication_key), return_value);
+   }
+
+bool LowLevel::C_Login(SessionHandle session,
+                       UserType user_type,
+                       Utf8Char* pin_ptr,
+                       Ulong pin_len,
+                       ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Login(session, static_cast< CK_USER_TYPE >(user_type), pin_ptr, pin_len),
+                              return_value);
+   }
+
+bool LowLevel::C_Logout(SessionHandle session,
+                        ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Logout(session), return_value);
+   }
+
+/****************************** Object management functions ******************************/
+
+bool LowLevel::C_CreateObject(SessionHandle session,
+                              Attribute* attribute_template_ptr,
+                              Ulong count,
+                              ObjectHandle* object_ptr,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_CreateObject(session, attribute_template_ptr, count, object_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_CopyObject(SessionHandle session,
+                            ObjectHandle object,
+                            Attribute* attribute_template_ptr,
+                            Ulong count,
+                            ObjectHandle* new_object_ptr,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_CopyObject(session, object, attribute_template_ptr, count,
+                              new_object_ptr), return_value);
+   }
+
+bool LowLevel::C_DestroyObject(SessionHandle session,
+                               ObjectHandle object,
+                               ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DestroyObject(session, object), return_value);
+   }
+
+bool LowLevel::C_GetObjectSize(SessionHandle session,
+                               ObjectHandle object,
+                               Ulong* size_ptr,
+                               ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetObjectSize(session, object, size_ptr), return_value);
+   }
+
+bool LowLevel::C_GetAttributeValue(SessionHandle session,
+                                   ObjectHandle object,
+                                   Attribute* attribute_template_ptr,
+                                   Ulong count,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetAttributeValue(session, object, attribute_template_ptr, count),
+                              return_value);
+   }
+
+bool LowLevel::C_SetAttributeValue(SessionHandle session,
+                                   ObjectHandle object,
+                                   Attribute* attribute_template_ptr,
+                                   Ulong count,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SetAttributeValue(session, object, attribute_template_ptr, count),
+                              return_value);
+   }
+
+bool LowLevel::C_FindObjectsInit(SessionHandle session,
+                                 Attribute* attribute_template_ptr,
+                                 Ulong count,
+                                 ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_FindObjectsInit(session, attribute_template_ptr, count), return_value);
+   }
+
+bool LowLevel::C_FindObjects(SessionHandle session,
+                             ObjectHandle* object_ptr,
+                             Ulong max_object_count,
+                             Ulong* object_count_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_FindObjects(session, object_ptr, max_object_count, object_count_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_FindObjectsFinal(SessionHandle session,
+                                  ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_FindObjectsFinal(session), return_value);
+   }
+
+/****************************** Encryption functions ******************************/
+
+bool LowLevel::C_EncryptInit(SessionHandle session,
+                             Mechanism* mechanism_ptr,
+                             ObjectHandle key,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_EncryptInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_Encrypt(SessionHandle session,
+                         Byte* data_ptr,
+                         Ulong data_len,
+                         Byte* encrypted_data_ptr,
+                         Ulong* encrypted_data_len_ptr,
+                         ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Encrypt(session, data_ptr, data_len, encrypted_data_ptr,
+                              encrypted_data_len_ptr), return_value);
+   }
+
+bool LowLevel::C_EncryptUpdate(SessionHandle session,
+                               Byte* part_ptr,
+                               Ulong part_len,
+                               Byte* encrypted_part_ptr,
+                               Ulong* encrypted_part_len_ptr,
+                               ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_EncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+                              encrypted_part_len_ptr), return_value);
+   }
+
+bool LowLevel::C_EncryptFinal(SessionHandle session,
+                              Byte* last_encrypted_part_ptr,
+                              Ulong* last_encrypted_part_len_ptr,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_EncryptFinal(session, last_encrypted_part_ptr,
+                              last_encrypted_part_len_ptr), return_value);
+   }
+
+/****************************** Decryption functions ******************************/
+
+bool LowLevel::C_DecryptInit(SessionHandle session,
+                             Mechanism* mechanism_ptr,
+                             ObjectHandle key,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DecryptInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_Decrypt(SessionHandle session,
+                         Byte* encrypted_data_ptr,
+                         Ulong encrypted_data_len,
+                         Byte* data_ptr,
+                         Ulong* data_len_ptr,
+                         ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Decrypt(session, encrypted_data_ptr, encrypted_data_len, data_ptr,
+                              data_len_ptr), return_value);
+   }
+
+bool LowLevel::C_DecryptUpdate(SessionHandle session,
+                               Byte* encrypted_part_ptr,
+                               Ulong encrypted_part_len,
+                               Byte* part_ptr,
+                               Ulong* part_len_ptr,
+                               ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DecryptUpdate(session, encrypted_part_ptr, encrypted_part_len, part_ptr,
+                              part_len_ptr), return_value);
+   }
+
+bool LowLevel::C_DecryptFinal(SessionHandle session,
+                              Byte* last_part_ptr,
+                              Ulong* last_part_len_ptr,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DecryptFinal(session, last_part_ptr, last_part_len_ptr), return_value);
+   }
+
+/****************************** Message digesting functions ******************************/
+
+bool LowLevel::C_DigestInit(SessionHandle session,
+                            Mechanism* mechanism,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DigestInit(session, mechanism), return_value);
+   }
+
+bool LowLevel::C_Digest(SessionHandle session,
+                        Byte* data_ptr,
+                        Ulong data_len,
+                        Byte* digest_ptr,
+                        Ulong* digest_len_ptr,
+                        ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Digest(session, data_ptr, data_len, digest_ptr, digest_len_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_DigestUpdate(SessionHandle session,
+                              Byte* part_ptr,
+                              Ulong part_len,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DigestUpdate(session, part_ptr, part_len), return_value);
+   }
+
+bool LowLevel::C_DigestKey(SessionHandle session,
+                           ObjectHandle key,
+                           ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DigestKey(session, key), return_value);
+   }
+
+bool LowLevel::C_DigestFinal(SessionHandle session,
+                             Byte* digest_ptr,
+                             Ulong* digest_len_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DigestFinal(session, digest_ptr, digest_len_ptr), return_value);
+   }
+
+/****************************** Signing and MACing functions ******************************/
+
+bool LowLevel::C_SignInit(SessionHandle session,
+                          Mechanism* mechanism_ptr,
+                          ObjectHandle key,
+                          ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_Sign(SessionHandle session,
+                      Byte* data_ptr,
+                      Ulong data_len,
+                      Byte* signature_ptr,
+                      Ulong* signature_len_ptr,
+                      ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Sign(session, data_ptr, data_len, signature_ptr, signature_len_ptr),
+                              return_value);
+   }
+
+bool LowLevel::C_SignUpdate(SessionHandle session,
+                            Byte* part_ptr,
+                            Ulong part_len,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignUpdate(session, part_ptr, part_len), return_value);
+   }
+
+bool LowLevel::C_SignFinal(SessionHandle session,
+                           Byte* signature_ptr,
+                           Ulong* signature_len_ptr,
+                           ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignFinal(session, signature_ptr, signature_len_ptr), return_value);
+   }
+
+bool LowLevel::C_SignRecoverInit(SessionHandle session,
+                                 Mechanism* mechanism_ptr,
+                                 ObjectHandle key,
+                                 ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignRecoverInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_SignRecover(SessionHandle session,
+                             Byte* data,
+                             Ulong data_len,
+                             Byte* signature,
+                             Ulong* signature_len,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignRecover(session, data, data_len, signature, signature_len),
+                              return_value);
+   }
+
+/****************************** Functions for verifying signatures and MACs ******************************/
+
+bool LowLevel::C_VerifyInit(SessionHandle session,
+                            Mechanism* mechanism_ptr,
+                            ObjectHandle key,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_VerifyInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_Verify(SessionHandle session,
+                        Byte* data_ptr,
+                        Ulong data_len,
+                        Byte* signature_ptr,
+                        Ulong signature_len,
+                        ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_Verify(session, data_ptr, data_len, signature_ptr, signature_len),
+                              return_value);
+   }
+
+bool LowLevel::C_VerifyUpdate(SessionHandle session,
+                              Byte* part_ptr,
+                              Ulong part_len,
+                              ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_VerifyUpdate(session, part_ptr, part_len), return_value);
+   }
+
+bool LowLevel::C_VerifyFinal(SessionHandle session,
+                             Byte* signature_ptr,
+                             Ulong signature_len,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_VerifyFinal(session, signature_ptr, signature_len), return_value);
+   }
+
+bool LowLevel::C_VerifyRecoverInit(SessionHandle session,
+                                   Mechanism* mechanism_ptr,
+                                   ObjectHandle key,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_VerifyRecoverInit(session, mechanism_ptr, key), return_value);
+   }
+
+bool LowLevel::C_VerifyRecover(SessionHandle session,
+                               Byte* signature_ptr,
+                               Ulong signature_len,
+                               Byte* data_ptr,
+                               Ulong* data_len_ptr,
+                               ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_VerifyRecover(session, signature_ptr, signature_len, data_ptr,
+                              data_len_ptr), return_value);
+   }
+
+/****************************** Dual-purpose cryptographic functions ******************************/
+
+bool LowLevel::C_DigestEncryptUpdate(SessionHandle session,
+                                     Byte* part_ptr,
+                                     Ulong part_len,
+                                     Byte* encrypted_part_ptr,
+                                     Ulong* encrypted_part_len_ptr,
+                                     ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DigestEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+                              encrypted_part_len_ptr), return_value);
+   }
+
+bool LowLevel::C_DecryptDigestUpdate(SessionHandle session,
+                                     Byte* encrypted_part_ptr,
+                                     Ulong encrypted_part_len,
+                                     Byte* part_ptr,
+                                     Ulong* part_len_ptr,
+                                     ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DecryptDigestUpdate(session, encrypted_part_ptr, encrypted_part_len,
+                              part_ptr, part_len_ptr), return_value);
+   }
+
+bool LowLevel::C_SignEncryptUpdate(SessionHandle session,
+                                   Byte* part_ptr,
+                                   Ulong part_len,
+                                   Byte* encrypted_part_ptr,
+                                   Ulong* encrypted_part_len_ptr,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SignEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+                              encrypted_part_len_ptr), return_value);
+   }
+
+bool LowLevel::C_DecryptVerifyUpdate(SessionHandle session,
+                                     Byte* encrypted_part_ptr,
+                                     Ulong encrypted_part_len,
+                                     Byte* part_ptr,
+                                     Ulong* part_len_ptr,
+                                     ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DecryptVerifyUpdate(session, encrypted_part_ptr, encrypted_part_len,
+                              part_ptr, part_len_ptr), return_value);
+   }
+
+/****************************** Key management functions ******************************/
+
+bool LowLevel::C_GenerateKey(SessionHandle session,
+                             Mechanism* mechanism_ptr,
+                             Attribute* attribute_template_ptr,
+                             Ulong count,
+                             ObjectHandle* key_ptr,
+                             ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GenerateKey(session, mechanism_ptr, attribute_template_ptr, count,
+                              key_ptr), return_value);
+   }
+
+bool LowLevel::C_GenerateKeyPair(SessionHandle session,
+                                 Mechanism* mechanism_ptr,
+                                 Attribute* public_key_template_ptr,
+                                 Ulong public_key_attribute_count,
+                                 Attribute* private_key_template_ptr,
+                                 Ulong private_key_attribute_count,
+                                 ObjectHandle* public_key_ptr,
+                                 ObjectHandle* private_key_ptr,
+                                 ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GenerateKeyPair(session, mechanism_ptr, public_key_template_ptr,
+                              public_key_attribute_count, private_key_template_ptr,
+                              private_key_attribute_count, public_key_ptr, private_key_ptr), return_value);
+   }
+
+bool LowLevel::C_WrapKey(SessionHandle session,
+                         Mechanism* mechanism_ptr,
+                         ObjectHandle wrapping_key,
+                         ObjectHandle key,
+                         Byte* wrapped_key_ptr,
+                         Ulong* wrapped_key_len_ptr,
+                         ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_WrapKey(session, mechanism_ptr, wrapping_key, key, wrapped_key_ptr,
+                              wrapped_key_len_ptr), return_value);
+   }
+
+bool LowLevel::C_UnwrapKey(SessionHandle session,
+                           Mechanism* mechanism_ptr,
+                           ObjectHandle unwrapping_key,
+                           Byte* wrapped_key_ptr,
+                           Ulong wrapped_key_len,
+                           Attribute* attribute_template_ptr,
+                           Ulong attribute_count,
+                           ObjectHandle* key_ptr,
+                           ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_UnwrapKey(session, mechanism_ptr, unwrapping_key, wrapped_key_ptr,
+                              wrapped_key_len, attribute_template_ptr,
+                              attribute_count, key_ptr), return_value);
+   }
+
+bool LowLevel::C_DeriveKey(SessionHandle session,
+                           Mechanism* mechanism_ptr,
+                           ObjectHandle base_key,
+                           Attribute* attribute_template_ptr,
+                           Ulong attribute_count,
+                           ObjectHandle* key_ptr,
+                           ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_DeriveKey(session, mechanism_ptr, base_key, attribute_template_ptr,
+                              attribute_count, key_ptr), return_value);
+   }
+
+/****************************** Random number generation functions ******************************/
+
+bool LowLevel::C_SeedRandom(SessionHandle session,
+                            Byte* seed_ptr,
+                            Ulong seed_len,
+                            ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_SeedRandom(session, seed_ptr, seed_len), return_value);
+   }
+
+bool LowLevel::C_GenerateRandom(SessionHandle session,
+                                Byte* random_data_ptr,
+                                Ulong random_len,
+                                ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GenerateRandom(session, random_data_ptr, random_len), return_value);
+   }
+
+/****************************** Parallel function management functions ******************************/
+
+bool LowLevel::C_GetFunctionStatus(SessionHandle session,
+                                   ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_GetFunctionStatus(session), return_value);
+   }
+
+bool LowLevel::C_CancelFunction(SessionHandle session,
+                                ReturnValue* return_value) const
+   {
+   return handle_return_value(m_func_list_ptr->C_CancelFunction(session), return_value);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11.h b/third_party/botan/src/lib/prov/pkcs11/p11.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11.h
@@ -0,0 +1,2928 @@
+/*
+* PKCS#11
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_H_
+#define BOTAN_P11_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <botan/dyn_load.h>
+
+#include <vector>
+#include <string>
+#include <map>
+
+#define CK_PTR *
+
+#if defined(_MSC_VER)
+#define CK_DECLARE_FUNCTION(returnType, name) \
+   returnType __declspec(dllimport) name
+#else
+#define CK_DECLARE_FUNCTION(returnType, name) \
+   returnType name
+#endif
+
+#if defined(_MSC_VER)
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+   returnType __declspec(dllimport) (* name)
+#else
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+   returnType (* name)
+#endif
+
+#define CK_CALLBACK_FUNCTION(returnType, name) \
+   returnType (* name)
+
+#ifndef NULL_PTR
+   #define NULL_PTR nullptr
+#endif
+
+#if defined(_MSC_VER)
+   #pragma pack(push, cryptoki, 1)
+#endif
+
+#include "pkcs11.h"
+
+#if defined(_MSC_VER)
+   #pragma pack(pop, cryptoki)
+#endif
+
+static_assert(CRYPTOKI_VERSION_MAJOR == 2 && CRYPTOKI_VERSION_MINOR == 40,
+              "The Botan PKCS#11 module was implemented against PKCS#11 v2.40. Please use the correct PKCS#11 headers.");
+
+namespace Botan {
+namespace PKCS11 {
+
+using secure_string = secure_vector<uint8_t>;
+
+enum class AttributeType : CK_ATTRIBUTE_TYPE
+   {
+   Class = CKA_CLASS,
+   Token = CKA_TOKEN,
+   Private = CKA_PRIVATE,
+   Label = CKA_LABEL,
+   Application = CKA_APPLICATION,
+   Value = CKA_VALUE,
+   ObjectId = CKA_OBJECT_ID,
+   CertificateType = CKA_CERTIFICATE_TYPE,
+   Issuer = CKA_ISSUER,
+   SerialNumber = CKA_SERIAL_NUMBER,
+   AcIssuer = CKA_AC_ISSUER,
+   Owner = CKA_OWNER,
+   AttrTypes = CKA_ATTR_TYPES,
+   Trusted = CKA_TRUSTED,
+   CertificateCategory = CKA_CERTIFICATE_CATEGORY,
+   JavaMidpSecurityDomain = CKA_JAVA_MIDP_SECURITY_DOMAIN,
+   Url = CKA_URL,
+   HashOfSubjectPublicKey = CKA_HASH_OF_SUBJECT_PUBLIC_KEY,
+   HashOfIssuerPublicKey = CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+   NameHashAlgorithm = CKA_NAME_HASH_ALGORITHM,
+   CheckValue = CKA_CHECK_VALUE,
+   KeyType = CKA_KEY_TYPE,
+   Subject = CKA_SUBJECT,
+   Id = CKA_ID,
+   Sensitive = CKA_SENSITIVE,
+   Encrypt = CKA_ENCRYPT,
+   Decrypt = CKA_DECRYPT,
+   Wrap = CKA_WRAP,
+   Unwrap = CKA_UNWRAP,
+   Sign = CKA_SIGN,
+   SignRecover = CKA_SIGN_RECOVER,
+   Verify = CKA_VERIFY,
+   VerifyRecover = CKA_VERIFY_RECOVER,
+   Derive = CKA_DERIVE,
+   StartDate = CKA_START_DATE,
+   EndDate = CKA_END_DATE,
+   Modulus = CKA_MODULUS,
+   ModulusBits = CKA_MODULUS_BITS,
+   PublicExponent = CKA_PUBLIC_EXPONENT,
+   PrivateExponent = CKA_PRIVATE_EXPONENT,
+   Prime1 = CKA_PRIME_1,
+   Prime2 = CKA_PRIME_2,
+   Exponent1 = CKA_EXPONENT_1,
+   Exponent2 = CKA_EXPONENT_2,
+   Coefficient = CKA_COEFFICIENT,
+   PublicKeyInfo = CKA_PUBLIC_KEY_INFO,
+   Prime = CKA_PRIME,
+   Subprime = CKA_SUBPRIME,
+   Base = CKA_BASE,
+   PrimeBits = CKA_PRIME_BITS,
+   SubprimeBits = CKA_SUBPRIME_BITS,
+   SubPrimeBits = CKA_SUB_PRIME_BITS,
+   ValueBits = CKA_VALUE_BITS,
+   ValueLen = CKA_VALUE_LEN,
+   Extractable = CKA_EXTRACTABLE,
+   Local = CKA_LOCAL,
+   NeverExtractable = CKA_NEVER_EXTRACTABLE,
+   AlwaysSensitive = CKA_ALWAYS_SENSITIVE,
+   KeyGenMechanism = CKA_KEY_GEN_MECHANISM,
+   Modifiable = CKA_MODIFIABLE,
+   Copyable = CKA_COPYABLE,
+   Destroyable = CKA_DESTROYABLE,
+   EcdsaParams = CKA_ECDSA_PARAMS,
+   EcParams = CKA_EC_PARAMS,
+   EcPoint = CKA_EC_POINT,
+   SecondaryAuth = CKA_SECONDARY_AUTH,
+   AuthPinFlags = CKA_AUTH_PIN_FLAGS,
+   AlwaysAuthenticate = CKA_ALWAYS_AUTHENTICATE,
+   WrapWithTrusted = CKA_WRAP_WITH_TRUSTED,
+   WrapTemplate = CKA_WRAP_TEMPLATE,
+   UnwrapTemplate = CKA_UNWRAP_TEMPLATE,
+   DeriveTemplate = CKA_DERIVE_TEMPLATE,
+   OtpFormat = CKA_OTP_FORMAT,
+   OtpLength = CKA_OTP_LENGTH,
+   OtpTimeInterval = CKA_OTP_TIME_INTERVAL,
+   OtpUserFriendlyMode = CKA_OTP_USER_FRIENDLY_MODE,
+   OtpChallengeRequirement = CKA_OTP_CHALLENGE_REQUIREMENT,
+   OtpTimeRequirement = CKA_OTP_TIME_REQUIREMENT,
+   OtpCounterRequirement = CKA_OTP_COUNTER_REQUIREMENT,
+   OtpPinRequirement = CKA_OTP_PIN_REQUIREMENT,
+   OtpCounter = CKA_OTP_COUNTER,
+   OtpTime = CKA_OTP_TIME,
+   OtpUserIdentifier = CKA_OTP_USER_IDENTIFIER,
+   OtpServiceIdentifier = CKA_OTP_SERVICE_IDENTIFIER,
+   OtpServiceLogo = CKA_OTP_SERVICE_LOGO,
+   OtpServiceLogoType = CKA_OTP_SERVICE_LOGO_TYPE,
+   Gostr3410Params = CKA_GOSTR3410_PARAMS,
+   Gostr3411Params = CKA_GOSTR3411_PARAMS,
+   Gost28147Params = CKA_GOST28147_PARAMS,
+   HwFeatureType = CKA_HW_FEATURE_TYPE,
+   ResetOnInit = CKA_RESET_ON_INIT,
+   HasReset = CKA_HAS_RESET,
+   PixelX = CKA_PIXEL_X,
+   PixelY = CKA_PIXEL_Y,
+   Resolution = CKA_RESOLUTION,
+   CharRows = CKA_CHAR_ROWS,
+   CharColumns = CKA_CHAR_COLUMNS,
+   Color = CKA_COLOR,
+   BitsPerPixel = CKA_BITS_PER_PIXEL,
+   CharSets = CKA_CHAR_SETS,
+   EncodingMethods = CKA_ENCODING_METHODS,
+   MimeTypes = CKA_MIME_TYPES,
+   MechanismType = CKA_MECHANISM_TYPE,
+   RequiredCmsAttributes = CKA_REQUIRED_CMS_ATTRIBUTES,
+   DefaultCmsAttributes = CKA_DEFAULT_CMS_ATTRIBUTES,
+   SupportedCmsAttributes = CKA_SUPPORTED_CMS_ATTRIBUTES,
+   AllowedMechanisms = CKA_ALLOWED_MECHANISMS,
+   VendorDefined = CKA_VENDOR_DEFINED,
+   };
+
+enum class CertificateType : CK_CERTIFICATE_TYPE
+   {
+   X509 = CKC_X_509,
+   X509AttrCert = CKC_X_509_ATTR_CERT,
+   Wtls = CKC_WTLS,
+   VendorDefined = CKC_VENDOR_DEFINED,
+   };
+
+/// Indicates if a stored certificate is a user certificate for which the corresponding private key is available
+/// on the token ("token user"), a CA certificate ("authority"), or another end-entity certificate ("other entity").
+enum class CertificateCategory : CK_ULONG
+   {
+   Unspecified = CK_CERTIFICATE_CATEGORY_UNSPECIFIED,
+   TokenUser = CK_CERTIFICATE_CATEGORY_TOKEN_USER,
+   Authority = CK_CERTIFICATE_CATEGORY_AUTHORITY,
+   OtherEntity = CK_CERTIFICATE_CATEGORY_OTHER_ENTITY
+   };
+
+enum class KeyDerivation : CK_ULONG
+   {
+   Null = CKD_NULL,
+   Sha1Kdf = CKD_SHA1_KDF,
+   Sha1KdfAsn1 = CKD_SHA1_KDF_ASN1,
+   Sha1KdfConcatenate = CKD_SHA1_KDF_CONCATENATE,
+   Sha224Kdf = CKD_SHA224_KDF,
+   Sha256Kdf = CKD_SHA256_KDF,
+   Sha384Kdf = CKD_SHA384_KDF,
+   Sha512Kdf = CKD_SHA512_KDF,
+   CpdiversifyKdf = CKD_CPDIVERSIFY_KDF,
+   };
+
+enum class Flag : CK_FLAGS
+   {
+   None = 0,
+   TokenPresent = CKF_TOKEN_PRESENT,
+   RemovableDevice = CKF_REMOVABLE_DEVICE,
+   HwSlot = CKF_HW_SLOT,
+   Rng = CKF_RNG,
+   WriteProtected = CKF_WRITE_PROTECTED,
+   LoginRequired = CKF_LOGIN_REQUIRED,
+   UserPinInitialized = CKF_USER_PIN_INITIALIZED,
+   RestoreKeyNotNeeded = CKF_RESTORE_KEY_NOT_NEEDED,
+   ClockOnToken = CKF_CLOCK_ON_TOKEN,
+   ProtectedAuthenticationPath = CKF_PROTECTED_AUTHENTICATION_PATH,
+   DualCryptoOperations = CKF_DUAL_CRYPTO_OPERATIONS,
+   TokenInitialized = CKF_TOKEN_INITIALIZED,
+   SecondaryAuthentication = CKF_SECONDARY_AUTHENTICATION,
+   UserPinCountLow = CKF_USER_PIN_COUNT_LOW,
+   UserPinFinalTry = CKF_USER_PIN_FINAL_TRY,
+   UserPinLocked = CKF_USER_PIN_LOCKED,
+   UserPinToBeChanged = CKF_USER_PIN_TO_BE_CHANGED,
+   SoPinCountLow = CKF_SO_PIN_COUNT_LOW,
+   SoPinFinalTry = CKF_SO_PIN_FINAL_TRY,
+   SoPinLocked = CKF_SO_PIN_LOCKED,
+   SoPinToBeChanged = CKF_SO_PIN_TO_BE_CHANGED,
+   ErrorState = CKF_ERROR_STATE,
+   RwSession = CKF_RW_SESSION,
+   SerialSession = CKF_SERIAL_SESSION,
+   ArrayAttribute = CKF_ARRAY_ATTRIBUTE,
+   Hw = CKF_HW,
+   Encrypt = CKF_ENCRYPT,
+   Decrypt = CKF_DECRYPT,
+   Digest = CKF_DIGEST,
+   Sign = CKF_SIGN,
+   SignRecover = CKF_SIGN_RECOVER,
+   Verify = CKF_VERIFY,
+   VerifyRecover = CKF_VERIFY_RECOVER,
+   Generate = CKF_GENERATE,
+   GenerateKeyPair = CKF_GENERATE_KEY_PAIR,
+   Wrap = CKF_WRAP,
+   Unwrap = CKF_UNWRAP,
+   Derive = CKF_DERIVE,
+   EcFP = CKF_EC_F_P,
+   EcF2m = CKF_EC_F_2M,
+   EcEcparameters = CKF_EC_ECPARAMETERS,
+   EcNamedcurve = CKF_EC_NAMEDCURVE,
+   EcUncompress = CKF_EC_UNCOMPRESS,
+   EcCompress = CKF_EC_COMPRESS,
+   Extension = CKF_EXTENSION,
+   LibraryCantCreateOsThreads = CKF_LIBRARY_CANT_CREATE_OS_THREADS,
+   OsLockingOk = CKF_OS_LOCKING_OK,
+   DontBlock = CKF_DONT_BLOCK,
+   NextOtp = CKF_NEXT_OTP,
+   ExcludeTime = CKF_EXCLUDE_TIME,
+   ExcludeCounter = CKF_EXCLUDE_COUNTER,
+   ExcludeChallenge = CKF_EXCLUDE_CHALLENGE,
+   ExcludePin = CKF_EXCLUDE_PIN,
+   UserFriendlyOtp = CKF_USER_FRIENDLY_OTP,
+   };
+
+inline Flag operator | (Flag a, Flag b)
+   {
+   return static_cast< Flag >(static_cast< CK_FLAGS >(a) | static_cast< CK_FLAGS >(b));
+   }
+
+enum class MGF : CK_RSA_PKCS_MGF_TYPE
+   {
+   Mgf1Sha1 = CKG_MGF1_SHA1,
+   Mgf1Sha256 = CKG_MGF1_SHA256,
+   Mgf1Sha384 = CKG_MGF1_SHA384,
+   Mgf1Sha512 = CKG_MGF1_SHA512,
+   Mgf1Sha224 = CKG_MGF1_SHA224,
+   };
+
+enum class HardwareType : CK_HW_FEATURE_TYPE
+   {
+   MonotonicCounter = CKH_MONOTONIC_COUNTER,
+   Clock = CKH_CLOCK,
+   UserInterface = CKH_USER_INTERFACE,
+   VendorDefined = CKH_VENDOR_DEFINED,
+   };
+
+enum class KeyType : CK_KEY_TYPE
+   {
+   Rsa = CKK_RSA,
+   Dsa = CKK_DSA,
+   Dh = CKK_DH,
+   Ecdsa = CKK_ECDSA,
+   Ec = CKK_EC,
+   X942Dh = CKK_X9_42_DH,
+   Kea = CKK_KEA,
+   GenericSecret = CKK_GENERIC_SECRET,
+   Rc2 = CKK_RC2,
+   Rc4 = CKK_RC4,
+   Des = CKK_DES,
+   Des2 = CKK_DES2,
+   Des3 = CKK_DES3,
+   Cast = CKK_CAST,
+   Cast3 = CKK_CAST3,
+   Cast5 = CKK_CAST5,
+   Cast128 = CKK_CAST128,
+   Rc5 = CKK_RC5,
+   Idea = CKK_IDEA,
+   Skipjack = CKK_SKIPJACK,
+   Baton = CKK_BATON,
+   Juniper = CKK_JUNIPER,
+   Cdmf = CKK_CDMF,
+   Aes = CKK_AES,
+   Blowfish = CKK_BLOWFISH,
+   Twofish = CKK_TWOFISH,
+   Securid = CKK_SECURID,
+   Hotp = CKK_HOTP,
+   Acti = CKK_ACTI,
+   Camellia = CKK_CAMELLIA,
+   Aria = CKK_ARIA,
+   Md5Hmac = CKK_MD5_HMAC,
+   Sha1Hmac = CKK_SHA_1_HMAC,
+   Ripemd128Hmac = CKK_RIPEMD128_HMAC,
+   Ripemd160Hmac = CKK_RIPEMD160_HMAC,
+   Sha256Hmac = CKK_SHA256_HMAC,
+   Sha384Hmac = CKK_SHA384_HMAC,
+   Sha512Hmac = CKK_SHA512_HMAC,
+   Sha224Hmac = CKK_SHA224_HMAC,
+   Seed = CKK_SEED,
+   Gostr3410 = CKK_GOSTR3410,
+   Gostr3411 = CKK_GOSTR3411,
+   Gost28147 = CKK_GOST28147,
+   VendorDefined = CKK_VENDOR_DEFINED,
+   };
+
+enum class MechanismType : CK_MECHANISM_TYPE
+   {
+   RsaPkcsKeyPairGen = CKM_RSA_PKCS_KEY_PAIR_GEN,
+   RsaPkcs = CKM_RSA_PKCS,
+   Rsa9796 = CKM_RSA_9796,
+   RsaX509 = CKM_RSA_X_509,
+   Md2RsaPkcs = CKM_MD2_RSA_PKCS,
+   Md5RsaPkcs = CKM_MD5_RSA_PKCS,
+   Sha1RsaPkcs = CKM_SHA1_RSA_PKCS,
+   Ripemd128RsaPkcs = CKM_RIPEMD128_RSA_PKCS,
+   Ripemd160RsaPkcs = CKM_RIPEMD160_RSA_PKCS,
+   RsaPkcsOaep = CKM_RSA_PKCS_OAEP,
+   RsaX931KeyPairGen = CKM_RSA_X9_31_KEY_PAIR_GEN,
+   RsaX931 = CKM_RSA_X9_31,
+   Sha1RsaX931 = CKM_SHA1_RSA_X9_31,
+   RsaPkcsPss = CKM_RSA_PKCS_PSS,
+   Sha1RsaPkcsPss = CKM_SHA1_RSA_PKCS_PSS,
+   DsaKeyPairGen = CKM_DSA_KEY_PAIR_GEN,
+   Dsa = CKM_DSA,
+   DsaSha1 = CKM_DSA_SHA1,
+   DsaSha224 = CKM_DSA_SHA224,
+   DsaSha256 = CKM_DSA_SHA256,
+   DsaSha384 = CKM_DSA_SHA384,
+   DsaSha512 = CKM_DSA_SHA512,
+   DhPkcsKeyPairGen = CKM_DH_PKCS_KEY_PAIR_GEN,
+   DhPkcsDerive = CKM_DH_PKCS_DERIVE,
+   X942DhKeyPairGen = CKM_X9_42_DH_KEY_PAIR_GEN,
+   X942DhDerive = CKM_X9_42_DH_DERIVE,
+   X942DhHybridDerive = CKM_X9_42_DH_HYBRID_DERIVE,
+   X942MqvDerive = CKM_X9_42_MQV_DERIVE,
+   Sha256RsaPkcs = CKM_SHA256_RSA_PKCS,
+   Sha384RsaPkcs = CKM_SHA384_RSA_PKCS,
+   Sha512RsaPkcs = CKM_SHA512_RSA_PKCS,
+   Sha256RsaPkcsPss = CKM_SHA256_RSA_PKCS_PSS,
+   Sha384RsaPkcsPss = CKM_SHA384_RSA_PKCS_PSS,
+   Sha512RsaPkcsPss = CKM_SHA512_RSA_PKCS_PSS,
+   Sha224RsaPkcs = CKM_SHA224_RSA_PKCS,
+   Sha224RsaPkcsPss = CKM_SHA224_RSA_PKCS_PSS,
+   Sha512224 = CKM_SHA512_224,
+   Sha512224Hmac = CKM_SHA512_224_HMAC,
+   Sha512224HmacGeneral = CKM_SHA512_224_HMAC_GENERAL,
+   Sha512224KeyDerivation = CKM_SHA512_224_KEY_DERIVATION,
+   Sha512256 = CKM_SHA512_256,
+   Sha512256Hmac = CKM_SHA512_256_HMAC,
+   Sha512256HmacGeneral = CKM_SHA512_256_HMAC_GENERAL,
+   Sha512256KeyDerivation = CKM_SHA512_256_KEY_DERIVATION,
+   Sha512T = CKM_SHA512_T,
+   Sha512THmac = CKM_SHA512_T_HMAC,
+   Sha512THmacGeneral = CKM_SHA512_T_HMAC_GENERAL,
+   Sha512TKeyDerivation = CKM_SHA512_T_KEY_DERIVATION,
+   Rc2KeyGen = CKM_RC2_KEY_GEN,
+   Rc2Ecb = CKM_RC2_ECB,
+   Rc2Cbc = CKM_RC2_CBC,
+   Rc2Mac = CKM_RC2_MAC,
+   Rc2MacGeneral = CKM_RC2_MAC_GENERAL,
+   Rc2CbcPad = CKM_RC2_CBC_PAD,
+   Rc4KeyGen = CKM_RC4_KEY_GEN,
+   Rc4 = CKM_RC4,
+   DesKeyGen = CKM_DES_KEY_GEN,
+   DesEcb = CKM_DES_ECB,
+   DesCbc = CKM_DES_CBC,
+   DesMac = CKM_DES_MAC,
+   DesMacGeneral = CKM_DES_MAC_GENERAL,
+   DesCbcPad = CKM_DES_CBC_PAD,
+   Des2KeyGen = CKM_DES2_KEY_GEN,
+   Des3KeyGen = CKM_DES3_KEY_GEN,
+   Des3Ecb = CKM_DES3_ECB,
+   Des3Cbc = CKM_DES3_CBC,
+   Des3Mac = CKM_DES3_MAC,
+   Des3MacGeneral = CKM_DES3_MAC_GENERAL,
+   Des3CbcPad = CKM_DES3_CBC_PAD,
+   Des3CmacGeneral = CKM_DES3_CMAC_GENERAL,
+   Des3Cmac = CKM_DES3_CMAC,
+   CdmfKeyGen = CKM_CDMF_KEY_GEN,
+   CdmfEcb = CKM_CDMF_ECB,
+   CdmfCbc = CKM_CDMF_CBC,
+   CdmfMac = CKM_CDMF_MAC,
+   CdmfMacGeneral = CKM_CDMF_MAC_GENERAL,
+   CdmfCbcPad = CKM_CDMF_CBC_PAD,
+   DesOfb64 = CKM_DES_OFB64,
+   DesOfb8 = CKM_DES_OFB8,
+   DesCfb64 = CKM_DES_CFB64,
+   DesCfb8 = CKM_DES_CFB8,
+   Md2 = CKM_MD2,
+   Md2Hmac = CKM_MD2_HMAC,
+   Md2HmacGeneral = CKM_MD2_HMAC_GENERAL,
+   Md5 = CKM_MD5,
+   Md5Hmac = CKM_MD5_HMAC,
+   Md5HmacGeneral = CKM_MD5_HMAC_GENERAL,
+   Sha1 = CKM_SHA_1,
+   Sha1Hmac = CKM_SHA_1_HMAC,
+   Sha1HmacGeneral = CKM_SHA_1_HMAC_GENERAL,
+   Ripemd128 = CKM_RIPEMD128,
+   Ripemd128Hmac = CKM_RIPEMD128_HMAC,
+   Ripemd128HmacGeneral = CKM_RIPEMD128_HMAC_GENERAL,
+   Ripemd160 = CKM_RIPEMD160,
+   Ripemd160Hmac = CKM_RIPEMD160_HMAC,
+   Ripemd160HmacGeneral = CKM_RIPEMD160_HMAC_GENERAL,
+   Sha256 = CKM_SHA256,
+   Sha256Hmac = CKM_SHA256_HMAC,
+   Sha256HmacGeneral = CKM_SHA256_HMAC_GENERAL,
+   Sha224 = CKM_SHA224,
+   Sha224Hmac = CKM_SHA224_HMAC,
+   Sha224HmacGeneral = CKM_SHA224_HMAC_GENERAL,
+   Sha384 = CKM_SHA384,
+   Sha384Hmac = CKM_SHA384_HMAC,
+   Sha384HmacGeneral = CKM_SHA384_HMAC_GENERAL,
+   Sha512 = CKM_SHA512,
+   Sha512Hmac = CKM_SHA512_HMAC,
+   Sha512HmacGeneral = CKM_SHA512_HMAC_GENERAL,
+   SecuridKeyGen = CKM_SECURID_KEY_GEN,
+   Securid = CKM_SECURID,
+   HotpKeyGen = CKM_HOTP_KEY_GEN,
+   Hotp = CKM_HOTP,
+   Acti = CKM_ACTI,
+   ActiKeyGen = CKM_ACTI_KEY_GEN,
+   CastKeyGen = CKM_CAST_KEY_GEN,
+   CastEcb = CKM_CAST_ECB,
+   CastCbc = CKM_CAST_CBC,
+   CastMac = CKM_CAST_MAC,
+   CastMacGeneral = CKM_CAST_MAC_GENERAL,
+   CastCbcPad = CKM_CAST_CBC_PAD,
+   Cast3KeyGen = CKM_CAST3_KEY_GEN,
+   Cast3Ecb = CKM_CAST3_ECB,
+   Cast3Cbc = CKM_CAST3_CBC,
+   Cast3Mac = CKM_CAST3_MAC,
+   Cast3MacGeneral = CKM_CAST3_MAC_GENERAL,
+   Cast3CbcPad = CKM_CAST3_CBC_PAD,
+   Cast5KeyGen = CKM_CAST5_KEY_GEN,
+   Cast128KeyGen = CKM_CAST128_KEY_GEN,
+   Cast5Ecb = CKM_CAST5_ECB,
+   Cast128Ecb = CKM_CAST128_ECB,
+   Cast5Cbc = CKM_CAST5_CBC,
+   Cast128Cbc = CKM_CAST128_CBC,
+   Cast5Mac = CKM_CAST5_MAC,
+   Cast128Mac = CKM_CAST128_MAC,
+   Cast5MacGeneral = CKM_CAST5_MAC_GENERAL,
+   Cast128MacGeneral = CKM_CAST128_MAC_GENERAL,
+   Cast5CbcPad = CKM_CAST5_CBC_PAD,
+   Cast128CbcPad = CKM_CAST128_CBC_PAD,
+   Rc5KeyGen = CKM_RC5_KEY_GEN,
+   Rc5Ecb = CKM_RC5_ECB,
+   Rc5Cbc = CKM_RC5_CBC,
+   Rc5Mac = CKM_RC5_MAC,
+   Rc5MacGeneral = CKM_RC5_MAC_GENERAL,
+   Rc5CbcPad = CKM_RC5_CBC_PAD,
+   IdeaKeyGen = CKM_IDEA_KEY_GEN,
+   IdeaEcb = CKM_IDEA_ECB,
+   IdeaCbc = CKM_IDEA_CBC,
+   IdeaMac = CKM_IDEA_MAC,
+   IdeaMacGeneral = CKM_IDEA_MAC_GENERAL,
+   IdeaCbcPad = CKM_IDEA_CBC_PAD,
+   GenericSecretKeyGen = CKM_GENERIC_SECRET_KEY_GEN,
+   ConcatenateBaseAndKey = CKM_CONCATENATE_BASE_AND_KEY,
+   ConcatenateBaseAndData = CKM_CONCATENATE_BASE_AND_DATA,
+   ConcatenateDataAndBase = CKM_CONCATENATE_DATA_AND_BASE,
+   XorBaseAndData = CKM_XOR_BASE_AND_DATA,
+   ExtractKeyFromKey = CKM_EXTRACT_KEY_FROM_KEY,
+   Ssl3PreMasterKeyGen = CKM_SSL3_PRE_MASTER_KEY_GEN,
+   Ssl3MasterKeyDerive = CKM_SSL3_MASTER_KEY_DERIVE,
+   Ssl3KeyAndMacDerive = CKM_SSL3_KEY_AND_MAC_DERIVE,
+   Ssl3MasterKeyDeriveDh = CKM_SSL3_MASTER_KEY_DERIVE_DH,
+   TlsPreMasterKeyGen = CKM_TLS_PRE_MASTER_KEY_GEN,
+   TlsMasterKeyDerive = CKM_TLS_MASTER_KEY_DERIVE,
+   TlsKeyAndMacDerive = CKM_TLS_KEY_AND_MAC_DERIVE,
+   TlsMasterKeyDeriveDh = CKM_TLS_MASTER_KEY_DERIVE_DH,
+   TlsPrf = CKM_TLS_PRF,
+   Ssl3Md5Mac = CKM_SSL3_MD5_MAC,
+   Ssl3Sha1Mac = CKM_SSL3_SHA1_MAC,
+   Md5KeyDerivation = CKM_MD5_KEY_DERIVATION,
+   Md2KeyDerivation = CKM_MD2_KEY_DERIVATION,
+   Sha1KeyDerivation = CKM_SHA1_KEY_DERIVATION,
+   Sha256KeyDerivation = CKM_SHA256_KEY_DERIVATION,
+   Sha384KeyDerivation = CKM_SHA384_KEY_DERIVATION,
+   Sha512KeyDerivation = CKM_SHA512_KEY_DERIVATION,
+   Sha224KeyDerivation = CKM_SHA224_KEY_DERIVATION,
+   PbeMd2DesCbc = CKM_PBE_MD2_DES_CBC,
+   PbeMd5DesCbc = CKM_PBE_MD5_DES_CBC,
+   PbeMd5CastCbc = CKM_PBE_MD5_CAST_CBC,
+   PbeMd5Cast3Cbc = CKM_PBE_MD5_CAST3_CBC,
+   PbeMd5Cast5Cbc = CKM_PBE_MD5_CAST5_CBC,
+   PbeMd5Cast128Cbc = CKM_PBE_MD5_CAST128_CBC,
+   PbeSha1Cast5Cbc = CKM_PBE_SHA1_CAST5_CBC,
+   PbeSha1Cast128Cbc = CKM_PBE_SHA1_CAST128_CBC,
+   PbeSha1Rc4128 = CKM_PBE_SHA1_RC4_128,
+   PbeSha1Rc440 = CKM_PBE_SHA1_RC4_40,
+   PbeSha1Des3EdeCbc = CKM_PBE_SHA1_DES3_EDE_CBC,
+   PbeSha1Des2EdeCbc = CKM_PBE_SHA1_DES2_EDE_CBC,
+   PbeSha1Rc2128Cbc = CKM_PBE_SHA1_RC2_128_CBC,
+   PbeSha1Rc240Cbc = CKM_PBE_SHA1_RC2_40_CBC,
+   Pkcs5Pbkd2 = CKM_PKCS5_PBKD2,
+   PbaSha1WithSha1Hmac = CKM_PBA_SHA1_WITH_SHA1_HMAC,
+   WtlsPreMasterKeyGen = CKM_WTLS_PRE_MASTER_KEY_GEN,
+   WtlsMasterKeyDerive = CKM_WTLS_MASTER_KEY_DERIVE,
+   WtlsMasterKeyDeriveDhEcc = CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC,
+   WtlsPrf = CKM_WTLS_PRF,
+   WtlsServerKeyAndMacDerive = CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE,
+   WtlsClientKeyAndMacDerive = CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE,
+   Tls10MacServer = CKM_TLS10_MAC_SERVER,
+   Tls10MacClient = CKM_TLS10_MAC_CLIENT,
+   Tls12Mac = CKM_TLS12_MAC,
+   Tls12Kdf = CKM_TLS12_KDF,
+   Tls12MasterKeyDerive = CKM_TLS12_MASTER_KEY_DERIVE,
+   Tls12KeyAndMacDerive = CKM_TLS12_KEY_AND_MAC_DERIVE,
+   Tls12MasterKeyDeriveDh = CKM_TLS12_MASTER_KEY_DERIVE_DH,
+   Tls12KeySafeDerive = CKM_TLS12_KEY_SAFE_DERIVE,
+   TlsMac = CKM_TLS_MAC,
+   TlsKdf = CKM_TLS_KDF,
+   KeyWrapLynks = CKM_KEY_WRAP_LYNKS,
+   KeyWrapSetOaep = CKM_KEY_WRAP_SET_OAEP,
+   CmsSig = CKM_CMS_SIG,
+   KipDerive = CKM_KIP_DERIVE,
+   KipWrap = CKM_KIP_WRAP,
+   KipMac = CKM_KIP_MAC,
+   CamelliaKeyGen = CKM_CAMELLIA_KEY_GEN,
+   CamelliaEcb = CKM_CAMELLIA_ECB,
+   CamelliaCbc = CKM_CAMELLIA_CBC,
+   CamelliaMac = CKM_CAMELLIA_MAC,
+   CamelliaMacGeneral = CKM_CAMELLIA_MAC_GENERAL,
+   CamelliaCbcPad = CKM_CAMELLIA_CBC_PAD,
+   CamelliaEcbEncryptData = CKM_CAMELLIA_ECB_ENCRYPT_DATA,
+   CamelliaCbcEncryptData = CKM_CAMELLIA_CBC_ENCRYPT_DATA,
+   CamelliaCtr = CKM_CAMELLIA_CTR,
+   AriaKeyGen = CKM_ARIA_KEY_GEN,
+   AriaEcb = CKM_ARIA_ECB,
+   AriaCbc = CKM_ARIA_CBC,
+   AriaMac = CKM_ARIA_MAC,
+   AriaMacGeneral = CKM_ARIA_MAC_GENERAL,
+   AriaCbcPad = CKM_ARIA_CBC_PAD,
+   AriaEcbEncryptData = CKM_ARIA_ECB_ENCRYPT_DATA,
+   AriaCbcEncryptData = CKM_ARIA_CBC_ENCRYPT_DATA,
+   SeedKeyGen = CKM_SEED_KEY_GEN,
+   SeedEcb = CKM_SEED_ECB,
+   SeedCbc = CKM_SEED_CBC,
+   SeedMac = CKM_SEED_MAC,
+   SeedMacGeneral = CKM_SEED_MAC_GENERAL,
+   SeedCbcPad = CKM_SEED_CBC_PAD,
+   SeedEcbEncryptData = CKM_SEED_ECB_ENCRYPT_DATA,
+   SeedCbcEncryptData = CKM_SEED_CBC_ENCRYPT_DATA,
+   SkipjackKeyGen = CKM_SKIPJACK_KEY_GEN,
+   SkipjackEcb64 = CKM_SKIPJACK_ECB64,
+   SkipjackCbc64 = CKM_SKIPJACK_CBC64,
+   SkipjackOfb64 = CKM_SKIPJACK_OFB64,
+   SkipjackCfb64 = CKM_SKIPJACK_CFB64,
+   SkipjackCfb32 = CKM_SKIPJACK_CFB32,
+   SkipjackCfb16 = CKM_SKIPJACK_CFB16,
+   SkipjackCfb8 = CKM_SKIPJACK_CFB8,
+   SkipjackWrap = CKM_SKIPJACK_WRAP,
+   SkipjackPrivateWrap = CKM_SKIPJACK_PRIVATE_WRAP,
+   SkipjackRelayx = CKM_SKIPJACK_RELAYX,
+   KeaKeyPairGen = CKM_KEA_KEY_PAIR_GEN,
+   KeaKeyDerive = CKM_KEA_KEY_DERIVE,
+   KeaDerive = CKM_KEA_DERIVE,
+   FortezzaTimestamp = CKM_FORTEZZA_TIMESTAMP,
+   BatonKeyGen = CKM_BATON_KEY_GEN,
+   BatonEcb128 = CKM_BATON_ECB128,
+   BatonEcb96 = CKM_BATON_ECB96,
+   BatonCbc128 = CKM_BATON_CBC128,
+   BatonCounter = CKM_BATON_COUNTER,
+   BatonShuffle = CKM_BATON_SHUFFLE,
+   BatonWrap = CKM_BATON_WRAP,
+   EcdsaKeyPairGen = CKM_ECDSA_KEY_PAIR_GEN,
+   EcKeyPairGen = CKM_EC_KEY_PAIR_GEN,
+   Ecdsa = CKM_ECDSA,
+   EcdsaSha1 = CKM_ECDSA_SHA1,
+   EcdsaSha224 = CKM_ECDSA_SHA224,
+   EcdsaSha256 = CKM_ECDSA_SHA256,
+   EcdsaSha384 = CKM_ECDSA_SHA384,
+   EcdsaSha512 = CKM_ECDSA_SHA512,
+   Ecdh1Derive = CKM_ECDH1_DERIVE,
+   Ecdh1CofactorDerive = CKM_ECDH1_COFACTOR_DERIVE,
+   EcmqvDerive = CKM_ECMQV_DERIVE,
+   EcdhAesKeyWrap = CKM_ECDH_AES_KEY_WRAP,
+   RsaAesKeyWrap = CKM_RSA_AES_KEY_WRAP,
+   JuniperKeyGen = CKM_JUNIPER_KEY_GEN,
+   JuniperEcb128 = CKM_JUNIPER_ECB128,
+   JuniperCbc128 = CKM_JUNIPER_CBC128,
+   JuniperCounter = CKM_JUNIPER_COUNTER,
+   JuniperShuffle = CKM_JUNIPER_SHUFFLE,
+   JuniperWrap = CKM_JUNIPER_WRAP,
+   Fasthash = CKM_FASTHASH,
+   AesKeyGen = CKM_AES_KEY_GEN,
+   AesEcb = CKM_AES_ECB,
+   AesCbc = CKM_AES_CBC,
+   AesMac = CKM_AES_MAC,
+   AesMacGeneral = CKM_AES_MAC_GENERAL,
+   AesCbcPad = CKM_AES_CBC_PAD,
+   AesCtr = CKM_AES_CTR,
+   AesGcm = CKM_AES_GCM,
+   AesCcm = CKM_AES_CCM,
+   AesCts = CKM_AES_CTS,
+   AesCmac = CKM_AES_CMAC,
+   AesCmacGeneral = CKM_AES_CMAC_GENERAL,
+   AesXcbcMac = CKM_AES_XCBC_MAC,
+   AesXcbcMac96 = CKM_AES_XCBC_MAC_96,
+   AesGmac = CKM_AES_GMAC,
+   BlowfishKeyGen = CKM_BLOWFISH_KEY_GEN,
+   BlowfishCbc = CKM_BLOWFISH_CBC,
+   TwofishKeyGen = CKM_TWOFISH_KEY_GEN,
+   TwofishCbc = CKM_TWOFISH_CBC,
+   BlowfishCbcPad = CKM_BLOWFISH_CBC_PAD,
+   TwofishCbcPad = CKM_TWOFISH_CBC_PAD,
+   DesEcbEncryptData = CKM_DES_ECB_ENCRYPT_DATA,
+   DesCbcEncryptData = CKM_DES_CBC_ENCRYPT_DATA,
+   Des3EcbEncryptData = CKM_DES3_ECB_ENCRYPT_DATA,
+   Des3CbcEncryptData = CKM_DES3_CBC_ENCRYPT_DATA,
+   AesEcbEncryptData = CKM_AES_ECB_ENCRYPT_DATA,
+   AesCbcEncryptData = CKM_AES_CBC_ENCRYPT_DATA,
+   Gostr3410KeyPairGen = CKM_GOSTR3410_KEY_PAIR_GEN,
+   Gostr3410 = CKM_GOSTR3410,
+   Gostr3410WithGostr3411 = CKM_GOSTR3410_WITH_GOSTR3411,
+   Gostr3410KeyWrap = CKM_GOSTR3410_KEY_WRAP,
+   Gostr3410Derive = CKM_GOSTR3410_DERIVE,
+   Gostr3411 = CKM_GOSTR3411,
+   Gostr3411Hmac = CKM_GOSTR3411_HMAC,
+   Gost28147KeyGen = CKM_GOST28147_KEY_GEN,
+   Gost28147Ecb = CKM_GOST28147_ECB,
+   Gost28147 = CKM_GOST28147,
+   Gost28147Mac = CKM_GOST28147_MAC,
+   Gost28147KeyWrap = CKM_GOST28147_KEY_WRAP,
+   DsaParameterGen = CKM_DSA_PARAMETER_GEN,
+   DhPkcsParameterGen = CKM_DH_PKCS_PARAMETER_GEN,
+   X942DhParameterGen = CKM_X9_42_DH_PARAMETER_GEN,
+   DsaProbablisticParameterGen = CKM_DSA_PROBABLISTIC_PARAMETER_GEN,
+   DsaShaweTaylorParameterGen = CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN,
+   AesOfb = CKM_AES_OFB,
+   AesCfb64 = CKM_AES_CFB64,
+   AesCfb8 = CKM_AES_CFB8,
+   AesCfb128 = CKM_AES_CFB128,
+   AesCfb1 = CKM_AES_CFB1,
+   AesKeyWrap = CKM_AES_KEY_WRAP,
+   AesKeyWrapPad = CKM_AES_KEY_WRAP_PAD,
+   RsaPkcsTpm11 = CKM_RSA_PKCS_TPM_1_1,
+   RsaPkcsOaepTpm11 = CKM_RSA_PKCS_OAEP_TPM_1_1,
+   VendorDefined = CKM_VENDOR_DEFINED,
+   };
+
+enum class Notification : CK_NOTIFICATION
+   {
+   Surrender = CKN_SURRENDER,
+   OtpChanged = CKN_OTP_CHANGED,
+   };
+
+enum class ObjectClass : CK_OBJECT_CLASS
+   {
+   Data = CKO_DATA,
+   Certificate = CKO_CERTIFICATE,
+   PublicKey = CKO_PUBLIC_KEY,
+   PrivateKey = CKO_PRIVATE_KEY,
+   SecretKey = CKO_SECRET_KEY,
+   HwFeature = CKO_HW_FEATURE,
+   DomainParameters = CKO_DOMAIN_PARAMETERS,
+   Mechanism = CKO_MECHANISM,
+   OtpKey = CKO_OTP_KEY,
+   VendorDefined = CKO_VENDOR_DEFINED,
+   };
+
+enum class PseudoRandom : CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE
+   {
+   Pkcs5Pbkd2HmacSha1 = CKP_PKCS5_PBKD2_HMAC_SHA1,
+   Pkcs5Pbkd2HmacGostr3411 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411,
+   Pkcs5Pbkd2HmacSha224 = CKP_PKCS5_PBKD2_HMAC_SHA224,
+   Pkcs5Pbkd2HmacSha256 = CKP_PKCS5_PBKD2_HMAC_SHA256,
+   Pkcs5Pbkd2HmacSha384 = CKP_PKCS5_PBKD2_HMAC_SHA384,
+   Pkcs5Pbkd2HmacSha512 = CKP_PKCS5_PBKD2_HMAC_SHA512,
+   Pkcs5Pbkd2HmacSha512224 = CKP_PKCS5_PBKD2_HMAC_SHA512_224,
+   Pkcs5Pbkd2HmacSha512256 = CKP_PKCS5_PBKD2_HMAC_SHA512_256,
+   };
+
+enum class SessionState : CK_STATE
+   {
+   RoPublicSession = CKS_RO_PUBLIC_SESSION,
+   RoUserFunctions = CKS_RO_USER_FUNCTIONS,
+   RwPublicSession = CKS_RW_PUBLIC_SESSION,
+   RwUserFunctions = CKS_RW_USER_FUNCTIONS,
+   RwSoFunctions = CKS_RW_SO_FUNCTIONS,
+   };
+
+enum class ReturnValue : CK_RV
+   {
+   OK = CKR_OK,
+   Cancel = CKR_CANCEL,
+   HostMemory = CKR_HOST_MEMORY,
+   SlotIdInvalid = CKR_SLOT_ID_INVALID,
+   GeneralError = CKR_GENERAL_ERROR,
+   FunctionFailed = CKR_FUNCTION_FAILED,
+   ArgumentsBad = CKR_ARGUMENTS_BAD,
+   NoEvent = CKR_NO_EVENT,
+   NeedToCreateThreads = CKR_NEED_TO_CREATE_THREADS,
+   CantLock = CKR_CANT_LOCK,
+   AttributeReadOnly = CKR_ATTRIBUTE_READ_ONLY,
+   AttributeSensitive = CKR_ATTRIBUTE_SENSITIVE,
+   AttributeTypeInvalid = CKR_ATTRIBUTE_TYPE_INVALID,
+   AttributeValueInvalid = CKR_ATTRIBUTE_VALUE_INVALID,
+   ActionProhibited = CKR_ACTION_PROHIBITED,
+   DataInvalid = CKR_DATA_INVALID,
+   DataLenRange = CKR_DATA_LEN_RANGE,
+   DeviceError = CKR_DEVICE_ERROR,
+   DeviceMemory = CKR_DEVICE_MEMORY,
+   DeviceRemoved = CKR_DEVICE_REMOVED,
+   EncryptedDataInvalid = CKR_ENCRYPTED_DATA_INVALID,
+   EncryptedDataLenRange = CKR_ENCRYPTED_DATA_LEN_RANGE,
+   FunctionCanceled = CKR_FUNCTION_CANCELED,
+   FunctionNotParallel = CKR_FUNCTION_NOT_PARALLEL,
+   FunctionNotSupported = CKR_FUNCTION_NOT_SUPPORTED,
+   KeyHandleInvalid = CKR_KEY_HANDLE_INVALID,
+   KeySizeRange = CKR_KEY_SIZE_RANGE,
+   KeyTypeInconsistent = CKR_KEY_TYPE_INCONSISTENT,
+   KeyNotNeeded = CKR_KEY_NOT_NEEDED,
+   KeyChanged = CKR_KEY_CHANGED,
+   KeyNeeded = CKR_KEY_NEEDED,
+   KeyIndigestible = CKR_KEY_INDIGESTIBLE,
+   KeyFunctionNotPermitted = CKR_KEY_FUNCTION_NOT_PERMITTED,
+   KeyNotWrappable = CKR_KEY_NOT_WRAPPABLE,
+   KeyUnextractable = CKR_KEY_UNEXTRACTABLE,
+   MechanismInvalid = CKR_MECHANISM_INVALID,
+   MechanismParamInvalid = CKR_MECHANISM_PARAM_INVALID,
+   ObjectHandleInvalid = CKR_OBJECT_HANDLE_INVALID,
+   OperationActive = CKR_OPERATION_ACTIVE,
+   OperationNotInitialized = CKR_OPERATION_NOT_INITIALIZED,
+   PinIncorrect = CKR_PIN_INCORRECT,
+   PinInvalid = CKR_PIN_INVALID,
+   PinLenRange = CKR_PIN_LEN_RANGE,
+   PinExpired = CKR_PIN_EXPIRED,
+   PinLocked = CKR_PIN_LOCKED,
+   SessionClosed = CKR_SESSION_CLOSED,
+   SessionCount = CKR_SESSION_COUNT,
+   SessionHandleInvalid = CKR_SESSION_HANDLE_INVALID,
+   SessionParallelNotSupported = CKR_SESSION_PARALLEL_NOT_SUPPORTED,
+   SessionReadOnly = CKR_SESSION_READ_ONLY,
+   SessionExists = CKR_SESSION_EXISTS,
+   SessionReadOnlyExists = CKR_SESSION_READ_ONLY_EXISTS,
+   SessionReadWriteSoExists = CKR_SESSION_READ_WRITE_SO_EXISTS,
+   SignatureInvalid = CKR_SIGNATURE_INVALID,
+   SignatureLenRange = CKR_SIGNATURE_LEN_RANGE,
+   TemplateIncomplete = CKR_TEMPLATE_INCOMPLETE,
+   TemplateInconsistent = CKR_TEMPLATE_INCONSISTENT,
+   TokenNotPresent = CKR_TOKEN_NOT_PRESENT,
+   TokenNotRecognized = CKR_TOKEN_NOT_RECOGNIZED,
+   TokenWriteProtected = CKR_TOKEN_WRITE_PROTECTED,
+   UnwrappingKeyHandleInvalid = CKR_UNWRAPPING_KEY_HANDLE_INVALID,
+   UnwrappingKeySizeRange = CKR_UNWRAPPING_KEY_SIZE_RANGE,
+   UnwrappingKeyTypeInconsistent = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT,
+   UserAlreadyLoggedIn = CKR_USER_ALREADY_LOGGED_IN,
+   UserNotLoggedIn = CKR_USER_NOT_LOGGED_IN,
+   UserPinNotInitialized = CKR_USER_PIN_NOT_INITIALIZED,
+   UserTypeInvalid = CKR_USER_TYPE_INVALID,
+   UserAnotherAlreadyLoggedIn = CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
+   UserTooManyTypes = CKR_USER_TOO_MANY_TYPES,
+   WrappedKeyInvalid = CKR_WRAPPED_KEY_INVALID,
+   WrappedKeyLenRange = CKR_WRAPPED_KEY_LEN_RANGE,
+   WrappingKeyHandleInvalid = CKR_WRAPPING_KEY_HANDLE_INVALID,
+   WrappingKeySizeRange = CKR_WRAPPING_KEY_SIZE_RANGE,
+   WrappingKeyTypeInconsistent = CKR_WRAPPING_KEY_TYPE_INCONSISTENT,
+   RandomSeedNotSupported = CKR_RANDOM_SEED_NOT_SUPPORTED,
+   RandomNoRng = CKR_RANDOM_NO_RNG,
+   DomainParamsInvalid = CKR_DOMAIN_PARAMS_INVALID,
+   CurveNotSupported = CKR_CURVE_NOT_SUPPORTED,
+   BufferTooSmall = CKR_BUFFER_TOO_SMALL,
+   SavedStateInvalid = CKR_SAVED_STATE_INVALID,
+   InformationSensitive = CKR_INFORMATION_SENSITIVE,
+   StateUnsaveable = CKR_STATE_UNSAVEABLE,
+   CryptokiNotInitialized = CKR_CRYPTOKI_NOT_INITIALIZED,
+   CryptokiAlreadyInitialized = CKR_CRYPTOKI_ALREADY_INITIALIZED,
+   MutexBad = CKR_MUTEX_BAD,
+   MutexNotLocked = CKR_MUTEX_NOT_LOCKED,
+   NewPinMode = CKR_NEW_PIN_MODE,
+   NextOtp = CKR_NEXT_OTP,
+   ExceededMaxIterations = CKR_EXCEEDED_MAX_ITERATIONS,
+   FipsSelfTestFailed = CKR_FIPS_SELF_TEST_FAILED,
+   LibraryLoadFailed = CKR_LIBRARY_LOAD_FAILED,
+   PinTooWeak = CKR_PIN_TOO_WEAK,
+   PublicKeyInvalid = CKR_PUBLIC_KEY_INVALID,
+   FunctionRejected = CKR_FUNCTION_REJECTED,
+   VendorDefined = CKR_VENDOR_DEFINED,
+   };
+
+enum class UserType : CK_USER_TYPE
+   {
+   SO = CKU_SO,
+   User = CKU_USER,
+   ContextSpecific = CKU_CONTEXT_SPECIFIC,
+   };
+
+enum class PublicPointEncoding : uint32_t
+   {
+   Raw,
+   Der
+   };
+
+using FunctionListPtr = CK_FUNCTION_LIST_PTR;
+using VoidPtr = CK_VOID_PTR;
+using C_InitializeArgs = CK_C_INITIALIZE_ARGS;
+using CreateMutex = CK_CREATEMUTEX;
+using DestroyMutex = CK_DESTROYMUTEX;
+using LockMutex = CK_LOCKMUTEX;
+using UnlockMutex = CK_UNLOCKMUTEX;
+using Flags = CK_FLAGS;
+using Info = CK_INFO;
+using Bbool = CK_BBOOL;
+using SlotId = CK_SLOT_ID;
+using Ulong = CK_ULONG;
+using SlotInfo = CK_SLOT_INFO;
+using TokenInfo = CK_TOKEN_INFO;
+using Mechanism = CK_MECHANISM;
+using MechanismInfo = CK_MECHANISM_INFO;
+using Utf8Char = CK_UTF8CHAR;
+using Notify = CK_NOTIFY;
+using SessionHandle = CK_SESSION_HANDLE;
+using SessionInfo = CK_SESSION_INFO;
+using Attribute = CK_ATTRIBUTE;
+using ObjectHandle = CK_OBJECT_HANDLE;
+using Byte = CK_BYTE;
+using RsaPkcsOaepParams = CK_RSA_PKCS_OAEP_PARAMS;
+using RsaPkcsPssParams = CK_RSA_PKCS_PSS_PARAMS;
+using Ecdh1DeriveParams = CK_ECDH1_DERIVE_PARAMS;
+using Date = CK_DATE;
+
+BOTAN_PUBLIC_API(2,0) extern ReturnValue* ThrowException;
+
+const Bbool True = CK_TRUE;
+const Bbool False = CK_FALSE;
+
+inline Flags flags(Flag flags)
+   {
+   return static_cast<Flags>(flags);
+   }
+
+class Slot;
+
+/**
+* Initializes a token
+* @param slot The slot with the attached token that should be initialized
+* @param label The token label
+* @param so_pin PIN of the security officer. Will be set if the token is uninitialized other this has to be the current SO_PIN
+* @param pin The user PIN that will be set
+*/
+BOTAN_PUBLIC_API(2,0) void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin,
+                                const secure_string& pin);
+
+/**
+* Change PIN with old PIN to new PIN
+* @param slot The slot with the attached token
+* @param old_pin The old user PIN
+* @param new_pin The new user PIN
+*/
+
+BOTAN_PUBLIC_API(2,0) void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin);
+
+/**
+* Change SO_PIN with old SO_PIN to new SO_PIN
+* @param slot The slot with the attached token
+* @param old_so_pin The old SO_PIN
+* @param new_so_pin The new SO_PIN
+*/
+BOTAN_PUBLIC_API(2,0) void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin);
+
+/**
+* Sets user PIN with SO_PIN
+* @param slot The slot with the attached token
+* @param so_pin PIN of the security officer
+* @param pin The user PIN that should be set
+*/
+BOTAN_PUBLIC_API(2,0) void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin);
+
+/// Provides access to all PKCS#11 functions
+class BOTAN_PUBLIC_API(2,0) LowLevel
+   {
+   public:
+      /// @param ptr the functon list pointer to use. Can be retrieved via `LowLevel::C_GetFunctionList`
+      explicit LowLevel(FunctionListPtr ptr);
+
+      /****************************** General purpose functions ******************************/
+
+      /**
+      * C_Initialize initializes the Cryptoki library.
+      * @param init_args if this is not nullptr, it gets cast to (`C_InitializeArgs`) and dereferenced
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CantLock \li CryptokiAlreadyInitialized
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li NeedToCreateThreads \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_Initialize(VoidPtr init_args,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Finalize indicates that an application is done with the Cryptoki library.
+      * @param reserved reserved.  Should be nullptr
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_Finalize(VoidPtr reserved,
+                      ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetInfo returns general information about Cryptoki.
+      * @param info_ptr location that receives information
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_GetInfo(Info* info_ptr,
+                     ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetFunctionList returns the function list.
+      * @param pkcs11_module The PKCS#11 module
+      * @param function_list_ptr_ptr receives pointer to function list
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK
+      * @return true on success, false otherwise
+      */
+      static bool C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr,
+                                    ReturnValue* return_value = ThrowException);
+
+      /****************************** Slot and token management functions ******************************/
+
+      /**
+      * C_GetSlotList obtains a list of slots in the system.
+      * @param token_present only slots with tokens
+      * @param slot_list_ptr receives array of slot IDs
+      * @param count_ptr receives number of slots
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_GetSlotList(Bbool token_present,
+                         SlotId* slot_list_ptr,
+                         Ulong* count_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetSlotList obtains a list of slots in the system.
+      * @param token_present only slots with tokens
+      * @param slot_ids receives vector of slot IDs
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_GetSlotList(bool token_present,
+                         std::vector<SlotId>& slot_ids,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetSlotInfo obtains information about a particular slot in the system.
+      * @param slot_id the ID of the slot
+      * @param info_ptr receives the slot information
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li SlotIdInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_GetSlotInfo(SlotId slot_id,
+                         SlotInfo* info_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetTokenInfo obtains information about a particular token in the system.
+      * @param slot_id ID of the token's slot
+      * @param info_ptr receives the token information
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SlotIdInvalid
+      *     \li TokenNotPresent \li TokenNotRecognized \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetTokenInfo(SlotId slot_id,
+                          TokenInfo* info_ptr,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_WaitForSlotEvent waits for a slot event (token insertion, removal, etc.) to occur.
+      * @param flags blocking/nonblocking flag
+      * @param slot_ptr location that receives the slot ID
+      * @param reserved reserved.  Should be NULL_PTR
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li NoEvent
+      *     \li OK
+      * @return true on success, false otherwise
+      */
+      bool C_WaitForSlotEvent(Flags flags,
+                              SlotId* slot_ptr,
+                              VoidPtr reserved,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetMechanismList obtains a list of mechanism types supported by a token.
+      * @param slot_id ID of token's slot
+      * @param mechanism_list_ptr gets mech. array
+      * @param count_ptr gets # of mechs.
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+      *     \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetMechanismList(SlotId slot_id,
+                              MechanismType* mechanism_list_ptr,
+                              Ulong* count_ptr,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetMechanismList obtains a list of mechanism types supported by a token.
+      * @param slot_id ID of token's slot
+      * @param mechanisms receives vector of supported mechanisms
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+      *     \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetMechanismList(SlotId slot_id,
+                              std::vector<MechanismType>& mechanisms,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetMechanismInfo obtains information about a particular mechanism possibly supported by a token.
+      * @param slot_id ID of the token's slot
+      * @param type type of mechanism
+      * @param info_ptr receives mechanism info
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li MechanismInvalid \li OK
+      *     \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+      *     \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetMechanismInfo(SlotId slot_id,
+                              MechanismType type,
+                              MechanismInfo* info_ptr,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_InitToken initializes a token.
+      * @param slot_id ID of the token's slot
+      * @param so_pin_ptr the SO's initial PIN
+      * @param so_pin_len length in bytes of the SO_PIN
+      * @param label_ptr 32-byte token label (blank padded)
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinIncorrect \li PinLocked \li SessionExists
+      *     \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+      *     \li TokenWriteProtected \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_InitToken(SlotId slot_id,
+                       Utf8Char* so_pin_ptr,
+                       Ulong so_pin_len,
+                       Utf8Char* label_ptr,
+                       ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_InitToken initializes a token.
+      * @param slot_id ID of the token's slot
+      * @param so_pin the SO's initial PIN
+      * @param label token label (at max 32 bytes long)
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinIncorrect \li PinLocked \li SessionExists
+      *     \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+      *     \li TokenWriteProtected \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_InitToken(SlotId slot_id,
+                       const std::vector<uint8_t, TAlloc>& so_pin,
+                       const std::string& label,
+                       ReturnValue* return_value = ThrowException) const
+         {
+         std::string padded_label = label;
+         if(label.size() < 32)
+            {
+            padded_label.insert(padded_label.end(), 32 - label.size(), ' ');
+            }
+
+         return C_InitToken(slot_id,
+                            reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(so_pin.data())),
+                            static_cast<Ulong>(so_pin.size()),
+                            reinterpret_cast< Utf8Char* >(const_cast< char* >(padded_label.c_str())),
+                            return_value);
+         }
+
+      /**
+      * C_InitPIN initializes the normal user's PIN.
+      * @param session the session's handle
+      * @param pin_ptr the normal user's PIN
+      * @param pin_len length in bytes of the PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinInvalid \li PinLenRange \li SessionClosed
+      *     \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected
+      *     \li UserNotLoggedIn \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_InitPIN(SessionHandle session,
+                     Utf8Char* pin_ptr,
+                     Ulong pin_len,
+                     ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_InitPIN initializes the normal user's PIN.
+      * @param session the session's handle
+      * @param pin the normal user's PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinInvalid \li PinLenRange \li SessionClosed
+      *     \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected
+      *     \li UserNotLoggedIn \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_InitPIN(SessionHandle session,
+                     const std::vector<uint8_t, TAlloc>& pin,
+                     ReturnValue* return_value = ThrowException) const
+         {
+         return C_InitPIN(session,
+                          reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(pin.data())),
+                          static_cast<Ulong>(pin.size()),
+                          return_value);
+         }
+
+      /**
+      * C_SetPIN modifies the PIN of the user who is logged in.
+      * @param session the session's handle
+      * @param old_pin_ptr the old PIN
+      * @param old_len length of the old PIN
+      * @param new_pin_ptr the new PIN
+      * @param new_len length of the new PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinIncorrect \li PinInvalid \li PinLenRange
+      *     \li PinLocked \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_SetPIN(SessionHandle session,
+                    Utf8Char* old_pin_ptr,
+                    Ulong old_len,
+                    Utf8Char* new_pin_ptr,
+                    Ulong new_len,
+                    ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SetPIN modifies the PIN of the user who is logged in.
+      * @param session the session's handle
+      * @param old_pin the old PIN
+      * @param new_pin the new PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li PinIncorrect \li PinInvalid \li PinLenRange
+      *     \li PinLocked \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_SetPIN(SessionHandle session,
+                    const std::vector<uint8_t, TAlloc>& old_pin,
+                    const std::vector<uint8_t, TAlloc>& new_pin,
+                    ReturnValue* return_value = ThrowException) const
+         {
+         return C_SetPIN(session,
+                         reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(old_pin.data())),
+                         static_cast<Ulong>(old_pin.size()),
+                         reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(new_pin.data())),
+                         static_cast<Ulong>(new_pin.size()),
+                         return_value);
+         }
+
+
+      /****************************** Session management ******************************/
+
+      /**
+      * C_OpenSession opens a session between an application and a token.
+      * @param slot_id the slot's ID
+      * @param flags from CK_SESSION_INFO
+      * @param application passed to callback
+      * @param notify callback function
+      * @param session_ptr gets session handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SessionCount
+      *     \li SessionParallelNotSupported \li SessionReadWriteSoExists \li SlotIdInvalid
+      *     \li TokenNotPresent \li TokenNotRecognized \li TokenWriteProtected
+      *     \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_OpenSession(SlotId slot_id,
+                         Flags flags,
+                         VoidPtr application,
+                         Notify notify,
+                         SessionHandle* session_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_CloseSession closes a session between an application and a token.
+      * @param session the session's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_CloseSession(SessionHandle session,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_CloseAllSessions closes all sessions with a token.
+      * @param slot_id the token's slot
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SlotIdInvalid
+      *     \li TokenNotPresent
+      * @return true on success, false otherwise
+      */
+      bool C_CloseAllSessions(SlotId slot_id,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetSessionInfo obtains information about the session.
+      * @param session the session's handle
+      * @param info_ptr receives session info
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SessionClosed
+      *     \li SessionHandleInvalid \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetSessionInfo(SessionHandle session,
+                            SessionInfo* info_ptr,
+                            ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetOperationState obtains the state of the cryptographic operation in a session.
+      * @param session session's handle
+      * @param operation_state_ptr gets state
+      * @param operation_state_len_ptr gets state length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li StateUnsaveable \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_GetOperationState(SessionHandle session,
+                               Byte* operation_state_ptr,
+                               Ulong* operation_state_len_ptr,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SetOperationState restores the state of the cryptographic operation in a session.
+      * @param session session's handle
+      * @param operation_state_ptr holds state
+      * @param operation_state_len holds state length
+      * @param encryption_key en/decryption key
+      * @param authentication_key sign/verify key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li KeyChanged \li KeyNeeded
+      *     \li KeyNotNeeded \li OK \li SavedStateInvalid
+      *     \li SessionClosed \li SessionHandleInvalid \li ArgumentsBad
+      * @return true on success, false otherwise
+      */
+      bool C_SetOperationState(SessionHandle session,
+                               Byte* operation_state_ptr,
+                               Ulong operation_state_len,
+                               ObjectHandle encryption_key,
+                               ObjectHandle authentication_key,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Login logs a user into a token.
+      * @param session the session's handle
+      * @param user_type the user type
+      * @param pin_ptr the user's PIN
+      * @param pin_len the length of the PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li PinIncorrect
+      *     \li PinLocked \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn
+      *     \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_Login(SessionHandle session,
+                   UserType user_type,
+                   Utf8Char* pin_ptr,
+                   Ulong pin_len,
+                   ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Login logs a user into a token.
+      * @param session the session's handle
+      * @param user_type the user type
+      * @param pin the user or security officer's PIN
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li PinIncorrect
+      *     \li PinLocked \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn
+      *     \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_Login(SessionHandle session,
+                   UserType user_type,
+                   const std::vector<uint8_t, TAlloc>& pin,
+                   ReturnValue* return_value = ThrowException) const
+         {
+         return C_Login(session, user_type,
+                        reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(pin.data())),
+                        static_cast<Ulong>(pin.size()),
+                        return_value);
+         }
+
+      /**
+      * C_Logout logs a user out from a token.
+      * @param session the session's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_Logout(SessionHandle session,
+                    ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Object management functions ******************************/
+
+      /**
+      * C_CreateObject creates a new object.
+      * @param session the session's handle
+      * @param attribute_template_ptr the object's template
+      * @param count attributes in template
+      * @param object_ptr gets new object's handle.
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+      *     \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li DomainParamsInvalid \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+      *     \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected
+      *     \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_CreateObject(SessionHandle session,
+                          Attribute* attribute_template_ptr,
+                          Ulong count,
+                          ObjectHandle* object_ptr,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_CopyObject copies an object, creating a new object for the copy.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param attribute_template_ptr template for new object
+      * @param count attributes in template
+      * @param new_object_ptr receives handle of copy
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+      *     \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li ObjectHandleInvalid \li OK \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+      *     \li TemplateInconsistent \li TokenWriteProtected \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_CopyObject(SessionHandle session,
+                        ObjectHandle object,
+                        Attribute* attribute_template_ptr,
+                        Ulong count,
+                        ObjectHandle* new_object_ptr,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DestroyObject destroys an object.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ActionProhibited \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li ObjectHandleInvalid
+      *     \li OK \li PinExpired \li SessionClosed
+      *     \li SessionHandleInvalid \li SessionReadOnly \li TokenWriteProtected
+      * @return true on success, false otherwise
+      */
+      bool C_DestroyObject(SessionHandle session,
+                           ObjectHandle object,
+                           ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetObjectSize gets the size of an object in bytes.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param size_ptr receives size of object
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li InformationSensitive
+      *     \li ObjectHandleInvalid \li OK \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_GetObjectSize(SessionHandle session,
+                           ObjectHandle object,
+                           Ulong* size_ptr,
+                           ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetAttributeValue obtains the value of one or more object attributes.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param attribute_template_ptr specifies attrs; gets vals
+      * @param count attributes in template
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid
+      *     \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li ObjectHandleInvalid
+      *     \li OK \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_GetAttributeValue(SessionHandle session,
+                               ObjectHandle object,
+                               Attribute* attribute_template_ptr,
+                               Ulong count,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GetAttributeValue obtains the value of one or more object attributes.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param attribute_values specifies attrs; gets vals
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid
+      *     \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li ObjectHandleInvalid
+      *     \li OK \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_GetAttributeValue(SessionHandle session,
+                               ObjectHandle object,
+                               std::map<AttributeType, std::vector<uint8_t, TAlloc>>& attribute_values,
+                               ReturnValue* return_value = ThrowException) const
+         {
+         std::vector<Attribute> getter_template;
+
+         for(const auto& entry : attribute_values)
+            {
+            getter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), nullptr, 0 });
+            }
+
+         bool success = C_GetAttributeValue(session,
+                                            object,
+                                            const_cast< Attribute* >(getter_template.data()),
+                                            static_cast<Ulong>(getter_template.size()),
+                                            return_value);
+
+         if(!success)
+            {
+            return success;
+            }
+
+         size_t i = 0;
+         for(auto& entry : attribute_values)
+            {
+            entry.second.clear();
+            entry.second.resize(getter_template.at(i).ulValueLen);
+            getter_template.at(i).pValue = const_cast< uint8_t* >(entry.second.data());
+            i++;
+            }
+
+         return C_GetAttributeValue(session, object,
+                                    const_cast< Attribute* >(getter_template.data()),
+                                    static_cast<Ulong>(getter_template.size()),
+                                    return_value);
+         }
+
+      /**
+      * C_SetAttributeValue modifies the value of one or more object attributes.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param attribute_template_ptr specifies attrs and values
+      * @param count attributes in template
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+      *     \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li ObjectHandleInvalid \li OK \li SessionClosed
+      *     \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent
+      *     \li TokenWriteProtected \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SetAttributeValue(SessionHandle session,
+                               ObjectHandle object,
+                               Attribute* attribute_template_ptr,
+                               Ulong count,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SetAttributeValue modifies the value of one or more object attributes.
+      * @param session the session's handle
+      * @param object the object's handle
+      * @param attribute_values specifies attrs and values
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+      *     \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li ObjectHandleInvalid \li OK \li SessionClosed
+      *     \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent
+      *     \li TokenWriteProtected \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_SetAttributeValue(SessionHandle session,
+                               ObjectHandle object,
+                               std::map<AttributeType, std::vector<uint8_t, TAlloc>>& attribute_values,
+                               ReturnValue* return_value = ThrowException) const
+         {
+         std::vector<Attribute> setter_template;
+
+         for(auto& entry : attribute_values)
+            {
+            setter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), entry.second.data(), static_cast<CK_ULONG>(entry.second.size()) });
+            }
+
+         return C_SetAttributeValue(session, object,
+                                    const_cast< Attribute* >(setter_template.data()),
+                                    static_cast<Ulong>(setter_template.size()),
+                                    return_value);
+         }
+
+      /**
+      * C_FindObjectsInit initializes a search for token and session objects that match a template.
+      * @param session the session's handle
+      * @param attribute_template_ptr attribute values to match
+      * @param count attrs in search template
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeTypeInvalid \li AttributeValueInvalid
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationActive
+      *     \li PinExpired \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_FindObjectsInit(SessionHandle session,
+                             Attribute* attribute_template_ptr,
+                             Ulong count,
+                             ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_FindObjects continues a search for token and session objects that match a template, obtaining additional object handles.
+      * @param session session's handle
+      * @param object_ptr gets obj. handles
+      * @param max_object_count max handles to get
+      * @param object_count_ptr actual # returned
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_FindObjects(SessionHandle session,
+                         ObjectHandle* object_ptr,
+                         Ulong max_object_count,
+                         Ulong* object_count_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_FindObjectsFinal finishes a search for token and session objects.
+      * @param session the session's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_FindObjectsFinal(SessionHandle session,
+                              ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Encryption functions ******************************/
+
+      /**
+      * C_EncryptInit initializes an encryption operation.
+      * @param session the session's handle
+      * @param mechanism_ptr the encryption mechanism
+      * @param key handle of encryption key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li KeyFunctionNotPermitted
+      *     \li KeyHandleInvalid \li KeySizeRange \li KeyTypeInconsistent
+      *     \li MechanismInvalid \li MechanismParamInvalid \li OK
+      *     \li OperationActive \li PinExpired \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_EncryptInit(SessionHandle session,
+                         Mechanism* mechanism_ptr,
+                         ObjectHandle key,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Encrypt encrypts single-part data.
+      * @param session session's handle
+      * @param data_ptr the plaintext data
+      * @param data_len size of plaintext data in bytes
+      * @param encrypted_data gets ciphertext
+      * @param encrypted_data_len_ptr gets c-text size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_Encrypt(SessionHandle session,
+                     Byte* data_ptr,
+                     Ulong data_len,
+                     Byte* encrypted_data,
+                     Ulong* encrypted_data_len_ptr,
+                     ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Encrypt encrypts single-part data.
+      * @param session session's handle
+      * @param plaintext_data the plaintext data
+      * @param encrypted_data gets ciphertext
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      template<typename TAllocA, typename TAllocB>
+      bool C_Encrypt(SessionHandle session,
+                     const std::vector<uint8_t, TAllocA>& plaintext_data,
+                     std::vector<uint8_t, TAllocB>& encrypted_data,
+                     ReturnValue* return_value = ThrowException) const
+         {
+         Ulong encrypted_size = 0;
+         if(!C_Encrypt(session,
+                       const_cast<Byte*>((plaintext_data.data())),
+                       static_cast<Ulong>(plaintext_data.size()),
+                       nullptr, &encrypted_size,
+                       return_value))
+            {
+            return false;
+            }
+
+         encrypted_data.resize(encrypted_size);
+         if (!C_Encrypt(session,
+                          const_cast<Byte*>(plaintext_data.data()),
+                          static_cast<Ulong>(plaintext_data.size()),
+                          encrypted_data.data(),
+                          &encrypted_size, return_value))
+            {
+            return false;
+            }
+         encrypted_data.resize(encrypted_size);
+         return true;
+         }
+
+      /**
+      * C_EncryptUpdate continues a multiple-part encryption operation.
+      * @param session session's handle
+      * @param part_ptr the plaintext data
+      * @param part_len plaintext data len
+      * @param encrypted_part_ptr gets ciphertext
+      * @param encrypted_part_len_ptr gets c-text size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_EncryptUpdate(SessionHandle session,
+                           Byte* part_ptr,
+                           Ulong part_len,
+                           Byte* encrypted_part_ptr,
+                           Ulong* encrypted_part_len_ptr,
+                           ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_EncryptFinal finishes a multiple-part encryption operation.
+      * @param session session handle
+      * @param last_encrypted_part_ptr last c-text
+      * @param last_encrypted_part_len_ptr gets last size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_EncryptFinal(SessionHandle session,
+                          Byte* last_encrypted_part_ptr,
+                          Ulong* last_encrypted_part_len_ptr,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Decryption functions ******************************/
+
+      /**
+      * C_DecryptInit initializes a decryption operation.
+      * @param session the session's handle
+      * @param mechanism_ptr the decryption mechanism
+      * @param key handle of decryption key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+      *     \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_DecryptInit(SessionHandle session,
+                         Mechanism* mechanism_ptr,
+                         ObjectHandle key,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Decrypt decrypts encrypted data in a single part.
+      * @param session session's handle
+      * @param encrypted_data_ptr ciphertext
+      * @param encrypted_data_len ciphertext length
+      * @param data_ptr gets plaintext
+      * @param data_len_ptr gets p-text size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_Decrypt(SessionHandle session,
+                     Byte* encrypted_data_ptr,
+                     Ulong encrypted_data_len,
+                     Byte* data_ptr,
+                     Ulong* data_len_ptr,
+                     ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Decrypt decrypts encrypted data in a single part.
+      * @param session session's handle
+      * @param encrypted_data ciphertext
+      * @param decrypted_data gets plaintext
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      template<typename TAllocA, typename TAllocB>
+      bool C_Decrypt(SessionHandle session,
+                     const std::vector<uint8_t, TAllocA>& encrypted_data,
+                     std::vector<uint8_t, TAllocB>& decrypted_data,
+                     ReturnValue* return_value = ThrowException) const
+         {
+         Ulong decrypted_size = 0;
+         if(!C_Decrypt(session,
+                       const_cast<Byte*>((encrypted_data.data())),
+                       static_cast<Ulong>(encrypted_data.size()),
+                       nullptr, &decrypted_size,
+                       return_value))
+            {
+            return false;
+            }
+
+         decrypted_data.resize(decrypted_size);
+         if(!C_Decrypt(session,
+                       const_cast<Byte*>(encrypted_data.data()),
+                       static_cast<Ulong>(encrypted_data.size()),
+                       decrypted_data.data(),
+                       &decrypted_size, return_value))
+            {
+            return false;
+            }
+         decrypted_data.resize(decrypted_size);
+         return true;
+         }
+
+      /**
+      * C_DecryptUpdate continues a multiple-part decryption operation.
+      * @param session session's handle
+      * @param encrypted_part_ptr encrypted data
+      * @param encrypted_part_len input length
+      * @param part_ptr gets plaintext
+      * @param part_len_ptr p-text size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_DecryptUpdate(SessionHandle session,
+                           Byte* encrypted_part_ptr,
+                           Ulong encrypted_part_len,
+                           Byte* part_ptr,
+                           Ulong* part_len_ptr,
+                           ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DecryptFinal finishes a multiple-part decryption operation.
+      * @param session the session's handle
+      * @param last_part_ptr gets plaintext
+      * @param last_part_len_ptr p-text size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_DecryptFinal(SessionHandle session,
+                          Byte* last_part_ptr,
+                          Ulong* last_part_len_ptr,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Message digesting functions ******************************/
+
+      /**
+      * C_DigestInit initializes a message-digesting operation.
+      * @param session the session's handle
+      * @param mechanism_ptr the digesting mechanism
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li MechanismInvalid \li MechanismParamInvalid \li OK
+      *     \li OperationActive \li PinExpired \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_DigestInit(SessionHandle session,
+                        Mechanism* mechanism_ptr,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Digest digests data in a single part.
+      * @param session the session's handle
+      * @param data_ptr data to be digested
+      * @param data_len bytes of data to digest
+      * @param digest_ptr gets the message digest
+      * @param digest_len_ptr gets digest length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_Digest(SessionHandle session,
+                    Byte* data_ptr,
+                    Ulong data_len,
+                    Byte* digest_ptr,
+                    Ulong* digest_len_ptr,
+                    ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DigestUpdate continues a multiple-part message-digesting operation.
+      * @param session the session's handle
+      * @param part_ptr data to be digested
+      * @param part_len bytes of data to be digested
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DigestUpdate(SessionHandle session,
+                          Byte* part_ptr,
+                          Ulong part_len,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DigestKey continues a multi-part message-digesting operation, by digesting the value of a secret key as part of the data already digested.
+      * @param session the session's handle
+      * @param key secret key to digest
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li KeyHandleInvalid
+      *     \li KeyIndigestible \li KeySizeRange \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DigestKey(SessionHandle session,
+                       ObjectHandle key,
+                       ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DigestFinal finishes a multiple-part message-digesting operation.
+      * @param session the session's handle
+      * @param digest_ptr gets the message digest
+      * @param digest_len_ptr gets uint8_t count of digest
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DigestFinal(SessionHandle session,
+                         Byte* digest_ptr,
+                         Ulong* digest_len_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Signing and MACing functions ******************************/
+
+      /**
+      * C_SignInit initializes a signature (private key encryption) operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param mechanism_ptr the signature mechanism
+      * @param key handle of signature key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+      *     \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SignInit(SessionHandle session,
+                      Mechanism* mechanism_ptr,
+                      ObjectHandle key,
+                      ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param data_ptr the data to sign
+      * @param data_len count of bytes to sign
+      * @param signature_ptr gets the signature
+      * @param signature_len_ptr gets signature length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected
+      * @return true on success, false otherwise
+      */
+      bool C_Sign(SessionHandle session,
+                  Byte* data_ptr,
+                  Ulong data_len,
+                  Byte* signature_ptr,
+                  Ulong* signature_len_ptr,
+                  ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param data the data to sign
+      * @param signature gets the signature
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected
+      * @return true on success, false otherwise
+      */
+      template<typename TAllocA, typename TAllocB>
+      bool C_Sign(SessionHandle session,
+                  const std::vector<uint8_t, TAllocA>& data,
+                  std::vector<uint8_t, TAllocB>& signature,
+                  ReturnValue* return_value = ThrowException) const
+         {
+         Ulong signature_size = 0;
+         if(!C_Sign(session,
+                    const_cast<Byte*>((data.data())),
+                    static_cast<Ulong>(data.size()),
+                    nullptr,
+                    &signature_size,
+                    return_value))
+            {
+            return false;
+            }
+
+         signature.resize(signature_size);
+         if (!C_Sign(session,
+                       const_cast<Byte*>(data.data()),
+                       static_cast<Ulong>(data.size()),
+                       signature.data(),
+                       &signature_size,
+                       return_value))
+            {
+            return false;
+            }
+         signature.resize(signature_size);
+         return true;
+         }
+
+      /**
+      * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param part_ptr the data to sign
+      * @param part_len count of bytes to sign
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SignUpdate(SessionHandle session,
+                        Byte* part_ptr,
+                        Ulong part_len,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param part the data to sign
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_SignUpdate(SessionHandle session,
+                        const std::vector<uint8_t, TAlloc>& part,
+                        ReturnValue* return_value = ThrowException) const
+         {
+         return C_SignUpdate(session,
+                             const_cast<Byte*>(part.data()),
+                             static_cast<Ulong>(part.size()),
+                             return_value);
+         }
+
+      /**
+      * C_SignFinal finishes a multiple-part signature operation, returning the signature.
+      * @param session the session's handle
+      * @param signature_ptr gets the signature
+      * @param signature_len_ptr gets signature length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li UserNotLoggedIn \li FunctionRejected
+      * @return true on success, false otherwise
+      */
+      bool C_SignFinal(SessionHandle session,
+                       Byte* signature_ptr,
+                       Ulong* signature_len_ptr,
+                       ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SignFinal finishes a multiple-part signature operation, returning the signature.
+      * @param session the session's handle
+      * @param signature gets the signature
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li UserNotLoggedIn \li FunctionRejected
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_SignFinal(SessionHandle session,
+                       std::vector<uint8_t, TAlloc>& signature,
+                       ReturnValue* return_value = ThrowException) const
+         {
+         Ulong signature_size = 0;
+         if(!C_SignFinal(session, nullptr, &signature_size, return_value))
+            {
+            return false;
+            }
+
+         signature.resize(signature_size);
+         if (!C_SignFinal(session, signature.data(), &signature_size, return_value))
+            {
+            return false;
+            }
+         signature.resize(signature_size);
+         return true;
+         }
+
+      /**
+      * C_SignRecoverInit initializes a signature operation, where the data can be recovered from the signature.
+      * @param session the session's handle
+      * @param mechanism_ptr the signature mechanism
+      * @param key handle of the signature key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+      *     \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SignRecoverInit(SessionHandle session,
+                             Mechanism* mechanism_ptr,
+                             ObjectHandle key,
+                             ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SignRecover signs data in a single operation, where the data can be recovered from the signature.
+      * @param session the session's handle
+      * @param data_ptr the data to sign
+      * @param data_len count of bytes to sign
+      * @param signature_ptr gets the signature
+      * @param signature_len_ptr gets signature length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SignRecover(SessionHandle session,
+                         Byte* data_ptr,
+                         Ulong data_len,
+                         Byte* signature_ptr,
+                         Ulong* signature_len_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Functions for verifying signatures and MACs ******************************/
+
+      /**
+      * C_VerifyInit initializes a verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature (e.g. DSA).
+      * @param session the session's handle
+      * @param mechanism_ptr the verification mechanism
+      * @param key verification key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+      *     \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_VerifyInit(SessionHandle session,
+                        Mechanism* mechanism_ptr,
+                        ObjectHandle key,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param data_ptr signed data
+      * @param data_len length of signed data
+      * @param signature_ptr signature
+      * @param signature_len signature length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li SignatureInvalid \li SignatureLenRange
+      * @return true on success, false otherwise
+      */
+      bool C_Verify(SessionHandle session,
+                    Byte* data_ptr,
+                    Ulong data_len,
+                    Byte* signature_ptr,
+                    Ulong signature_len,
+                    ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param data signed data
+      * @param signature signature
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li SignatureInvalid \li SignatureLenRange
+      * @return true on success, false otherwise
+      */
+      template<typename TAllocA, typename TAllocB>
+      bool C_Verify(SessionHandle session,
+                    const std::vector<uint8_t, TAllocA>& data,
+                    std::vector<uint8_t, TAllocB>& signature,
+                    ReturnValue* return_value = ThrowException) const
+         {
+         return C_Verify(session,
+                         const_cast<Byte*>(data.data()),
+                         static_cast<Ulong>(data.size()),
+                         signature.data(),
+                         static_cast<Ulong>(signature.size()),
+                         return_value);
+         }
+
+      /**
+      * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param part_ptr signed data
+      * @param part_len length of signed data
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_VerifyUpdate(SessionHandle session,
+                          Byte* part_ptr,
+                          Ulong part_len,
+                          ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+      * @param session the session's handle
+      * @param part signed data
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      template<typename TAlloc>
+      bool C_VerifyUpdate(SessionHandle session,
+                          std::vector<uint8_t, TAlloc> part,
+                          ReturnValue* return_value = ThrowException) const
+         {
+         return C_VerifyUpdate(session, part.data(), static_cast<Ulong>(part.size()), return_value);
+         }
+
+      /**
+      * C_VerifyFinal finishes a multiple-part verification operation, checking the signature.
+      * @param session the session's handle
+      * @param signature_ptr signature to verify
+      * @param signature_len signature length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid \li SignatureInvalid
+      *     \li SignatureLenRange
+      * @return true on success, false otherwise
+      */
+      bool C_VerifyFinal(SessionHandle session,
+                         Byte* signature_ptr,
+                         Ulong signature_len,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_VerifyRecoverInit initializes a signature verification operation, where the data is recovered from the signature.
+      * @param session the session's handle
+      * @param mechanism_ptr the verification mechanism
+      * @param key verification key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+      *     \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_VerifyRecoverInit(SessionHandle session,
+                               Mechanism* mechanism_ptr,
+                               ObjectHandle key,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_VerifyRecover verifies a signature in a single-part operation, where the data is recovered from the signature.
+      * @param session the session's handle
+      * @param signature_ptr signature to verify
+      * @param signature_len signature length
+      * @param data_ptr gets signed data
+      * @param data_len_ptr gets signed data len
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataInvalid \li DataLenRange \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid \li SignatureLenRange \li SignatureInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_VerifyRecover(SessionHandle session,
+                           Byte* signature_ptr,
+                           Ulong signature_len,
+                           Byte* data_ptr,
+                           Ulong* data_len_ptr,
+                           ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Dual-purpose cryptographic functions ******************************/
+
+      /**
+      * C_DigestEncryptUpdate continues a multiple-part digesting and encryption operation.
+      * @param session session's handle
+      * @param part_ptr the plaintext data
+      * @param part_len plaintext length
+      * @param encrypted_part_ptr gets ciphertext
+      * @param encrypted_part_len_ptr gets c-text length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DigestEncryptUpdate(SessionHandle session,
+                                 Byte* part_ptr,
+                                 Ulong part_len,
+                                 Byte* encrypted_part_ptr,
+                                 Ulong* encrypted_part_len_ptr,
+                                 ReturnValue* return_value = ThrowException) const ;
+
+      /**
+      * C_DecryptDigestUpdate continues a multiple-part decryption and digesting operation.
+      * @param session session's handle
+      * @param encrypted_part_ptr ciphertext
+      * @param encrypted_part_len ciphertext length
+      * @param part_ptr gets plaintext
+      * @param part_len_ptr gets plaintext len
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationNotInitialized \li SessionClosed
+      *     \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DecryptDigestUpdate(SessionHandle session,
+                                 Byte* encrypted_part_ptr,
+                                 Ulong encrypted_part_len,
+                                 Byte* part_ptr,
+                                 Ulong* part_len_ptr,
+                                 ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_SignEncryptUpdate continues a multiple-part signing and encryption operation.
+      * @param session session's handle
+      * @param part_ptr the plaintext data
+      * @param part_len plaintext length
+      * @param encrypted_part_ptr gets ciphertext
+      * @param encrypted_part_len_ptr gets c-text length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li OK
+      *     \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+      *     \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SignEncryptUpdate(SessionHandle session,
+                               Byte* part_ptr,
+                               Ulong part_len,
+                               Byte* encrypted_part_ptr,
+                               Ulong* encrypted_part_len_ptr,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DecryptVerifyUpdate continues a multiple-part decryption and verify operation.
+      * @param session session's handle
+      * @param encrypted_part_ptr ciphertext
+      * @param encrypted_part_len ciphertext length
+      * @param part_ptr gets plaintext
+      * @param part_len_ptr gets p-text length
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DataLenRange \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li EncryptedDataInvalid \li EncryptedDataLenRange
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li OK \li OperationNotInitialized
+      *     \li SessionClosed \li SessionHandleInvalid
+      * @return true on success, false otherwise
+      */
+      bool C_DecryptVerifyUpdate(SessionHandle session,
+                                 Byte* encrypted_part_ptr,
+                                 Ulong encrypted_part_len,
+                                 Byte* part_ptr,
+                                 Ulong* part_len_ptr,
+                                 ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Key management functions ******************************/
+
+      /**
+      * C_GenerateKey generates a secret key, creating a new key object.
+      * @param session the session's handle
+      * @param mechanism_ptr key generation mech.
+      * @param attribute_template_ptr template for new key
+      * @param count # of attrs in template
+      * @param key_ptr gets handle of new key
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+      *     \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li MechanismInvalid \li MechanismParamInvalid
+      *     \li OK \li OperationActive \li PinExpired
+      *     \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+      *     \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected
+      *     \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_GenerateKey(SessionHandle session,
+                         Mechanism* mechanism_ptr,
+                         Attribute* attribute_template_ptr,
+                         Ulong count,
+                         ObjectHandle* key_ptr,
+                         ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GenerateKeyPair generates a public-key/private-key pair, creating new key objects.
+      * @param session session handle
+      * @param mechanism_ptr key-gen mech.
+      * @param public_key_template_ptr template for pub. key
+      * @param public_key_attribute_count # pub. attrs.
+      * @param private_key_template_ptr template for priv. key
+      * @param private_key_attribute_count # priv.  attrs.
+      * @param public_key_ptr gets pub. key handle
+      * @param private_key_ptr gets priv. key handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+      *     \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li MechanismInvalid
+      *     \li MechanismParamInvalid \li OK \li OperationActive
+      *     \li PinExpired \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent
+      *     \li TokenWriteProtected \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_GenerateKeyPair(SessionHandle session,
+                             Mechanism* mechanism_ptr,
+                             Attribute* public_key_template_ptr,
+                             Ulong public_key_attribute_count,
+                             Attribute* private_key_template_ptr,
+                             Ulong private_key_attribute_count,
+                             ObjectHandle* public_key_ptr,
+                             ObjectHandle* private_key_ptr,
+                             ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_WrapKey wraps (i.e., encrypts) a key.
+      * @param session the session's handle
+      * @param mechanism_ptr the wrapping mechanism
+      * @param wrapping_key wrapping key
+      * @param key key to be wrapped
+      * @param wrapped_key_ptr gets wrapped key
+      * @param wrapped_key_len_ptr gets wrapped key size
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li FunctionCanceled \li FunctionFailed \li GeneralError
+      *     \li HostMemory \li KeyHandleInvalid \li KeyNotWrappable
+      *     \li KeySizeRange \li KeyUnextractable \li MechanismInvalid
+      *     \li MechanismParamInvalid \li OK \li OperationActive
+      *     \li PinExpired \li SessionClosed \li SessionHandleInvalid
+      *     \li UserNotLoggedIn \li WrappingKeyHandleInvalid \li WrappingKeySizeRange
+      *     \li WrappingKeyTypeInconsistent
+      * @return true on success, false otherwise
+      */
+      bool C_WrapKey(SessionHandle session,
+                     Mechanism* mechanism_ptr,
+                     ObjectHandle wrapping_key,
+                     ObjectHandle key,
+                     Byte* wrapped_key_ptr,
+                     Ulong* wrapped_key_len_ptr,
+                     ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object.
+      * @param session session's handle
+      * @param mechanism_ptr unwrapping mech.
+      * @param unwrapping_key unwrapping key
+      * @param wrapped_key_ptr the wrapped key
+      * @param wrapped_key_len wrapped key len
+      * @param attribute_template_ptr new key template
+      * @param attribute_count template length
+      * @param key_ptr gets new handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+      *     \li AttributeValueInvalid \li BufferTooSmall \li CryptokiNotInitialized
+      *     \li CurveNotSupported \li DeviceError \li DeviceMemory
+      *     \li DeviceRemoved \li DomainParamsInvalid \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li MechanismInvalid \li MechanismParamInvalid \li OK
+      *     \li OperationActive \li PinExpired \li SessionClosed
+      *     \li SessionHandleInvalid \li SessionReadOnly \li TemplateIncomplete
+      *     \li TemplateInconsistent \li TokenWriteProtected \li UnwrappingKeyHandleInvalid
+      *     \li UnwrappingKeySizeRange \li UnwrappingKeyTypeInconsistent \li UserNotLoggedIn
+      *     \li WrappedKeyInvalid \li WrappedKeyLenRange
+      * @return true on success, false otherwise
+      */
+      bool C_UnwrapKey(SessionHandle session,
+                       Mechanism* mechanism_ptr,
+                       ObjectHandle unwrapping_key,
+                       Byte* wrapped_key_ptr,
+                       Ulong wrapped_key_len,
+                       Attribute* attribute_template_ptr,
+                       Ulong attribute_count,
+                       ObjectHandle* key_ptr,
+                       ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_DeriveKey derives a key from a base key, creating a new key object.
+      * @param session session's handle
+      * @param mechanism_ptr key deriv. mech.
+      * @param base_key base key
+      * @param attribute_template_ptr new key template
+      * @param attribute_count template length
+      * @param key_ptr gets new handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+      *     \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+      *     \li DeviceError \li DeviceMemory \li DeviceRemoved
+      *     \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed
+      *     \li GeneralError \li HostMemory \li KeyHandleInvalid
+      *     \li KeySizeRange \li KeyTypeInconsistent \li MechanismInvalid
+      *     \li MechanismParamInvalid \li OK \li OperationActive
+      *     \li PinExpired \li SessionClosed \li SessionHandleInvalid
+      *     \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent
+      *     \li TokenWriteProtected \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_DeriveKey(SessionHandle session,
+                       Mechanism* mechanism_ptr,
+                       ObjectHandle base_key,
+                       Attribute* attribute_template_ptr,
+                       Ulong attribute_count,
+                       ObjectHandle* key_ptr,
+                       ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Random number generation functions ******************************/
+
+      /**
+      * C_SeedRandom mixes additional seed material into the token's random number generator.
+      * @param session the session's handle
+      * @param seed_ptr the seed material
+      * @param seed_len length of seed material
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationActive \li RandomSeedNotSupported
+      *     \li RandomNoRng \li SessionClosed \li SessionHandleInvalid
+      *     \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_SeedRandom(SessionHandle session,
+                        Byte* seed_ptr,
+                        Ulong seed_len,
+                        ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_GenerateRandom generates random data.
+      * @param session the session's handle
+      * @param random_data_ptr receives the random data
+      * @param random_len # of bytes to generate
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+      *     \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+      *     \li FunctionFailed \li GeneralError \li HostMemory
+      *     \li OK \li OperationActive \li RandomNoRng
+      *     \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+      * @return true on success, false otherwise
+      */
+      bool C_GenerateRandom(SessionHandle session,
+                            Byte* random_data_ptr,
+                            Ulong random_len,
+                            ReturnValue* return_value = ThrowException) const;
+
+      /****************************** Parallel function management functions ******************************/
+
+      /**
+      * C_GetFunctionStatus is a legacy function; it obtains an updated status of a function running in parallel with an application.
+      * @param session the session's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel
+      *     \li GeneralError \li HostMemory \li SessionHandleInvalid
+      *     \li SessionClosed
+      * @return true on success, false otherwise
+      */
+      bool C_GetFunctionStatus(SessionHandle session,
+                               ReturnValue* return_value = ThrowException) const;
+
+      /**
+      * C_CancelFunction is a legacy function; it cancels a function running in parallel.
+      * @param session the session's handle
+      * @param return_value default value (`ThrowException`): throw exception on error.
+      * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+      * At least the following PKCS#11 return values may be returned:
+      *     \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel
+      *     \li GeneralError \li HostMemory \li SessionHandleInvalid
+      *     \li SessionClosed
+      * @return true on success, false otherwise
+      */
+      bool C_CancelFunction(SessionHandle session,
+                            ReturnValue* return_value = ThrowException) const;
+
+   private:
+      const FunctionListPtr m_func_list_ptr;
+   };
+
+class BOTAN_PUBLIC_API(2,0) PKCS11_Error : public Exception
+   {
+   public:
+      explicit PKCS11_Error(const std::string& what) :
+         Exception("PKCS11 error", what)
+         {
+         }
+
+      ErrorType error_type() const noexcept override { return ErrorType::Pkcs11Error; }
+   };
+
+class BOTAN_PUBLIC_API(2,0) PKCS11_ReturnError final : public PKCS11_Error
+   {
+   public:
+      explicit PKCS11_ReturnError(ReturnValue return_val) :
+         PKCS11_Error(std::to_string(static_cast< uint32_t >(return_val))),
+         m_return_val(return_val)
+         {}
+
+      inline ReturnValue get_return_value() const
+         {
+         return m_return_val;
+         }
+
+      int error_code() const noexcept override
+         {
+         return static_cast<int>(m_return_val);
+         }
+
+   private:
+      const ReturnValue m_return_val;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp
@@ -0,0 +1,136 @@
+/*
+* PKCS#11 ECC
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecc_key.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+#include <botan/workfactor.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+namespace PKCS11 {
+namespace {
+/// Converts a DER-encoded ANSI X9.62 ECPoint to PointGFp
+PointGFp decode_public_point(const secure_vector<uint8_t>& ec_point_data, const EC_Group& group)
+   {
+   secure_vector<uint8_t> ec_point;
+   BER_Decoder(ec_point_data).decode(ec_point, OCTET_STRING);
+   return group.OS2ECP(ec_point);
+   }
+}
+
+EC_PublicKeyGenerationProperties::EC_PublicKeyGenerationProperties(const std::vector<uint8_t>& ec_params)
+   : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params)
+   {
+   add_binary(AttributeType::EcParams, m_ec_params);
+   }
+
+EC_PublicKeyImportProperties::EC_PublicKeyImportProperties(const std::vector<uint8_t>& ec_params,
+      const std::vector<uint8_t>& ec_point)
+   : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_ec_point(ec_point)
+   {
+   add_binary(AttributeType::EcParams, m_ec_params);
+   add_binary(AttributeType::EcPoint, m_ec_point);
+   }
+
+PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, ObjectHandle handle)
+   : Object(session, handle)
+   {
+   secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
+   m_domain_params = EC_Group(unlock(ec_parameters));
+   m_public_key = decode_public_point(get_attribute_value(AttributeType::EcPoint), m_domain_params);
+   m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+   }
+
+PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+   : Object(session, props)
+   {
+   m_domain_params = EC_Group(props.ec_params());
+
+   secure_vector<uint8_t> ec_point;
+   BER_Decoder(props.ec_point()).decode(ec_point, OCTET_STRING);
+   m_public_key = m_domain_params.OS2ECP(ec_point);
+   m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+   }
+
+EC_PrivateKeyImportProperties::EC_PrivateKeyImportProperties(const std::vector<uint8_t>& ec_params, const BigInt& value)
+   : PrivateKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_value(value)
+   {
+   add_binary(AttributeType::EcParams, m_ec_params);
+   add_binary(AttributeType::Value, BigInt::encode(m_value));
+   }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle)
+   : Object(session, handle), m_domain_params(), m_public_key()
+   {
+   secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
+   m_domain_params = EC_Group(unlock(ec_parameters));
+   }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+   : Object(session, props)
+   {
+   m_domain_params = EC_Group(props.ec_params());
+   }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+      const EC_PrivateKeyGenerationProperties& props)
+   : Object(session)
+   {
+   m_domain_params = EC_Group(ec_params);
+
+   EC_PublicKeyGenerationProperties pub_key_props(ec_params);
+   pub_key_props.set_verify(true);
+   pub_key_props.set_private(false);
+   pub_key_props.set_token(false);    // don't create a persistent public key object
+
+   ObjectHandle pub_key_handle = CK_INVALID_HANDLE;
+   ObjectHandle priv_key_handle = CK_INVALID_HANDLE;
+   Mechanism mechanism = { CKM_EC_KEY_PAIR_GEN, nullptr, 0 };
+   session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+                                       pub_key_props.data(), static_cast<Ulong>(pub_key_props.count()),
+                                       props.data(), static_cast<Ulong>(props.count()),
+                                       &pub_key_handle, &priv_key_handle);
+
+   this->reset_handle(priv_key_handle);
+
+   Object public_key(session, pub_key_handle);
+   m_public_key = decode_public_point(public_key.get_attribute_value(AttributeType::EcPoint), m_domain_params);
+   }
+
+size_t PKCS11_EC_PrivateKey::key_length() const
+   {
+   return m_domain_params.get_order().bits();
+   }
+
+std::vector<uint8_t> PKCS11_EC_PrivateKey::public_key_bits() const
+   {
+   return public_point().encode(PointGFp::COMPRESSED);
+   }
+
+size_t PKCS11_EC_PrivateKey::estimated_strength() const
+   {
+   return ecp_work_factor(key_length());
+   }
+
+bool PKCS11_EC_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   return m_public_key.on_the_curve();
+   }
+
+AlgorithmIdentifier PKCS11_EC_PrivateKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), domain().DER_encode(EC_DOMPAR_ENC_EXPLICIT));
+   }
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h b/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h
@@ -0,0 +1,223 @@
+/*
+* PKCS#11 ECC
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECC_H_
+#define BOTAN_P11_ECC_H_
+
+#include <botan/p11_object.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+#include <botan/ecc_key.h>
+#include <botan/ec_group.h>
+#include <botan/alg_id.h>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Session;
+
+/// Properties for generating a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) EC_PublicKeyGenerationProperties final : public PublicKeyProperties
+   {
+   public:
+      /// @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      EC_PublicKeyGenerationProperties(const std::vector<uint8_t>& ec_params);
+
+      /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+      inline const std::vector<uint8_t>& ec_params() const
+         {
+         return m_ec_params;
+         }
+
+   private:
+      const std::vector<uint8_t> m_ec_params;
+   };
+
+/// Properties for importing a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) EC_PublicKeyImportProperties final : public PublicKeyProperties
+   {
+   public:
+      /**
+      * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      * @param ec_point DER-encoding of ANSI X9.62 ECPoint value Q
+      */
+      EC_PublicKeyImportProperties(const std::vector<uint8_t>& ec_params, const std::vector<uint8_t>& ec_point);
+
+      /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+      inline const std::vector<uint8_t>& ec_params() const
+         {
+         return m_ec_params;
+         }
+
+      /// @return the DER-encoding of the ec public point according to ANSI X9.62
+      inline const std::vector<uint8_t>& ec_point() const
+         {
+         return m_ec_point;
+         }
+
+   private:
+      const std::vector<uint8_t> m_ec_params;
+      const std::vector<uint8_t> m_ec_point;
+   };
+
+/// Represents a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_EC_PublicKey : public virtual EC_PublicKey,
+   public Object
+   {
+   public:
+      static const ObjectClass Class = ObjectClass::PublicKey;
+
+      /**
+      * Creates a PKCS11_EC_PublicKey object from an existing PKCS#11 EC public key
+      * @param session the session to use
+      * @param handle the handle of the ecc public key
+      */
+      PKCS11_EC_PublicKey(Session& session, ObjectHandle handle);
+
+      /**
+      * Imports an EC public key
+      * @param session the session to use
+      * @param props the attributes of the public key
+      */
+      PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props);
+   };
+
+/// Properties for generating a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKeyGenerationProperties final : public PrivateKeyProperties
+   {
+   public:
+      EC_PrivateKeyGenerationProperties()
+         : PrivateKeyProperties(KeyType::Ec)
+         {}
+   };
+
+/// Properties for importing a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKeyImportProperties final : public PrivateKeyProperties
+   {
+   public:
+      /**
+      * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      * @param value ANSI X9.62 private value d
+      */
+      EC_PrivateKeyImportProperties(const std::vector<uint8_t>& ec_params, const BigInt& value);
+
+      /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+      inline const std::vector<uint8_t>& ec_params() const
+         {
+         return m_ec_params;
+         }
+
+      /// @return the value of the ec private key
+      inline const BigInt& value() const
+         {
+         return m_value;
+         }
+
+   private:
+      const std::vector<uint8_t> m_ec_params;
+      const BigInt m_value;
+   };
+
+// note: don't inherit from PKCS11_EC_PublicKey: a private key object IS NOT A public key object on a smartcard (-> two different objects)
+// note: don't inherit from EC_PublicKey: the public key can not be extracted from a PKCS11-EC-PrivateKey (its only attributes are CKA_EC_PARAMS and CKA_VALUE)
+/// Represents a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_EC_PrivateKey : public virtual Private_Key,
+   public Object
+   {
+   public:
+      static const ObjectClass Class = ObjectClass::PrivateKey;
+
+      /**
+      * Creates a PKCS11_EC_PrivateKey object from an existing PKCS#11 EC private key
+      * @param session the session to use
+      * @param handle the handle of the EC private key
+      */
+      PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle);
+
+      /**
+      * Imports an EC private key
+      * @param session the session to use
+      * @param props the attributes of the private key
+      */
+      PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props);
+
+      /**
+      * Generates a PKCS#11 EC private key
+      * @param session the session to use
+      * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      * @param props the attributes of the private key
+      * @note no persistent public key object will be created
+      */
+      PKCS11_EC_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+                           const EC_PrivateKeyGenerationProperties& props);
+
+      /// @returns the domain of the EC private key
+      inline const EC_Group& domain() const
+         {
+         return m_domain_params;
+         }
+
+      /**
+      * Sets the associated public point of this private key
+      * @param point the public point
+      * @param point_encoding encoding of the point (default DER-encoded)
+      */
+      void set_public_point(const PointGFp& point, PublicPointEncoding point_encoding = PublicPointEncoding::Der)
+         {
+         m_public_key = point;
+         m_point_encoding = point_encoding;
+         }
+
+      /**
+      * Gets the public_point
+      * @note the public key must be set using `set_public_point`
+      *       because it is not possible to infer the public key from a PKCS#11 EC private key
+      * @return the public point of the private key
+      * @throws Exception if the public point was not set using set_public_point()
+      */
+      const PointGFp& public_point() const
+         {
+         if(m_public_key.is_zero())
+            {
+            throw Invalid_State("Public point not set. Inferring the public key from a PKCS#11 ec private key is not possible.");
+            }
+         return m_public_key;
+         }
+
+      /// @return the encoding format for the public point when it is passed to cryptoki functions as an argument
+      PublicPointEncoding point_encoding() const
+         {
+         return m_point_encoding;
+         }
+
+      // Private_Key methods
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      std::size_t key_length() const override;
+
+      std::size_t estimated_strength() const override;
+
+      bool check_key(RandomNumberGenerator&, bool) const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+   private:
+      EC_Group m_domain_params;
+      PointGFp m_public_key;
+      PublicPointEncoding m_point_encoding = PublicPointEncoding::Der;
+   };
+}
+
+}
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp
@@ -0,0 +1,125 @@
+/*
+* PKCS#11 ECDH
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecdh.h>
+
+#if defined(BOTAN_HAS_ECDH)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/der_enc.h>
+#include <botan/pk_ops.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+ECDH_PublicKey PKCS11_ECDH_PublicKey::export_key() const
+   {
+   return ECDH_PublicKey(domain(), public_point());
+   }
+
+ECDH_PrivateKey PKCS11_ECDH_PrivateKey::export_key() const
+   {
+   auto priv_key = get_attribute_value(AttributeType::Value);
+
+   Null_RNG rng;
+   return ECDH_PrivateKey(rng, domain(), BigInt::decode(priv_key));
+   }
+
+secure_vector<uint8_t> PKCS11_ECDH_PrivateKey::private_key_bits() const
+   {
+   return export_key().private_key_bits();
+   }
+
+namespace {
+class PKCS11_ECDH_KA_Operation final : public PK_Ops::Key_Agreement
+   {
+   public:
+      PKCS11_ECDH_KA_Operation(const PKCS11_EC_PrivateKey& key, const std::string& params)
+         : PK_Ops::Key_Agreement(), m_key(key), m_mechanism(MechanismWrapper::create_ecdh_mechanism(params))
+         {}
+
+      size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
+
+      /// The encoding in V2.20 was not specified and resulted in different implementations choosing different encodings.
+      /// Applications relying only on a V2.20 encoding (e.g. the DER variant) other than the one specified now (raw) may not work with all V2.30 compliant tokens.
+      secure_vector<uint8_t> agree(size_t key_len, const uint8_t other_key[], size_t other_key_len, const uint8_t salt[],
+                                size_t salt_len) override
+         {
+         std::vector<uint8_t> der_encoded_other_key;
+         if(m_key.point_encoding() == PublicPointEncoding::Der)
+            {
+            DER_Encoder(der_encoded_other_key).encode(other_key, other_key_len, OCTET_STRING);
+            m_mechanism.set_ecdh_other_key(der_encoded_other_key.data(), der_encoded_other_key.size());
+            }
+         else
+            {
+            m_mechanism.set_ecdh_other_key(other_key, other_key_len);
+            }
+
+         if(salt != nullptr && salt_len > 0)
+            {
+            m_mechanism.set_ecdh_salt(salt, salt_len);
+            }
+
+         ObjectHandle secret_handle = 0;
+         AttributeContainer attributes;
+         attributes.add_bool(AttributeType::Sensitive, false);
+         attributes.add_bool(AttributeType::Extractable, true);
+         attributes.add_numeric(AttributeType::Class, static_cast< CK_OBJECT_CLASS >(ObjectClass::SecretKey));
+         attributes.add_numeric(AttributeType::KeyType, static_cast< CK_KEY_TYPE >(KeyType::GenericSecret));
+         attributes.add_numeric(AttributeType::ValueLen, static_cast< CK_ULONG >(key_len));
+         m_key.module()->C_DeriveKey(m_key.session().handle(), m_mechanism.data(), m_key.handle(), attributes.data(),
+                                     static_cast<Ulong>(attributes.count()), &secret_handle);
+
+         Object secret_object(m_key.session(), secret_handle);
+         secure_vector<uint8_t> secret = secret_object.get_attribute_value(AttributeType::Value);
+         if(secret.size() < key_len)
+            {
+            throw PKCS11_Error("ECDH key derivation secret length is too short");
+            }
+         secret.resize(key_len);
+         return secret;
+         }
+
+   private:
+      const PKCS11_EC_PrivateKey& m_key;
+      MechanismWrapper m_mechanism;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+PKCS11_ECDH_PrivateKey::create_key_agreement_op(RandomNumberGenerator&,
+                                                const std::string& params,
+                                                const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Key_Agreement>(new PKCS11_ECDH_KA_Operation(*this, params));
+   }
+
+PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+      const EC_PrivateKeyGenerationProperties& priv_props)
+   {
+   ObjectHandle pub_key_handle = 0;
+   ObjectHandle priv_key_handle = 0;
+
+   Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::EcKeyPairGen), nullptr, 0 };
+
+   session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+                                       pub_props.data(), static_cast<Ulong>(pub_props.count()),
+                                       priv_props.data(), static_cast<Ulong>(priv_props.count()),
+                                       &pub_key_handle, &priv_key_handle);
+
+   return std::make_pair(PKCS11_ECDH_PublicKey(session, pub_key_handle), PKCS11_ECDH_PrivateKey(session, priv_key_handle));
+   }
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h b/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h
@@ -0,0 +1,127 @@
+/*
+* PKCS#11 ECDH
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECDH_H_
+#define BOTAN_P11_ECDH_H_
+
+#include <botan/p11.h>
+
+#if defined(BOTAN_HAS_ECDH)
+
+#include <botan/p11_ecc_key.h>
+#include <botan/ecdh.h>
+
+#include <string>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+class Session;
+
+/// Represents a PKCS#11 ECDH public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PublicKey : public PKCS11_EC_PublicKey
+   {
+   public:
+      /**
+      * Create a PKCS11_ECDH_PublicKey object from an existing PKCS#11 ECDH public key
+      * @param session the session to use
+      * @param handle the handle of the ECDH public key
+      */
+      PKCS11_ECDH_PublicKey(Session& session, ObjectHandle handle)
+         : EC_PublicKey(), PKCS11_EC_PublicKey(session, handle)
+         {}
+
+      /**
+      * Imports a ECDH public key
+      * @param session the session to use
+      * @param props the attributes of the public key
+      */
+      PKCS11_ECDH_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+         : EC_PublicKey(), PKCS11_EC_PublicKey(session, props)
+         {}
+
+      inline std::string algo_name() const override
+         {
+         return "ECDH";
+         }
+
+      /// @return the exported ECDH public key
+      ECDH_PublicKey export_key() const;
+   };
+
+/// Represents a PKCS#11 ECDH private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PrivateKey final : public virtual PKCS11_EC_PrivateKey, public virtual PK_Key_Agreement_Key
+   {
+   public:
+      /**
+      * Creates a PKCS11_ECDH_PrivateKey object from an existing PKCS#11 ECDH private key
+      * @param session the session to use
+      * @param handle the handle of the ECDH private key
+      */
+      PKCS11_ECDH_PrivateKey(Session& session, ObjectHandle handle)
+         : PKCS11_EC_PrivateKey(session, handle)
+         {}
+
+      /**
+      * Imports an ECDH private key
+      * @param session the session to use
+      * @param props the attributes of the private key
+      */
+      PKCS11_ECDH_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+         : PKCS11_EC_PrivateKey(session, props)
+         {}
+
+      /**
+      * Generates a PKCS#11 ECDH private key
+      * @param session the session to use
+      * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      * @param props the attributes of the private key
+      * @note no persistent public key object will be created
+      */
+      PKCS11_ECDH_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+                             const EC_PrivateKeyGenerationProperties& props)
+         : PKCS11_EC_PrivateKey(session, ec_params, props)
+         {}
+
+      inline std::string algo_name() const override
+         {
+         return "ECDH";
+         }
+
+      inline std::vector<uint8_t> public_value() const override
+         {
+         return public_point().encode(PointGFp::UNCOMPRESSED);
+         }
+
+      /// @return the exported ECDH private key
+      ECDH_PrivateKey export_key() const;
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const override;
+   };
+
+using PKCS11_ECDH_KeyPair = std::pair<PKCS11_ECDH_PublicKey, PKCS11_ECDH_PrivateKey>;
+
+/**
+* PKCS#11 ECDH key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props the properties of the public key
+* @param priv_props the properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+      const EC_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+
+#endif
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp
@@ -0,0 +1,213 @@
+/*
+* PKCS#11 ECDSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecdsa.h>
+
+#if defined(BOTAN_HAS_ECDSA)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/pk_ops.h>
+#include <botan/keypair.h>
+#include <botan/rng.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+ECDSA_PublicKey PKCS11_ECDSA_PublicKey::export_key() const
+   {
+   return ECDSA_PublicKey(domain(), public_point());
+   }
+
+bool PKCS11_ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+   {
+   if(!public_point().on_the_curve())
+      {
+      return false;
+      }
+
+   if(!strong)
+      {
+      return true;
+      }
+
+   ECDSA_PublicKey pubkey(domain(), public_point());
+   return KeyPair::signature_consistency_check(rng, *this, pubkey, "EMSA1(SHA-256)");
+   }
+
+ECDSA_PrivateKey PKCS11_ECDSA_PrivateKey::export_key() const
+   {
+   auto priv_key = get_attribute_value(AttributeType::Value);
+
+   Null_RNG rng;
+   return ECDSA_PrivateKey(rng, domain(), BigInt::decode(priv_key));
+   }
+
+secure_vector<uint8_t> PKCS11_ECDSA_PrivateKey::private_key_bits() const
+   {
+   return export_key().private_key_bits();
+   }
+
+namespace {
+
+class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature
+   {
+   public:
+      PKCS11_ECDSA_Signature_Operation(const PKCS11_EC_PrivateKey& key, const std::string& emsa)
+         : PK_Ops::Signature(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa))
+         {}
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(!m_initialized)
+            {
+            // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+            m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+            m_initialized = true;
+            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            return;
+            }
+
+         if(!m_first_message.empty())
+            {
+            // second call to update: start multiple-part operation
+            m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message);
+            m_first_message.clear();
+            }
+
+         m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast<Byte*>(msg), static_cast<Ulong>(msg_len));
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+         {
+         secure_vector<uint8_t> signature;
+         if(!m_first_message.empty())
+            {
+            // single call to update: perform single-part operation
+            m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
+            m_first_message.clear();
+            }
+         else
+            {
+            // multiple calls to update (or none): finish multiple-part operation
+            m_key.module()->C_SignFinal(m_key.session().handle(), signature);
+            }
+         m_initialized = false;
+         return signature;
+         }
+
+      size_t signature_length() const override { return 2*m_order.bytes(); }
+
+   private:
+      const PKCS11_EC_PrivateKey& m_key;
+      const BigInt& m_order;
+      MechanismWrapper m_mechanism;
+      secure_vector<uint8_t> m_first_message;
+      bool m_initialized = false;
+   };
+
+
+class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification
+   {
+   public:
+      PKCS11_ECDSA_Verification_Operation(const PKCS11_EC_PublicKey& key, const std::string& emsa)
+         : PK_Ops::Verification(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa))
+         {}
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(!m_initialized)
+            {
+            // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+            m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+            m_initialized = true;
+            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            return;
+            }
+
+         if(!m_first_message.empty())
+            {
+            // second call to update: start multiple-part operation
+            m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message);
+            m_first_message.clear();
+            }
+
+         m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast<Byte*>(msg), static_cast<Ulong>(msg_len));
+         }
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+         {
+         ReturnValue return_value = ReturnValue::SignatureInvalid;
+         if(!m_first_message.empty())
+            {
+            // single call to update: perform single-part operation
+            m_key.module()->C_Verify(m_key.session().handle(),
+                                     m_first_message.data(), static_cast<Ulong>(m_first_message.size()),
+                                     const_cast<Byte*>(sig), static_cast<Ulong>(sig_len),
+                                     &return_value);
+            m_first_message.clear();
+            }
+         else
+            {
+            // multiple calls to update (or none): finish multiple-part operation
+            m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast<Byte*>(sig), static_cast<Ulong>(sig_len), &return_value);
+            }
+         m_initialized = false;
+         if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid)
+            {
+            throw PKCS11_ReturnError(return_value);
+            }
+         return return_value == ReturnValue::OK;
+         }
+
+   private:
+      const PKCS11_EC_PublicKey& m_key;
+      const BigInt& m_order;
+      MechanismWrapper m_mechanism;
+      secure_vector<uint8_t> m_first_message;
+      bool m_initialized = false;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+PKCS11_ECDSA_PublicKey::create_verification_op(const std::string& params,
+                                               const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Verification>(new PKCS11_ECDSA_Verification_Operation(*this, params));
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+PKCS11_ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                             const std::string& params,
+                                             const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Signature>(new PKCS11_ECDSA_Signature_Operation(*this, params));
+   }
+
+PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+      const EC_PrivateKeyGenerationProperties& priv_props)
+   {
+   ObjectHandle pub_key_handle = 0;
+   ObjectHandle priv_key_handle = 0;
+
+   Mechanism mechanism = { static_cast<CK_MECHANISM_TYPE>(MechanismType::EcKeyPairGen), nullptr, 0 };
+
+   session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+                                       pub_props.data(), static_cast<Ulong>(pub_props.count()),
+                                       priv_props.data(), static_cast<Ulong>(priv_props.count()),
+                                       &pub_key_handle, &priv_key_handle);
+
+   return std::make_pair(PKCS11_ECDSA_PublicKey(session, pub_key_handle), PKCS11_ECDSA_PrivateKey(session,
+                         priv_key_handle));
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h b/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h
@@ -0,0 +1,133 @@
+/*
+* PKCS#11 ECDSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECDSA_H_
+#define BOTAN_P11_ECDSA_H_
+
+#include <botan/p11.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECDSA)
+
+#include <botan/p11_ecc_key.h>
+#include <botan/ecdsa.h>
+
+#include <string>
+
+namespace Botan {
+namespace PKCS11 {
+class Session;
+
+/// Represents a PKCS#11 ECDSA public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_PublicKey final : public PKCS11_EC_PublicKey, public virtual ECDSA_PublicKey
+   {
+   public:
+      /**
+      * Creates a PKCS11_ECDSA_PublicKey object from an existing PKCS#11 ECDSA public key
+      * @param session the session to use
+      * @param handle the handle of the ECDSA public key
+      */
+      PKCS11_ECDSA_PublicKey(Session& session, ObjectHandle handle)
+         : EC_PublicKey(), PKCS11_EC_PublicKey(session, handle)
+         {}
+
+      /**
+      * Imports an ECDSA public key
+      * @param session the session to use
+      * @param props the attributes of the public key
+      */
+      PKCS11_ECDSA_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+         : EC_PublicKey(), PKCS11_EC_PublicKey(session, props)
+         {}
+
+      inline std::string algo_name() const override
+         {
+         return "ECDSA";
+         }
+
+      /// @return the exported ECDSA public key
+      ECDSA_PublicKey export_key() const;
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   };
+
+/// Represents a PKCS#11 ECDSA private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_PrivateKey
+   {
+   public:
+      /**
+      * Creates a PKCS11_ECDSA_PrivateKey object from an existing PKCS#11 ECDSA private key
+      * @param session the session to use
+      * @param handle the handle of the ECDSA private key
+      */
+      PKCS11_ECDSA_PrivateKey(Session& session, ObjectHandle handle)
+         : PKCS11_EC_PrivateKey(session, handle)
+         {}
+
+      /**
+      * Imports a ECDSA private key
+      * @param session the session to use
+      * @param props the attributes of the private key
+      */
+      PKCS11_ECDSA_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+         : PKCS11_EC_PrivateKey(session, props)
+         {}
+
+      /**
+      * Generates a PKCS#11 ECDSA private key
+      * @param session the session to use
+      * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+      * @param props the attributes of the private key
+      * @note no persistent public key object will be created
+      */
+      PKCS11_ECDSA_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+                              const EC_PrivateKeyGenerationProperties& props)
+         : PKCS11_EC_PrivateKey(session, ec_params, props)
+         {}
+
+      inline std::string algo_name() const override
+         {
+         return "ECDSA";
+         }
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      /// @return the exported ECDSA private key
+      ECDSA_PrivateKey export_key() const;
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator&, bool) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+using PKCS11_ECDSA_KeyPair = std::pair<PKCS11_ECDSA_PublicKey, PKCS11_ECDSA_PrivateKey>;
+
+/**
+* ECDSA key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props the properties of the public key
+* @param priv_props the properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session,
+      const EC_PublicKeyGenerationProperties& pub_props, const EC_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+
+#endif
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp
@@ -0,0 +1,278 @@
+/*
+* PKCS#11 Mechanism
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <botan/emsa.h>
+
+#include <tuple>
+
+namespace Botan {
+namespace PKCS11 {
+
+namespace {
+using PSS_Params = std::tuple<size_t, MechanismType, MGF>;
+
+// maps a PSS mechanism type to the number of bytes used for the salt, the mechanism type of the underlying hash algorithm and the MGF
+static const std::map<MechanismType, PSS_Params> PssOptions =
+   {
+      { MechanismType::RsaPkcsPss, PSS_Params(0, MechanismType::Sha1, MGF::Mgf1Sha1) },
+      { MechanismType::Sha1RsaPkcsPss, PSS_Params(20, MechanismType::Sha1, MGF::Mgf1Sha1) },
+      { MechanismType::Sha224RsaPkcsPss, PSS_Params(28, MechanismType::Sha224, MGF::Mgf1Sha224) },
+      { MechanismType::Sha256RsaPkcsPss, PSS_Params(32, MechanismType::Sha256, MGF::Mgf1Sha256) },
+      { MechanismType::Sha384RsaPkcsPss, PSS_Params(48, MechanismType::Sha384, MGF::Mgf1Sha384) },
+      { MechanismType::Sha512RsaPkcsPss, PSS_Params(64, MechanismType::Sha512, MGF::Mgf1Sha512) }
+   };
+
+struct MechanismData
+   {
+   explicit MechanismData(MechanismType _type)
+      : type(_type)
+      {}
+
+   MechanismData(MechanismData const&) = default;
+   MechanismData& operator=(MechanismData const&) = default;
+   virtual ~MechanismData() = default;
+
+   // the mechanism to perform
+   MechanismType type;
+   };
+
+struct RSA_SignMechanism final : public MechanismData
+   {
+   explicit RSA_SignMechanism(MechanismType _type)
+      : MechanismData(_type), hash(static_cast<MechanismType>(0)), mgf(static_cast<MGF>(0)), salt_size(0)
+      {
+      auto pss_option = PssOptions.find(type);
+      if(pss_option != PssOptions.end())
+         {
+         hash = std::get<1>(pss_option->second);
+         mgf = std::get<2>(pss_option->second);
+         salt_size = std::get<0>(pss_option->second);
+         }
+      }
+
+   // hash algorithm used in the PSS encoding; if the signature mechanism does not include message hashing,
+   // then this value must be the mechanism used by the application to generate the message hash;
+   // if the signature mechanism includes hashing, then this value must match the hash algorithm indicated by the signature mechanism
+   MechanismType hash;
+
+   // mask generation function to use on the encoded block
+   MGF mgf;
+
+   // length, in bytes, of the salt value used in the PSS encoding; typical values are the length of the message hash and zero
+   size_t salt_size;
+   };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_sign_mechanism`
+static std::map<std::string, RSA_SignMechanism> SignMechanisms =
+   {
+      { "Raw", RSA_SignMechanism(MechanismType::RsaX509) },
+
+      { "EMSA2(Raw)", RSA_SignMechanism(MechanismType::RsaX931) },
+      { "EMSA2(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaX931) },
+
+      // RSASSA PKCS#1 v1.5
+      { "EMSA3(Raw)", RSA_SignMechanism(MechanismType::RsaPkcs) },
+      { "EMSA3(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs) },
+      { "EMSA3(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs) },
+      { "EMSA3(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs) },
+      { "EMSA3(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs) },
+      { "EMSA3(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs) },
+
+      { "EMSA_PKCS1(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs) },
+      { "EMSA_PKCS1(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs) },
+      { "EMSA_PKCS1(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs) },
+      { "EMSA_PKCS1(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs) },
+      { "EMSA_PKCS1(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs) },
+
+      // RSASSA PKCS#1 PSS
+      { "EMSA4(Raw)", RSA_SignMechanism(MechanismType::RsaPkcsPss) },
+      { "EMSA4(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcsPss) },
+      { "EMSA4(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcsPss) },
+
+      { "EMSA4(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+      { "EMSA4(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+      { "PSSR(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+
+      { "EMSA4(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+      { "EMSA4(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+      { "PSSR(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+
+      { "EMSA4(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+      { "EMSA4(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+      { "PSSR(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+
+      { "ISO9796", RSA_SignMechanism(MechanismType::Rsa9796) }
+   };
+
+struct RSA_CryptMechanism final : public MechanismData
+   {
+   RSA_CryptMechanism(MechanismType _type, size_t _padding_size, MechanismType _hash, MGF _mgf)
+      : MechanismData(_type), hash(_hash), mgf(_mgf), padding_size(_padding_size)
+      {}
+
+   RSA_CryptMechanism(MechanismType _type, size_t _padding_size)
+      : RSA_CryptMechanism(_type, _padding_size, static_cast<MechanismType>(0), static_cast<MGF>(0))
+      {}
+
+   // mechanism ID of the message digest algorithm used to calculate the digest of the encoding parameter
+   MechanismType hash;
+
+   // mask generation function to use on the encoded block
+   MGF mgf;
+
+   // number of bytes required for the padding
+   size_t padding_size;
+   };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_crypt_mechanism`
+static const std::map<std::string, RSA_CryptMechanism> CryptMechanisms =
+   {
+      { "Raw", RSA_CryptMechanism(MechanismType::RsaX509, 0) },
+      { "EME-PKCS1-v1_5", RSA_CryptMechanism(MechanismType::RsaPkcs, 11) },
+      { "OAEP(SHA-1)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 20, MechanismType::Sha1, MGF::Mgf1Sha1) },
+      { "OAEP(SHA-224)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 28, MechanismType::Sha224, MGF::Mgf1Sha224) },
+      { "OAEP(SHA-256)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 32, MechanismType::Sha256, MGF::Mgf1Sha256) },
+      { "OAEP(SHA-384)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 48, MechanismType::Sha384, MGF::Mgf1Sha384) },
+      { "OAEP(SHA-512)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 64, MechanismType::Sha512, MGF::Mgf1Sha512) }
+   };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdsa_mechanism`
+static std::map<std::string, MechanismType> EcdsaHash =
+   {
+      { "Raw", MechanismType::Ecdsa },
+      { "SHA-160", MechanismType::EcdsaSha1 },
+      { "SHA-224", MechanismType::EcdsaSha224 },
+      { "SHA-256", MechanismType::EcdsaSha256 },
+      { "SHA-384", MechanismType::EcdsaSha384 },
+      { "SHA-512", MechanismType::EcdsaSha512 }
+   };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdh_mechanism`
+static std::map<std::string, KeyDerivation> EcdhHash =
+   {
+      { "Raw", KeyDerivation::Null },
+      { "SHA-160", KeyDerivation::Sha1Kdf },
+      { "SHA-224", KeyDerivation::Sha224Kdf },
+      { "SHA-256", KeyDerivation::Sha256Kdf },
+      { "SHA-384", KeyDerivation::Sha384Kdf },
+      { "SHA-512", KeyDerivation::Sha512Kdf }
+   };
+}
+
+MechanismWrapper::MechanismWrapper(MechanismType mechanism_type)
+   : m_mechanism( { static_cast<CK_MECHANISM_TYPE>(mechanism_type), nullptr, 0 }), m_parameters(nullptr)
+   {}
+
+MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(const std::string& padding)
+   {
+   auto mechanism_info_it = CryptMechanisms.find(padding);
+   if(mechanism_info_it == CryptMechanisms.end())
+      {
+      // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding`
+      throw Lookup_Error("PKCS#11 RSA encrypt/decrypt does not support EME " + padding);
+      }
+   RSA_CryptMechanism mechanism_info = mechanism_info_it->second;
+
+   MechanismWrapper mech(mechanism_info.type);
+   if(mechanism_info.type == MechanismType::RsaPkcsOaep)
+      {
+      mech.m_parameters = std::make_shared<MechanismParameters>();
+      mech.m_parameters->oaep_params.hashAlg = static_cast<CK_MECHANISM_TYPE>(mechanism_info.hash);
+      mech.m_parameters->oaep_params.mgf = static_cast<CK_RSA_PKCS_MGF_TYPE>(mechanism_info.mgf);
+      mech.m_parameters->oaep_params.source = CKZ_DATA_SPECIFIED;
+      mech.m_parameters->oaep_params.pSourceData = nullptr;
+      mech.m_parameters->oaep_params.ulSourceDataLen = 0;
+      mech.m_mechanism.pParameter = mech.m_parameters.get();
+      mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsOaepParams);
+      }
+   mech.m_padding_size = mechanism_info.padding_size;
+   return mech;
+   }
+
+MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(const std::string& padding)
+   {
+   auto mechanism_info_it = SignMechanisms.find(padding);
+   if(mechanism_info_it == SignMechanisms.end())
+      {
+      // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding`
+      throw Lookup_Error("PKCS#11 RSA sign/verify does not support EMSA " + padding);
+      }
+   RSA_SignMechanism mechanism_info = mechanism_info_it->second;
+
+   MechanismWrapper mech(mechanism_info.type);
+   if(PssOptions.find(mechanism_info.type) != PssOptions.end())
+      {
+      mech.m_parameters = std::make_shared<MechanismParameters>();
+      mech.m_parameters->pss_params.hashAlg = static_cast<CK_MECHANISM_TYPE>(mechanism_info.hash);
+      mech.m_parameters->pss_params.mgf = static_cast<CK_RSA_PKCS_MGF_TYPE>(mechanism_info.mgf);
+      mech.m_parameters->pss_params.sLen = static_cast<Ulong>(mechanism_info.salt_size);
+      mech.m_mechanism.pParameter = mech.m_parameters.get();
+      mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsPssParams);
+      }
+   return mech;
+   }
+
+MechanismWrapper MechanismWrapper::create_ecdsa_mechanism(const std::string& hash)
+   {
+   std::string hash_name = hash;
+
+   if(hash_name != "Raw")
+      {
+      hash_name = hash_for_emsa(hash);
+      }
+
+   auto mechanism_type = EcdsaHash.find(hash_name);
+   if(mechanism_type == EcdsaHash.end())
+      {
+      throw Lookup_Error("PKCS#11 ECDSA sign/verify does not support " + hash);
+      }
+   return MechanismWrapper(mechanism_type->second);
+   }
+
+MechanismWrapper MechanismWrapper::create_ecdh_mechanism(const std::string& params)
+   {
+   std::vector<std::string> param_parts = split_on(params, ',');
+
+   if(param_parts.empty() || param_parts.size() > 2)
+      throw Invalid_Argument("PKCS #11 ECDH key derivation bad params " + params);
+
+   const bool use_cofactor =
+      (param_parts[0] == "Cofactor") ||
+      (param_parts.size() == 2 && param_parts[1] == "Cofactor");
+
+   std::string kdf_name = (param_parts[0] == "Cofactor" ? param_parts[1] : param_parts[0]);
+   std::string hash = kdf_name;
+
+   if(kdf_name != "Raw")
+      {
+      SCAN_Name kdf_hash(kdf_name);
+
+      if(kdf_hash.arg_count() > 0)
+         {
+         hash = kdf_hash.arg(0);
+         }
+      }
+
+   auto kdf = EcdhHash.find(hash);
+   if(kdf == EcdhHash.end())
+      {
+      throw Lookup_Error("PKCS#11 ECDH key derivation does not support KDF " + kdf_name);
+      }
+   MechanismWrapper mech(use_cofactor ? MechanismType::Ecdh1CofactorDerive : MechanismType::Ecdh1Derive);
+   mech.m_parameters = std::make_shared<MechanismParameters>();
+   mech.m_parameters->ecdh_params.kdf = static_cast<CK_EC_KDF_TYPE>(kdf->second);
+   mech.m_mechanism.pParameter = mech.m_parameters.get();
+   mech.m_mechanism.ulParameterLen = sizeof(Ecdh1DeriveParams);
+   return mech;
+   }
+
+}
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h b/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h
@@ -0,0 +1,118 @@
+/*
+* PKCS#11 Mechanism
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_MECHANISM_H_
+#define BOTAN_P11_MECHANISM_H_
+
+#include <botan/p11.h>
+
+#include <utility>
+#include <string>
+#include <memory>
+
+namespace Botan {
+namespace PKCS11 {
+
+/**
+* Simple class to build and hold the data for a CK_MECHANISM struct
+* for RSA (encryption/decryption, signature/verification)
+* and EC (ECDSA signature/verification, ECDH key derivation).
+*/
+class MechanismWrapper final
+   {
+   public:
+      /// @param mechanism_type the CK_MECHANISM_TYPE for the `mechanism` field of the CK_MECHANISM struct
+      explicit MechanismWrapper(MechanismType mechanism_type);
+
+      /**
+      * Creates the CK_MECHANISM data for RSA encryption/decryption
+      * @param padding supported paddings are Raw (X.509), EME-PKCS1-v1_5 (PKCS#1 v1.5) and OAEP (PKCS#1 OAEP)
+      */
+      static MechanismWrapper create_rsa_crypt_mechanism(const std::string& padding);
+
+      /**
+      * Creates the CK_MECHANISM data for RSA signature/verification
+      * @param padding supported paddings are Raw (X.509), EMSA3 (PKCS#1 v1.5), EMSA4 (PKCS#1 PSS),
+      * EMSA2 (ANSI X9.31) and ISO9796 (ISO/IEC 9796)
+      */
+      static MechanismWrapper create_rsa_sign_mechanism(const std::string& padding);
+
+      /**
+      * Creates the CK_MECHANISM data for ECDSA signature/verification
+      * @param hash the hash algorithm used to hash the data to sign.
+      * supported hash functions are Raw and SHA-160 to SHA-512
+      */
+      static MechanismWrapper create_ecdsa_mechanism(const std::string& hash);
+
+      /**
+      * Creates the CK_MECHANISM data for ECDH key derivation (CKM_ECDH1_DERIVE or CKM_ECDH1_COFACTOR_DERIVE)
+      * @param params specifies the key derivation function to use.
+      * Supported KDFs are Raw and SHA-160 to SHA-512.
+      * Params can also include the string "Cofactor" if the cofactor
+      * key derivation mechanism should be used, for example "SHA-512,Cofactor"
+      */
+      static MechanismWrapper create_ecdh_mechanism(const std::string& params);
+
+      /**
+      * Sets the salt for the ECDH mechanism parameters.
+      * @param salt the salt
+      * @param salt_len size of the salt in bytes
+      */
+      inline void set_ecdh_salt(const uint8_t salt[], size_t salt_len)
+         {
+         m_parameters->ecdh_params.pSharedData = const_cast<uint8_t*>(salt);
+         m_parameters->ecdh_params.ulSharedDataLen = static_cast<Ulong>(salt_len);
+         }
+
+      /**
+      * Sets the public key of the other party for the ECDH mechanism parameters.
+      * @param other_key key of the other party
+      * @param other_key_len size of the key of the other party in bytes
+      */
+      inline void set_ecdh_other_key(const uint8_t other_key[], size_t other_key_len)
+         {
+         m_parameters->ecdh_params.pPublicData = const_cast<uint8_t*>(other_key);
+         m_parameters->ecdh_params.ulPublicDataLen = static_cast<Ulong>(other_key_len);
+         }
+
+      /// @return a pointer to the CK_MECHANISM struct that can be passed to the cryptoki functions
+      inline Mechanism* data() const
+         {
+         return const_cast<Mechanism*>(&m_mechanism);
+         }
+
+      /// @return the size of the padding in bytes (for encryption/decryption)
+      inline size_t padding_size() const
+         {
+         return m_padding_size;
+         }
+
+      /// Holds the mechanism parameters for OAEP, PSS and ECDH
+      union MechanismParameters
+         {
+         MechanismParameters()
+            {
+            clear_mem(this, 1);
+            }
+
+         RsaPkcsOaepParams oaep_params;
+         RsaPkcsPssParams pss_params;
+         Ecdh1DeriveParams ecdh_params;
+         };
+
+   private:
+      Mechanism m_mechanism;
+      std::shared_ptr<MechanismParameters> m_parameters;
+      size_t m_padding_size = 0;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp
@@ -0,0 +1,50 @@
+/*
+* PKCS#11 Module
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_module.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+Module::Module(const std::string& file_path, C_InitializeArgs init_args)
+   : m_file_path(file_path)
+   {
+   if(file_path.empty())
+      throw Invalid_Argument("PKCS11 no module path specified");
+   reload(init_args);
+   }
+
+Module::~Module() noexcept
+   {
+   try
+      {
+      m_low_level->C_Finalize(nullptr, nullptr);
+      }
+   catch(...)
+      {
+      // we are noexcept and must swallow any exception here
+      }
+   }
+
+void Module::reload(C_InitializeArgs init_args)
+   {
+   if(m_low_level)
+      {
+      m_low_level->C_Finalize(nullptr);
+      }
+
+   m_library.reset(new Dynamically_Loaded_Library(m_file_path));
+   LowLevel::C_GetFunctionList(*m_library, &m_func_list);
+   m_low_level.reset(new LowLevel(m_func_list));
+
+   m_low_level->C_Initialize(&init_args);
+   }
+
+}
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_module.h b/third_party/botan/src/lib/prov/pkcs11/p11_module.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_module.h
@@ -0,0 +1,75 @@
+/*
+* PKCS#11 Module
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_MODULE_H_
+#define BOTAN_P11_MODULE_H_
+
+#include <string>
+#include <memory>
+
+#include <botan/p11.h>
+#include <botan/dyn_load.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+/**
+* Loads the PKCS#11 shared library
+* Calls C_Initialize on load and C_Finalize on destruction
+*/
+class BOTAN_PUBLIC_API(2,0) Module final
+   {
+   public:
+      /**
+      * Loads the shared library and calls C_Initialize
+      * @param file_path the path to the PKCS#11 shared library
+      * @param init_args flags to use for `C_Initialize`
+      */
+      Module(const std::string& file_path, C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr });
+
+      Module(Module&& other) = default;
+      Module& operator=(Module&& other) = delete;
+
+      // Dtor calls C_Finalize(). A copy could be deleted while the origin still exists
+      // Furthermore std::unique_ptr member -> not copyable
+      Module(const Module& other) = delete;
+      Module& operator=(const Module& other) = delete;
+
+      /// Calls C_Finalize()
+      ~Module() noexcept;
+
+      /**
+      * Reloads the module and reinitializes it
+      * @param init_args flags to use for `C_Initialize`
+      */
+      void reload(C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr });
+
+      inline LowLevel* operator->() const
+         {
+         return m_low_level.get();
+         }
+
+      /// @return general information about Cryptoki
+      inline Info get_info() const
+         {
+         Info info;
+         m_low_level->C_GetInfo(&info);
+         return info;
+         }
+
+   private:
+      const std::string m_file_path;
+      FunctionListPtr m_func_list = nullptr;
+      std::unique_ptr<Dynamically_Loaded_Library> m_library = nullptr;
+      std::unique_ptr<LowLevel> m_low_level = nullptr;
+   };
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp
@@ -0,0 +1,227 @@
+/*
+* PKCS#11 Object
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_object.h>
+#include <map>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+AttributeContainer::AttributeContainer(ObjectClass object_class)
+   {
+   add_class(object_class);
+   }
+
+void AttributeContainer::add_class(ObjectClass object_class)
+   {
+   m_numerics.emplace_back(static_cast< uint64_t >(object_class));
+   add_attribute(AttributeType::Class,
+                 reinterpret_cast< uint8_t* >(&m_numerics.back()),
+                 static_cast<Ulong>(sizeof(ObjectClass)));
+   }
+
+void AttributeContainer::add_string(AttributeType attribute, const std::string& value)
+   {
+   m_strings.push_back(value);
+   add_attribute(attribute, reinterpret_cast<const uint8_t*>(m_strings.back().data()), static_cast<Ulong>(value.size()));
+   }
+
+void AttributeContainer::add_binary(AttributeType attribute, const uint8_t* value, size_t length)
+   {
+   m_vectors.push_back(secure_vector<uint8_t>(value, value + length));
+   add_attribute(attribute, reinterpret_cast< const uint8_t* >(m_vectors.back().data()), static_cast<Ulong>(length));
+   }
+
+void AttributeContainer::add_bool(AttributeType attribute, bool value)
+   {
+   m_numerics.push_back(value ? True : False);
+   add_attribute(attribute, reinterpret_cast< uint8_t* >(&m_numerics.back()), sizeof(Bbool));
+   }
+
+void AttributeContainer::add_attribute(AttributeType attribute, const uint8_t* value, uint32_t size)
+   {
+   bool exists = false;
+   // check if the attribute has been added already
+   for(auto& existing_attribute : m_attributes)
+      {
+      if(existing_attribute.type == static_cast< CK_ATTRIBUTE_TYPE >(attribute))
+         {
+         // remove old entries
+         m_strings.erase(std::remove_if(m_strings.begin(), m_strings.end(), [ &existing_attribute ](const std::string& data)
+            {
+            return data.data() == existing_attribute.pValue;
+            }), m_strings.end());
+
+         m_numerics.erase(std::remove_if(m_numerics.begin(), m_numerics.end(), [ &existing_attribute ](const uint64_t& data)
+            {
+            return &data == existing_attribute.pValue;
+            }), m_numerics.end());
+
+         m_vectors.erase(std::remove_if(m_vectors.begin(),
+                                        m_vectors.end(), [ &existing_attribute ](const secure_vector<uint8_t>& data)
+            {
+            return data.data() == existing_attribute.pValue;
+            }), m_vectors.end());
+
+         existing_attribute.pValue = const_cast< uint8_t* >(value);
+         existing_attribute.ulValueLen = size;
+         exists = true;
+         break;
+         }
+      }
+
+   if(!exists)
+      {
+      m_attributes.push_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(attribute), const_cast< uint8_t* >(value), size });
+      }
+   }
+
+// ====================================================================================================
+
+ObjectFinder::ObjectFinder(Session& session, const std::vector<Attribute>& search_template)
+   : m_session(session), m_search_terminated(false)
+   {
+   module()->C_FindObjectsInit(m_session.get().handle(),
+                               const_cast< Attribute* >(search_template.data()),
+                               static_cast<Ulong>(search_template.size()));
+   }
+
+ObjectFinder::~ObjectFinder() noexcept
+   {
+   try
+      {
+      if(m_search_terminated == false)
+         {
+         module()->C_FindObjectsFinal(m_session.get().handle(), nullptr);
+         }
+      }
+   catch(...)
+      {
+      // ignore error during noexcept function
+      }
+   }
+
+std::vector<ObjectHandle> ObjectFinder::find(uint32_t max_count) const
+   {
+   std::vector<ObjectHandle> result(max_count);
+   Ulong objectCount = 0;
+   module()->C_FindObjects(m_session.get().handle(), result.data(), max_count, &objectCount);
+   if(objectCount < max_count)
+      {
+      result.resize(objectCount);
+      }
+   return result;
+   }
+
+void ObjectFinder::finish()
+   {
+   module()->C_FindObjectsFinal(m_session.get().handle());
+   m_search_terminated = true;
+   }
+
+// ====================================================================================================
+
+ObjectProperties::ObjectProperties(ObjectClass object_class)
+   : AttributeContainer(object_class), m_object_class(object_class)
+   {}
+
+// ====================================================================================================
+
+StorageObjectProperties::StorageObjectProperties(ObjectClass object_class)
+   : ObjectProperties(object_class)
+   {}
+
+// ====================================================================================================
+
+DataObjectProperties::DataObjectProperties()
+   : StorageObjectProperties(ObjectClass::Data)
+   {}
+
+// ====================================================================================================
+
+CertificateProperties::CertificateProperties(CertificateType cert_type)
+   : StorageObjectProperties(ObjectClass::Certificate), m_cert_type(cert_type)
+   {
+   add_numeric(AttributeType::CertificateType, static_cast< CK_CERTIFICATE_TYPE >(m_cert_type));
+   }
+
+// ====================================================================================================
+
+KeyProperties::KeyProperties(ObjectClass object_class, KeyType key_type)
+   : StorageObjectProperties(object_class), m_key_type(key_type)
+   {
+   add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type));
+   }
+
+// ====================================================================================================
+
+PublicKeyProperties::PublicKeyProperties(KeyType key_type)
+   : KeyProperties(ObjectClass::PublicKey, key_type)
+   {}
+
+// ====================================================================================================
+
+PrivateKeyProperties::PrivateKeyProperties(KeyType key_type)
+   : KeyProperties(ObjectClass::PrivateKey, key_type)
+   {}
+
+// ====================================================================================================
+
+SecretKeyProperties::SecretKeyProperties(KeyType key_type)
+   : KeyProperties(ObjectClass::SecretKey, key_type)
+   {}
+
+// ====================================================================================================
+
+DomainParameterProperties::DomainParameterProperties(KeyType key_type)
+   : StorageObjectProperties(ObjectClass::DomainParameters), m_key_type(key_type)
+   {
+   add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type));
+   }
+
+// ====================================================================================================
+
+Object::Object(Session& session, ObjectHandle handle)
+   : m_session(session), m_handle(handle)
+   {}
+
+Object::Object(Session& session, const ObjectProperties& obj_props)
+   : m_session(session), m_handle(0)
+   {
+   m_session.get().module()->C_CreateObject(m_session.get().handle(), obj_props.data(), static_cast<Ulong>(obj_props.count()), &m_handle);
+   }
+
+secure_vector<uint8_t> Object::get_attribute_value(AttributeType attribute) const
+   {
+   std::map<AttributeType, secure_vector<uint8_t>> attribute_map = { { attribute, secure_vector<uint8_t>() } };
+   module()->C_GetAttributeValue(m_session.get().handle(), m_handle, attribute_map);
+   return attribute_map.at(attribute);
+   }
+
+void Object::set_attribute_value(AttributeType attribute, const secure_vector<uint8_t>& value) const
+   {
+   std::map<AttributeType, secure_vector<uint8_t>> attribute_map = { { attribute, value } };
+   module()->C_SetAttributeValue(m_session.get().handle(), m_handle, attribute_map);
+   }
+
+void Object::destroy() const
+   {
+   module()->C_DestroyObject(m_session.get().handle(), m_handle);
+   }
+
+ObjectHandle Object::copy(const AttributeContainer& modified_attributes) const
+   {
+   ObjectHandle copied_handle;
+   module()->C_CopyObject(m_session.get().handle(), m_handle,
+                          modified_attributes.data(), static_cast<Ulong>(modified_attributes.count()),
+                          &copied_handle);
+   return copied_handle;
+   }
+}
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_object.h b/third_party/botan/src/lib/prov/pkcs11/p11_object.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_object.h
@@ -0,0 +1,773 @@
+/*
+* PKCS#11 Object
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_OBJECT_H_
+#define BOTAN_P11_OBJECT_H_
+
+#include <botan/p11.h>
+#include <botan/p11_session.h>
+#include <botan/secmem.h>
+
+#include <vector>
+#include <string>
+#include <type_traits>
+#include <list>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Module;
+
+/// Helper class to build the Attribute / CK_ATTRIBUTE structures
+class BOTAN_PUBLIC_API(2,0) AttributeContainer
+   {
+   public:
+      AttributeContainer() = default;
+
+      /// @param object_class the class type of this container
+      AttributeContainer(ObjectClass object_class);
+
+      virtual ~AttributeContainer() = default;
+
+      AttributeContainer(AttributeContainer&& other) = default;
+      AttributeContainer& operator=(AttributeContainer&& other) = default;
+
+      // Warning when implementing copy/assignment: m_attributes contains pointers to the other members which must be updated after a copy
+      AttributeContainer(const AttributeContainer& other) = delete;
+      AttributeContainer& operator=(const AttributeContainer& other) = delete;
+
+      /// @return the attributes this container contains
+      inline const std::vector<Attribute>& attributes() const
+         {
+         return m_attributes;
+         }
+
+      /// @return raw attribute data
+      inline Attribute* data() const
+         {
+         return const_cast< Attribute* >(m_attributes.data());
+         }
+
+      /// @return the number of attributes in this container
+      inline size_t count() const
+         {
+         return m_attributes.size();
+         }
+
+      /**
+      * Add a class attribute (CKA_CLASS / AttributeType::Class).
+      * @param object_class class attribute to add
+      */
+      void add_class(ObjectClass object_class);
+
+      /**
+      * Add a string attribute (e.g. CKA_LABEL / AttributeType::Label).
+      * @param attribute attribute type
+      * @param value string value to add
+      */
+      void add_string(AttributeType attribute, const std::string& value);
+
+      /**
+      * Add a binary attribute (e.g. CKA_ID / AttributeType::Id).
+      * @param attribute attribute type
+      * @param value binary attribute value to add
+      * @param length size of the binary attribute value in bytes
+      */
+      void add_binary(AttributeType attribute, const uint8_t* value, size_t length);
+
+      /**
+      * Add a binary attribute (e.g. CKA_ID / AttributeType::Id).
+      * @param attribute attribute type
+      * @param binary binary attribute value to add
+      */
+      template<typename TAlloc>
+      void add_binary(AttributeType attribute, const std::vector<uint8_t, TAlloc>& binary)
+         {
+         add_binary(attribute, binary.data(), binary.size());
+         }
+
+      /**
+      * Add a bool attribute (e.g. CKA_SENSITIVE / AttributeType::Sensitive).
+      * @param attribute attribute type
+      * @param value boolean value to add
+      */
+      void add_bool(AttributeType attribute, bool value);
+
+      /**
+      * Add a numeric attribute (e.g. CKA_MODULUS_BITS / AttributeType::ModulusBits).
+      * @param attribute attribute type
+      * @param value numeric value to add
+      */
+      template<typename T>
+      void add_numeric(AttributeType attribute, T value)
+         {
+         static_assert(std::is_integral<T>::value, "Numeric value required.");
+         m_numerics.push_back(static_cast< uint64_t >(value));
+         add_attribute(attribute, reinterpret_cast< uint8_t* >(&m_numerics.back()), sizeof(T));
+         }
+
+   protected:
+      /// Add an attribute with the given value and size to the attribute collection `m_attributes`
+      void add_attribute(AttributeType attribute, const uint8_t* value, uint32_t size);
+
+   private:
+      std::vector<Attribute> m_attributes;
+      std::list<uint64_t> m_numerics;
+      std::list<std::string> m_strings;
+      std::list<secure_vector<uint8_t>> m_vectors;
+   };
+
+/// Manages calls to C_FindObjects* functions (C_FindObjectsInit -> C_FindObjects -> C_FindObjectsFinal)
+class BOTAN_PUBLIC_API(2,0) ObjectFinder final
+   {
+   public:
+      /**
+      * Initializes a search for token and session objects that match a template (calls C_FindObjectsInit)
+      * @param session the session to use for the search
+      * @param search_template the search_template as a vector of `Attribute`
+      */
+      ObjectFinder(Session& session, const std::vector<Attribute>& search_template);
+
+      ObjectFinder(const ObjectFinder& other) = default;
+      ObjectFinder& operator=(const ObjectFinder& other) = delete;
+
+      ObjectFinder(ObjectFinder&& other) = default;
+      ObjectFinder& operator=(ObjectFinder&& other) = delete;
+
+      /// Terminates a search for token and session objects (calls C_FindObjectsFinal)
+      ~ObjectFinder() noexcept;
+
+      /**
+      * Starts or continues a search for token and session objects that match a template, obtaining additional object handles (calls C_FindObjects)
+      * @param max_count maximum amount of object handles to retrieve. Default = 100
+      * @return the result of the search as a vector of `ObjectHandle`
+      */
+      std::vector<ObjectHandle> find(std::uint32_t max_count = 100) const;
+
+      /// Finishes the search operation manually to allow a new ObjectFinder to exist
+      void finish();
+
+      /// @return the module this `ObjectFinder` belongs to
+      inline Module& module() const
+         {
+         return m_session.get().module();
+         }
+
+   private:
+      const std::reference_wrapper<Session> m_session;
+      bool m_search_terminated;
+   };
+
+/// Common attributes of all objects
+class BOTAN_PUBLIC_API(2,0) ObjectProperties : public AttributeContainer
+   {
+   public:
+      /// @param object_class the object class of the object
+      ObjectProperties(ObjectClass object_class);
+
+      /// @return the object class of this object
+      inline ObjectClass object_class() const
+         {
+         return m_object_class;
+         }
+
+   private:
+      const ObjectClass m_object_class;
+   };
+
+/// Common attributes of all storage objects
+class BOTAN_PUBLIC_API(2,0) StorageObjectProperties : public ObjectProperties
+   {
+   public:
+      /// @param object_class the CK_OBJECT_CLASS this storage object belongs to
+      StorageObjectProperties(ObjectClass object_class);
+
+      /// @param label description of the object (RFC2279 string)
+      inline void set_label(const std::string& label)
+         {
+         add_string(AttributeType::Label, label);
+         }
+
+      /// @param value if true the object is a token object; otherwise the object is a session object
+      inline void set_token(bool value)
+         {
+         add_bool(AttributeType::Token, value);
+         }
+
+      /**
+      * @param value if true the object is a private object; otherwise the object is a public object
+      * When private, a user may not access the object until the user has been authenticated to the token
+      */
+      inline void set_private(bool value)
+         {
+         add_bool(AttributeType::Private, value);
+         }
+
+      /// @param value if true the object can be modified, otherwise it is read-only
+      void set_modifiable(bool value)
+         {
+         add_bool(AttributeType::Modifiable, value);
+         }
+
+      /// @param value if true the object can be copied using C_CopyObject
+      void set_copyable(bool value)
+         {
+         add_bool(AttributeType::Copyable, value);
+         }
+
+      /// @param value if true the object can be destroyed using C_DestroyObject
+      void set_destroyable(bool value)
+         {
+         add_bool(AttributeType::Destroyable, value);
+         }
+   };
+
+/// Common attributes of all data objects
+class BOTAN_PUBLIC_API(2,0) DataObjectProperties final : public StorageObjectProperties
+   {
+   public:
+      DataObjectProperties();
+
+      /// @param value description of the application that manages the object (RFC2279 string)
+      inline void set_application(const std::string& value)
+         {
+         add_string(AttributeType::Application, value);
+         }
+
+      /// @param object_id DER-encoding of the object identifier indicating the data object type
+      inline void set_object_id(const std::vector<uint8_t>& object_id)
+         {
+         add_binary(AttributeType::ObjectId, object_id);
+         }
+
+      /// @param value value of the object
+      inline void set_value(const secure_vector<uint8_t>& value)
+         {
+         add_binary(AttributeType::Value, value);
+         }
+   };
+
+/// Common attributes of all certificate objects
+class BOTAN_PUBLIC_API(2,0) CertificateProperties : public StorageObjectProperties
+   {
+   public:
+      /// @param cert_type type of certificate
+      CertificateProperties(CertificateType cert_type);
+
+      /// @param value the certificate can be trusted for the application that it was created (can only be set to true by SO user)
+      inline void set_trusted(bool value)
+         {
+         add_bool(AttributeType::Trusted, value);
+         }
+
+      /// @param category one of `CertificateCategory`
+      inline void set_category(CertificateCategory category)
+         {
+         add_numeric(AttributeType::CertificateCategory, static_cast< CK_CERTIFICATE_CATEGORY >(category));
+         }
+
+      /**
+      * @param checksum the value of this attribute is derived from the certificate by taking the
+      * first three bytes of the SHA - 1 hash of the certificate object's `CKA_VALUE` attribute
+      */
+      inline void set_check_value(const std::vector<uint8_t>& checksum)
+         {
+         add_binary(AttributeType::CheckValue, checksum);
+         }
+
+      /// @param date start date for the certificate
+      inline void set_start_date(Date date)
+         {
+         add_binary(AttributeType::StartDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+         }
+
+      /// @param date end date for the certificate
+      inline void set_end_date(Date date)
+         {
+         add_binary(AttributeType::EndDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+         }
+
+      /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for the public key contained in this certificate
+      inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+         {
+         add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+         }
+
+      /// @return the certificate type of this certificate object
+      inline CertificateType cert_type() const
+         {
+         return m_cert_type;
+         }
+
+   private:
+      const CertificateType m_cert_type;
+   };
+
+/// Common attributes of all key objects
+class BOTAN_PUBLIC_API(2,0) KeyProperties : public StorageObjectProperties
+   {
+   public:
+      /**
+      * @param object_class the `CK_OBJECT_CLASS` this key object belongs to
+      * @param key_type type of key
+      */
+      KeyProperties(ObjectClass object_class, KeyType key_type);
+
+      /// @param id key identifier for key
+      inline void set_id(const std::vector<uint8_t>& id)
+         {
+         add_binary(AttributeType::Id, id);
+         }
+
+      /// @param date start date for the key
+      inline void set_start_date(Date date)
+         {
+         add_binary(AttributeType::StartDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+         }
+
+      /// @param date end date for the key
+      inline void set_end_date(Date date)
+         {
+         add_binary(AttributeType::EndDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+         }
+
+      /// @param value true if key supports key derivation (i.e., if other keys can be derived from this one)
+      inline void set_derive(bool value)
+         {
+         add_bool(AttributeType::Derive, value);
+         }
+
+      /**
+      * Sets a list of mechanisms allowed to be used with this key
+      * Not implemented
+      */
+      inline void set_allowed_mechanisms(const std::vector<MechanismType>&)
+         {
+         throw Not_Implemented("KeyProperties::set_allowed_mechanisms");
+         }
+
+      /// @return the key type of this key object
+      inline KeyType key_type() const
+         {
+         return m_key_type;
+         }
+
+   private:
+      const KeyType m_key_type;
+   };
+
+/// Common attributes of all public key objects
+class BOTAN_PUBLIC_API(2,0) PublicKeyProperties : public KeyProperties
+   {
+   public:
+      /// @param key_type type of key
+      PublicKeyProperties(KeyType key_type);
+
+      /// @param subject DER-encoding of the key subject name
+      inline void set_subject(const std::vector<uint8_t>& subject)
+         {
+         add_binary(AttributeType::Subject, subject);
+         }
+
+      /// @param value true if the key supports encryption
+      inline void set_encrypt(bool value)
+         {
+         add_bool(AttributeType::Encrypt, value);
+         }
+
+      /// @param value true if the key supports verification where the signature is an appendix to the data
+      inline void set_verify(bool value)
+         {
+         add_bool(AttributeType::Verify, value);
+         }
+
+      /// @param value true if the key supports verification where the data is recovered from the signature
+      inline void set_verify_recover(bool value)
+         {
+         add_bool(AttributeType::VerifyRecover, value);
+         }
+
+      /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys)
+      inline void set_wrap(bool value)
+         {
+         add_bool(AttributeType::Wrap, value);
+         }
+
+      /**
+      * @param value true if the key can be trusted for the application that it was created.
+      * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE`
+      */
+      inline void set_trusted(bool value)
+         {
+         add_bool(AttributeType::Trusted, value);
+         }
+
+      /**
+      * For wrapping keys
+      * The attribute template to match against any keys wrapped using this wrapping key.
+      * Keys that do not match cannot be wrapped
+      * Not implemented
+      */
+      inline void set_wrap_template(const AttributeContainer&)
+         {
+         throw Not_Implemented("PublicKeyProperties::set_wrap_template");
+         }
+
+      /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key
+      inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+         {
+         add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+         }
+   };
+
+/// Common attributes of all private keys
+class BOTAN_PUBLIC_API(2,0) PrivateKeyProperties : public KeyProperties
+   {
+   public:
+      /// @param key_type type of key
+      PrivateKeyProperties(KeyType key_type);
+
+      /// @param subject DER-encoding of the key subject name
+      inline void set_subject(const std::vector<uint8_t>& subject)
+         {
+         add_binary(AttributeType::Subject, subject);
+         }
+
+      /// @param value true if the key is sensitive
+      inline void set_sensitive(bool value)
+         {
+         add_bool(AttributeType::Sensitive, value);
+         }
+
+      /// @param value true if the key supports decryption
+      inline void set_decrypt(bool value)
+         {
+         add_bool(AttributeType::Decrypt, value);
+         }
+
+      /// @param value true if the key supports signatures where the signature is an appendix to the data
+      inline void set_sign(bool value)
+         {
+         add_bool(AttributeType::Sign, value);
+         }
+
+      /// @param value true if the key supports signatures where the data can be recovered from the signature
+      inline void set_sign_recover(bool value)
+         {
+         add_bool(AttributeType::SignRecover, value);
+         }
+
+      /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys)
+      inline void set_unwrap(bool value)
+         {
+         add_bool(AttributeType::Unwrap, value);
+         }
+
+      /// @param value true if the key is extractable and can be wrapped
+      inline void set_extractable(bool value)
+         {
+         add_bool(AttributeType::Extractable, value);
+         }
+
+      /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE`
+      inline void set_wrap_with_trusted(bool value)
+         {
+         add_bool(AttributeType::WrapWithTrusted, value);
+         }
+
+      /// @param value If true, the user has to supply the PIN for each use (sign or decrypt) with the key
+      inline void set_always_authenticate(bool value)
+         {
+         add_bool(AttributeType::AlwaysAuthenticate, value);
+         }
+
+      /**
+      * For wrapping keys
+      * The attribute template to apply to any keys unwrapped using this wrapping key.
+      * Any user supplied template is applied after this template as if the object has already been created
+      * Not implemented
+      */
+      inline void set_unwrap_template(const AttributeContainer&)
+         {
+         throw Not_Implemented("PrivateKeyProperties::set_unwrap_template");
+         }
+
+      /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key
+      inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+         {
+         add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+         }
+   };
+
+/// Common attributes of all secret (symmetric) keys
+class BOTAN_PUBLIC_API(2,0) SecretKeyProperties final : public KeyProperties
+   {
+   public:
+      /// @param key_type type of key
+      SecretKeyProperties(KeyType key_type);
+
+      /// @param value true if the key is sensitive
+      inline void set_sensitive(bool value)
+         {
+         add_bool(AttributeType::Sensitive, value);
+         }
+
+      /// @param value true if the key supports encryption
+      inline void set_encrypt(bool value)
+         {
+         add_bool(AttributeType::Encrypt, value);
+         }
+
+      /// @param value true if the key supports decryption
+      inline void set_decrypt(bool value)
+         {
+         add_bool(AttributeType::Decrypt, value);
+         }
+
+      /// @param value true if the key supports signatures where the signature is an appendix to the data
+      inline void set_sign(bool value)
+         {
+         add_bool(AttributeType::Sign, value);
+         }
+
+      /// @param value true if the key supports verification where the signature is an appendix to the data
+      inline void set_verify(bool value)
+         {
+         add_bool(AttributeType::Verify, value);
+         }
+
+      /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys)
+      inline void set_unwrap(bool value)
+         {
+         add_bool(AttributeType::Unwrap, value);
+         }
+
+      /// @param value true if the key is extractable and can be wrapped
+      inline void set_extractable(bool value)
+         {
+         add_bool(AttributeType::Extractable, value);
+         }
+
+      /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE`
+      inline void set_wrap_with_trusted(bool value)
+         {
+         add_bool(AttributeType::WrapWithTrusted, value);
+         }
+
+      /// @param value if true, the user has to supply the PIN for each use (sign or decrypt) with the key
+      inline void set_always_authenticate(bool value)
+         {
+         add_bool(AttributeType::AlwaysAuthenticate, value);
+         }
+
+      /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys)
+      inline void set_wrap(bool value)
+         {
+         add_bool(AttributeType::Wrap, value);
+         }
+
+      /**
+      * @param value the key can be trusted for the application that it was created.
+      * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE`
+      */
+      inline void set_trusted(bool value)
+         {
+         add_bool(AttributeType::Trusted, value);
+         }
+
+      /// @param checksum the key check value of this key
+      inline void set_check_value(const std::vector<uint8_t>& checksum)
+         {
+         add_binary(AttributeType::CheckValue, checksum);
+         }
+
+      /**
+      * For wrapping keys
+      * The attribute template to match against any keys wrapped using this wrapping key.
+      * Keys that do not match cannot be wrapped
+      * Not implemented
+      */
+      inline void set_wrap_template(const AttributeContainer&)
+         {
+         throw Not_Implemented("SecretKeyProperties::set_wrap_template");
+         }
+
+      /**
+      * For wrapping keys
+      * The attribute template to apply to any keys unwrapped using this wrapping key
+      * Any user supplied template is applied after this template as if the object has already been created
+      * Not Implemented
+      */
+      inline void set_unwrap_template(const AttributeContainer&)
+         {
+         throw Not_Implemented("SecretKeyProperties::set_unwrap_template");
+         }
+   };
+
+/// Common attributes of domain parameter
+class BOTAN_PUBLIC_API(2,0) DomainParameterProperties final : public StorageObjectProperties
+   {
+   public:
+      /// @param key_type type of key the domain parameters can be used to generate
+      DomainParameterProperties(KeyType key_type);
+
+      /// @return the key type
+      inline KeyType key_type() const
+         {
+         return m_key_type;
+         }
+
+   private:
+      const KeyType m_key_type;
+   };
+
+/**
+* Represents a PKCS#11 object.
+*/
+class BOTAN_PUBLIC_API(2,0) Object
+   {
+   public:
+      /**
+      * Creates an `Object` from an existing PKCS#11 object
+      * @param session the session the object belongs to
+      * @param handle handle of the object
+      */
+
+      Object(Session& session, ObjectHandle handle);
+
+      /**
+      * Creates the object
+      * @param session the session in which the object should be created
+      * @param obj_props properties of this object
+      */
+      Object(Session& session, const ObjectProperties& obj_props);
+
+      Object(const Object&) = default;
+      Object& operator=(const Object&) = delete;
+      virtual ~Object() = default;
+
+      /// Searches for all objects of the given type that match `search_template`
+      template<typename T>
+      static std::vector<T> search(Session& session, const std::vector<Attribute>& search_template);
+
+      /// Searches for all objects of the given type using the label (`CKA_LABEL`)
+      template<typename T>
+      static std::vector<T> search(Session& session, const std::string& label);
+
+      /// Searches for all objects of the given type using the id (`CKA_ID`)
+      template<typename T>
+      static std::vector<T> search(Session& session, const std::vector<uint8_t>& id);
+
+      /// Searches for all objects of the given type using the label (`CKA_LABEL`) and id (`CKA_ID`)
+      template<typename T>
+      static std::vector<T> search(Session& session, const std::string& label, const std::vector<uint8_t>& id);
+
+      /// Searches for all objects of the given type
+      template<typename T>
+      static std::vector<T> search(Session& session);
+
+      /// @returns the value of the given attribute (using `C_GetAttributeValue`)
+      secure_vector<uint8_t> get_attribute_value(AttributeType attribute) const;
+
+      /// Sets the given value for the attribute (using `C_SetAttributeValue`)
+      void set_attribute_value(AttributeType attribute, const secure_vector<uint8_t>& value) const;
+
+      /// Destroys the object
+      void destroy() const;
+
+      /**
+      * Copies the object
+      * @param modified_attributes the attributes of the copied object
+      */
+      ObjectHandle copy(const AttributeContainer& modified_attributes) const;
+
+      /// @return the handle of this object.
+      inline ObjectHandle handle() const
+         {
+         return m_handle;
+         }
+
+      /// @return the session this objects belongs to
+      inline Session& session() const
+         {
+         return m_session;
+         }
+
+      /// @return the module this object belongs to
+      inline Module& module() const
+         {
+         return m_session.get().module();
+         }
+   protected:
+      Object(Session& session)
+         : m_session(session)
+         {}
+
+      void reset_handle(ObjectHandle handle)
+         {
+         if(m_handle != CK_INVALID_HANDLE)
+            throw Invalid_Argument("Cannot reset handle on already valid PKCS11 object");
+         m_handle = handle;
+         }
+
+   private:
+      const std::reference_wrapper<Session> m_session;
+      ObjectHandle m_handle = CK_INVALID_HANDLE;
+   };
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::vector<Attribute>& search_template)
+   {
+   ObjectFinder finder(session, search_template);
+   std::vector<ObjectHandle> handles = finder.find();
+   std::vector<T> result;
+   result.reserve(handles.size());
+   for(const auto& handle : handles)
+      {
+      result.emplace_back(T(session, handle));
+      }
+   return result;
+   }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::string& label)
+   {
+   AttributeContainer search_template(T::Class);
+   search_template.add_string(AttributeType::Label, label);
+   return search<T>(session, search_template.attributes());
+   }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::vector<uint8_t>& id)
+   {
+   AttributeContainer search_template(T::Class);
+   search_template.add_binary(AttributeType::Id, id);
+   return search<T>(session, search_template.attributes());
+   }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::string& label, const std::vector<uint8_t>& id)
+   {
+   AttributeContainer search_template(T::Class);
+   search_template.add_string(AttributeType::Label, label);
+   search_template.add_binary(AttributeType::Id, id);
+   return search<T>(session, search_template.attributes());
+   }
+
+template<typename T>
+std::vector<T> Object::search(Session& session)
+   {
+   return search<T>(session, AttributeContainer(T::Class).attributes());
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp
@@ -0,0 +1,31 @@
+/*
+* PKCS#11 Random Generator
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_randomgenerator.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+PKCS11_RNG::PKCS11_RNG(Session& session)
+   : m_session(session)
+   {}
+
+void PKCS11_RNG::randomize(uint8_t output[], std::size_t length)
+   {
+   module()->C_GenerateRandom(m_session.get().handle(), output, Ulong(length));
+   }
+
+void PKCS11_RNG::add_entropy(const uint8_t in[], std::size_t length)
+   {
+   module()->C_SeedRandom(m_session.get().handle(), const_cast<uint8_t*>(in), Ulong(length));
+   }
+
+}
+}
+
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h b/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h
@@ -0,0 +1,70 @@
+/*
+* PKCS#11 Random Generator
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_RNG_H_
+#define BOTAN_P11_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/p11_session.h>
+#include <botan/entropy_src.h>
+
+#include <string>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Module;
+
+/// A random generator that only fetches random from the PKCS#11 RNG
+class BOTAN_PUBLIC_API(2,0) PKCS11_RNG final : public Hardware_RNG
+   {
+   public:
+      /// Initialize the RNG with the PKCS#11 session that provides access to the cryptoki functions
+      explicit PKCS11_RNG(Session& session);
+
+      std::string name() const override
+         {
+         return "PKCS11_RNG";
+         }
+
+      /// Always returns true
+      bool is_seeded() const override
+         {
+         return true;
+         }
+
+      /// No operation - always returns 0
+      size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
+         {
+         return 0;
+         }
+
+      /// @return the module used by this RNG
+      inline Module& module() const
+         {
+         return m_session.get().module();
+         }
+
+      /// Calls `C_GenerateRandom` to generate random data
+      void randomize(uint8_t output[], std::size_t length) override;
+
+      /// Calls `C_SeedRandom` to add entropy to the random generation function of the token/middleware
+      void add_entropy(const uint8_t in[], std::size_t length) override;
+
+      // C_SeedRandom may suceed
+      bool accepts_input() const override { return true; }
+
+   private:
+      const std::reference_wrapper<Session> m_session;
+   };
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp
@@ -0,0 +1,373 @@
+/*
+* PKCS#11 RSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_rsa.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_RSA)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/pk_ops.h>
+#include <botan/rng.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+RSA_PublicKeyImportProperties::RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent)
+   : PublicKeyProperties(KeyType::Rsa), m_modulus(modulus), m_pub_exponent(pub_exponent)
+   {
+   add_binary(AttributeType::Modulus, BigInt::encode(m_modulus));
+   add_binary(AttributeType::PublicExponent, BigInt::encode(m_pub_exponent));
+   }
+
+RSA_PublicKeyGenerationProperties::RSA_PublicKeyGenerationProperties(Ulong bits)
+   : PublicKeyProperties(KeyType::Rsa)
+   {
+   add_numeric(AttributeType::ModulusBits, bits);
+   }
+
+PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle)
+   : Object(session, handle),
+     RSA_PublicKey(BigInt::decode(get_attribute_value(AttributeType::Modulus)),
+                   BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+   {
+   }
+
+PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props)
+   : Object(session, pubkey_props), RSA_PublicKey(pubkey_props.modulus(), pubkey_props.pub_exponent())
+   {}
+
+
+RSA_PrivateKeyImportProperties::RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent)
+   : PrivateKeyProperties(KeyType::Rsa), m_modulus(modulus), m_priv_exponent(priv_exponent)
+   {
+   add_binary(AttributeType::Modulus, BigInt::encode(m_modulus));
+   add_binary(AttributeType::PrivateExponent, BigInt::encode(m_priv_exponent));
+   }
+
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle)
+   : Object(session, handle),
+     RSA_PublicKey(BigInt::decode(get_attribute_value(AttributeType::Modulus)),
+                   BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+   {
+   }
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props)
+   : Object(session, priv_key_props),
+     RSA_PublicKey(priv_key_props.modulus(),
+                   BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+   {
+   }
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, uint32_t bits,
+                                             const RSA_PrivateKeyGenerationProperties& priv_key_props)
+   : Object(session), RSA_PublicKey()
+   {
+   RSA_PublicKeyGenerationProperties pub_key_props(bits);
+   pub_key_props.set_encrypt(true);
+   pub_key_props.set_verify(true);
+   pub_key_props.set_token(false);    // don't create a persistent public key object
+
+   ObjectHandle pub_key_handle = CK_INVALID_HANDLE;
+   ObjectHandle priv_key_handle = CK_INVALID_HANDLE;
+   Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 };
+   session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+                                       pub_key_props.data(), static_cast<Ulong>(pub_key_props.count()),
+                                       priv_key_props.data(), static_cast<Ulong>(priv_key_props.count()),
+                                       &pub_key_handle, &priv_key_handle);
+
+   this->reset_handle(priv_key_handle);
+
+   BigInt n = BigInt::decode(get_attribute_value(AttributeType::Modulus));
+   BigInt e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent));
+   RSA_PublicKey::init(std::move(n), std::move(e));
+   }
+
+RSA_PrivateKey PKCS11_RSA_PrivateKey::export_key() const
+   {
+   auto p = get_attribute_value(AttributeType::Prime1);
+   auto q = get_attribute_value(AttributeType::Prime2);
+   auto e = get_attribute_value(AttributeType::PublicExponent);
+   auto d = get_attribute_value(AttributeType::PrivateExponent);
+   auto n = get_attribute_value(AttributeType::Modulus);
+
+   return RSA_PrivateKey(  BigInt::decode(p)
+                         , BigInt::decode(q)
+                         , BigInt::decode(e)
+                         , BigInt::decode(d)
+                         , BigInt::decode(n));
+   }
+
+secure_vector<uint8_t> PKCS11_RSA_PrivateKey::private_key_bits() const
+   {
+   return export_key().private_key_bits();
+   }
+
+
+namespace {
+// note: multiple-part decryption operations (with C_DecryptUpdate/C_DecryptFinal)
+// are not supported (PK_Ops::Decryption does not provide an `update` method)
+class PKCS11_RSA_Decryption_Operation final : public PK_Ops::Decryption
+   {
+   public:
+
+      PKCS11_RSA_Decryption_Operation(const PKCS11_RSA_PrivateKey& key,
+                                      const std::string& padding,
+                                      RandomNumberGenerator& rng)
+         : m_key(key),
+           m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding)),
+           m_blinder(m_key.get_n(), rng,
+                     [ this ](const BigInt& k) { return power_mod(k, m_key.get_e(), m_key.get_n()); },
+                     [ this ](const BigInt& k) { return inverse_mod(k, m_key.get_n()); })
+         {
+         m_bits = m_key.get_n().bits() - 1;
+         }
+
+      size_t plaintext_length(size_t) const override { return m_key.get_n().bytes(); }
+
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], size_t ciphertext_len) override
+         {
+         valid_mask = 0;
+         m_key.module()->C_DecryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+
+         std::vector<uint8_t> encrypted_data(ciphertext, ciphertext + ciphertext_len);
+
+         // blind for RSA/RAW decryption
+         if(! m_mechanism.padding_size())
+            {
+            encrypted_data = BigInt::encode(m_blinder.blind(BigInt::decode(encrypted_data)));
+            }
+
+         secure_vector<uint8_t> decrypted_data;
+         m_key.module()->C_Decrypt(m_key.session().handle(), encrypted_data, decrypted_data);
+
+         // Unblind for RSA/RAW decryption
+         if(!m_mechanism.padding_size())
+            {
+            decrypted_data = BigInt::encode_1363(m_blinder.unblind(BigInt::decode(decrypted_data)), m_key.get_n().bits() / 8 );
+            }
+
+         valid_mask = 0xFF;
+         return decrypted_data;
+         }
+
+   private:
+      const PKCS11_RSA_PrivateKey& m_key;
+      MechanismWrapper m_mechanism;
+      size_t m_bits = 0;
+      Blinder m_blinder;
+   };
+
+// note: multiple-part encryption operations (with C_EncryptUpdate/C_EncryptFinal)
+// are not supported (PK_Ops::Encryption does not provide an `update` method)
+class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption
+   {
+   public:
+
+      PKCS11_RSA_Encryption_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding)
+         : m_key(key), m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding))
+         {
+         m_bits = 8 * (key.get_n().bytes() - m_mechanism.padding_size()) - 1;
+         }
+
+      size_t ciphertext_length(size_t) const override { return m_key.get_n().bytes(); }
+
+      size_t max_input_bits() const override
+         {
+         return m_bits;
+         }
+
+      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator&) override
+         {
+         m_key.module()->C_EncryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+
+         secure_vector<uint8_t> encrytped_data;
+         m_key.module()->C_Encrypt(m_key.session().handle(), secure_vector<uint8_t>(msg, msg + msg_len), encrytped_data);
+         return encrytped_data;
+         }
+
+   private:
+      const PKCS11_RSA_PublicKey& m_key;
+      MechanismWrapper m_mechanism;
+      size_t m_bits = 0;
+   };
+
+
+class PKCS11_RSA_Signature_Operation final : public PK_Ops::Signature
+   {
+   public:
+
+      PKCS11_RSA_Signature_Operation(const PKCS11_RSA_PrivateKey& key, const std::string& padding)
+         : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding))
+         {}
+
+      size_t signature_length() const override { return m_key.get_n().bytes(); }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(!m_initialized)
+            {
+            // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+            m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+            m_initialized = true;
+            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            return;
+            }
+
+         if(!m_first_message.empty())
+            {
+            // second call to update: start multiple-part operation
+            m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message);
+            m_first_message.clear();
+            }
+
+         m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast< Byte* >(msg), static_cast<Ulong>(msg_len));
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+         {
+         secure_vector<uint8_t> signature;
+         if(!m_first_message.empty())
+            {
+            // single call to update: perform single-part operation
+            m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
+            m_first_message.clear();
+            }
+         else
+            {
+            // multiple calls to update (or none): finish multiple-part operation
+            m_key.module()->C_SignFinal(m_key.session().handle(), signature);
+            }
+         m_initialized = false;
+         return signature;
+         }
+
+   private:
+      const PKCS11_RSA_PrivateKey& m_key;
+      bool m_initialized = false;
+      secure_vector<uint8_t> m_first_message;
+      MechanismWrapper m_mechanism;
+   };
+
+
+class PKCS11_RSA_Verification_Operation final : public PK_Ops::Verification
+   {
+   public:
+
+      PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding)
+         : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding))
+         {}
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(!m_initialized)
+            {
+            // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+            m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+            m_initialized = true;
+            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            return;
+            }
+
+         if(!m_first_message.empty())
+            {
+            // second call to update: start multiple-part operation
+            m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message);
+            m_first_message.clear();
+            }
+
+         m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast< Byte* >(msg), static_cast<Ulong>(msg_len));
+         }
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+         {
+         ReturnValue return_value = ReturnValue::SignatureInvalid;
+         if(!m_first_message.empty())
+            {
+            // single call to update: perform single-part operation
+            m_key.module()->C_Verify(m_key.session().handle(),
+                                     m_first_message.data(), static_cast<Ulong>(m_first_message.size()),
+                                     const_cast< Byte* >(sig), static_cast<Ulong>(sig_len), &return_value);
+            m_first_message.clear();
+            }
+         else
+            {
+            // multiple calls to update (or none): finish multiple-part operation
+            m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast< Byte* >(sig), static_cast<Ulong>(sig_len), &return_value);
+            }
+         m_initialized = false;
+         if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid)
+            {
+            throw PKCS11_ReturnError(return_value);
+            }
+         return return_value == ReturnValue::OK;
+         }
+
+   private:
+      const PKCS11_RSA_PublicKey& m_key;
+      bool m_initialized = false;
+      secure_vector<uint8_t> m_first_message;
+      MechanismWrapper m_mechanism;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+PKCS11_RSA_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+                                           const std::string& params,
+                                           const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Encryption>(new PKCS11_RSA_Encryption_Operation(*this, params));
+   }
+
+std::unique_ptr<PK_Ops::Verification>
+PKCS11_RSA_PublicKey::create_verification_op(const std::string& params,
+                                             const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Verification>(new PKCS11_RSA_Verification_Operation(*this, params));
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+PKCS11_RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+                                            const std::string& params,
+                                            const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Decryption>(new PKCS11_RSA_Decryption_Operation(*this, params, rng));
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+PKCS11_RSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                           const std::string& params,
+                                           const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Signature>(new PKCS11_RSA_Signature_Operation(*this, params));
+   }
+
+PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props,
+                                        const RSA_PrivateKeyGenerationProperties& priv_props)
+   {
+   ObjectHandle pub_key_handle = 0;
+   ObjectHandle priv_key_handle = 0;
+
+   Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 };
+
+   session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+                                       pub_props.data(), static_cast<Ulong>(pub_props.count()),
+                                       priv_props.data(), static_cast<Ulong>(priv_props.count()),
+                                       &pub_key_handle, &priv_key_handle);
+
+   return std::make_pair(PKCS11_RSA_PublicKey(session, pub_key_handle), PKCS11_RSA_PrivateKey(session, priv_key_handle));
+   }
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h b/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h
@@ -0,0 +1,229 @@
+/*
+* PKCS#11 RSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_RSA_H_
+#define BOTAN_P11_RSA_H_
+
+#include <botan/p11_session.h>
+#include <botan/p11_object.h>
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+
+#if defined(BOTAN_HAS_RSA)
+#include <botan/rsa.h>
+#include <utility>
+
+namespace Botan {
+namespace PKCS11 {
+
+/// Properties for generating a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKeyGenerationProperties final : public PublicKeyProperties
+   {
+   public:
+      /// @param bits length in bits of modulus n
+      explicit RSA_PublicKeyGenerationProperties(Ulong bits);
+
+      /// @param pub_exponent public exponent e
+      inline void set_pub_exponent(const BigInt& pub_exponent = BigInt(0x10001))
+         {
+         add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent));
+         }
+
+      virtual ~RSA_PublicKeyGenerationProperties() = default;
+   };
+
+/// Properties for importing a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKeyImportProperties final : public PublicKeyProperties
+   {
+   public:
+      /// @param modulus modulus n
+      /// @param pub_exponent public exponent e
+      RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent);
+
+      /// @return the modulus
+      inline const BigInt& modulus() const
+         {
+         return m_modulus;
+         }
+
+      /// @return the public exponent
+      inline const BigInt& pub_exponent() const
+         {
+         return m_pub_exponent;
+         }
+
+      virtual ~RSA_PublicKeyImportProperties() = default;
+   private:
+      const BigInt m_modulus;
+      const BigInt m_pub_exponent;
+   };
+
+/// Represents a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PublicKey : public Object, public RSA_PublicKey
+   {
+   public:
+      static const ObjectClass Class = ObjectClass::PublicKey;
+
+      /**
+      * Creates a PKCS11_RSA_PublicKey object from an existing PKCS#11 RSA public key
+      * @param session the session to use
+      * @param handle the handle of the RSA public key
+      */
+      PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle);
+
+      /**
+      * Imports a RSA public key
+      * @param session the session to use
+      * @param pubkey_props the attributes of the public key
+      */
+      PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props);
+
+      std::unique_ptr<PK_Ops::Encryption>
+         create_encryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   };
+
+/// Properties for importing a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKeyImportProperties final : public PrivateKeyProperties
+   {
+   public:
+      /**
+      * @param modulus modulus n
+      * @param priv_exponent private exponent d
+      */
+      RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent);
+
+      /// @param pub_exponent public exponent e
+      inline void set_pub_exponent(const BigInt& pub_exponent)
+         {
+         add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent));
+         }
+
+      /// @param prime1 prime p
+      inline void set_prime_1(const BigInt& prime1)
+         {
+         add_binary(AttributeType::Prime1, BigInt::encode(prime1));
+         }
+
+      /// @param prime2 prime q
+      inline void set_prime_2(const BigInt& prime2)
+         {
+         add_binary(AttributeType::Prime2, BigInt::encode(prime2));
+         }
+
+      /// @param exp1 private exponent d modulo p-1
+      inline void set_exponent_1(const BigInt& exp1)
+         {
+         add_binary(AttributeType::Exponent1, BigInt::encode(exp1));
+         }
+
+      /// @param exp2 private exponent d modulo q-1
+      inline void set_exponent_2(const BigInt& exp2)
+         {
+         add_binary(AttributeType::Exponent2, BigInt::encode(exp2));
+         }
+
+      /// @param coeff CRT coefficient q^-1 mod p
+      inline void set_coefficient(const BigInt& coeff)
+         {
+         add_binary(AttributeType::Coefficient, BigInt::encode(coeff));
+         }
+
+      /// @return the modulus
+      inline const BigInt& modulus() const
+         {
+         return m_modulus;
+         }
+
+      /// @return the private exponent
+      inline const BigInt& priv_exponent() const
+         {
+         return m_priv_exponent;
+         }
+
+      virtual ~RSA_PrivateKeyImportProperties() = default;
+
+   private:
+      const BigInt m_modulus;
+      const BigInt m_priv_exponent;
+   };
+
+/// Properties for generating a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKeyGenerationProperties final : public PrivateKeyProperties
+   {
+   public:
+      RSA_PrivateKeyGenerationProperties()
+         : PrivateKeyProperties(KeyType::Rsa)
+         {}
+
+      virtual ~RSA_PrivateKeyGenerationProperties() = default;
+   };
+
+/// Represents a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PrivateKey final :
+   public Object, public Private_Key, public RSA_PublicKey
+   {
+   public:
+      static const ObjectClass Class = ObjectClass::PrivateKey;
+
+      /// Creates a PKCS11_RSA_PrivateKey object from an existing PKCS#11 RSA private key
+      PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle);
+
+      /**
+      * Imports a RSA private key
+      * @param session the session to use
+      * @param priv_key_props the properties of the RSA private key
+      */
+      PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props);
+
+      /**
+      * Generates a PKCS#11 RSA private key
+      * @param session the session to use
+      * @param bits length in bits of modulus n
+      * @param priv_key_props the properties of the RSA private key
+      * @note no persistent public key object will be created
+      */
+      PKCS11_RSA_PrivateKey(Session& session, uint32_t bits, const RSA_PrivateKeyGenerationProperties& priv_key_props);
+
+      /// @return the exported RSA private key
+      RSA_PrivateKey export_key() const;
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      std::unique_ptr<PK_Ops::Decryption>
+         create_decryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+using PKCS11_RSA_KeyPair = std::pair<PKCS11_RSA_PublicKey, PKCS11_RSA_PrivateKey>;
+
+/**
+* RSA key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props properties of the public key
+* @param priv_props properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props,
+      const RSA_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp
@@ -0,0 +1,96 @@
+/*
+* PKCS#11 Session
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_session.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+Session::Session(Slot& slot, bool read_only)
+   : Session(slot, PKCS11::flags(Flag::SerialSession | (read_only ? Flag::None : Flag::RwSession)), nullptr, nullptr)
+   {}
+
+Session::Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback)
+   : m_slot(slot), m_handle(0), m_logged_in(false)
+   {
+   module()->C_OpenSession(m_slot.slot_id(), flags, callback_data, notify_callback, &m_handle);
+   }
+
+Session::Session(Slot& slot, SessionHandle handle)
+   : m_slot(slot), m_handle(handle)
+   {
+   SessionInfo info = get_info();
+   if(info.state == static_cast<CK_STATE>(SessionState::RoPublicSession)
+         || info.state == static_cast<CK_STATE>(SessionState::RwPublicSession))
+      {
+      m_logged_in = false;
+      }
+   else
+      {
+      m_logged_in = true;
+      }
+   }
+
+Session::~Session() noexcept
+   {
+   try
+      {
+      if(m_handle)
+         {
+         if(m_logged_in)
+            {
+            module()->C_Logout(m_handle, nullptr);
+            }
+         module()->C_CloseSession(m_handle, nullptr);
+         m_handle = 0;
+         }
+      }
+   catch(...)
+      {
+      // exception during noexcept destructor is ignored
+      }
+   }
+
+SessionHandle Session::release()
+   {
+   SessionHandle handle = 0;
+   std::swap(handle, m_handle);
+   return handle;
+   }
+
+void Session::login(UserType user_type, const secure_string& pin)
+   {
+   module()->C_Login(m_handle, user_type, pin);
+   m_logged_in = true;
+   }
+
+void Session::logoff()
+   {
+   module()->C_Logout(m_handle);
+   m_logged_in = false;
+   }
+
+SessionInfo Session::get_info() const
+   {
+   SessionInfo info;
+   module()->C_GetSessionInfo(m_handle, &info);
+   return info;
+   }
+
+void Session::set_pin(const secure_string& old_pin, const secure_string& new_pin) const
+   {
+   module()->C_SetPIN(m_handle, old_pin, new_pin);
+   }
+
+void Session::init_pin(const secure_string& new_pin)
+   {
+   module()->C_InitPIN(m_handle, new_pin);
+   }
+
+}
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_session.h b/third_party/botan/src/lib/prov/pkcs11/p11_session.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_session.h
@@ -0,0 +1,100 @@
+/*
+* PKCS#11 Session
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_SESSION_H_
+#define BOTAN_P11_SESSION_H_
+
+#include <botan/p11_slot.h>
+
+#include <utility>
+
+namespace Botan {
+namespace PKCS11 {
+class Module;
+
+/// Represents a PKCS#11 session
+class BOTAN_PUBLIC_API(2,0) Session final
+   {
+   public:
+      /**
+      * @param slot the slot to use
+      * @param read_only true if the session should be read only, false to create a read-write session
+      */
+      Session(Slot& slot, bool read_only);
+
+      /**
+      * @param slot the slot to use
+      * @param flags the flags to use for the session. Remark: Flag::SerialSession is mandatory
+      * @param callback_data application-defined pointer to be passed to the notification callback
+      * @param notify_callback address of the notification callback function
+      */
+      Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback);
+
+      /// Takes ownership of a session
+      Session(Slot& slot, SessionHandle handle);
+
+      Session(Session&& other) = default;
+      Session& operator=(Session&& other) = delete;
+
+      // Dtor calls C_CloseSession() and eventually C_Logout. A copy could close the session while the origin still exists
+      Session(const Session& other) = delete;
+      Session& operator=(const Session& other) = delete;
+
+      /// Logout user and close the session on destruction
+      ~Session() noexcept;
+
+      /// @return a reference to the slot
+      inline const Slot& slot() const
+         {
+         return m_slot;
+         }
+
+      /// @return the session handle of this session
+      inline SessionHandle handle() const
+         {
+         return m_handle;
+         }
+
+      /// @return a reference to the used module
+      inline Module& module() const
+         {
+         return m_slot.module();
+         }
+
+      /// @return the released session handle
+      SessionHandle release();
+
+      /**
+      * Login to this session
+      * @param userType the user type to use for the login
+      * @param pin the PIN of the user
+      */
+      void login(UserType userType, const secure_string& pin);
+
+      /// Logout from this session
+      void logoff();
+
+      /// @return information about this session
+      SessionInfo get_info() const;
+
+      /// Calls `C_SetPIN` to change the PIN using the old PIN (requires a logged in session)
+      void set_pin(const secure_string& old_pin, const secure_string& new_pin) const;
+
+      /// Calls `C_InitPIN` to change or initialize the PIN using the SO_PIN (requires a logged in session)
+      void init_pin(const secure_string& new_pin);
+
+   private:
+      const Slot& m_slot;
+      SessionHandle m_handle;
+      bool m_logged_in;
+   };
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp
@@ -0,0 +1,60 @@
+/*
+* PKCS#11 Slot
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_slot.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+Slot::Slot(Module& module, SlotId slot_id)
+   : m_module(module), m_slot_id(slot_id)
+   {}
+
+SlotInfo Slot::get_slot_info() const
+   {
+   SlotInfo slot_info = {};
+   m_module.get()->C_GetSlotInfo(m_slot_id, &slot_info);
+   return slot_info;
+   }
+
+std::vector<MechanismType> Slot::get_mechanism_list() const
+   {
+   std::vector<MechanismType> mechanism_list;
+   m_module.get()->C_GetMechanismList(m_slot_id, mechanism_list);
+   return mechanism_list;
+   }
+
+MechanismInfo Slot::get_mechanism_info(MechanismType mechanism_type) const
+   {
+   MechanismInfo mechanism_info = {};
+   m_module.get()->C_GetMechanismInfo(m_slot_id, mechanism_type, &mechanism_info);
+   return mechanism_info;
+   }
+
+std::vector<SlotId> Slot::get_available_slots(Module& module, bool token_present)
+   {
+   std::vector<SlotId> slot_vec;
+   module->C_GetSlotList(token_present, slot_vec);
+   return slot_vec;
+   }
+
+TokenInfo Slot::get_token_info() const
+   {
+   TokenInfo token_info;
+   m_module.get()->C_GetTokenInfo(m_slot_id, &token_info);
+   return token_info;
+   }
+
+void Slot::initialize(const std::string& label, const secure_string& so_pin) const
+   {
+   m_module.get()->C_InitToken(m_slot_id, so_pin, label);
+   }
+}
+
+}
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_slot.h b/third_party/botan/src/lib/prov/pkcs11/p11_slot.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_slot.h
@@ -0,0 +1,78 @@
+/*
+* PKCS#11 Slot
+* (C) 2016 Daniel Neus
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_SLOT_H_
+#define BOTAN_P11_SLOT_H_
+
+#include <string>
+#include <vector>
+#include <functional>
+
+#include <botan/p11_module.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+/// Represents a PKCS#11 Slot, i.e., a card reader
+class BOTAN_PUBLIC_API(2,0) Slot final
+   {
+   public:
+      /**
+      * @param module the PKCS#11 module to use
+      * @param slot_id the slot id to use
+      */
+      Slot(Module& module, SlotId slot_id);
+
+      /// @return a reference to the module that is used
+      inline Module& module() const
+         {
+         return m_module;
+         }
+
+      /// @return the slot id
+      inline SlotId slot_id() const
+         {
+         return m_slot_id;
+         }
+
+      /**
+      * Get available slots
+      * @param module the module to use
+      * @param token_present true if only slots with attached tokens should be returned, false for all slots
+      * @return a list of available slots (calls C_GetSlotList)
+      */
+      static std::vector<SlotId> get_available_slots(Module& module, bool token_present);
+
+      /// @return information about the slot (`C_GetSlotInfo`)
+      SlotInfo get_slot_info() const;
+
+      /// Obtains a list of mechanism types supported by the slot (`C_GetMechanismList`)
+      std::vector<MechanismType> get_mechanism_list() const;
+
+      /// Obtains information about a particular mechanism possibly supported by a slot (`C_GetMechanismInfo`)
+      MechanismInfo get_mechanism_info(MechanismType mechanism_type) const;
+
+      /// Obtains information about a particular token in the system (`C_GetTokenInfo`)
+      TokenInfo get_token_info() const;
+
+      /**
+      * Calls `C_InitToken` to initialize the token
+      * @param label the label for the token (must not exceed 32 bytes according to PKCS#11)
+      * @param so_pin the PIN of the security officer
+      */
+      void initialize(const std::string& label, const secure_string& so_pin) const;
+
+   private:
+      const std::reference_wrapper<Module> m_module;
+      const SlotId m_slot_id;
+   };
+
+}
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp b/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp
@@ -0,0 +1,37 @@
+/*
+* PKCS#11 X.509
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_x509.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+namespace Botan {
+namespace PKCS11 {
+
+X509_CertificateProperties::X509_CertificateProperties(const std::vector<uint8_t>& subject, const std::vector<uint8_t>& value)
+   : CertificateProperties(CertificateType::X509), m_subject(subject), m_value(value)
+   {
+   add_binary(AttributeType::Subject, m_subject);
+   add_binary(AttributeType::Value, m_value);
+   }
+
+PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, ObjectHandle handle)
+   : Object(session, handle), X509_Certificate(unlock(get_attribute_value(AttributeType::Value)))
+   {
+   }
+
+PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props)
+   : Object(session, props), X509_Certificate(props.value())
+   {
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/p11_x509.h b/third_party/botan/src/lib/prov/pkcs11/p11_x509.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/p11_x509.h
@@ -0,0 +1,117 @@
+/*
+* PKCS#11 X.509
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_X509_H_
+#define BOTAN_P11_X509_H_
+
+#include <botan/p11_object.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/x509cert.h>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Session;
+
+/// Common attributes of all PKCS#11 X509 certificates
+class BOTAN_PUBLIC_API(2,0) X509_CertificateProperties final : public CertificateProperties
+   {
+   public:
+      /**
+      * @param subject DER-encoding of the certificate subject name
+      * @param value BER-encoding of the certificate
+      */
+      X509_CertificateProperties(const std::vector<uint8_t>& subject, const std::vector<uint8_t>& value);
+
+      X509_CertificateProperties(const X509_Certificate& cert) :
+         X509_CertificateProperties(cert.raw_subject_dn(), cert.BER_encode())
+         {}
+
+      /// @param id key identifier for public/private key pair
+      inline void set_id(const std::vector<uint8_t>& id)
+         {
+         add_binary(AttributeType::Id, id);
+         }
+
+      /// @param issuer DER-encoding of the certificate issuer name
+      inline void set_issuer(const std::vector<uint8_t>& issuer)
+         {
+         add_binary(AttributeType::Issuer, issuer);
+         }
+
+      /// @param serial DER-encoding of the certificate serial number
+      inline void set_serial(const std::vector<uint8_t>& serial)
+         {
+         add_binary(AttributeType::SerialNumber, serial);
+         }
+
+      /// @param hash hash value of the subject public key
+      inline void set_subject_pubkey_hash(const std::vector<uint8_t>& hash)
+         {
+         add_binary(AttributeType::HashOfSubjectPublicKey, hash);
+         }
+
+      /// @param hash hash value of the issuer public key
+      inline void set_issuer_pubkey_hash(const std::vector<uint8_t>& hash)
+         {
+         add_binary(AttributeType::HashOfIssuerPublicKey, hash);
+         }
+
+      /// @param alg defines the mechanism used to calculate `CKA_HASH_OF_SUBJECT_PUBLIC_KEY` and `CKA_HASH_OF_ISSUER_PUBLIC_KEY`
+      inline void set_hash_alg(MechanismType alg)
+         {
+         add_numeric(AttributeType::NameHashAlgorithm, static_cast<Ulong>(alg));
+         }
+
+      /// @return the subject
+      inline const std::vector<uint8_t>& subject() const
+         {
+         return m_subject;
+         }
+
+      /// @return the BER-encoding of the certificate
+      inline const std::vector<uint8_t>& value() const
+         {
+         return m_value;
+         }
+
+   private:
+      const std::vector<uint8_t> m_subject;
+      const std::vector<uint8_t> m_value;
+   };
+
+/// Represents a PKCS#11 X509 certificate
+class BOTAN_PUBLIC_API(2,0) PKCS11_X509_Certificate final : public Object, public X509_Certificate
+   {
+   public:
+      static const ObjectClass Class = ObjectClass::Certificate;
+
+      /**
+      * Create a PKCS11_X509_Certificate object from an existing PKCS#11 X509 cert
+      * @param session the session to use
+      * @param handle the handle of the X.509 certificate
+      */
+      PKCS11_X509_Certificate(Session& session, ObjectHandle handle);
+
+      /**
+      * Imports a X.509 certificate
+      * @param session the session to use
+      * @param props the attributes of the X.509 certificate
+      */
+      PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props);
+   };
+
+}
+}
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/prov/pkcs11/pkcs11.h b/third_party/botan/src/lib/prov/pkcs11/pkcs11.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/pkcs11.h
@@ -0,0 +1,264 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 5 platform-specific macros must be defined.  These
+ * macros are described below, and typical definitions for them
+ * are also given.  Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a Cryptoki library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 5 macros, the packing convention
+ * for Cryptoki structures should be set.  The Cryptoki
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, this might be done by using the following
+ * preprocessor directive before including pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, this might be done by using
+ * the following preprocessor directive before including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own for this.  You might
+ * not need to do (or be able to do!) anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object.  It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, it might be defined by:
+ *
+ * #define CK_PTR far *
+ *
+ * In a typical UNIX environment, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable Cryptoki library function declaration out of a
+ * return type and a function name.  It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ *   CK_VOID_PTR pReserved
+ * );
+ *
+ * If you're using Microsoft Developer Studio 5.0 to declare a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType __declspec(dllimport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to declare a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a Cryptoki API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name.  It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a Cryptoki API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // Cryptoki API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to access
+ * functions in a Win32 Cryptoki .dll, in might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType __declspec(dllimport) (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to access functions in a Win16 Cryptoki .dll, it might
+ * be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType (* name)
+ *
+ *
+ * 4. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV.  It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to do Win32
+ * Cryptoki development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to do Win16 development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType (* name)
+ *
+ *
+ * 5. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should best be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various Cryptoki types and #define'd values are in the
+ * file pkcs11t.h.
+ */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y)      x##y
+
+
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST  1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  extern CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points.  That is, for
+ * each Cryptoki function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST  1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points.  A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's Cryptoki version
+ * and then a whole slew of function pointers to the routines in
+ * the library.  This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  __PASTE(CK_,name) name;
+
+struct CK_FUNCTION_LIST {
+
+  CK_VERSION    version;  /* Cryptoki version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKCS11_H_ */
+
diff --git a/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h b/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h
@@ -0,0 +1,938 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+/* This header file contains pretty much everything about all the
+ * Cryptoki function prototypes.  Because this information is
+ * used for more than just declaring function prototypes, the
+ * order of the functions appearing herein is important, and
+ * should not be altered.
+ */
+
+/* General-purpose */
+
+/* C_Initialize initializes the Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_VOID_PTR   pInitArgs  /* if this is not NULL_PTR, it gets
+                            * cast to CK_C_INITIALIZE_ARGS_PTR
+                            * and dereferenced
+                            */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * Cryptoki library.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_VOID_PTR   pReserved  /* reserved.  Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about Cryptoki. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_INFO_PTR   pInfo  /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_FUNCTION_LIST_PTR_PTR ppFunctionList  /* receives pointer to
+                                            * function list
+                                            */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_BBOOL       tokenPresent,  /* only slots with tokens */
+  CK_SLOT_ID_PTR pSlotList,     /* receives array of slot IDs */
+  CK_ULONG_PTR   pulCount       /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID       slotID,  /* the ID of the slot */
+  CK_SLOT_INFO_PTR pInfo    /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID        slotID,  /* ID of the token's slot */
+  CK_TOKEN_INFO_PTR pInfo    /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,          /* ID of token's slot */
+  CK_MECHANISM_TYPE_PTR pMechanismList,  /* gets mech. array */
+  CK_ULONG_PTR          pulCount         /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,  /* ID of the token's slot */
+  CK_MECHANISM_TYPE     type,    /* type of mechanism */
+  CK_MECHANISM_INFO_PTR pInfo    /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID      slotID,    /* ID of the token's slot */
+  CK_UTF8CHAR_PTR pPin,      /* the SO's initial PIN */
+  CK_ULONG        ulPinLen,  /* length in bytes of the PIN */
+  CK_UTF8CHAR_PTR pLabel     /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_UTF8CHAR_PTR   pPin,      /* the normal user's PIN */
+  CK_ULONG          ulPinLen   /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_UTF8CHAR_PTR   pOldPin,   /* the old PIN */
+  CK_ULONG          ulOldLen,  /* length of the old PIN */
+  CK_UTF8CHAR_PTR   pNewPin,   /* the new PIN */
+  CK_ULONG          ulNewLen   /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,        /* the slot's ID */
+  CK_FLAGS              flags,         /* from CK_SESSION_INFO */
+  CK_VOID_PTR           pApplication,  /* passed to callback */
+  CK_NOTIFY             Notify,        /* callback function */
+  CK_SESSION_HANDLE_PTR phSession      /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID     slotID  /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE   hSession,  /* the session's handle */
+  CK_SESSION_INFO_PTR pInfo      /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,             /* session's handle */
+  CK_BYTE_PTR       pOperationState,      /* gets state */
+  CK_ULONG_PTR      pulOperationStateLen  /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR      pOperationState,      /* holds state */
+  CK_ULONG         ulOperationStateLen,  /* holds state length */
+  CK_OBJECT_HANDLE hEncryptionKey,       /* en/decryption key */
+  CK_OBJECT_HANDLE hAuthenticationKey    /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_USER_TYPE      userType,  /* the user type */
+  CK_UTF8CHAR_PTR   pPin,      /* the user's PIN */
+  CK_ULONG          ulPinLen   /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,   /* the object's template */
+  CK_ULONG          ulCount,     /* attributes in template */
+  CK_OBJECT_HANDLE_PTR phObject  /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,    /* the session's handle */
+  CK_OBJECT_HANDLE     hObject,     /* the object's handle */
+  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new object */
+  CK_ULONG             ulCount,     /* attributes in template */
+  CK_OBJECT_HANDLE_PTR phNewObject  /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hObject    /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,   /* the object's handle */
+  CK_ULONG_PTR      pulSize    /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs; gets vals */
+  CK_ULONG          ulCount     /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs and values */
+  CK_ULONG          ulCount     /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* attribute values to match */
+  CK_ULONG          ulCount     /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE    hSession,          /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject,          /* gets obj. handles */
+ CK_ULONG             ulMaxObjectCount,  /* max handles to get */
+ CK_ULONG_PTR         pulObjectCount     /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the encryption mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pData,               /* the plaintext data */
+  CK_ULONG          ulDataLen,           /* bytes of plaintext */
+  CK_BYTE_PTR       pEncryptedData,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedDataLen  /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,           /* session's handle */
+  CK_BYTE_PTR       pPart,              /* the plaintext data */
+  CK_ULONG          ulPartLen,          /* plaintext data len */
+  CK_BYTE_PTR       pEncryptedPart,     /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,                /* session handle */
+  CK_BYTE_PTR       pLastEncryptedPart,      /* last c-text */
+  CK_ULONG_PTR      pulLastEncryptedPartLen  /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the decryption mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,           /* session's handle */
+  CK_BYTE_PTR       pEncryptedData,     /* ciphertext */
+  CK_ULONG          ulEncryptedDataLen, /* ciphertext length */
+  CK_BYTE_PTR       pData,              /* gets plaintext */
+  CK_ULONG_PTR      pulDataLen          /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* encrypted data */
+  CK_ULONG          ulEncryptedPartLen,  /* input length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pLastPart,      /* gets plaintext */
+  CK_ULONG_PTR      pulLastPartLen  /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism  /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_BYTE_PTR       pData,        /* data to be digested */
+  CK_ULONG          ulDataLen,    /* bytes of data to digest */
+  CK_BYTE_PTR       pDigest,      /* gets the message digest */
+  CK_ULONG_PTR      pulDigestLen  /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* data to be digested */
+  CK_ULONG          ulPartLen  /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hKey       /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_BYTE_PTR       pDigest,      /* gets the message digest */
+  CK_ULONG_PTR      pulDigestLen  /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ * signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the signature mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pData,           /* the data to sign */
+  CK_ULONG          ulDataLen,       /* count of bytes to sign */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* the data to sign */
+  CK_ULONG          ulPartLen  /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation,
+ * returning the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism, /* the signature mechanism */
+  CK_OBJECT_HANDLE  hKey        /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pData,           /* the data to sign */
+  CK_ULONG          ulDataLen,       /* count of bytes to sign */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ * cannot be recovered from the signature (e.g. DSA).
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
+  CK_OBJECT_HANDLE  hKey         /* verification key */
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pData,          /* signed data */
+  CK_ULONG          ulDataLen,      /* length of signed data */
+  CK_BYTE_PTR       pSignature,     /* signature */
+  CK_ULONG          ulSignatureLen  /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* signed data */
+  CK_ULONG          ulPartLen  /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pSignature,     /* signature to verify */
+  CK_ULONG          ulSignatureLen  /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
+  CK_OBJECT_HANDLE  hKey         /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pSignature,      /* signature to verify */
+  CK_ULONG          ulSignatureLen,  /* signature length */
+  CK_BYTE_PTR       pData,           /* gets signed data */
+  CK_ULONG_PTR      pulDataLen       /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pPart,               /* the plaintext data */
+  CK_ULONG          ulPartLen,           /* plaintext length */
+  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
+  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pPart,               /* the plaintext data */
+  CK_ULONG          ulPartLen,           /* plaintext length */
+  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
+  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,    /* the session's handle */
+  CK_MECHANISM_PTR     pMechanism,  /* key generation mech. */
+  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new key */
+  CK_ULONG             ulCount,     /* # of attrs in template */
+  CK_OBJECT_HANDLE_PTR phKey        /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,                    /* session handle */
+  CK_MECHANISM_PTR     pMechanism,                  /* key-gen mech. */
+  CK_ATTRIBUTE_PTR     pPublicKeyTemplate,          /* template for pub. key */
+  CK_ULONG             ulPublicKeyAttributeCount,   /* # pub. attrs. */
+  CK_ATTRIBUTE_PTR     pPrivateKeyTemplate,         /* template for priv. key */
+  CK_ULONG             ulPrivateKeyAttributeCount,  /* # priv.  attrs. */
+  CK_OBJECT_HANDLE_PTR phPublicKey,                 /* gets pub. key handle */
+  CK_OBJECT_HANDLE_PTR phPrivateKey                 /* gets priv. key handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,      /* the wrapping mechanism */
+  CK_OBJECT_HANDLE  hWrappingKey,    /* wrapping key */
+  CK_OBJECT_HANDLE  hKey,            /* key to be wrapped */
+  CK_BYTE_PTR       pWrappedKey,     /* gets wrapped key */
+  CK_ULONG_PTR      pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,          /* session's handle */
+  CK_MECHANISM_PTR     pMechanism,        /* unwrapping mech. */
+  CK_OBJECT_HANDLE     hUnwrappingKey,    /* unwrapping key */
+  CK_BYTE_PTR          pWrappedKey,       /* the wrapped key */
+  CK_ULONG             ulWrappedKeyLen,   /* wrapped key len */
+  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
+  CK_ULONG             ulAttributeCount,  /* template length */
+  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,          /* session's handle */
+  CK_MECHANISM_PTR     pMechanism,        /* key deriv. mech. */
+  CK_OBJECT_HANDLE     hBaseKey,          /* base key */
+  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
+  CK_ULONG             ulAttributeCount,  /* template length */
+  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pSeed,     /* the seed material */
+  CK_ULONG          ulSeedLen  /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_BYTE_PTR       RandomData,  /* receives the random data */
+  CK_ULONG          ulRandomLen  /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur.
+ */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_FLAGS flags,        /* blocking/nonblocking flag */
+  CK_SLOT_ID_PTR pSlot,  /* location that receives the slot ID */
+  CK_VOID_PTR pRserved   /* reserved.  Should be NULL_PTR */
+);
+#endif
+
diff --git a/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h b/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h
@@ -0,0 +1,2002 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+/* See top of pkcs11.h for information about the macros that
+ * must be defined and the structure-packing conventions that
+ * must be set before including this file.
+ */
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CRYPTOKI_VERSION_MAJOR          2
+#define CRYPTOKI_VERSION_MINOR          40
+#define CRYPTOKI_VERSION_AMENDMENT      0
+
+#define CK_TRUE         1
+#define CK_FALSE        0
+
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE CK_FALSE
+#endif
+#ifndef TRUE
+#define TRUE CK_TRUE
+#endif
+#endif
+
+/* an unsigned 8-bit value */
+typedef unsigned char     CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE           CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE           CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE           CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+typedef long int          CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG          CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION      (~0UL)
+#define CK_EFFECTIVELY_INFINITE         0UL
+
+
+typedef CK_BYTE     CK_PTR   CK_BYTE_PTR;
+typedef CK_CHAR     CK_PTR   CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR   CK_UTF8CHAR_PTR;
+typedef CK_ULONG    CK_PTR   CK_ULONG_PTR;
+typedef void        CK_PTR   CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session
+ * handle or object handle
+ */
+#define CK_INVALID_HANDLE       0UL
+
+
+typedef struct CK_VERSION {
+  CK_BYTE       major;  /* integer portion of version number */
+  CK_BYTE       minor;  /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+  CK_VERSION    cryptokiVersion;     /* Cryptoki interface ver */
+  CK_UTF8CHAR   manufacturerID[32];  /* blank padded */
+  CK_FLAGS      flags;               /* must be zero */
+  CK_UTF8CHAR   libraryDescription[32];  /* blank padded */
+  CK_VERSION    libraryVersion;          /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR    CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * Cryptoki provides to an application
+ */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER           0UL
+#define CKN_OTP_CHANGED         1UL
+
+typedef CK_ULONG          CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+  CK_UTF8CHAR   slotDescription[64];  /* blank padded */
+  CK_UTF8CHAR   manufacturerID[32];   /* blank padded */
+  CK_FLAGS      flags;
+
+  CK_VERSION    hardwareVersion;  /* version of hardware */
+  CK_VERSION    firmwareVersion;  /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ *      Bit Flag              Mask        Meaning
+ */
+#define CKF_TOKEN_PRESENT     0x00000001UL  /* a token is there */
+#define CKF_REMOVABLE_DEVICE  0x00000002UL  /* removable devices*/
+#define CKF_HW_SLOT           0x00000004UL  /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+  CK_UTF8CHAR   label[32];           /* blank padded */
+  CK_UTF8CHAR   manufacturerID[32];  /* blank padded */
+  CK_UTF8CHAR   model[16];           /* blank padded */
+  CK_CHAR       serialNumber[16];    /* blank padded */
+  CK_FLAGS      flags;               /* see below */
+
+  CK_ULONG      ulMaxSessionCount;     /* max open sessions */
+  CK_ULONG      ulSessionCount;        /* sess. now open */
+  CK_ULONG      ulMaxRwSessionCount;   /* max R/W sessions */
+  CK_ULONG      ulRwSessionCount;      /* R/W sess. now open */
+  CK_ULONG      ulMaxPinLen;           /* in bytes */
+  CK_ULONG      ulMinPinLen;           /* in bytes */
+  CK_ULONG      ulTotalPublicMemory;   /* in bytes */
+  CK_ULONG      ulFreePublicMemory;    /* in bytes */
+  CK_ULONG      ulTotalPrivateMemory;  /* in bytes */
+  CK_ULONG      ulFreePrivateMemory;   /* in bytes */
+  CK_VERSION    hardwareVersion;       /* version of hardware */
+  CK_VERSION    firmwareVersion;       /* version of firmware */
+  CK_CHAR       utcTime[16];           /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ *      Bit Flag                    Mask        Meaning
+ */
+#define CKF_RNG                     0x00000001UL  /* has random # generator */
+#define CKF_WRITE_PROTECTED         0x00000002UL  /* token is write-protected */
+#define CKF_LOGIN_REQUIRED          0x00000004UL  /* user must login */
+#define CKF_USER_PIN_INITIALIZED    0x00000008UL  /* normal user's PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED.  If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state
+ */
+#define CKF_RESTORE_KEY_NOT_NEEDED  0x00000020UL
+
+/* CKF_CLOCK_ON_TOKEN.  If it is set, that means
+ * that the token has some sort of clock.  The time on that
+ * clock is returned in the token info structure
+ */
+#define CKF_CLOCK_ON_TOKEN          0x00000040UL
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH.  If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the Cryptoki library itself
+ */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL
+
+/* CKF_DUAL_CRYPTO_OPERATIONS.  If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign)
+ */
+#define CKF_DUAL_CRYPTO_OPERATIONS  0x00000200UL
+
+/* CKF_TOKEN_INITIALIZED. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized.
+ */
+#define CKF_TOKEN_INITIALIZED       0x00000400UL
+
+/* CKF_SECONDARY_AUTHENTICATION. If it is
+ * true, the token supports secondary authentication for
+ * private key objects.
+ */
+#define CKF_SECONDARY_AUTHENTICATION  0x00000800UL
+
+/* CKF_USER_PIN_COUNT_LOW. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication.
+ */
+#define CKF_USER_PIN_COUNT_LOW       0x00010000UL
+
+/* CKF_USER_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect user PIN will it to become locked.
+ */
+#define CKF_USER_PIN_FINAL_TRY       0x00020000UL
+
+/* CKF_USER_PIN_LOCKED. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible.
+ */
+#define CKF_USER_PIN_LOCKED          0x00040000UL
+
+/* CKF_USER_PIN_TO_BE_CHANGED. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_USER_PIN_TO_BE_CHANGED   0x00080000UL
+
+/* CKF_SO_PIN_COUNT_LOW. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication.
+ */
+#define CKF_SO_PIN_COUNT_LOW         0x00100000UL
+
+/* CKF_SO_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect SO PIN will it to become locked.
+ */
+#define CKF_SO_PIN_FINAL_TRY         0x00200000UL
+
+/* CKF_SO_PIN_LOCKED. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED            0x00400000UL
+
+/* CKF_SO_PIN_TO_BE_CHANGED. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_SO_PIN_TO_BE_CHANGED     0x00800000UL
+
+#define CKF_ERROR_STATE              0x01000000UL
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a Cryptoki-assigned value that
+ * identifies a session
+ */
+typedef CK_ULONG          CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of Cryptoki users */
+typedef CK_ULONG          CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO                  0UL
+/* Normal user */
+#define CKU_USER                1UL
+/* Context specific */
+#define CKU_CONTEXT_SPECIFIC    2UL
+
+/* CK_STATE enumerates the session states */
+typedef CK_ULONG          CK_STATE;
+#define CKS_RO_PUBLIC_SESSION   0UL
+#define CKS_RO_USER_FUNCTIONS   1UL
+#define CKS_RW_PUBLIC_SESSION   2UL
+#define CKS_RW_USER_FUNCTIONS   3UL
+#define CKS_RW_SO_FUNCTIONS     4UL
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+  CK_SLOT_ID    slotID;
+  CK_STATE      state;
+  CK_FLAGS      flags;          /* see below */
+  CK_ULONG      ulDeviceError;  /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ *      Bit Flag                Mask        Meaning
+ */
+#define CKF_RW_SESSION          0x00000002UL /* session is r/w */
+#define CKF_SERIAL_SESSION      0x00000004UL /* no parallel    */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object
+ */
+typedef CK_ULONG          CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that Cryptoki recognizes.  It is defined
+ * as follows:
+ */
+typedef CK_ULONG          CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+#define CKO_DATA              0x00000000UL
+#define CKO_CERTIFICATE       0x00000001UL
+#define CKO_PUBLIC_KEY        0x00000002UL
+#define CKO_PRIVATE_KEY       0x00000003UL
+#define CKO_SECRET_KEY        0x00000004UL
+#define CKO_HW_FEATURE        0x00000005UL
+#define CKO_DOMAIN_PARAMETERS 0x00000006UL
+#define CKO_MECHANISM         0x00000007UL
+#define CKO_OTP_KEY           0x00000008UL
+
+#define CKO_VENDOR_DEFINED    0x80000000UL
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type
+ * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE.
+ */
+typedef CK_ULONG          CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+#define CKH_MONOTONIC_COUNTER  0x00000001UL
+#define CKH_CLOCK              0x00000002UL
+#define CKH_USER_INTERFACE     0x00000003UL
+#define CKH_VENDOR_DEFINED     0x80000000UL
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+typedef CK_ULONG          CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA                 0x00000000UL
+#define CKK_DSA                 0x00000001UL
+#define CKK_DH                  0x00000002UL
+#define CKK_ECDSA               0x00000003UL /* Deprecated */
+#define CKK_EC                  0x00000003UL
+#define CKK_X9_42_DH            0x00000004UL
+#define CKK_KEA                 0x00000005UL
+#define CKK_GENERIC_SECRET      0x00000010UL
+#define CKK_RC2                 0x00000011UL
+#define CKK_RC4                 0x00000012UL
+#define CKK_DES                 0x00000013UL
+#define CKK_DES2                0x00000014UL
+#define CKK_DES3                0x00000015UL
+#define CKK_CAST                0x00000016UL
+#define CKK_CAST3               0x00000017UL
+#define CKK_CAST5               0x00000018UL /* Deprecated */
+#define CKK_CAST128             0x00000018UL
+#define CKK_RC5                 0x00000019UL
+#define CKK_IDEA                0x0000001AUL
+#define CKK_SKIPJACK            0x0000001BUL
+#define CKK_BATON               0x0000001CUL
+#define CKK_JUNIPER             0x0000001DUL
+#define CKK_CDMF                0x0000001EUL
+#define CKK_AES                 0x0000001FUL
+#define CKK_BLOWFISH            0x00000020UL
+#define CKK_TWOFISH             0x00000021UL
+#define CKK_SECURID             0x00000022UL
+#define CKK_HOTP                0x00000023UL
+#define CKK_ACTI                0x00000024UL
+#define CKK_CAMELLIA            0x00000025UL
+#define CKK_ARIA                0x00000026UL
+
+#define CKK_MD5_HMAC            0x00000027UL
+#define CKK_SHA_1_HMAC          0x00000028UL
+#define CKK_RIPEMD128_HMAC      0x00000029UL
+#define CKK_RIPEMD160_HMAC      0x0000002AUL
+#define CKK_SHA256_HMAC         0x0000002BUL
+#define CKK_SHA384_HMAC         0x0000002CUL
+#define CKK_SHA512_HMAC         0x0000002DUL
+#define CKK_SHA224_HMAC         0x0000002EUL
+
+#define CKK_SEED                0x0000002FUL
+#define CKK_GOSTR3410           0x00000030UL
+#define CKK_GOSTR3411           0x00000031UL
+#define CKK_GOST28147           0x00000032UL
+
+
+
+#define CKK_VENDOR_DEFINED      0x80000000UL
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type
+ */
+typedef CK_ULONG          CK_CERTIFICATE_TYPE;
+
+#define CK_CERTIFICATE_CATEGORY_UNSPECIFIED     0UL
+#define CK_CERTIFICATE_CATEGORY_TOKEN_USER      1UL
+#define CK_CERTIFICATE_CATEGORY_AUTHORITY       2UL
+#define CK_CERTIFICATE_CATEGORY_OTHER_ENTITY    3UL
+
+#define CK_SECURITY_DOMAIN_UNSPECIFIED     0UL
+#define CK_SECURITY_DOMAIN_MANUFACTURER    1UL
+#define CK_SECURITY_DOMAIN_OPERATOR        2UL
+#define CK_SECURITY_DOMAIN_THIRD_PARTY     3UL
+
+
+/* The following certificate types are defined: */
+#define CKC_X_509               0x00000000UL
+#define CKC_X_509_ATTR_CERT     0x00000001UL
+#define CKC_WTLS                0x00000002UL
+#define CKC_VENDOR_DEFINED      0x80000000UL
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type
+ */
+typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+ * consists of an array of values.
+ */
+#define CKF_ARRAY_ATTRIBUTE     0x40000000UL
+
+/* The following OTP-related defines relate to the CKA_OTP_FORMAT attribute */
+#define CK_OTP_FORMAT_DECIMAL           0UL
+#define CK_OTP_FORMAT_HEXADECIMAL       1UL
+#define CK_OTP_FORMAT_ALPHANUMERIC      2UL
+#define CK_OTP_FORMAT_BINARY            3UL
+
+/* The following OTP-related defines relate to the CKA_OTP_..._REQUIREMENT
+ * attributes
+ */
+#define CK_OTP_PARAM_IGNORED            0UL
+#define CK_OTP_PARAM_OPTIONAL           1UL
+#define CK_OTP_PARAM_MANDATORY          2UL
+
+/* The following attribute types are defined: */
+#define CKA_CLASS              0x00000000UL
+#define CKA_TOKEN              0x00000001UL
+#define CKA_PRIVATE            0x00000002UL
+#define CKA_LABEL              0x00000003UL
+#define CKA_APPLICATION        0x00000010UL
+#define CKA_VALUE              0x00000011UL
+#define CKA_OBJECT_ID          0x00000012UL
+#define CKA_CERTIFICATE_TYPE   0x00000080UL
+#define CKA_ISSUER             0x00000081UL
+#define CKA_SERIAL_NUMBER      0x00000082UL
+#define CKA_AC_ISSUER          0x00000083UL
+#define CKA_OWNER              0x00000084UL
+#define CKA_ATTR_TYPES         0x00000085UL
+#define CKA_TRUSTED            0x00000086UL
+#define CKA_CERTIFICATE_CATEGORY        0x00000087UL
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN   0x00000088UL
+#define CKA_URL                         0x00000089UL
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008AUL
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008BUL
+#define CKA_NAME_HASH_ALGORITHM         0x0000008CUL
+#define CKA_CHECK_VALUE                 0x00000090UL
+
+#define CKA_KEY_TYPE           0x00000100UL
+#define CKA_SUBJECT            0x00000101UL
+#define CKA_ID                 0x00000102UL
+#define CKA_SENSITIVE          0x00000103UL
+#define CKA_ENCRYPT            0x00000104UL
+#define CKA_DECRYPT            0x00000105UL
+#define CKA_WRAP               0x00000106UL
+#define CKA_UNWRAP             0x00000107UL
+#define CKA_SIGN               0x00000108UL
+#define CKA_SIGN_RECOVER       0x00000109UL
+#define CKA_VERIFY             0x0000010AUL
+#define CKA_VERIFY_RECOVER     0x0000010BUL
+#define CKA_DERIVE             0x0000010CUL
+#define CKA_START_DATE         0x00000110UL
+#define CKA_END_DATE           0x00000111UL
+#define CKA_MODULUS            0x00000120UL
+#define CKA_MODULUS_BITS       0x00000121UL
+#define CKA_PUBLIC_EXPONENT    0x00000122UL
+#define CKA_PRIVATE_EXPONENT   0x00000123UL
+#define CKA_PRIME_1            0x00000124UL
+#define CKA_PRIME_2            0x00000125UL
+#define CKA_EXPONENT_1         0x00000126UL
+#define CKA_EXPONENT_2         0x00000127UL
+#define CKA_COEFFICIENT        0x00000128UL
+#define CKA_PUBLIC_KEY_INFO    0x00000129UL
+#define CKA_PRIME              0x00000130UL
+#define CKA_SUBPRIME           0x00000131UL
+#define CKA_BASE               0x00000132UL
+
+#define CKA_PRIME_BITS         0x00000133UL
+#define CKA_SUBPRIME_BITS      0x00000134UL
+#define CKA_SUB_PRIME_BITS     CKA_SUBPRIME_BITS
+
+#define CKA_VALUE_BITS         0x00000160UL
+#define CKA_VALUE_LEN          0x00000161UL
+#define CKA_EXTRACTABLE        0x00000162UL
+#define CKA_LOCAL              0x00000163UL
+#define CKA_NEVER_EXTRACTABLE  0x00000164UL
+#define CKA_ALWAYS_SENSITIVE   0x00000165UL
+#define CKA_KEY_GEN_MECHANISM  0x00000166UL
+
+#define CKA_MODIFIABLE         0x00000170UL
+#define CKA_COPYABLE           0x00000171UL
+
+#define CKA_DESTROYABLE        0x00000172UL
+
+#define CKA_ECDSA_PARAMS       0x00000180UL /* Deprecated */
+#define CKA_EC_PARAMS          0x00000180UL
+
+#define CKA_EC_POINT           0x00000181UL
+
+#define CKA_SECONDARY_AUTH     0x00000200UL /* Deprecated */
+#define CKA_AUTH_PIN_FLAGS     0x00000201UL /* Deprecated */
+
+#define CKA_ALWAYS_AUTHENTICATE  0x00000202UL
+
+#define CKA_WRAP_WITH_TRUSTED    0x00000210UL
+#define CKA_WRAP_TEMPLATE        (CKF_ARRAY_ATTRIBUTE|0x00000211UL)
+#define CKA_UNWRAP_TEMPLATE      (CKF_ARRAY_ATTRIBUTE|0x00000212UL)
+#define CKA_DERIVE_TEMPLATE      (CKF_ARRAY_ATTRIBUTE|0x00000213UL)
+
+#define CKA_OTP_FORMAT                0x00000220UL
+#define CKA_OTP_LENGTH                0x00000221UL
+#define CKA_OTP_TIME_INTERVAL         0x00000222UL
+#define CKA_OTP_USER_FRIENDLY_MODE    0x00000223UL
+#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224UL
+#define CKA_OTP_TIME_REQUIREMENT      0x00000225UL
+#define CKA_OTP_COUNTER_REQUIREMENT   0x00000226UL
+#define CKA_OTP_PIN_REQUIREMENT       0x00000227UL
+#define CKA_OTP_COUNTER               0x0000022EUL
+#define CKA_OTP_TIME                  0x0000022FUL
+#define CKA_OTP_USER_IDENTIFIER       0x0000022AUL
+#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022BUL
+#define CKA_OTP_SERVICE_LOGO          0x0000022CUL
+#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022DUL
+
+#define CKA_GOSTR3410_PARAMS            0x00000250UL
+#define CKA_GOSTR3411_PARAMS            0x00000251UL
+#define CKA_GOST28147_PARAMS            0x00000252UL
+
+#define CKA_HW_FEATURE_TYPE             0x00000300UL
+#define CKA_RESET_ON_INIT               0x00000301UL
+#define CKA_HAS_RESET                   0x00000302UL
+
+#define CKA_PIXEL_X                     0x00000400UL
+#define CKA_PIXEL_Y                     0x00000401UL
+#define CKA_RESOLUTION                  0x00000402UL
+#define CKA_CHAR_ROWS                   0x00000403UL
+#define CKA_CHAR_COLUMNS                0x00000404UL
+#define CKA_COLOR                       0x00000405UL
+#define CKA_BITS_PER_PIXEL              0x00000406UL
+#define CKA_CHAR_SETS                   0x00000480UL
+#define CKA_ENCODING_METHODS            0x00000481UL
+#define CKA_MIME_TYPES                  0x00000482UL
+#define CKA_MECHANISM_TYPE              0x00000500UL
+#define CKA_REQUIRED_CMS_ATTRIBUTES     0x00000501UL
+#define CKA_DEFAULT_CMS_ATTRIBUTES      0x00000502UL
+#define CKA_SUPPORTED_CMS_ATTRIBUTES    0x00000503UL
+#define CKA_ALLOWED_MECHANISMS          (CKF_ARRAY_ATTRIBUTE|0x00000600UL)
+
+#define CKA_VENDOR_DEFINED              0x80000000UL
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute
+ */
+typedef struct CK_ATTRIBUTE {
+  CK_ATTRIBUTE_TYPE type;
+  CK_VOID_PTR       pValue;
+  CK_ULONG          ulValueLen;  /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+  CK_CHAR       year[4];   /* the year ("1900" - "9999") */
+  CK_CHAR       month[2];  /* the month ("01" - "12") */
+  CK_CHAR       day[2];    /* the day   ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type
+ */
+typedef CK_ULONG          CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN      0x00000000UL
+#define CKM_RSA_PKCS                   0x00000001UL
+#define CKM_RSA_9796                   0x00000002UL
+#define CKM_RSA_X_509                  0x00000003UL
+
+#define CKM_MD2_RSA_PKCS               0x00000004UL
+#define CKM_MD5_RSA_PKCS               0x00000005UL
+#define CKM_SHA1_RSA_PKCS              0x00000006UL
+
+#define CKM_RIPEMD128_RSA_PKCS         0x00000007UL
+#define CKM_RIPEMD160_RSA_PKCS         0x00000008UL
+#define CKM_RSA_PKCS_OAEP              0x00000009UL
+
+#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000AUL
+#define CKM_RSA_X9_31                  0x0000000BUL
+#define CKM_SHA1_RSA_X9_31             0x0000000CUL
+#define CKM_RSA_PKCS_PSS               0x0000000DUL
+#define CKM_SHA1_RSA_PKCS_PSS          0x0000000EUL
+
+#define CKM_DSA_KEY_PAIR_GEN           0x00000010UL
+#define CKM_DSA                        0x00000011UL
+#define CKM_DSA_SHA1                   0x00000012UL
+#define CKM_DSA_SHA224                 0x00000013UL
+#define CKM_DSA_SHA256                 0x00000014UL
+#define CKM_DSA_SHA384                 0x00000015UL
+#define CKM_DSA_SHA512                 0x00000016UL
+
+#define CKM_DH_PKCS_KEY_PAIR_GEN       0x00000020UL
+#define CKM_DH_PKCS_DERIVE             0x00000021UL
+
+#define CKM_X9_42_DH_KEY_PAIR_GEN      0x00000030UL
+#define CKM_X9_42_DH_DERIVE            0x00000031UL
+#define CKM_X9_42_DH_HYBRID_DERIVE     0x00000032UL
+#define CKM_X9_42_MQV_DERIVE           0x00000033UL
+
+#define CKM_SHA256_RSA_PKCS            0x00000040UL
+#define CKM_SHA384_RSA_PKCS            0x00000041UL
+#define CKM_SHA512_RSA_PKCS            0x00000042UL
+#define CKM_SHA256_RSA_PKCS_PSS        0x00000043UL
+#define CKM_SHA384_RSA_PKCS_PSS        0x00000044UL
+#define CKM_SHA512_RSA_PKCS_PSS        0x00000045UL
+
+#define CKM_SHA224_RSA_PKCS            0x00000046UL
+#define CKM_SHA224_RSA_PKCS_PSS        0x00000047UL
+
+#define CKM_SHA512_224                 0x00000048UL
+#define CKM_SHA512_224_HMAC            0x00000049UL
+#define CKM_SHA512_224_HMAC_GENERAL    0x0000004AUL
+#define CKM_SHA512_224_KEY_DERIVATION  0x0000004BUL
+#define CKM_SHA512_256                 0x0000004CUL
+#define CKM_SHA512_256_HMAC            0x0000004DUL
+#define CKM_SHA512_256_HMAC_GENERAL    0x0000004EUL
+#define CKM_SHA512_256_KEY_DERIVATION  0x0000004FUL
+
+#define CKM_SHA512_T                   0x00000050UL
+#define CKM_SHA512_T_HMAC              0x00000051UL
+#define CKM_SHA512_T_HMAC_GENERAL      0x00000052UL
+#define CKM_SHA512_T_KEY_DERIVATION    0x00000053UL
+
+#define CKM_RC2_KEY_GEN                0x00000100UL
+#define CKM_RC2_ECB                    0x00000101UL
+#define CKM_RC2_CBC                    0x00000102UL
+#define CKM_RC2_MAC                    0x00000103UL
+
+#define CKM_RC2_MAC_GENERAL            0x00000104UL
+#define CKM_RC2_CBC_PAD                0x00000105UL
+
+#define CKM_RC4_KEY_GEN                0x00000110UL
+#define CKM_RC4                        0x00000111UL
+#define CKM_DES_KEY_GEN                0x00000120UL
+#define CKM_DES_ECB                    0x00000121UL
+#define CKM_DES_CBC                    0x00000122UL
+#define CKM_DES_MAC                    0x00000123UL
+
+#define CKM_DES_MAC_GENERAL            0x00000124UL
+#define CKM_DES_CBC_PAD                0x00000125UL
+
+#define CKM_DES2_KEY_GEN               0x00000130UL
+#define CKM_DES3_KEY_GEN               0x00000131UL
+#define CKM_DES3_ECB                   0x00000132UL
+#define CKM_DES3_CBC                   0x00000133UL
+#define CKM_DES3_MAC                   0x00000134UL
+
+#define CKM_DES3_MAC_GENERAL           0x00000135UL
+#define CKM_DES3_CBC_PAD               0x00000136UL
+#define CKM_DES3_CMAC_GENERAL          0x00000137UL
+#define CKM_DES3_CMAC                  0x00000138UL
+#define CKM_CDMF_KEY_GEN               0x00000140UL
+#define CKM_CDMF_ECB                   0x00000141UL
+#define CKM_CDMF_CBC                   0x00000142UL
+#define CKM_CDMF_MAC                   0x00000143UL
+#define CKM_CDMF_MAC_GENERAL           0x00000144UL
+#define CKM_CDMF_CBC_PAD               0x00000145UL
+
+#define CKM_DES_OFB64                  0x00000150UL
+#define CKM_DES_OFB8                   0x00000151UL
+#define CKM_DES_CFB64                  0x00000152UL
+#define CKM_DES_CFB8                   0x00000153UL
+
+#define CKM_MD2                        0x00000200UL
+
+#define CKM_MD2_HMAC                   0x00000201UL
+#define CKM_MD2_HMAC_GENERAL           0x00000202UL
+
+#define CKM_MD5                        0x00000210UL
+
+#define CKM_MD5_HMAC                   0x00000211UL
+#define CKM_MD5_HMAC_GENERAL           0x00000212UL
+
+#define CKM_SHA_1                      0x00000220UL
+
+#define CKM_SHA_1_HMAC                 0x00000221UL
+#define CKM_SHA_1_HMAC_GENERAL         0x00000222UL
+
+#define CKM_RIPEMD128                  0x00000230UL
+#define CKM_RIPEMD128_HMAC             0x00000231UL
+#define CKM_RIPEMD128_HMAC_GENERAL     0x00000232UL
+#define CKM_RIPEMD160                  0x00000240UL
+#define CKM_RIPEMD160_HMAC             0x00000241UL
+#define CKM_RIPEMD160_HMAC_GENERAL     0x00000242UL
+
+#define CKM_SHA256                     0x00000250UL
+#define CKM_SHA256_HMAC                0x00000251UL
+#define CKM_SHA256_HMAC_GENERAL        0x00000252UL
+#define CKM_SHA224                     0x00000255UL
+#define CKM_SHA224_HMAC                0x00000256UL
+#define CKM_SHA224_HMAC_GENERAL        0x00000257UL
+#define CKM_SHA384                     0x00000260UL
+#define CKM_SHA384_HMAC                0x00000261UL
+#define CKM_SHA384_HMAC_GENERAL        0x00000262UL
+#define CKM_SHA512                     0x00000270UL
+#define CKM_SHA512_HMAC                0x00000271UL
+#define CKM_SHA512_HMAC_GENERAL        0x00000272UL
+#define CKM_SECURID_KEY_GEN            0x00000280UL
+#define CKM_SECURID                    0x00000282UL
+#define CKM_HOTP_KEY_GEN               0x00000290UL
+#define CKM_HOTP                       0x00000291UL
+#define CKM_ACTI                       0x000002A0UL
+#define CKM_ACTI_KEY_GEN               0x000002A1UL
+
+#define CKM_CAST_KEY_GEN               0x00000300UL
+#define CKM_CAST_ECB                   0x00000301UL
+#define CKM_CAST_CBC                   0x00000302UL
+#define CKM_CAST_MAC                   0x00000303UL
+#define CKM_CAST_MAC_GENERAL           0x00000304UL
+#define CKM_CAST_CBC_PAD               0x00000305UL
+#define CKM_CAST3_KEY_GEN              0x00000310UL
+#define CKM_CAST3_ECB                  0x00000311UL
+#define CKM_CAST3_CBC                  0x00000312UL
+#define CKM_CAST3_MAC                  0x00000313UL
+#define CKM_CAST3_MAC_GENERAL          0x00000314UL
+#define CKM_CAST3_CBC_PAD              0x00000315UL
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST5_KEY_GEN              0x00000320UL
+#define CKM_CAST128_KEY_GEN            0x00000320UL
+#define CKM_CAST5_ECB                  0x00000321UL
+#define CKM_CAST128_ECB                0x00000321UL
+#define CKM_CAST5_CBC                  0x00000322UL /* Deprecated */
+#define CKM_CAST128_CBC                0x00000322UL
+#define CKM_CAST5_MAC                  0x00000323UL /* Deprecated */
+#define CKM_CAST128_MAC                0x00000323UL
+#define CKM_CAST5_MAC_GENERAL          0x00000324UL /* Deprecated */
+#define CKM_CAST128_MAC_GENERAL        0x00000324UL
+#define CKM_CAST5_CBC_PAD              0x00000325UL /* Deprecated */
+#define CKM_CAST128_CBC_PAD            0x00000325UL
+#define CKM_RC5_KEY_GEN                0x00000330UL
+#define CKM_RC5_ECB                    0x00000331UL
+#define CKM_RC5_CBC                    0x00000332UL
+#define CKM_RC5_MAC                    0x00000333UL
+#define CKM_RC5_MAC_GENERAL            0x00000334UL
+#define CKM_RC5_CBC_PAD                0x00000335UL
+#define CKM_IDEA_KEY_GEN               0x00000340UL
+#define CKM_IDEA_ECB                   0x00000341UL
+#define CKM_IDEA_CBC                   0x00000342UL
+#define CKM_IDEA_MAC                   0x00000343UL
+#define CKM_IDEA_MAC_GENERAL           0x00000344UL
+#define CKM_IDEA_CBC_PAD               0x00000345UL
+#define CKM_GENERIC_SECRET_KEY_GEN     0x00000350UL
+#define CKM_CONCATENATE_BASE_AND_KEY   0x00000360UL
+#define CKM_CONCATENATE_BASE_AND_DATA  0x00000362UL
+#define CKM_CONCATENATE_DATA_AND_BASE  0x00000363UL
+#define CKM_XOR_BASE_AND_DATA          0x00000364UL
+#define CKM_EXTRACT_KEY_FROM_KEY       0x00000365UL
+#define CKM_SSL3_PRE_MASTER_KEY_GEN    0x00000370UL
+#define CKM_SSL3_MASTER_KEY_DERIVE     0x00000371UL
+#define CKM_SSL3_KEY_AND_MAC_DERIVE    0x00000372UL
+
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH  0x00000373UL
+#define CKM_TLS_PRE_MASTER_KEY_GEN     0x00000374UL
+#define CKM_TLS_MASTER_KEY_DERIVE      0x00000375UL
+#define CKM_TLS_KEY_AND_MAC_DERIVE     0x00000376UL
+#define CKM_TLS_MASTER_KEY_DERIVE_DH   0x00000377UL
+
+#define CKM_TLS_PRF                    0x00000378UL
+
+#define CKM_SSL3_MD5_MAC               0x00000380UL
+#define CKM_SSL3_SHA1_MAC              0x00000381UL
+#define CKM_MD5_KEY_DERIVATION         0x00000390UL
+#define CKM_MD2_KEY_DERIVATION         0x00000391UL
+#define CKM_SHA1_KEY_DERIVATION        0x00000392UL
+
+#define CKM_SHA256_KEY_DERIVATION      0x00000393UL
+#define CKM_SHA384_KEY_DERIVATION      0x00000394UL
+#define CKM_SHA512_KEY_DERIVATION      0x00000395UL
+#define CKM_SHA224_KEY_DERIVATION      0x00000396UL
+
+#define CKM_PBE_MD2_DES_CBC            0x000003A0UL
+#define CKM_PBE_MD5_DES_CBC            0x000003A1UL
+#define CKM_PBE_MD5_CAST_CBC           0x000003A2UL
+#define CKM_PBE_MD5_CAST3_CBC          0x000003A3UL
+#define CKM_PBE_MD5_CAST5_CBC          0x000003A4UL /* Deprecated */
+#define CKM_PBE_MD5_CAST128_CBC        0x000003A4UL
+#define CKM_PBE_SHA1_CAST5_CBC         0x000003A5UL /* Deprecated */
+#define CKM_PBE_SHA1_CAST128_CBC       0x000003A5UL
+#define CKM_PBE_SHA1_RC4_128           0x000003A6UL
+#define CKM_PBE_SHA1_RC4_40            0x000003A7UL
+#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003A8UL
+#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003A9UL
+#define CKM_PBE_SHA1_RC2_128_CBC       0x000003AAUL
+#define CKM_PBE_SHA1_RC2_40_CBC        0x000003ABUL
+
+#define CKM_PKCS5_PBKD2                0x000003B0UL
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003C0UL
+
+#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003D0UL
+#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003D1UL
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003D2UL
+#define CKM_WTLS_PRF                        0x000003D3UL
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003D4UL
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003D5UL
+
+#define CKM_TLS10_MAC_SERVER                0x000003D6UL
+#define CKM_TLS10_MAC_CLIENT                0x000003D7UL
+#define CKM_TLS12_MAC                       0x000003D8UL
+#define CKM_TLS12_KDF                       0x000003D9UL
+#define CKM_TLS12_MASTER_KEY_DERIVE         0x000003E0UL
+#define CKM_TLS12_KEY_AND_MAC_DERIVE        0x000003E1UL
+#define CKM_TLS12_MASTER_KEY_DERIVE_DH      0x000003E2UL
+#define CKM_TLS12_KEY_SAFE_DERIVE           0x000003E3UL
+#define CKM_TLS_MAC                         0x000003E4UL
+#define CKM_TLS_KDF                         0x000003E5UL
+
+#define CKM_KEY_WRAP_LYNKS             0x00000400UL
+#define CKM_KEY_WRAP_SET_OAEP          0x00000401UL
+
+#define CKM_CMS_SIG                    0x00000500UL
+#define CKM_KIP_DERIVE                 0x00000510UL
+#define CKM_KIP_WRAP                   0x00000511UL
+#define CKM_KIP_MAC                    0x00000512UL
+
+#define CKM_CAMELLIA_KEY_GEN           0x00000550UL
+#define CKM_CAMELLIA_ECB               0x00000551UL
+#define CKM_CAMELLIA_CBC               0x00000552UL
+#define CKM_CAMELLIA_MAC               0x00000553UL
+#define CKM_CAMELLIA_MAC_GENERAL       0x00000554UL
+#define CKM_CAMELLIA_CBC_PAD           0x00000555UL
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA  0x00000556UL
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA  0x00000557UL
+#define CKM_CAMELLIA_CTR               0x00000558UL
+
+#define CKM_ARIA_KEY_GEN               0x00000560UL
+#define CKM_ARIA_ECB                   0x00000561UL
+#define CKM_ARIA_CBC                   0x00000562UL
+#define CKM_ARIA_MAC                   0x00000563UL
+#define CKM_ARIA_MAC_GENERAL           0x00000564UL
+#define CKM_ARIA_CBC_PAD               0x00000565UL
+#define CKM_ARIA_ECB_ENCRYPT_DATA      0x00000566UL
+#define CKM_ARIA_CBC_ENCRYPT_DATA      0x00000567UL
+
+#define CKM_SEED_KEY_GEN               0x00000650UL
+#define CKM_SEED_ECB                   0x00000651UL
+#define CKM_SEED_CBC                   0x00000652UL
+#define CKM_SEED_MAC                   0x00000653UL
+#define CKM_SEED_MAC_GENERAL           0x00000654UL
+#define CKM_SEED_CBC_PAD               0x00000655UL
+#define CKM_SEED_ECB_ENCRYPT_DATA      0x00000656UL
+#define CKM_SEED_CBC_ENCRYPT_DATA      0x00000657UL
+
+#define CKM_SKIPJACK_KEY_GEN           0x00001000UL
+#define CKM_SKIPJACK_ECB64             0x00001001UL
+#define CKM_SKIPJACK_CBC64             0x00001002UL
+#define CKM_SKIPJACK_OFB64             0x00001003UL
+#define CKM_SKIPJACK_CFB64             0x00001004UL
+#define CKM_SKIPJACK_CFB32             0x00001005UL
+#define CKM_SKIPJACK_CFB16             0x00001006UL
+#define CKM_SKIPJACK_CFB8              0x00001007UL
+#define CKM_SKIPJACK_WRAP              0x00001008UL
+#define CKM_SKIPJACK_PRIVATE_WRAP      0x00001009UL
+#define CKM_SKIPJACK_RELAYX            0x0000100aUL
+#define CKM_KEA_KEY_PAIR_GEN           0x00001010UL
+#define CKM_KEA_KEY_DERIVE             0x00001011UL
+#define CKM_KEA_DERIVE                 0x00001012UL
+#define CKM_FORTEZZA_TIMESTAMP         0x00001020UL
+#define CKM_BATON_KEY_GEN              0x00001030UL
+#define CKM_BATON_ECB128               0x00001031UL
+#define CKM_BATON_ECB96                0x00001032UL
+#define CKM_BATON_CBC128               0x00001033UL
+#define CKM_BATON_COUNTER              0x00001034UL
+#define CKM_BATON_SHUFFLE              0x00001035UL
+#define CKM_BATON_WRAP                 0x00001036UL
+
+#define CKM_ECDSA_KEY_PAIR_GEN         0x00001040UL /* Deprecated */
+#define CKM_EC_KEY_PAIR_GEN            0x00001040UL
+
+#define CKM_ECDSA                      0x00001041UL
+#define CKM_ECDSA_SHA1                 0x00001042UL
+#define CKM_ECDSA_SHA224               0x00001043UL
+#define CKM_ECDSA_SHA256               0x00001044UL
+#define CKM_ECDSA_SHA384               0x00001045UL
+#define CKM_ECDSA_SHA512               0x00001046UL
+
+#define CKM_ECDH1_DERIVE               0x00001050UL
+#define CKM_ECDH1_COFACTOR_DERIVE      0x00001051UL
+#define CKM_ECMQV_DERIVE               0x00001052UL
+
+#define CKM_ECDH_AES_KEY_WRAP          0x00001053UL
+#define CKM_RSA_AES_KEY_WRAP           0x00001054UL
+
+#define CKM_JUNIPER_KEY_GEN            0x00001060UL
+#define CKM_JUNIPER_ECB128             0x00001061UL
+#define CKM_JUNIPER_CBC128             0x00001062UL
+#define CKM_JUNIPER_COUNTER            0x00001063UL
+#define CKM_JUNIPER_SHUFFLE            0x00001064UL
+#define CKM_JUNIPER_WRAP               0x00001065UL
+#define CKM_FASTHASH                   0x00001070UL
+
+#define CKM_AES_KEY_GEN                0x00001080UL
+#define CKM_AES_ECB                    0x00001081UL
+#define CKM_AES_CBC                    0x00001082UL
+#define CKM_AES_MAC                    0x00001083UL
+#define CKM_AES_MAC_GENERAL            0x00001084UL
+#define CKM_AES_CBC_PAD                0x00001085UL
+#define CKM_AES_CTR                    0x00001086UL
+#define CKM_AES_GCM                    0x00001087UL
+#define CKM_AES_CCM                    0x00001088UL
+#define CKM_AES_CTS                    0x00001089UL
+#define CKM_AES_CMAC                   0x0000108AUL
+#define CKM_AES_CMAC_GENERAL           0x0000108BUL
+
+#define CKM_AES_XCBC_MAC               0x0000108CUL
+#define CKM_AES_XCBC_MAC_96            0x0000108DUL
+#define CKM_AES_GMAC                   0x0000108EUL
+
+#define CKM_BLOWFISH_KEY_GEN           0x00001090UL
+#define CKM_BLOWFISH_CBC               0x00001091UL
+#define CKM_TWOFISH_KEY_GEN            0x00001092UL
+#define CKM_TWOFISH_CBC                0x00001093UL
+#define CKM_BLOWFISH_CBC_PAD           0x00001094UL
+#define CKM_TWOFISH_CBC_PAD            0x00001095UL
+
+#define CKM_DES_ECB_ENCRYPT_DATA       0x00001100UL
+#define CKM_DES_CBC_ENCRYPT_DATA       0x00001101UL
+#define CKM_DES3_ECB_ENCRYPT_DATA      0x00001102UL
+#define CKM_DES3_CBC_ENCRYPT_DATA      0x00001103UL
+#define CKM_AES_ECB_ENCRYPT_DATA       0x00001104UL
+#define CKM_AES_CBC_ENCRYPT_DATA       0x00001105UL
+
+#define CKM_GOSTR3410_KEY_PAIR_GEN     0x00001200UL
+#define CKM_GOSTR3410                  0x00001201UL
+#define CKM_GOSTR3410_WITH_GOSTR3411   0x00001202UL
+#define CKM_GOSTR3410_KEY_WRAP         0x00001203UL
+#define CKM_GOSTR3410_DERIVE           0x00001204UL
+#define CKM_GOSTR3411                  0x00001210UL
+#define CKM_GOSTR3411_HMAC             0x00001211UL
+#define CKM_GOST28147_KEY_GEN          0x00001220UL
+#define CKM_GOST28147_ECB              0x00001221UL
+#define CKM_GOST28147                  0x00001222UL
+#define CKM_GOST28147_MAC              0x00001223UL
+#define CKM_GOST28147_KEY_WRAP         0x00001224UL
+
+#define CKM_DSA_PARAMETER_GEN          0x00002000UL
+#define CKM_DH_PKCS_PARAMETER_GEN      0x00002001UL
+#define CKM_X9_42_DH_PARAMETER_GEN     0x00002002UL
+#define CKM_DSA_PROBABLISTIC_PARAMETER_GEN    0x00002003UL
+#define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN    0x00002004UL
+
+#define CKM_AES_OFB                    0x00002104UL
+#define CKM_AES_CFB64                  0x00002105UL
+#define CKM_AES_CFB8                   0x00002106UL
+#define CKM_AES_CFB128                 0x00002107UL
+
+#define CKM_AES_CFB1                   0x00002108UL
+#define CKM_AES_KEY_WRAP               0x00002109UL     /* WAS: 0x00001090 */
+#define CKM_AES_KEY_WRAP_PAD           0x0000210AUL     /* WAS: 0x00001091 */
+
+#define CKM_RSA_PKCS_TPM_1_1           0x00004001UL
+#define CKM_RSA_PKCS_OAEP_TPM_1_1      0x00004002UL
+
+#define CKM_VENDOR_DEFINED             0x80000000UL
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM {
+  CK_MECHANISM_TYPE mechanism;
+  CK_VOID_PTR       pParameter;
+  CK_ULONG          ulParameterLen;  /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM_INFO {
+    CK_ULONG    ulMinKeySize;
+    CK_ULONG    ulMaxKeySize;
+    CK_FLAGS    flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ *      Bit Flag               Mask          Meaning */
+#define CKF_HW                 0x00000001UL  /* performed by HW */
+
+/* Specify whether or not a mechanism can be used for a particular task */
+#define CKF_ENCRYPT            0x00000100UL
+#define CKF_DECRYPT            0x00000200UL
+#define CKF_DIGEST             0x00000400UL
+#define CKF_SIGN               0x00000800UL
+#define CKF_SIGN_RECOVER       0x00001000UL
+#define CKF_VERIFY             0x00002000UL
+#define CKF_VERIFY_RECOVER     0x00004000UL
+#define CKF_GENERATE           0x00008000UL
+#define CKF_GENERATE_KEY_PAIR  0x00010000UL
+#define CKF_WRAP               0x00020000UL
+#define CKF_UNWRAP             0x00040000UL
+#define CKF_DERIVE             0x00080000UL
+
+/* Describe a token's EC capabilities not available in mechanism
+ * information.
+ */
+#define CKF_EC_F_P             0x00100000UL
+#define CKF_EC_F_2M            0x00200000UL
+#define CKF_EC_ECPARAMETERS    0x00400000UL
+#define CKF_EC_NAMEDCURVE      0x00800000UL
+#define CKF_EC_UNCOMPRESS      0x01000000UL
+#define CKF_EC_COMPRESS        0x02000000UL
+
+#define CKF_EXTENSION          0x80000000UL
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+/* CK_RV is a value that identifies the return value of a
+ * Cryptoki function
+ */
+typedef CK_ULONG          CK_RV;
+
+#define CKR_OK                                0x00000000UL
+#define CKR_CANCEL                            0x00000001UL
+#define CKR_HOST_MEMORY                       0x00000002UL
+#define CKR_SLOT_ID_INVALID                   0x00000003UL
+
+#define CKR_GENERAL_ERROR                     0x00000005UL
+#define CKR_FUNCTION_FAILED                   0x00000006UL
+
+#define CKR_ARGUMENTS_BAD                     0x00000007UL
+#define CKR_NO_EVENT                          0x00000008UL
+#define CKR_NEED_TO_CREATE_THREADS            0x00000009UL
+#define CKR_CANT_LOCK                         0x0000000AUL
+
+#define CKR_ATTRIBUTE_READ_ONLY               0x00000010UL
+#define CKR_ATTRIBUTE_SENSITIVE               0x00000011UL
+#define CKR_ATTRIBUTE_TYPE_INVALID            0x00000012UL
+#define CKR_ATTRIBUTE_VALUE_INVALID           0x00000013UL
+
+#define CKR_ACTION_PROHIBITED                 0x0000001BUL
+
+#define CKR_DATA_INVALID                      0x00000020UL
+#define CKR_DATA_LEN_RANGE                    0x00000021UL
+#define CKR_DEVICE_ERROR                      0x00000030UL
+#define CKR_DEVICE_MEMORY                     0x00000031UL
+#define CKR_DEVICE_REMOVED                    0x00000032UL
+#define CKR_ENCRYPTED_DATA_INVALID            0x00000040UL
+#define CKR_ENCRYPTED_DATA_LEN_RANGE          0x00000041UL
+#define CKR_FUNCTION_CANCELED                 0x00000050UL
+#define CKR_FUNCTION_NOT_PARALLEL             0x00000051UL
+
+#define CKR_FUNCTION_NOT_SUPPORTED            0x00000054UL
+
+#define CKR_KEY_HANDLE_INVALID                0x00000060UL
+
+#define CKR_KEY_SIZE_RANGE                    0x00000062UL
+#define CKR_KEY_TYPE_INCONSISTENT             0x00000063UL
+
+#define CKR_KEY_NOT_NEEDED                    0x00000064UL
+#define CKR_KEY_CHANGED                       0x00000065UL
+#define CKR_KEY_NEEDED                        0x00000066UL
+#define CKR_KEY_INDIGESTIBLE                  0x00000067UL
+#define CKR_KEY_FUNCTION_NOT_PERMITTED        0x00000068UL
+#define CKR_KEY_NOT_WRAPPABLE                 0x00000069UL
+#define CKR_KEY_UNEXTRACTABLE                 0x0000006AUL
+
+#define CKR_MECHANISM_INVALID                 0x00000070UL
+#define CKR_MECHANISM_PARAM_INVALID           0x00000071UL
+
+#define CKR_OBJECT_HANDLE_INVALID             0x00000082UL
+#define CKR_OPERATION_ACTIVE                  0x00000090UL
+#define CKR_OPERATION_NOT_INITIALIZED         0x00000091UL
+#define CKR_PIN_INCORRECT                     0x000000A0UL
+#define CKR_PIN_INVALID                       0x000000A1UL
+#define CKR_PIN_LEN_RANGE                     0x000000A2UL
+
+#define CKR_PIN_EXPIRED                       0x000000A3UL
+#define CKR_PIN_LOCKED                        0x000000A4UL
+
+#define CKR_SESSION_CLOSED                    0x000000B0UL
+#define CKR_SESSION_COUNT                     0x000000B1UL
+#define CKR_SESSION_HANDLE_INVALID            0x000000B3UL
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED    0x000000B4UL
+#define CKR_SESSION_READ_ONLY                 0x000000B5UL
+#define CKR_SESSION_EXISTS                    0x000000B6UL
+
+#define CKR_SESSION_READ_ONLY_EXISTS          0x000000B7UL
+#define CKR_SESSION_READ_WRITE_SO_EXISTS      0x000000B8UL
+
+#define CKR_SIGNATURE_INVALID                 0x000000C0UL
+#define CKR_SIGNATURE_LEN_RANGE               0x000000C1UL
+#define CKR_TEMPLATE_INCOMPLETE               0x000000D0UL
+#define CKR_TEMPLATE_INCONSISTENT             0x000000D1UL
+#define CKR_TOKEN_NOT_PRESENT                 0x000000E0UL
+#define CKR_TOKEN_NOT_RECOGNIZED              0x000000E1UL
+#define CKR_TOKEN_WRITE_PROTECTED             0x000000E2UL
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID     0x000000F0UL
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE         0x000000F1UL
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT  0x000000F2UL
+#define CKR_USER_ALREADY_LOGGED_IN            0x00000100UL
+#define CKR_USER_NOT_LOGGED_IN                0x00000101UL
+#define CKR_USER_PIN_NOT_INITIALIZED          0x00000102UL
+#define CKR_USER_TYPE_INVALID                 0x00000103UL
+
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN    0x00000104UL
+#define CKR_USER_TOO_MANY_TYPES               0x00000105UL
+
+#define CKR_WRAPPED_KEY_INVALID               0x00000110UL
+#define CKR_WRAPPED_KEY_LEN_RANGE             0x00000112UL
+#define CKR_WRAPPING_KEY_HANDLE_INVALID       0x00000113UL
+#define CKR_WRAPPING_KEY_SIZE_RANGE           0x00000114UL
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT    0x00000115UL
+#define CKR_RANDOM_SEED_NOT_SUPPORTED         0x00000120UL
+
+#define CKR_RANDOM_NO_RNG                     0x00000121UL
+
+#define CKR_DOMAIN_PARAMS_INVALID             0x00000130UL
+
+#define CKR_CURVE_NOT_SUPPORTED               0x00000140UL
+
+#define CKR_BUFFER_TOO_SMALL                  0x00000150UL
+#define CKR_SAVED_STATE_INVALID               0x00000160UL
+#define CKR_INFORMATION_SENSITIVE             0x00000170UL
+#define CKR_STATE_UNSAVEABLE                  0x00000180UL
+
+#define CKR_CRYPTOKI_NOT_INITIALIZED          0x00000190UL
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED      0x00000191UL
+#define CKR_MUTEX_BAD                         0x000001A0UL
+#define CKR_MUTEX_NOT_LOCKED                  0x000001A1UL
+
+#define CKR_NEW_PIN_MODE                      0x000001B0UL
+#define CKR_NEXT_OTP                          0x000001B1UL
+
+#define CKR_EXCEEDED_MAX_ITERATIONS           0x000001B5UL
+#define CKR_FIPS_SELF_TEST_FAILED             0x000001B6UL
+#define CKR_LIBRARY_LOAD_FAILED               0x000001B7UL
+#define CKR_PIN_TOO_WEAK                      0x000001B8UL
+#define CKR_PUBLIC_KEY_INVALID                0x000001B9UL
+
+#define CKR_FUNCTION_REJECTED                 0x00000200UL
+
+#define CKR_VENDOR_DEFINED                    0x80000000UL
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_NOTIFICATION   event,
+  CK_VOID_PTR       pApplication  /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec
+ * version and pointers of appropriate types to all the
+ * Cryptoki functions
+ */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+  CK_VOID_PTR_PTR ppMutex  /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize
+ */
+typedef struct CK_C_INITIALIZE_ARGS {
+  CK_CREATEMUTEX CreateMutex;
+  CK_DESTROYMUTEX DestroyMutex;
+  CK_LOCKMUTEX LockMutex;
+  CK_UNLOCKMUTEX UnlockMutex;
+  CK_FLAGS flags;
+  CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ *      Bit Flag                           Mask       Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL
+#define CKF_OS_LOCKING_OK                  0x00000002UL
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK     1
+
+/* CK_RSA_PKCS_MGF_TYPE  is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+#define CKG_MGF1_SHA1         0x00000001UL
+#define CKG_MGF1_SHA256       0x00000002UL
+#define CKG_MGF1_SHA384       0x00000003UL
+#define CKG_MGF1_SHA512       0x00000004UL
+#define CKG_MGF1_SHA224       0x00000005UL
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE  is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED    0x00000001UL
+
+/* CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism.
+ */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+        CK_MECHANISM_TYPE hashAlg;
+        CK_RSA_PKCS_MGF_TYPE mgf;
+        CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+        CK_VOID_PTR pSourceData;
+        CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s).
+ */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+        CK_MECHANISM_TYPE    hashAlg;
+        CK_RSA_PKCS_MGF_TYPE mgf;
+        CK_ULONG             sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL                 0x00000001UL
+#define CKD_SHA1_KDF             0x00000002UL
+
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1        0x00000003UL
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004UL
+#define CKD_SHA224_KDF           0x00000005UL
+#define CKD_SHA256_KDF           0x00000006UL
+#define CKD_SHA384_KDF           0x00000007UL
+#define CKD_SHA512_KDF           0x00000008UL
+#define CKD_CPDIVERSIFY_KDF      0x00000009UL
+
+
+/* CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+/*
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs.
+ */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+  CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms
+ */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair
+ */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs
+ */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+  CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism
+ */
+typedef struct CK_KEA_DERIVE_PARAMS {
+  CK_BBOOL      isSender;
+  CK_ULONG      ulRandomLen;
+  CK_BYTE_PTR   pRandomA;
+  CK_BYTE_PTR   pRandomB;
+  CK_ULONG      ulPublicDataLen;
+  CK_BYTE_PTR   pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms.  An instance of CK_RC2_PARAMS just
+ * holds the effective keysize
+ */
+typedef CK_ULONG          CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism
+ */
+typedef struct CK_RC2_CBC_PARAMS {
+  CK_ULONG      ulEffectiveBits;  /* effective bits (1-1024) */
+  CK_BYTE       iv[8];            /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+  CK_ULONG      ulEffectiveBits;  /* effective bits (1-1024) */
+  CK_ULONG      ulMacLength;      /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+  CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms
+ */
+typedef struct CK_RC5_PARAMS {
+  CK_ULONG      ulWordsize;  /* wordsize in bits */
+  CK_ULONG      ulRounds;    /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism
+ */
+typedef struct CK_RC5_CBC_PARAMS {
+  CK_ULONG      ulWordsize;  /* wordsize in bits */
+  CK_ULONG      ulRounds;    /* number of rounds */
+  CK_BYTE_PTR   pIv;         /* pointer to IV */
+  CK_ULONG      ulIvLen;     /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+  CK_ULONG      ulWordsize;   /* wordsize in bits */
+  CK_ULONG      ulRounds;     /* number of rounds */
+  CK_ULONG      ulMacLength;  /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+  CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms.  Its value is the length of
+ * the MAC
+ */
+typedef CK_ULONG          CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+  CK_BYTE      iv[8];
+  CK_BYTE_PTR  pData;
+  CK_ULONG     length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+  CK_BYTE      iv[16];
+  CK_BYTE_PTR  pData;
+  CK_ULONG     length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism
+ */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+  CK_ULONG      ulPasswordLen;
+  CK_BYTE_PTR   pPassword;
+  CK_ULONG      ulPublicDataLen;
+  CK_BYTE_PTR   pPublicData;
+  CK_ULONG      ulPAndGLen;
+  CK_ULONG      ulQLen;
+  CK_ULONG      ulRandomLen;
+  CK_BYTE_PTR   pRandomA;
+  CK_BYTE_PTR   pPrimeP;
+  CK_BYTE_PTR   pBaseG;
+  CK_BYTE_PTR   pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+  CK_SKIPJACK_PRIVATE_WRAP_PARAMS_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism
+ */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+  CK_ULONG      ulOldWrappedXLen;
+  CK_BYTE_PTR   pOldWrappedX;
+  CK_ULONG      ulOldPasswordLen;
+  CK_BYTE_PTR   pOldPassword;
+  CK_ULONG      ulOldPublicDataLen;
+  CK_BYTE_PTR   pOldPublicData;
+  CK_ULONG      ulOldRandomLen;
+  CK_BYTE_PTR   pOldRandomA;
+  CK_ULONG      ulNewPasswordLen;
+  CK_BYTE_PTR   pNewPassword;
+  CK_ULONG      ulNewPublicDataLen;
+  CK_BYTE_PTR   pNewPublicData;
+  CK_ULONG      ulNewRandomLen;
+  CK_BYTE_PTR   pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+  CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+  CK_BYTE_PTR      pInitVector;
+  CK_UTF8CHAR_PTR  pPassword;
+  CK_ULONG         ulPasswordLen;
+  CK_BYTE_PTR      pSalt;
+  CK_ULONG         ulSaltLen;
+  CK_ULONG         ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism
+ */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+  CK_BYTE       bBC;     /* block contents byte */
+  CK_BYTE_PTR   pX;      /* extra data */
+  CK_ULONG      ulXLen;  /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+typedef struct CK_SSL3_RANDOM_DATA {
+  CK_BYTE_PTR  pClientRandom;
+  CK_ULONG     ulClientRandomLen;
+  CK_BYTE_PTR  pServerRandom;
+  CK_ULONG     ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+  CK_SSL3_RANDOM_DATA RandomInfo;
+  CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+  CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+  CK_OBJECT_HANDLE hClientMacSecret;
+  CK_OBJECT_HANDLE hServerMacSecret;
+  CK_OBJECT_HANDLE hClientKey;
+  CK_OBJECT_HANDLE hServerKey;
+  CK_BYTE_PTR      pIVClient;
+  CK_BYTE_PTR      pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+  CK_ULONG                ulMacSizeInBits;
+  CK_ULONG                ulKeySizeInBits;
+  CK_ULONG                ulIVSizeInBits;
+  CK_BBOOL                bIsExport;
+  CK_SSL3_RANDOM_DATA     RandomInfo;
+  CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_PRF_PARAMS {
+  CK_BYTE_PTR  pSeed;
+  CK_ULONG     ulSeedLen;
+  CK_BYTE_PTR  pLabel;
+  CK_ULONG     ulLabelLen;
+  CK_BYTE_PTR  pOutput;
+  CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_RANDOM_DATA {
+  CK_BYTE_PTR pClientRandom;
+  CK_ULONG    ulClientRandomLen;
+  CK_BYTE_PTR pServerRandom;
+  CK_ULONG    ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+  CK_MECHANISM_TYPE   DigestMechanism;
+  CK_WTLS_RANDOM_DATA RandomInfo;
+  CK_BYTE_PTR         pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+  CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+  CK_MECHANISM_TYPE DigestMechanism;
+  CK_BYTE_PTR       pSeed;
+  CK_ULONG          ulSeedLen;
+  CK_BYTE_PTR       pLabel;
+  CK_ULONG          ulLabelLen;
+  CK_BYTE_PTR       pOutput;
+  CK_ULONG_PTR      pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+  CK_OBJECT_HANDLE hMacSecret;
+  CK_OBJECT_HANDLE hKey;
+  CK_BYTE_PTR      pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+  CK_MECHANISM_TYPE       DigestMechanism;
+  CK_ULONG                ulMacSizeInBits;
+  CK_ULONG                ulKeySizeInBits;
+  CK_ULONG                ulIVSizeInBits;
+  CK_ULONG                ulSequenceNumber;
+  CK_BBOOL                bIsExport;
+  CK_WTLS_RANDOM_DATA     RandomInfo;
+  CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_CMS_SIG_PARAMS {
+  CK_OBJECT_HANDLE      certificateHandle;
+  CK_MECHANISM_PTR      pSigningMechanism;
+  CK_MECHANISM_PTR      pDigestMechanism;
+  CK_UTF8CHAR_PTR       pContentType;
+  CK_BYTE_PTR           pRequestedAttributes;
+  CK_ULONG              ulRequestedAttributesLen;
+  CK_BYTE_PTR           pRequiredAttributes;
+  CK_ULONG              ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+  CK_BYTE_PTR pData;
+  CK_ULONG    ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+  CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism.  It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key
+ */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR \
+                        CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+#define CKP_PKCS5_PBKD2_HMAC_SHA1          0x00000001UL
+#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411     0x00000002UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA224        0x00000003UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA256        0x00000004UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA384        0x00000005UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512        0x00000006UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_224    0x00000007UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_256    0x00000008UL
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR \
+                        CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED        0x00000001UL
+
+/* CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+        CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE           saltSource;
+        CK_VOID_PTR                                pSaltSourceData;
+        CK_ULONG                                   ulSaltSourceDataLen;
+        CK_ULONG                                   iterations;
+        CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+        CK_VOID_PTR                                pPrfData;
+        CK_ULONG                                   ulPrfDataLen;
+        CK_UTF8CHAR_PTR                            pPassword;
+        CK_ULONG_PTR                               ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PARAMS2 is a corrected version of the CK_PKCS5_PBKD2_PARAMS
+ * structure that provides the parameters to the CKM_PKCS5_PBKD2 mechanism
+ * noting that the ulPasswordLen field is a CK_ULONG and not a CK_ULONG_PTR.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS2 {
+        CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+        CK_VOID_PTR pSaltSourceData;
+        CK_ULONG ulSaltSourceDataLen;
+        CK_ULONG iterations;
+        CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+        CK_VOID_PTR pPrfData;
+        CK_ULONG ulPrfDataLen;
+        CK_UTF8CHAR_PTR pPassword;
+        CK_ULONG ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS2;
+
+typedef CK_PKCS5_PBKD2_PARAMS2 CK_PTR CK_PKCS5_PBKD2_PARAMS2_PTR;
+
+typedef CK_ULONG CK_OTP_PARAM_TYPE;
+typedef CK_OTP_PARAM_TYPE CK_PARAM_TYPE; /* backward compatibility */
+
+typedef struct CK_OTP_PARAM {
+    CK_OTP_PARAM_TYPE type;
+    CK_VOID_PTR pValue;
+    CK_ULONG ulValueLen;
+} CK_OTP_PARAM;
+
+typedef CK_OTP_PARAM CK_PTR CK_OTP_PARAM_PTR;
+
+typedef struct CK_OTP_PARAMS {
+    CK_OTP_PARAM_PTR pParams;
+    CK_ULONG ulCount;
+} CK_OTP_PARAMS;
+
+typedef CK_OTP_PARAMS CK_PTR CK_OTP_PARAMS_PTR;
+
+typedef struct CK_OTP_SIGNATURE_INFO {
+    CK_OTP_PARAM_PTR pParams;
+    CK_ULONG ulCount;
+} CK_OTP_SIGNATURE_INFO;
+
+typedef CK_OTP_SIGNATURE_INFO CK_PTR CK_OTP_SIGNATURE_INFO_PTR;
+
+#define CK_OTP_VALUE          0UL
+#define CK_OTP_PIN            1UL
+#define CK_OTP_CHALLENGE      2UL
+#define CK_OTP_TIME           3UL
+#define CK_OTP_COUNTER        4UL
+#define CK_OTP_FLAGS          5UL
+#define CK_OTP_OUTPUT_LENGTH  6UL
+#define CK_OTP_OUTPUT_FORMAT  7UL
+
+#define CKF_NEXT_OTP          0x00000001UL
+#define CKF_EXCLUDE_TIME      0x00000002UL
+#define CKF_EXCLUDE_COUNTER   0x00000004UL
+#define CKF_EXCLUDE_CHALLENGE 0x00000008UL
+#define CKF_EXCLUDE_PIN       0x00000010UL
+#define CKF_USER_FRIENDLY_OTP 0x00000020UL
+
+typedef struct CK_KIP_PARAMS {
+    CK_MECHANISM_PTR  pMechanism;
+    CK_OBJECT_HANDLE  hKey;
+    CK_BYTE_PTR       pSeed;
+    CK_ULONG          ulSeedLen;
+} CK_KIP_PARAMS;
+
+typedef CK_KIP_PARAMS CK_PTR CK_KIP_PARAMS_PTR;
+
+typedef struct CK_AES_CTR_PARAMS {
+    CK_ULONG ulCounterBits;
+    CK_BYTE cb[16];
+} CK_AES_CTR_PARAMS;
+
+typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR;
+
+typedef struct CK_GCM_PARAMS {
+    CK_BYTE_PTR       pIv;
+    CK_ULONG          ulIvLen;
+    CK_ULONG          ulIvBits;
+    CK_BYTE_PTR       pAAD;
+    CK_ULONG          ulAADLen;
+    CK_ULONG          ulTagBits;
+} CK_GCM_PARAMS;
+
+typedef CK_GCM_PARAMS CK_PTR CK_GCM_PARAMS_PTR;
+
+typedef struct CK_CCM_PARAMS {
+    CK_ULONG          ulDataLen;
+    CK_BYTE_PTR       pNonce;
+    CK_ULONG          ulNonceLen;
+    CK_BYTE_PTR       pAAD;
+    CK_ULONG          ulAADLen;
+    CK_ULONG          ulMACLen;
+} CK_CCM_PARAMS;
+
+typedef CK_CCM_PARAMS CK_PTR CK_CCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_GCM_PARAMS */
+typedef struct CK_AES_GCM_PARAMS {
+  CK_BYTE_PTR pIv;
+  CK_ULONG ulIvLen;
+  CK_ULONG ulIvBits;
+  CK_BYTE_PTR pAAD;
+  CK_ULONG ulAADLen;
+  CK_ULONG ulTagBits;
+} CK_AES_GCM_PARAMS;
+
+typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_CCM_PARAMS */
+typedef struct CK_AES_CCM_PARAMS {
+    CK_ULONG          ulDataLen;
+    CK_BYTE_PTR       pNonce;
+    CK_ULONG          ulNonceLen;
+    CK_BYTE_PTR       pAAD;
+    CK_ULONG          ulAADLen;
+    CK_ULONG          ulMACLen;
+} CK_AES_CCM_PARAMS;
+
+typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CTR_PARAMS {
+    CK_ULONG          ulCounterBits;
+    CK_BYTE           cb[16];
+} CK_CAMELLIA_CTR_PARAMS;
+
+typedef CK_CAMELLIA_CTR_PARAMS CK_PTR CK_CAMELLIA_CTR_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS {
+    CK_BYTE           iv[16];
+    CK_BYTE_PTR       pData;
+    CK_ULONG          length;
+} CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+                                CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS {
+    CK_BYTE           iv[16];
+    CK_BYTE_PTR       pData;
+    CK_ULONG          length;
+} CK_ARIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_ARIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+                                CK_ARIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_DSA_PARAMETER_GEN_PARAM {
+    CK_MECHANISM_TYPE  hash;
+    CK_BYTE_PTR        pSeed;
+    CK_ULONG           ulSeedLen;
+    CK_ULONG           ulIndex;
+} CK_DSA_PARAMETER_GEN_PARAM;
+
+typedef CK_DSA_PARAMETER_GEN_PARAM CK_PTR CK_DSA_PARAMETER_GEN_PARAM_PTR;
+
+typedef struct CK_ECDH_AES_KEY_WRAP_PARAMS {
+    CK_ULONG           ulAESKeyBits;
+    CK_EC_KDF_TYPE     kdf;
+    CK_ULONG           ulSharedDataLen;
+    CK_BYTE_PTR        pSharedData;
+} CK_ECDH_AES_KEY_WRAP_PARAMS;
+
+typedef CK_ECDH_AES_KEY_WRAP_PARAMS CK_PTR CK_ECDH_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef CK_ULONG CK_JAVA_MIDP_SECURITY_DOMAIN;
+
+typedef CK_ULONG CK_CERTIFICATE_CATEGORY;
+
+typedef struct CK_RSA_AES_KEY_WRAP_PARAMS {
+    CK_ULONG                      ulAESKeyBits;
+    CK_RSA_PKCS_OAEP_PARAMS_PTR   pOAEPParams;
+} CK_RSA_AES_KEY_WRAP_PARAMS;
+
+typedef CK_RSA_AES_KEY_WRAP_PARAMS CK_PTR CK_RSA_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS {
+    CK_SSL3_RANDOM_DATA       RandomInfo;
+    CK_VERSION_PTR            pVersion;
+    CK_MECHANISM_TYPE         prfHashMechanism;
+} CK_TLS12_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_TLS12_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+                                CK_TLS12_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_TLS12_KEY_MAT_PARAMS {
+    CK_ULONG                  ulMacSizeInBits;
+    CK_ULONG                  ulKeySizeInBits;
+    CK_ULONG                  ulIVSizeInBits;
+    CK_BBOOL                  bIsExport;
+    CK_SSL3_RANDOM_DATA       RandomInfo;
+    CK_SSL3_KEY_MAT_OUT_PTR   pReturnedKeyMaterial;
+    CK_MECHANISM_TYPE         prfHashMechanism;
+} CK_TLS12_KEY_MAT_PARAMS;
+
+typedef CK_TLS12_KEY_MAT_PARAMS CK_PTR CK_TLS12_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_KDF_PARAMS {
+    CK_MECHANISM_TYPE         prfMechanism;
+    CK_BYTE_PTR               pLabel;
+    CK_ULONG                  ulLabelLength;
+    CK_SSL3_RANDOM_DATA       RandomInfo;
+    CK_BYTE_PTR               pContextData;
+    CK_ULONG                  ulContextDataLength;
+} CK_TLS_KDF_PARAMS;
+
+typedef CK_TLS_KDF_PARAMS CK_PTR CK_TLS_KDF_PARAMS_PTR;
+
+typedef struct CK_TLS_MAC_PARAMS {
+    CK_MECHANISM_TYPE         prfHashMechanism;
+    CK_ULONG                  ulMacLength;
+    CK_ULONG                  ulServerOrClient;
+} CK_TLS_MAC_PARAMS;
+
+typedef CK_TLS_MAC_PARAMS CK_PTR CK_TLS_MAC_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_DERIVE_PARAMS {
+    CK_EC_KDF_TYPE            kdf;
+    CK_BYTE_PTR               pPublicData;
+    CK_ULONG                  ulPublicDataLen;
+    CK_BYTE_PTR               pUKM;
+    CK_ULONG                  ulUKMLen;
+} CK_GOSTR3410_DERIVE_PARAMS;
+
+typedef CK_GOSTR3410_DERIVE_PARAMS CK_PTR CK_GOSTR3410_DERIVE_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_KEY_WRAP_PARAMS {
+    CK_BYTE_PTR               pWrapOID;
+    CK_ULONG                  ulWrapOIDLen;
+    CK_BYTE_PTR               pUKM;
+    CK_ULONG                  ulUKMLen;
+    CK_OBJECT_HANDLE          hKey;
+} CK_GOSTR3410_KEY_WRAP_PARAMS;
+
+typedef CK_GOSTR3410_KEY_WRAP_PARAMS CK_PTR CK_GOSTR3410_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_SEED_CBC_ENCRYPT_DATA_PARAMS {
+    CK_BYTE                   iv[16];
+    CK_BYTE_PTR               pData;
+    CK_ULONG                  length;
+} CK_SEED_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_SEED_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+                                        CK_SEED_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+#endif /* _PKCS11T_H_ */
+
diff --git a/third_party/botan/src/lib/prov/tpm/tpm.cpp b/third_party/botan/src/lib/prov/tpm/tpm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/tpm/tpm.cpp
@@ -0,0 +1,460 @@
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tpm.h>
+#include <botan/rsa.h>
+#include <botan/hash.h>
+#include <botan/hash_id.h>
+#include <botan/der_enc.h>
+#include <botan/workfactor.h>
+#include <botan/pk_ops.h>
+#include <sstream>
+
+#include <tss/platform.h>
+#include <tss/tspi.h>
+#include <trousers/trousers.h>
+
+// TODO: dynamically load the TPM libraries?
+
+namespace Botan {
+
+namespace {
+
+void tss_error(TSS_RESULT res, const char* expr, const char* file, int line)
+   {
+   std::ostringstream err;
+   err << "TPM error " << Trspi_Error_String(res)
+       << " layer " << Trspi_Error_Layer(res)
+       << " in " << expr << " at " << file << ":" << line;
+
+   throw TPM_Error(err.str());
+   }
+
+TSS_FLAG bit_flag(size_t bits)
+   {
+   switch(bits)
+      {
+      // 512 supported, but ignored and rejected here
+      case 1024:
+         return TSS_KEY_SIZE_1024;
+      case 2048:
+         return TSS_KEY_SIZE_2048;
+
+      // Most? v1.2 TPMs only support 1024 and 2048 bit keys ...
+      case 4096:
+         return TSS_KEY_SIZE_4096;
+      case 8192:
+         return TSS_KEY_SIZE_8192;
+      case 16384:
+         return TSS_KEY_SIZE_16384;
+      default:
+         throw Invalid_Argument("Unsupported TPM key size " + std::to_string(bits));
+      }
+   }
+
+#if 0
+bool is_srk_uuid(const UUID& uuid)
+   {
+   static const uint8_t srk[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+   const std::vector<uint8_t>& b = uuid.binary_value();
+   return (b.size() == 16 && same_mem(b.data(), srk, 16));
+   }
+#endif
+
+#define TSPI_CHECK_SUCCESS(expr) do {   \
+   TSS_RESULT res = expr;           \
+   if(res != TSS_SUCCESS)           \
+      tss_error(res, #expr, __FILE__, __LINE__);         \
+   } while(0)
+
+std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx,
+                                  TSS_HOBJECT obj,
+                                  TSS_FLAG flag,
+                                  TSS_FLAG sub_flag)
+   {
+   BYTE *data = nullptr;
+   UINT32 data_len = 0;
+   TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
+
+   std::vector<uint8_t> r(data, data + data_len);
+
+   TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
+
+   return r;
+   }
+
+void set_policy_secret(TSS_HPOLICY policy, const char* secret)
+   {
+   if(secret)
+      {
+      BYTE* as_b = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(secret));
+      TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+                                                 TSS_SECRET_MODE_PLAIN,
+                                                 std::strlen(secret),
+                                                 as_b));
+      }
+   else
+      {
+      static const uint8_t nullpass[20] = { 0 };
+
+      TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+                                             TSS_SECRET_MODE_SHA1,
+                                             sizeof(nullpass),
+                                             const_cast<BYTE*>(nullpass)));
+      }
+   }
+
+TSS_UUID to_tss_uuid(const UUID& uuid)
+   {
+   static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+   TSS_UUID tss_uuid;
+   typecast_copy(tss_uuid, uuid.binary_value().data());
+   return tss_uuid;
+   }
+
+UUID from_tss_uuid(const TSS_UUID& tss_uuid)
+   {
+   static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+   std::vector<uint8_t> mem(16);
+   typecast_copy(mem.data(), tss_uuid);
+   UUID uuid(std::move(mem));
+   return uuid;
+   }
+
+TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag)
+   {
+   if(flag == TSS_PS_TYPE_USER)
+      return TPM_Storage_Type::User;
+   else if(flag == TSS_PS_TYPE_SYSTEM)
+      return TPM_Storage_Type::System;
+   else
+      throw TPM_Error("Invalid storage flag " + std::to_string(flag));
+   }
+
+std::string format_url(const UUID& uuid, TPM_Storage_Type storage)
+   {
+   std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
+   return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
+   }
+
+std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type)
+   {
+   UUID uuid = from_tss_uuid(tss_uuid);
+
+   return format_url(from_tss_uuid(tss_uuid),
+                     storage_type_from_tss_flag(store_type));
+   }
+
+}
+
+TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) : 
+   m_pin_cb(cb),
+   m_srk_policy(0)
+   {
+   TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
+   TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
+
+   TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
+
+   const TSS_UUID SRK_UUID = TSS_UUID_SRK;
+
+   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
+
+   TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &m_srk_policy));
+   set_policy_secret(m_srk_policy, srk_password);
+
+   // TODO: do we have to cache it?
+   // TODO: try to use SRK with null, if it fails call the pin cb?
+   }
+
+TPM_Context::~TPM_Context()
+   {
+   TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
+   //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
+   TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_srk_policy));
+   TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
+   }
+
+uint32_t TPM_Context::current_counter()
+   {
+   uint32_t r = 0;
+   TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
+   return r;
+   }
+
+void TPM_Context::gen_random(uint8_t out[], size_t out_len)
+   {
+   BYTE* mem;
+   TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, out_len, &mem));
+   copy_mem(out, reinterpret_cast<const uint8_t*>(mem), out_len);
+   TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
+   }
+
+void TPM_Context::stir_random(const uint8_t in[], size_t in_len)
+   {
+   TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, in_len, const_cast<BYTE*>(in)));
+   }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, size_t bits,
+                               const char* key_password) : m_ctx(ctx)
+   {
+   // TODO: can also do OAEP decryption via binding keys
+   // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
+
+   TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
+
+   TSS_HKEY key;
+   TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
+
+   TSPI_CHECK_SUCCESS(::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
+                                         TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
+                                         TSS_SS_RSASSAPKCS1V15_DER));
+
+   TSS_HPOLICY policy;
+   TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
+   set_policy_secret(policy, key_password);
+   TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
+
+   TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
+   m_key = key;
+   }
+
+// reference a registered TPM key
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::string& uuid_str,
+                               TPM_Storage_Type storage_type) :
+   m_ctx(ctx),
+   m_uuid(uuid_str),
+   m_storage(storage_type)
+   {
+   const TSS_FLAG key_ps_type =
+      (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(),
+                                               key_ps_type,
+                                               to_tss_uuid(m_uuid),
+                                               &m_key));
+   }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx,
+                               const std::vector<uint8_t>& blob) : m_ctx(ctx)
+   {
+   TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(m_ctx.handle(), m_ctx.srk(), blob.size(),
+                                               const_cast<uint8_t*>(blob.data()),
+                                               &m_key));
+
+   //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
+   }
+
+std::string TPM_PrivateKey::register_key(TPM_Storage_Type storage_type)
+   {
+   if(!m_uuid.is_valid())
+      {
+      TPM_RNG rng(ctx()); // use system_rng or arg RNG& instead?
+      m_uuid = UUID(rng);
+      m_storage = storage_type;
+
+      const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
+      const TSS_FLAG key_ps_type =
+         (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+      const TSS_UUID srk_uuid = TSS_UUID_SRK;
+
+      TSPI_CHECK_SUCCESS(::Tspi_Context_RegisterKey(m_ctx.handle(),
+                                                m_key,
+                                                key_ps_type,
+                                                key_uuid,
+                                                TSS_PS_TYPE_SYSTEM,
+                                                srk_uuid));
+
+      }
+
+   // Presumably we could re-register in the other store and same UUID
+   // Doesn't seem like what is desired most of the time here
+   if(storage_type != m_storage)
+      {
+      throw TPM_Error("TPM key " + m_uuid.to_string() +
+                      " already registered with different storage type");
+      }
+
+   return format_url(m_uuid, m_storage);
+   }
+
+std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx)
+   {
+   TSS_KM_KEYINFO2* key_info;
+   UINT32 key_info_size;
+
+   // TODO: does the PS type matter here at all?
+   TSPI_CHECK_SUCCESS(::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(),
+                                                          TSS_PS_TYPE_SYSTEM,
+                                                          nullptr,
+                                                          &key_info_size,
+                                                          &key_info));
+
+   std::vector<std::string> r(key_info_size);
+
+   for(size_t i = 0; i != key_info_size; ++i)
+      {
+      r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
+      }
+
+   // TODO: are we supposed to free this memory and if so how?
+   //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
+
+   return r;
+   }
+
+BigInt TPM_PrivateKey::get_n() const
+   {
+   if(m_n == 0)
+      {
+      m_n = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+                                        TSS_TSPATTRIB_RSAKEY_INFO,
+                                        TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
+      }
+
+   return m_n;
+   }
+
+BigInt TPM_PrivateKey::get_e() const
+   {
+   if(m_e == 0)
+      {
+      m_e = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+                                        TSS_TSPATTRIB_RSAKEY_INFO,
+                                        TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
+      }
+
+   return m_e;
+   }
+
+size_t TPM_PrivateKey::estimated_strength() const
+   {
+   return if_work_factor(key_length());
+   }
+
+size_t TPM_PrivateKey::key_length() const
+   {
+   return get_n().bits();
+   }
+
+AlgorithmIdentifier TPM_PrivateKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(),
+                              AlgorithmIdentifier::USE_NULL_PARAM);
+   }
+
+std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const
+   {
+   std::vector<uint8_t> bits;
+   DER_Encoder(bits)
+      .start_cons(SEQUENCE)
+        .encode(get_n())
+        .encode(get_e())
+      .end_cons();
+   return bits;
+   }
+
+secure_vector<uint8_t> TPM_PrivateKey::private_key_bits() const
+   {
+   throw TPM_Error("Private key export not supported for TPM keys");
+   }
+
+std::vector<uint8_t> TPM_PrivateKey::export_blob() const
+   {
+   return get_obj_attr(m_ctx.handle(), m_key,
+                       TSS_TSPATTRIB_KEY_BLOB,
+                       TSS_TSPATTRIB_KEYBLOB_BLOB);
+   }
+
+std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const
+   {
+   return std::unique_ptr<Public_Key>(new RSA_PublicKey(get_n(), get_e()));
+   }
+
+bool TPM_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   return true; // TODO do a kat or pairwise check
+   }
+
+namespace {
+
+class TPM_Signing_Operation final : public PK_Ops::Signature
+   {
+   public:
+      TPM_Signing_Operation(const TPM_PrivateKey& key,
+                            const std::string& hash_name) :
+         m_key(key),
+         m_hash(HashFunction::create(hash_name)),
+         m_hash_id(pkcs_hash_id(hash_name))
+         {
+         }
+
+      size_t signature_length() const override
+         {
+         return m_key.get_n().bytes();
+         }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         m_hash->update(msg, msg_len);
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+         {
+         /*
+         * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
+         * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
+         * concatenation of the hash OID and hash value and signed with just the
+         * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
+         * since it is going to be much faster than pushing data over the LPC bus.
+         */
+         secure_vector<uint8_t> msg_hash = m_hash->final();
+
+         std::vector<uint8_t> id_and_msg;
+         id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
+         id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
+         id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
+
+         TSS_HCONTEXT ctx = m_key.ctx().handle();
+         TSS_HHASH tpm_hash;
+         TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
+         TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, id_and_msg.size(), id_and_msg.data()));
+
+         BYTE* sig_bytes = nullptr;
+         UINT32 sig_len = 0;
+         TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
+         secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
+
+         // TODO: RAII for Context_FreeMemory
+         TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
+
+         // TODO: RAII for Context_CloseObject
+         TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
+
+         return sig;
+         }
+
+   private:
+      const TPM_PrivateKey& m_key;
+      std::unique_ptr<HashFunction> m_hash;
+      std::vector<uint8_t> m_hash_id;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Signature>
+TPM_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                    const std::string& params,
+                                    const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Signature>(new TPM_Signing_Operation(*this, params));
+   }
+
+}
diff --git a/third_party/botan/src/lib/prov/tpm/tpm.h b/third_party/botan/src/lib/prov/tpm/tpm.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/prov/tpm/tpm.h
@@ -0,0 +1,194 @@
+
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TPM_H_
+#define BOTAN_TPM_H_
+
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+#include <botan/rng.h>
+#include <botan/uuid.h>
+#include <functional>
+
+//TODO remove this
+#include <tss/tspi.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) TPM_Error final : public Exception
+   {
+   public:
+      TPM_Error(const std::string& err) : Exception(err) {}
+      ErrorType error_type() const noexcept override { return ErrorType::TPMError; }
+   };
+
+/**
+* Creates a connection to the TPM. All other TPM types take and hold
+* a TPM_Context reference, so all other objects must be deallocated
+* before ~TPM_Context runs.
+*
+* Use nullptr for the srk_password to indicate the well known secret
+* (ie, an unencrypted SRK). This is usually what you want.
+*
+* TODO: handling owner password?
+*/
+class BOTAN_PUBLIC_API(2,0) TPM_Context final
+   {
+   public:
+      /**
+      * User callback for getting the PIN. Will be passed the best available
+      * description of what we are attempting to load.
+      */
+      typedef std::function<std::string (std::string)> pin_cb;
+
+      TPM_Context(pin_cb cb, const char* srk_password);
+
+      ~TPM_Context();
+
+      // Get data from the TPM's RNG, whatever that is
+      void gen_random(uint8_t out[], size_t out_len);
+
+      // Uses Tspi_TPM_StirRandom to add data to TPM's internal pool
+      void stir_random(const uint8_t in[], size_t in_len);
+
+      std::string get_user_pin(const std::string& who)
+         {
+         return m_pin_cb(who);
+         }
+
+      uint32_t current_counter();
+
+      TSS_HCONTEXT handle() const { return m_ctx; }
+      TSS_HKEY srk() const { return m_srk; }
+
+   private:
+      std::function<std::string (std::string)> m_pin_cb;
+      TSS_HCONTEXT m_ctx;
+      TSS_HKEY m_srk;
+      TSS_HTPM m_tpm;
+      TSS_HPOLICY m_srk_policy;
+   };
+
+class BOTAN_PUBLIC_API(2,0) TPM_RNG final : public Hardware_RNG
+   {
+   public:
+      TPM_RNG(TPM_Context& ctx) : m_ctx(ctx) {}
+
+      bool accepts_input() const override { return true; }
+
+      void add_entropy(const uint8_t in[], size_t in_len) override
+         {
+         m_ctx.stir_random(in, in_len);
+         }
+
+      void randomize(uint8_t out[], size_t out_len) override
+         {
+         m_ctx.gen_random(out, out_len);
+         }
+
+      std::string name() const override { return "TPM_RNG"; }
+
+      bool is_seeded() const override { return true; }
+
+   private:
+      TPM_Context& m_ctx;
+};
+
+enum class TPM_Storage_Type { User, System };
+
+/*
+* Also implements the public interface, but does not have usable
+* TODO: derive from RSA_PublicKey???
+*/
+class BOTAN_PUBLIC_API(2,0) TPM_PrivateKey final : public Private_Key
+   {
+   public:
+      // TODO: key import?
+
+      /*
+      * Create a new key on the TPM parented to the SRK
+      * @param bits must be 1024 or 2048
+      */
+      TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password);
+
+      // reference an existing TPM key using URL syntax from GnuTLS
+      // "tpmkey:uuid=79f07ca9-73ac-478a-9093-11ca6702e774;storage=user"
+      //TPM_PrivateKey(TPM_Context& ctx, const std::string& tpm_url);
+
+      TPM_PrivateKey(TPM_Context& ctx,
+                     const std::string& uuid,
+                     TPM_Storage_Type storage_type);
+
+      TPM_PrivateKey(TPM_Context& ctx,
+                     const std::vector<uint8_t>& blob);
+
+      /**
+      * If the key is not currently registered under a known UUID,
+      * generates a new random UUID and registers the key.
+      * Returns the access URL.
+      */
+      std::string register_key(TPM_Storage_Type storage_type);
+
+      /**
+      * Returns a copy of the public key
+      */
+      std::unique_ptr<Public_Key> public_key() const;
+
+      std::vector<uint8_t> export_blob() const;
+
+      TPM_Context& ctx() const { return m_ctx; }
+
+      TSS_HKEY handle() const { return m_key; }
+
+      /*
+      * Returns the list of all keys (in URL format) registered with the system
+      */
+      static std::vector<std::string> registered_keys(TPM_Context& ctx);
+
+      size_t estimated_strength() const override;
+
+      size_t key_length() const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      BigInt get_n() const;
+
+      BigInt get_e() const;
+
+      std::string algo_name() const override { return "RSA"; } // ???
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+
+   private:
+      TPM_Context& m_ctx;
+      TSS_HKEY m_key;
+
+      // Only set for registered keys
+      UUID m_uuid;
+      TPM_Storage_Type m_storage;
+
+      // Lazily computed in get_n, get_e
+      mutable BigInt m_n, m_e;
+   };
+
+// TODO: NVRAM interface
+// TODO: PCR measurement, writing, key locking
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/psk_db/psk_db.cpp b/third_party/botan/src/lib/psk_db/psk_db.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/psk_db/psk_db.cpp
@@ -0,0 +1,105 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/psk_db.h>
+#include <botan/exceptn.h>
+#include <botan/nist_keywrap.h>
+#include <botan/base64.h>
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+Encrypted_PSK_Database::Encrypted_PSK_Database(const secure_vector<uint8_t>& master_key)
+   {
+   m_cipher = BlockCipher::create_or_throw("AES-256");
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_hmac->set_key(master_key);
+
+   m_cipher->set_key(m_hmac->process("wrap"));
+   m_hmac->set_key(m_hmac->process("hmac"));
+   }
+
+Encrypted_PSK_Database::~Encrypted_PSK_Database()
+   {
+   // for ~unique_ptr
+   }
+
+std::set<std::string> Encrypted_PSK_Database::list_names() const
+   {
+   const std::set<std::string> encrypted_names = kv_get_all();
+
+   std::set<std::string> names;
+
+   for(std::string enc_name : encrypted_names)
+      {
+      try
+         {
+         const secure_vector<uint8_t> raw_name = base64_decode(enc_name);
+         const secure_vector<uint8_t> name_bits =
+            nist_key_unwrap_padded(raw_name.data(), raw_name.size(), *m_cipher);
+
+         std::string pt_name(cast_uint8_ptr_to_char(name_bits.data()), name_bits.size());
+         names.insert(pt_name);
+         }
+      catch(Invalid_Authentication_Tag&)
+         {
+         }
+      }
+
+   return names;
+   }
+
+void Encrypted_PSK_Database::remove(const std::string& name)
+   {
+   const std::vector<uint8_t> wrapped_name =
+      nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+                           name.size(),
+                           *m_cipher);
+
+   this->kv_del(base64_encode(wrapped_name));
+   }
+
+secure_vector<uint8_t> Encrypted_PSK_Database::get(const std::string& name) const
+   {
+   const std::vector<uint8_t> wrapped_name =
+      nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+                           name.size(),
+                           *m_cipher);
+
+   const std::string val_base64 = kv_get(base64_encode(wrapped_name));
+
+   if(val_base64.empty())
+      throw Invalid_Argument("Named PSK not located");
+
+   const secure_vector<uint8_t> val = base64_decode(val_base64);
+
+   std::unique_ptr<BlockCipher> wrap_cipher(m_cipher->clone());
+   wrap_cipher->set_key(m_hmac->process(wrapped_name));
+
+   return nist_key_unwrap_padded(val.data(), val.size(), *wrap_cipher);
+   }
+
+void Encrypted_PSK_Database::set(const std::string& name, const uint8_t val[], size_t len)
+   {
+   /*
+   * Both as a basic precaution wrt key seperation, and specifically to prevent
+   * cut-and-paste attacks against the database, each PSK is encrypted with a
+   * distinct key which is derived by hashing the wrapped key name with HMAC.
+   */
+   const std::vector<uint8_t> wrapped_name =
+      nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+                           name.size(),
+                           *m_cipher);
+
+   std::unique_ptr<BlockCipher> wrap_cipher(m_cipher->clone());
+   wrap_cipher->set_key(m_hmac->process(wrapped_name));
+   const std::vector<uint8_t> wrapped_key = nist_key_wrap_padded(val, len, *wrap_cipher);
+
+   this->kv_set(base64_encode(wrapped_name), base64_encode(wrapped_key));
+   }
+
+}
diff --git a/third_party/botan/src/lib/psk_db/psk_db.h b/third_party/botan/src/lib/psk_db/psk_db.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/psk_db/psk_db.h
@@ -0,0 +1,146 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PSK_DB_H_
+#define BOTAN_PSK_DB_H_
+
+#include <botan/secmem.h>
+#include <memory>
+#include <string>
+#include <set>
+
+namespace Botan {
+
+class BlockCipher;
+class MessageAuthenticationCode;
+
+/**
+* This is an interface to a generic PSK (pre-shared key) database.
+* It might be implemented as a plaintext storage or via some mechanism
+* that encrypts the keys and/or values.
+*/
+class BOTAN_PUBLIC_API(2,4) PSK_Database
+   {
+   public:
+      /**
+      * Return the set of names for which get() will return a value.
+      */
+      virtual std::set<std::string> list_names() const = 0;
+
+      /**
+      * Return the value associated with the specified @param name or otherwise
+      * throw an exception.
+      */
+      virtual secure_vector<uint8_t> get(const std::string& name) const = 0;
+
+      /**
+      * Set a value that can later be accessed with get().
+      * If name already exists in the database, the old value will be overwritten.
+      */
+      virtual void set(const std::string& name, const uint8_t psk[], size_t psk_len) = 0;
+
+      /**
+      * Remove a PSK from the database
+      */
+      virtual void remove(const std::string& name) = 0;
+
+      /**
+      * Returns if the values in the PSK database are encrypted. If
+      * false, saved values are being stored in plaintext.
+      */
+      virtual bool is_encrypted() const = 0;
+
+      /**
+      * Get a PSK in the form of a string (eg if the PSK is a password)
+      */
+      std::string get_str(const std::string& name) const
+         {
+         secure_vector<uint8_t> psk = get(name);
+         return std::string(cast_uint8_ptr_to_char(psk.data()), psk.size());
+         }
+
+      void set_str(const std::string& name, const std::string& psk)
+         {
+         set(name, cast_char_ptr_to_uint8(psk.data()), psk.size());
+         }
+
+      template<typename Alloc>
+      void set_vec(const std::string& name,
+                   const std::vector<uint8_t, Alloc>& psk)
+
+         {
+         set(name, psk.data(), psk.size());
+         }
+
+      virtual ~PSK_Database() = default;
+   };
+
+/**
+* A mixin for an encrypted PSK database.
+* Both keys and values are encrypted with NIST AES-256 key wrapping.
+* Values are padded to obscure their length before encryption, allowing
+* it to be used as a password vault.
+*
+* Subclasses must implement the virtual calls to handle storing and
+* getting raw (base64 encoded) values.
+*/
+class BOTAN_PUBLIC_API(2,4) Encrypted_PSK_Database : public PSK_Database
+   {
+   public:
+      /**
+      * @param master_key specifies the master key used to encrypt all
+      * keys and value. It can be of any length, but should be at least 256 bits.
+      *
+      * Subkeys for the cryptographic algorithms used are derived from this
+      * master key. No key stretching is performed; if encrypting a PSK database
+      * using a password, it is recommended to use PBKDF2 to derive the database
+      * master key.
+      */
+      Encrypted_PSK_Database(const secure_vector<uint8_t>& master_key);
+
+      ~Encrypted_PSK_Database();
+
+      std::set<std::string> list_names() const override;
+
+      secure_vector<uint8_t> get(const std::string& name) const override;
+
+      void set(const std::string& name, const uint8_t psk[], size_t psk_len) override;
+
+      void remove(const std::string& name) override;
+
+      bool is_encrypted() const override { return true; }
+
+   protected:
+      /**
+      * Save a encrypted (name.value) pair to the database. Both will be base64 encoded strings.
+      */
+      virtual void kv_set(const std::string& index, const std::string& value) = 0;
+
+      /**
+      * Get a value previously saved with set_raw_value. Should return an empty
+      * string if index is not found.
+      */
+      virtual std::string kv_get(const std::string& index) const = 0;
+
+      /**
+      * Remove an index
+      */
+      virtual void kv_del(const std::string& index) = 0;
+
+      /**
+      * Return all indexes in the table.
+      */
+      virtual std::set<std::string> kv_get_all() const = 0;
+
+   private:
+      std::unique_ptr<BlockCipher> m_cipher;
+      std::unique_ptr<MessageAuthenticationCode> m_hmac;
+      secure_vector<uint8_t> m_wrap_key;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/psk_db/psk_db_sql.cpp b/third_party/botan/src/lib/psk_db/psk_db_sql.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/psk_db/psk_db_sql.cpp
@@ -0,0 +1,69 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/psk_db_sql.h>
+
+namespace Botan {
+
+Encrypted_PSK_Database_SQL::Encrypted_PSK_Database_SQL(const secure_vector<uint8_t>& master_key,
+                                                       std::shared_ptr<SQL_Database> db,
+                                                       const std::string& table_name) :
+   Encrypted_PSK_Database(master_key),
+   m_db(db),
+   m_table_name(table_name)
+   {
+   m_db->create_table(
+      "create table if not exists " + m_table_name +
+      "(psk_name TEXT PRIMARY KEY, psk_value TEXT)");
+   }
+
+void Encrypted_PSK_Database_SQL::kv_del(const std::string& name)
+   {
+   auto stmt = m_db->new_statement("delete from " + m_table_name + " where psk_name=?1");
+   stmt->bind(1, name);
+   stmt->spin();
+   }
+
+void Encrypted_PSK_Database_SQL::kv_set(const std::string& name, const std::string& value)
+   {
+   auto stmt = m_db->new_statement("insert or replace into " + m_table_name + " values(?1, ?2)");
+
+   stmt->bind(1, name);
+   stmt->bind(2, value);
+
+   stmt->spin();
+   }
+
+std::string Encrypted_PSK_Database_SQL::kv_get(const std::string& name) const
+   {
+   auto stmt = m_db->new_statement("select psk_value from " + m_table_name +
+                                   " where psk_name = ?1");
+
+   stmt->bind(1, name);
+
+   while(stmt->step())
+      {
+      return stmt->get_str(0);
+      }
+   return "";
+   }
+
+std::set<std::string> Encrypted_PSK_Database_SQL::kv_get_all() const
+   {
+   std::set<std::string> names;
+
+   auto stmt = m_db->new_statement("select psk_name from " + m_table_name);
+
+   while(stmt->step())
+      {
+      names.insert(stmt->get_str(0));
+      }
+
+   return names;
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/psk_db/psk_db_sql.h b/third_party/botan/src/lib/psk_db/psk_db_sql.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/psk_db/psk_db_sql.h
@@ -0,0 +1,34 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PSK_DB_SQL_H_
+#define BOTAN_PSK_DB_SQL_H_
+
+#include <botan/psk_db.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,4) Encrypted_PSK_Database_SQL : public Encrypted_PSK_Database
+   {
+   public:
+      Encrypted_PSK_Database_SQL(const secure_vector<uint8_t>& master_key,
+                                 std::shared_ptr<SQL_Database> db,
+                                 const std::string& table_name);
+
+   private:
+      void kv_set(const std::string& index, const std::string& value) override;
+      std::string kv_get(const std::string& index) const override;
+      void kv_del(const std::string& index) override;
+      std::set<std::string> kv_get_all() const override;
+
+      std::shared_ptr<SQL_Database> m_db;
+      const std::string m_table_name;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/blinding.cpp b/third_party/botan/src/lib/pubkey/blinding.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/blinding.cpp
@@ -0,0 +1,66 @@
+/*
+* Blinding for public key operations
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blinding.h>
+
+namespace Botan {
+
+Blinder::Blinder(const BigInt& modulus,
+                 RandomNumberGenerator& rng,
+                 std::function<BigInt (const BigInt&)> fwd,
+                 std::function<BigInt (const BigInt&)> inv) :
+      m_reducer(modulus),
+      m_rng(rng),
+      m_fwd_fn(fwd),
+      m_inv_fn(inv),
+      m_modulus_bits(modulus.bits()),
+      m_e{},
+      m_d{},
+      m_counter{}
+   {
+   const BigInt k = blinding_nonce();
+   m_e = m_fwd_fn(k);
+   m_d = m_inv_fn(k);
+   }
+
+BigInt Blinder::blinding_nonce() const
+   {
+   return BigInt(m_rng, m_modulus_bits - 1);
+   }
+
+BigInt Blinder::blind(const BigInt& i) const
+   {
+   if(!m_reducer.initialized())
+      throw Invalid_State("Blinder not initialized, cannot blind");
+
+   ++m_counter;
+
+   if((BOTAN_BLINDING_REINIT_INTERVAL > 0) && (m_counter > BOTAN_BLINDING_REINIT_INTERVAL))
+      {
+      const BigInt k = blinding_nonce();
+      m_e = m_fwd_fn(k);
+      m_d = m_inv_fn(k);
+      m_counter = 0;
+      }
+   else
+      {
+      m_e = m_reducer.square(m_e);
+      m_d = m_reducer.square(m_d);
+      }
+
+   return m_reducer.multiply(i, m_e);
+   }
+
+BigInt Blinder::unblind(const BigInt& i) const
+   {
+   if(!m_reducer.initialized())
+      throw Invalid_State("Blinder not initialized, cannot unblind");
+
+   return m_reducer.multiply(i, m_d);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/blinding.h b/third_party/botan/src/lib/pubkey/blinding.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/blinding.h
@@ -0,0 +1,80 @@
+/*
+* Blinding for public key operations
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLINDER_H_
+#define BOTAN_BLINDER_H_
+
+#include <botan/bigint.h>
+#include <botan/reducer.h>
+#include <functional>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blinding.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Blinding Function Object.
+*/
+class BOTAN_PUBLIC_API(2,0) Blinder final
+   {
+   public:
+      /**
+      * Blind a value.
+      * The blinding nonce k is freshly generated after
+      * BOTAN_BLINDING_REINIT_INTERVAL calls to blind().
+      * BOTAN_BLINDING_REINIT_INTERVAL = 0 means a fresh
+      * nonce is only generated once. On every other call,
+      * an updated nonce is used for blinding: k' = k*k mod n.
+      * @param x value to blind
+      * @return blinded value
+      */
+      BigInt blind(const BigInt& x) const;
+
+      /**
+      * Unblind a value.
+      * @param x value to unblind
+      * @return unblinded value
+      */
+      BigInt unblind(const BigInt& x) const;
+
+      /**
+      * @param modulus the modulus
+      * @param rng the RNG to use for generating the nonce
+      * @param fwd_func a function that calculates the modular
+      * exponentiation of the public exponent and the given value (the nonce)
+      * @param inv_func a function that calculates the modular inverse
+      * of the given value (the nonce)
+      */
+      Blinder(const BigInt& modulus,
+              RandomNumberGenerator& rng,
+              std::function<BigInt (const BigInt&)> fwd_func,
+              std::function<BigInt (const BigInt&)> inv_func);
+
+      Blinder(const Blinder&) = delete;
+
+      Blinder& operator=(const Blinder&) = delete;
+
+      RandomNumberGenerator& rng() const { return m_rng; }
+
+   private:
+      BigInt blinding_nonce() const;
+
+      Modular_Reducer m_reducer;
+      RandomNumberGenerator& m_rng;
+      std::function<BigInt (const BigInt&)> m_fwd_fn;
+      std::function<BigInt (const BigInt&)> m_inv_fn;
+      size_t m_modulus_bits = 0;
+
+      mutable BigInt m_e, m_d;
+      mutable size_t m_counter = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp b/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp
@@ -0,0 +1,51 @@
+/*
+* CECPQ1 (x25519 + NewHope)
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cecpq1.h>
+#include <botan/newhope.h>
+#include <botan/curve25519.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void CECPQ1_offer(uint8_t send[CECPQ1_OFFER_BYTES],
+                  CECPQ1_key* offer_key_output,
+                  RandomNumberGenerator& rng)
+   {
+   offer_key_output->m_x25519 = rng.random_vec(32);
+   curve25519_basepoint(send, offer_key_output->m_x25519.data());
+
+   newhope_keygen(send + 32, &offer_key_output->m_newhope,
+                  rng, Newhope_Mode::BoringSSL);
+   }
+
+void CECPQ1_accept(uint8_t shared_key[CECPQ1_SHARED_KEY_BYTES],
+                   uint8_t send[CECPQ1_ACCEPT_BYTES],
+                   const uint8_t received[CECPQ1_OFFER_BYTES],
+                   RandomNumberGenerator& rng)
+   {
+   secure_vector<uint8_t> x25519_key = rng.random_vec(32);
+
+   curve25519_basepoint(send, x25519_key.data());
+
+   curve25519_donna(shared_key, x25519_key.data(), received);
+
+   newhope_sharedb(shared_key + 32, send + 32, received + 32,
+                   rng, Newhope_Mode::BoringSSL);
+   }
+
+void CECPQ1_finish(uint8_t shared_key[CECPQ1_SHARED_KEY_BYTES],
+                   const CECPQ1_key& offer_key,
+                   const uint8_t received[CECPQ1_ACCEPT_BYTES])
+   {
+   curve25519_donna(shared_key, offer_key.m_x25519.data(), received);
+
+   newhope_shareda(shared_key + 32, &offer_key.m_newhope, received + 32,
+                   Newhope_Mode::BoringSSL);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h b/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h
@@ -0,0 +1,38 @@
+/*
+* CECPQ1 (x25519 + NewHope)
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CECPQ1_H_
+#define BOTAN_CECPQ1_H_
+
+#include <botan/secmem.h>
+#include <botan/newhope.h>
+
+namespace Botan {
+
+class CECPQ1_key final
+   {
+   public:
+      secure_vector<uint8_t> m_x25519;
+      newhope_poly m_newhope;
+   };
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_offer(uint8_t* offer_message,
+                            CECPQ1_key* offer_key_output,
+                            RandomNumberGenerator& rng);
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_accept(uint8_t* shared_key,
+                             uint8_t* accept_message,
+                             const uint8_t* offer_message,
+                             RandomNumberGenerator& rng);
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_finish(uint8_t* shared_key,
+                             const CECPQ1_key& offer_key,
+                             const uint8_t* accept_message);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp b/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp
@@ -0,0 +1,143 @@
+/*
+* Curve25519
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve25519.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void curve25519_basepoint(uint8_t mypublic[32], const uint8_t secret[32])
+   {
+   const uint8_t basepoint[32] = { 9 };
+   curve25519_donna(mypublic, secret, basepoint);
+   }
+
+namespace {
+
+void size_check(size_t size, const char* thing)
+   {
+   if(size != 32)
+      throw Decoding_Error("Invalid size " + std::to_string(size) + " for Curve25519 " + thing);
+   }
+
+secure_vector<uint8_t> curve25519(const secure_vector<uint8_t>& secret,
+                               const uint8_t pubval[32])
+   {
+   secure_vector<uint8_t> out(32);
+   curve25519_donna(out.data(), secret.data(), pubval);
+   return out;
+   }
+
+}
+
+AlgorithmIdentifier Curve25519_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   }
+
+bool Curve25519_PublicKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   return true; // no tests possible?
+   }
+
+Curve25519_PublicKey::Curve25519_PublicKey(const AlgorithmIdentifier&,
+                                           const std::vector<uint8_t>& key_bits)
+   {
+   m_public = key_bits;
+
+   size_check(m_public.size(), "public key");
+   }
+
+std::vector<uint8_t> Curve25519_PublicKey::public_key_bits() const
+   {
+   return m_public;
+   }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(const secure_vector<uint8_t>& secret_key)
+   {
+   if(secret_key.size() != 32)
+     throw Decoding_Error("Invalid size for Curve25519 private key");
+
+   m_public.resize(32);
+   m_private = secret_key;
+   curve25519_basepoint(m_public.data(), m_private.data());
+   }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(RandomNumberGenerator& rng)
+   {
+   m_private = rng.random_vec(32);
+   m_public.resize(32);
+   curve25519_basepoint(m_public.data(), m_private.data());
+   }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(const AlgorithmIdentifier&,
+                                             const secure_vector<uint8_t>& key_bits)
+   {
+   BER_Decoder(key_bits).decode(m_private, OCTET_STRING).discard_remaining();
+
+   size_check(m_private.size(), "private key");
+   m_public.resize(32);
+   curve25519_basepoint(m_public.data(), m_private.data());
+   }
+
+secure_vector<uint8_t> Curve25519_PrivateKey::private_key_bits() const
+   {
+   return DER_Encoder().encode(m_private, OCTET_STRING).get_contents();
+   }
+
+bool Curve25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   std::vector<uint8_t> public_point(32);
+   curve25519_basepoint(public_point.data(), m_private.data());
+   return public_point == m_public;
+   }
+
+secure_vector<uint8_t> Curve25519_PrivateKey::agree(const uint8_t w[], size_t w_len) const
+   {
+   size_check(w_len, "public value");
+   return curve25519(m_private, w);
+   }
+
+namespace {
+
+/**
+* Curve25519 operation
+*/
+class Curve25519_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+   {
+   public:
+
+      Curve25519_KA_Operation(const Curve25519_PrivateKey& key, const std::string& kdf) :
+         PK_Ops::Key_Agreement_with_KDF(kdf),
+         m_key(key) {}
+
+      size_t agreed_value_size() const override { return 32; }
+
+      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+         {
+         return m_key.agree(w, w_len);
+         }
+   private:
+      const Curve25519_PrivateKey& m_key;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+Curve25519_PrivateKey::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
+                                               const std::string& params,
+                                               const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Key_Agreement>(new Curve25519_KA_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/curve25519/curve25519.h b/third_party/botan/src/lib/pubkey/curve25519/curve25519.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/curve25519/curve25519.h
@@ -0,0 +1,123 @@
+/*
+* Curve25519
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CURVE_25519_H_
+#define BOTAN_CURVE_25519_H_
+
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Curve25519_PublicKey : public virtual Public_Key
+   {
+   public:
+      std::string algo_name() const override { return "Curve25519"; }
+
+      size_t estimated_strength() const override { return 128; }
+
+      size_t key_length() const override { return 255; }
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      std::vector<uint8_t> public_value() const { return m_public; }
+
+      /**
+      * Create a Curve25519 Public Key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      Curve25519_PublicKey(const AlgorithmIdentifier& alg_id,
+                           const std::vector<uint8_t>& key_bits);
+
+      /**
+      * Create a Curve25519 Public Key.
+      * @param pub 32-byte raw public key
+      */
+      explicit Curve25519_PublicKey(const std::vector<uint8_t>& pub) : m_public(pub) {}
+
+      /**
+      * Create a Curve25519 Public Key.
+      * @param pub 32-byte raw public key
+      */
+      explicit Curve25519_PublicKey(const secure_vector<uint8_t>& pub) :
+         m_public(pub.begin(), pub.end()) {}
+
+   protected:
+      Curve25519_PublicKey() = default;
+      std::vector<uint8_t> m_public;
+   };
+
+class BOTAN_PUBLIC_API(2,0) Curve25519_PrivateKey final : public Curve25519_PublicKey,
+                                        public virtual Private_Key,
+                                        public virtual PK_Key_Agreement_Key
+   {
+   public:
+      /**
+      * Construct a private key from the specified parameters.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits PKCS #8 structure
+      */
+      Curve25519_PrivateKey(const AlgorithmIdentifier& alg_id,
+                            const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Generate a private key.
+      * @param rng the RNG to use
+      */
+      explicit Curve25519_PrivateKey(RandomNumberGenerator& rng);
+
+      /**
+      * Construct a private key from the specified parameters.
+      * @param secret_key the private key
+      */
+      explicit Curve25519_PrivateKey(const secure_vector<uint8_t>& secret_key);
+
+      std::vector<uint8_t> public_value() const override { return Curve25519_PublicKey::public_value(); }
+
+      secure_vector<uint8_t> agree(const uint8_t w[], size_t w_len) const;
+
+      const secure_vector<uint8_t>& get_x() const { return m_private; }
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const override;
+
+   private:
+      secure_vector<uint8_t> m_private;
+   };
+
+typedef Curve25519_PublicKey X25519_PublicKey;
+typedef Curve25519_PrivateKey X25519_PrivateKey;
+
+/*
+* The types above are just wrappers for curve25519_donna, plus defining
+* encodings for public and private keys.
+*/
+void BOTAN_PUBLIC_API(2,0) curve25519_donna(uint8_t mypublic[32],
+                                const uint8_t secret[32],
+                                const uint8_t basepoint[32]);
+
+/**
+* Exponentiate by the x25519 base point
+* @param mypublic output value
+* @param secret random scalar
+*/
+void BOTAN_PUBLIC_API(2,0) curve25519_basepoint(uint8_t mypublic[32],
+                                    const uint8_t secret[32]);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/curve25519/donna.cpp b/third_party/botan/src/lib/pubkey/curve25519/donna.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/curve25519/donna.cpp
@@ -0,0 +1,464 @@
+/*
+* Based on curve25519-donna-c64.c from github.com/agl/curve25519-donna
+* revision 80ad9b9930c9baef5829dd2a235b6b7646d32a8e
+*
+* Further changes
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/* Copyright 2008, Google Inc.
+* All rights reserved.
+*
+* Code released into the public domain.
+*
+* curve25519-donna: Curve25519 elliptic curve, public key function
+*
+* https://code.google.com/p/curve25519-donna/
+*
+* Adam Langley <agl@imperialviolet.org>
+*
+* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+*
+* More information about curve25519 can be found here
+*   https://cr.yp.to/ecdh.html
+*
+* djb's sample implementation of curve25519 is written in a special assembly
+* language called qhasm and uses the floating point registers.
+*
+* This is, almost, a clean room reimplementation from the curve25519 paper. It
+* uses many of the tricks described therein. Only the crecip function is taken
+* from the sample implementation.
+*/
+
+#include <botan/curve25519.h>
+#include <botan/mul128.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/donna128.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+typedef donna128 uint128_t;
+#endif
+
+/* Sum two numbers: output += in */
+inline void fsum(uint64_t out[5], const uint64_t in[5])
+   {
+   out[0] += in[0];
+   out[1] += in[1];
+   out[2] += in[2];
+   out[3] += in[3];
+   out[4] += in[4];
+   }
+
+/* Find the difference of two numbers: out = in - out
+* (note the order of the arguments!)
+*
+* Assumes that out[i] < 2**52
+* On return, out[i] < 2**55
+*/
+inline void fdifference_backwards(uint64_t out[5], const uint64_t in[5])
+   {
+   /* 152 is 19 << 3 */
+   const uint64_t two54m152 = (static_cast<uint64_t>(1) << 54) - 152;
+   const uint64_t two54m8   = (static_cast<uint64_t>(1) << 54) - 8;
+
+   out[0] = in[0] + two54m152 - out[0];
+   out[1] = in[1] + two54m8 - out[1];
+   out[2] = in[2] + two54m8 - out[2];
+   out[3] = in[3] + two54m8 - out[3];
+   out[4] = in[4] + two54m8 - out[4];
+   }
+
+inline void fadd_sub(uint64_t x[5],
+                     uint64_t y[5])
+   {
+   // TODO merge these and avoid the tmp array
+   uint64_t tmp[5];
+   copy_mem(tmp, y, 5);
+   fsum(y, x);
+   fdifference_backwards(x, tmp);  // does x - z
+   }
+
+/* Multiply a number by a scalar: out = in * scalar */
+inline void fscalar_product(uint64_t out[5], const uint64_t in[5], const uint64_t scalar)
+   {
+   uint128_t a = uint128_t(in[0]) * scalar;
+   out[0] = a & 0x7ffffffffffff;
+
+   a = uint128_t(in[1]) * scalar + carry_shift(a, 51);
+   out[1] = a & 0x7ffffffffffff;
+
+   a = uint128_t(in[2]) * scalar + carry_shift(a, 51);
+   out[2] = a & 0x7ffffffffffff;
+
+   a = uint128_t(in[3]) * scalar + carry_shift(a, 51);
+   out[3] = a & 0x7ffffffffffff;
+
+   a = uint128_t(in[4]) * scalar + carry_shift(a, 51);
+   out[4] = a & 0x7ffffffffffff;
+
+   out[0] += carry_shift(a, 51) * 19;
+   }
+
+/* Multiply two numbers: out = in2 * in
+*
+* out must be distinct to both inputs. The inputs are reduced coefficient
+* form, the output is not.
+*
+* Assumes that in[i] < 2**55 and likewise for in2.
+* On return, out[i] < 2**52
+*/
+inline void fmul(uint64_t out[5], const uint64_t in[5], const uint64_t in2[5])
+   {
+   const uint128_t s0 = in2[0];
+   const uint128_t s1 = in2[1];
+   const uint128_t s2 = in2[2];
+   const uint128_t s3 = in2[3];
+   const uint128_t s4 = in2[4];
+
+   uint64_t r0 = in[0];
+   uint64_t r1 = in[1];
+   uint64_t r2 = in[2];
+   uint64_t r3 = in[3];
+   uint64_t r4 = in[4];
+
+   uint128_t t0 = r0 * s0;
+   uint128_t t1 = r0 * s1 + r1 * s0;
+   uint128_t t2 = r0 * s2 + r2 * s0 + r1 * s1;
+   uint128_t t3 = r0 * s3 + r3 * s0 + r1 * s2 + r2 * s1;
+   uint128_t t4 = r0 * s4 + r4 * s0 + r3 * s1 + r1 * s3 + r2 * s2;
+
+   r4 *= 19;
+   r1 *= 19;
+   r2 *= 19;
+   r3 *= 19;
+
+   t0 += r4 * s1 + r1 * s4 + r2 * s3 + r3 * s2;
+   t1 += r4 * s2 + r2 * s4 + r3 * s3;
+   t2 += r4 * s3 + r3 * s4;
+   t3 += r4 * s4;
+
+   r0 = t0 & 0x7ffffffffffff; t1 += carry_shift(t0, 51);
+   r1 = t1 & 0x7ffffffffffff; t2 += carry_shift(t1, 51);
+   r2 = t2 & 0x7ffffffffffff; t3 += carry_shift(t2, 51);
+   r3 = t3 & 0x7ffffffffffff; t4 += carry_shift(t3, 51);
+   r4 = t4 & 0x7ffffffffffff; uint64_t c = carry_shift(t4, 51);
+
+   r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+   r1 += c;      c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+   r2 += c;
+
+   out[0] = r0;
+   out[1] = r1;
+   out[2] = r2;
+   out[3] = r3;
+   out[4] = r4;
+   }
+
+inline void fsquare(uint64_t out[5], const uint64_t in[5], size_t count = 1)
+   {
+   uint64_t r0 = in[0];
+   uint64_t r1 = in[1];
+   uint64_t r2 = in[2];
+   uint64_t r3 = in[3];
+   uint64_t r4 = in[4];
+
+   for(size_t i = 0; i != count; ++i)
+      {
+      const uint64_t d0 = r0 * 2;
+      const uint64_t d1 = r1 * 2;
+      const uint64_t d2 = r2 * 2 * 19;
+      const uint64_t d419 = r4 * 19;
+      const uint64_t d4 = d419 * 2;
+
+      uint128_t t0 = uint128_t(r0) * r0 + uint128_t(d4) * r1 + uint128_t(d2) * (r3     );
+      uint128_t t1 = uint128_t(d0) * r1 + uint128_t(d4) * r2 + uint128_t(r3) * (r3 * 19);
+      uint128_t t2 = uint128_t(d0) * r2 + uint128_t(r1) * r1 + uint128_t(d4) * (r3     );
+      uint128_t t3 = uint128_t(d0) * r3 + uint128_t(d1) * r2 + uint128_t(r4) * (d419   );
+      uint128_t t4 = uint128_t(d0) * r4 + uint128_t(d1) * r3 + uint128_t(r2) * (r2     );
+
+      r0 = t0 & 0x7ffffffffffff; t1 += carry_shift(t0, 51);
+      r1 = t1 & 0x7ffffffffffff; t2 += carry_shift(t1, 51);
+      r2 = t2 & 0x7ffffffffffff; t3 += carry_shift(t2, 51);
+      r3 = t3 & 0x7ffffffffffff; t4 += carry_shift(t3, 51);
+      r4 = t4 & 0x7ffffffffffff; uint64_t c = carry_shift(t4, 51);
+
+      r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+      r1 += c;      c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+      r2 += c;
+      }
+
+   out[0] = r0;
+   out[1] = r1;
+   out[2] = r2;
+   out[3] = r3;
+   out[4] = r4;
+   }
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+inline void fexpand(uint64_t *out, const uint8_t *in)
+   {
+   out[0] = load_le<uint64_t>(in, 0) & 0x7ffffffffffff;
+   out[1] = (load_le<uint64_t>(in+6, 0) >> 3) & 0x7ffffffffffff;
+   out[2] = (load_le<uint64_t>(in+12, 0) >> 6) & 0x7ffffffffffff;
+   out[3] = (load_le<uint64_t>(in+19, 0) >> 1) & 0x7ffffffffffff;
+   out[4] = (load_le<uint64_t>(in+24, 0) >> 12) & 0x7ffffffffffff;
+   }
+
+/* Take a fully reduced polynomial form number and contract it into a
+* little-endian, 32-byte array
+*/
+inline void fcontract(uint8_t *out, const uint64_t input[5])
+   {
+   uint128_t t0 = input[0];
+   uint128_t t1 = input[1];
+   uint128_t t2 = input[2];
+   uint128_t t3 = input[3];
+   uint128_t t4 = input[4];
+
+   for(size_t i = 0; i != 2; ++i)
+      {
+      t1 += t0 >> 51;        t0 &= 0x7ffffffffffff;
+      t2 += t1 >> 51;        t1 &= 0x7ffffffffffff;
+      t3 += t2 >> 51;        t2 &= 0x7ffffffffffff;
+      t4 += t3 >> 51;        t3 &= 0x7ffffffffffff;
+      t0 += (t4 >> 51) * 19; t4 &= 0x7ffffffffffff;
+      }
+
+   /* now t is between 0 and 2^255-1, properly carried. */
+   /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+
+   t0 += 19;
+
+   t1 += t0 >> 51; t0 &= 0x7ffffffffffff;
+   t2 += t1 >> 51; t1 &= 0x7ffffffffffff;
+   t3 += t2 >> 51; t2 &= 0x7ffffffffffff;
+   t4 += t3 >> 51; t3 &= 0x7ffffffffffff;
+   t0 += (t4 >> 51) * 19; t4 &= 0x7ffffffffffff;
+
+   /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+
+   t0 += 0x8000000000000 - 19;
+   t1 += 0x8000000000000 - 1;
+   t2 += 0x8000000000000 - 1;
+   t3 += 0x8000000000000 - 1;
+   t4 += 0x8000000000000 - 1;
+
+   /* now between 2^255 and 2^256-20, and offset by 2^255. */
+
+   t1 += t0 >> 51; t0 &= 0x7ffffffffffff;
+   t2 += t1 >> 51; t1 &= 0x7ffffffffffff;
+   t3 += t2 >> 51; t2 &= 0x7ffffffffffff;
+   t4 += t3 >> 51; t3 &= 0x7ffffffffffff;
+   t4 &= 0x7ffffffffffff;
+
+   store_le(out,
+            combine_lower(t0,  0, t1, 51),
+            combine_lower(t1, 13, t2, 38),
+            combine_lower(t2, 26, t3, 25),
+            combine_lower(t3, 39, t4, 12));
+   }
+
+/* Input: Q, Q', Q-Q'
+* Out: 2Q, Q+Q'
+*
+*   result.two_q (2*Q): long form
+*   result.q_plus_q_dash (Q + Q): long form
+*   in_q: short form, destroyed
+*   in_q_dash: short form, destroyed
+*   in_q_minus_q_dash: short form, preserved
+*/
+void fmonty(uint64_t result_two_q_x[5],
+            uint64_t result_two_q_z[5],
+            uint64_t result_q_plus_q_dash_x[5],
+            uint64_t result_q_plus_q_dash_z[5],
+            uint64_t in_q_x[5],
+            uint64_t in_q_z[5],
+            uint64_t in_q_dash_x[5],
+            uint64_t in_q_dash_z[5],
+            const uint64_t q_minus_q_dash[5])
+   {
+   uint64_t zzz[5];
+   uint64_t xx[5];
+   uint64_t zz[5];
+   uint64_t xxprime[5];
+   uint64_t zzprime[5];
+   uint64_t zzzprime[5];
+
+   fadd_sub(in_q_z, in_q_x);
+   fadd_sub(in_q_dash_z, in_q_dash_x);
+
+   fmul(xxprime, in_q_dash_x, in_q_z);
+   fmul(zzprime, in_q_dash_z, in_q_x);
+
+   fadd_sub(zzprime, xxprime);
+
+   fsquare(result_q_plus_q_dash_x, xxprime);
+   fsquare(zzzprime, zzprime);
+   fmul(result_q_plus_q_dash_z, zzzprime, q_minus_q_dash);
+
+   fsquare(xx, in_q_x);
+   fsquare(zz, in_q_z);
+   fmul(result_two_q_x, xx, zz);
+
+   fdifference_backwards(zz, xx);  // does zz = xx - zz
+   fscalar_product(zzz, zz, 121665);
+   fsum(zzz, xx);
+
+   fmul(result_two_q_z, zz, zzz);
+   }
+
+/*
+* Maybe swap the contents of two uint64_t arrays (@a and @b),
+* Param @iswap is assumed to be either 0 or 1
+*
+* This function performs the swap without leaking any side-channel
+* information.
+*/
+inline void swap_conditional(uint64_t a[5], uint64_t b[5],
+                             uint64_t c[5], uint64_t d[5],
+                             uint64_t iswap)
+   {
+   const uint64_t swap = 0 - iswap;
+
+   for(size_t i = 0; i < 5; ++i)
+      {
+      const uint64_t x0 = swap & (a[i] ^ b[i]);
+      const uint64_t x1 = swap & (c[i] ^ d[i]);
+      a[i] ^= x0;
+      b[i] ^= x0;
+      c[i] ^= x1;
+      d[i] ^= x1;
+      }
+   }
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+*
+*   resultx/resultz: the x/z coordinate of the resulting curve point (short form)
+*   n: a little endian, 32-byte number
+*   q: a point of the curve (short form)
+*/
+void cmult(uint64_t resultx[5], uint64_t resultz[5], const uint8_t n[32], const uint64_t q[5])
+   {
+   uint64_t a[5] = {0}; // nqpqx
+   uint64_t b[5] = {1}; // npqpz
+   uint64_t c[5] = {1}; // nqx
+   uint64_t d[5] = {0}; // nqz
+   uint64_t e[5] = {0}; // npqqx2
+   uint64_t f[5] = {1}; // npqqz2
+   uint64_t g[5] = {0}; // nqx2
+   uint64_t h[5] = {1}; // nqz2
+
+   copy_mem(a, q, 5);
+
+   for(size_t i = 0; i < 32; ++i)
+      {
+      const uint64_t bit0 = (n[31 - i] >> 7) & 1;
+      const uint64_t bit1 = (n[31 - i] >> 6) & 1;
+      const uint64_t bit2 = (n[31 - i] >> 5) & 1;
+      const uint64_t bit3 = (n[31 - i] >> 4) & 1;
+      const uint64_t bit4 = (n[31 - i] >> 3) & 1;
+      const uint64_t bit5 = (n[31 - i] >> 2) & 1;
+      const uint64_t bit6 = (n[31 - i] >> 1) & 1;
+      const uint64_t bit7 = (n[31 - i] >> 0) & 1;
+
+      swap_conditional(c, a, d, b, bit0);
+      fmonty(g, h, e, f, c, d, a, b, q);
+
+      swap_conditional(g, e, h, f, bit0 ^ bit1);
+      fmonty(c, d, a, b, g, h, e, f, q);
+
+      swap_conditional(c, a, d, b, bit1 ^ bit2);
+      fmonty(g, h, e, f, c, d, a, b, q);
+
+      swap_conditional(g, e, h, f, bit2 ^ bit3);
+      fmonty(c, d, a, b, g, h, e, f, q);
+
+      swap_conditional(c, a, d, b, bit3 ^ bit4);
+      fmonty(g, h, e, f, c, d, a, b, q);
+
+      swap_conditional(g, e, h, f, bit4 ^ bit5);
+      fmonty(c, d, a, b, g, h, e, f, q);
+
+      swap_conditional(c, a, d, b, bit5 ^ bit6);
+      fmonty(g, h, e, f, c, d, a, b, q);
+
+      swap_conditional(g, e, h, f, bit6 ^ bit7);
+      fmonty(c, d, a, b, g, h, e, f, q);
+
+      swap_conditional(c, a, d, b, bit7);
+      }
+
+   copy_mem(resultx, c, 5);
+   copy_mem(resultz, d, 5);
+   }
+
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code, tightened a little
+// -----------------------------------------------------------------------------
+void crecip(uint64_t out[5], const uint64_t z[5])
+   {
+   uint64_t a[5];
+   uint64_t b[5];
+   uint64_t c[5];
+   uint64_t t0[5];
+
+   fsquare(a, z);       // 2
+   fsquare(t0, a, 2);   // 8
+   fmul(b, t0, z);      // 9
+   fmul(a, b, a);       // 11
+   fsquare(t0, a);      // 22
+   fmul(b, t0, b);      // 2^5 - 2^0 = 31
+   fsquare(t0, b, 5);   // 2^10 - 2^5
+   fmul(b, t0, b);      // 2^10 - 2^0
+   fsquare(t0, b, 10);  // 2^20 - 2^10
+   fmul(c, t0, b);      // 2^20 - 2^0
+   fsquare(t0, c, 20);  // 2^40 - 2^20
+   fmul(t0, t0, c);     // 2^40 - 2^0
+   fsquare(t0, t0, 10); // 2^50 - 2^10
+   fmul(b, t0, b);      // 2^50 - 2^0
+   fsquare(t0, b, 50);  // 2^100 - 2^50
+   fmul(c, t0, b);      // 2^100 - 2^0
+   fsquare(t0, c, 100); // 2^200 - 2^100
+   fmul(t0, t0, c);     // 2^200 - 2^0
+   fsquare(t0, t0, 50); // 2^250 - 2^50
+   fmul(t0, t0, b);     // 2^250 - 2^0
+   fsquare(t0, t0, 5);  // 2^255 - 2^5
+   fmul(out, t0, a);    // 2^255 - 21
+   }
+
+}
+
+void
+curve25519_donna(uint8_t mypublic[32], const uint8_t secret[32], const uint8_t basepoint[32])
+   {
+   CT::poison(secret, 32);
+   CT::poison(basepoint, 32);
+
+   uint64_t bp[5], x[5], z[5], zmone[5];
+   uint8_t e[32];
+
+   copy_mem(e, secret, 32);
+   e[ 0] &= 248;
+   e[31] &= 127;
+   e[31] |= 64;
+
+   fexpand(bp, basepoint);
+   cmult(x, z, e, bp);
+   crecip(zmone, z);
+   fmul(z, x, zmone);
+   fcontract(mypublic, z);
+
+   CT::unpoison(secret, 32);
+   CT::unpoison(basepoint, 32);
+   CT::unpoison(mypublic, 32);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dh/dh.cpp b/third_party/botan/src/lib/pubkey/dh/dh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dh/dh.cpp
@@ -0,0 +1,142 @@
+/*
+* Diffie-Hellman
+* (C) 1999-2007,2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dh.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+/*
+* DH_PublicKey Constructor
+*/
+DH_PublicKey::DH_PublicKey(const DL_Group& grp, const BigInt& y1)
+   {
+   m_group = grp;
+   m_y = y1;
+   }
+
+/*
+* Return the public value for key agreement
+*/
+std::vector<uint8_t> DH_PublicKey::public_value() const
+   {
+   return unlock(BigInt::encode_1363(m_y, group_p().bytes()));
+   }
+
+/*
+* Create a DH private key
+*/
+DH_PrivateKey::DH_PrivateKey(RandomNumberGenerator& rng,
+                             const DL_Group& grp,
+                             const BigInt& x_arg)
+   {
+   m_group = grp;
+
+   if(x_arg == 0)
+      {
+      const size_t exp_bits = grp.exponent_bits();
+      m_x.randomize(rng, exp_bits);
+      m_y = m_group.power_g_p(m_x, exp_bits);
+      }
+   else
+      {
+      m_x = x_arg;
+
+      if(m_y == 0)
+         m_y = m_group.power_g_p(m_x, grp.p_bits());
+      }
+   }
+
+/*
+* Load a DH private key
+*/
+DH_PrivateKey::DH_PrivateKey(const AlgorithmIdentifier& alg_id,
+                             const secure_vector<uint8_t>& key_bits) :
+   DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+   {
+   if(m_y.is_zero())
+      {
+      m_y = m_group.power_g_p(m_x, m_group.p_bits());
+      }
+   }
+
+/*
+* Return the public value for key agreement
+*/
+std::vector<uint8_t> DH_PrivateKey::public_value() const
+   {
+   return DH_PublicKey::public_value();
+   }
+
+namespace {
+
+/**
+* DH operation
+*/
+class DH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+   {
+   public:
+
+      DH_KA_Operation(const DH_PrivateKey& key, const std::string& kdf, RandomNumberGenerator& rng) :
+         PK_Ops::Key_Agreement_with_KDF(kdf),
+         m_p(key.group_p()),
+         m_x(key.get_x()),
+         m_x_bits(m_x.bits()),
+         m_monty_p(key.get_group().monty_params_p()),
+         m_blinder(m_p,
+                   rng,
+                   [](const BigInt& k) { return k; },
+                   [this](const BigInt& k) { return powermod_x_p(inverse_mod(k, m_p)); })
+         {}
+
+      size_t agreed_value_size() const override { return m_p.bytes(); }
+
+      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override;
+   private:
+      BigInt powermod_x_p(const BigInt& v) const
+         {
+         const size_t powm_window = 4;
+         auto powm_v_p = monty_precompute(m_monty_p, v, powm_window);
+         return monty_execute(*powm_v_p, m_x, m_x_bits);
+         }
+
+      const BigInt& m_p;
+      const BigInt& m_x;
+      const size_t m_x_bits;
+      std::shared_ptr<const Montgomery_Params> m_monty_p;
+      Blinder m_blinder;
+   };
+
+secure_vector<uint8_t> DH_KA_Operation::raw_agree(const uint8_t w[], size_t w_len)
+   {
+   BigInt v = BigInt::decode(w, w_len);
+
+   if(v <= 1 || v >= m_p - 1)
+      throw Invalid_Argument("DH agreement - invalid key provided");
+
+   v = m_blinder.blind(v);
+   v = powermod_x_p(v);
+   v = m_blinder.unblind(v);
+
+   return BigInt::encode_1363(v, m_p.bytes());
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+DH_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+                                       const std::string& params,
+                                       const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Key_Agreement>(new DH_KA_Operation(*this, params, rng));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dh/dh.h b/third_party/botan/src/lib/pubkey/dh/dh.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dh/dh.h
@@ -0,0 +1,81 @@
+/*
+* Diffie-Hellman
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DIFFIE_HELLMAN_H_
+#define BOTAN_DIFFIE_HELLMAN_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* This class represents Diffie-Hellman public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DH_PublicKey : public virtual DL_Scheme_PublicKey
+   {
+   public:
+      std::string algo_name() const override { return "DH"; }
+
+      std::vector<uint8_t> public_value() const;
+
+      DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_42; }
+
+      /**
+      * Create a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      DH_PublicKey(const AlgorithmIdentifier& alg_id,
+                   const std::vector<uint8_t>& key_bits) :
+         DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_42) {}
+
+      /**
+      * Construct a public key with the specified parameters.
+      * @param grp the DL group to use in the key
+      * @param y the public value y
+      */
+      DH_PublicKey(const DL_Group& grp, const BigInt& y);
+   protected:
+      DH_PublicKey() = default;
+   };
+
+/**
+* This class represents Diffie-Hellman private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DH_PrivateKey final : public DH_PublicKey,
+                                public PK_Key_Agreement_Key,
+                                public virtual DL_Scheme_PrivateKey
+   {
+   public:
+      std::vector<uint8_t> public_value() const override;
+
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits PKCS #8 structure
+      */
+      DH_PrivateKey(const AlgorithmIdentifier& alg_id,
+                    const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Create a private key.
+      * @param rng random number generator to use
+      * @param grp the group to be used in the key
+      * @param x the key's secret value (or if zero, generate a new key)
+      */
+      DH_PrivateKey(RandomNumberGenerator& rng, const DL_Group& grp,
+                    const BigInt& x = 0);
+
+      std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp b/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp
@@ -0,0 +1,84 @@
+/*
+* DL Scheme
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_algo.h>
+#include <botan/numthry.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+size_t DL_Scheme_PublicKey::key_length() const
+   {
+   return m_group.p_bits();
+   }
+
+size_t DL_Scheme_PublicKey::estimated_strength() const
+   {
+   return m_group.estimated_strength();
+   }
+
+AlgorithmIdentifier DL_Scheme_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(),
+                              m_group.DER_encode(group_format()));
+   }
+
+std::vector<uint8_t> DL_Scheme_PublicKey::public_key_bits() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(m_y);
+   return output;
+   }
+
+DL_Scheme_PublicKey::DL_Scheme_PublicKey(const DL_Group& group, const BigInt& y) :
+   m_y(y),
+   m_group(group)
+   {
+   }
+
+DL_Scheme_PublicKey::DL_Scheme_PublicKey(const AlgorithmIdentifier& alg_id,
+                                         const std::vector<uint8_t>& key_bits,
+                                         DL_Group::Format format) :
+   m_group(alg_id.get_parameters(), format)
+   {
+   BER_Decoder(key_bits).decode(m_y);
+   }
+
+secure_vector<uint8_t> DL_Scheme_PrivateKey::private_key_bits() const
+   {
+   return DER_Encoder().encode(m_x).get_contents();
+   }
+
+DL_Scheme_PrivateKey::DL_Scheme_PrivateKey(const AlgorithmIdentifier& alg_id,
+                                           const secure_vector<uint8_t>& key_bits,
+                                           DL_Group::Format format)
+   {
+   m_group.BER_decode(alg_id.get_parameters(), format);
+
+   BER_Decoder(key_bits).decode(m_x);
+   }
+
+/*
+* Check Public DL Parameters
+*/
+bool DL_Scheme_PublicKey::check_key(RandomNumberGenerator& rng,
+                                    bool strong) const
+   {
+   return m_group.verify_group(rng, strong) && m_group.verify_public_element(m_y);
+   }
+
+/*
+* Check DL Scheme Private Parameters
+*/
+bool DL_Scheme_PrivateKey::check_key(RandomNumberGenerator& rng,
+                                     bool strong) const
+   {
+   return m_group.verify_group(rng, strong) && m_group.verify_element_pair(m_y, m_x);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h b/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h
@@ -0,0 +1,140 @@
+/*
+* DL Scheme
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DL_ALGO_H_
+#define BOTAN_DL_ALGO_H_
+
+#include <botan/dl_group.h>
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+/**
+* This class represents discrete logarithm (DL) public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Scheme_PublicKey : public virtual Public_Key
+   {
+   public:
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      /**
+      * Get the DL domain parameters of this key.
+      * @return DL domain parameters of this key
+      */
+      const DL_Group& get_domain() const { return m_group; }
+
+      /**
+      * Get the DL domain parameters of this key.
+      * @return DL domain parameters of this key
+      */
+      const DL_Group& get_group() const { return m_group; }
+
+      /**
+      * Get the public value y with y = g^x mod p where x is the secret key.
+      */
+      const BigInt& get_y() const { return m_y; }
+
+      /**
+      * Get the prime p of the underlying DL group.
+      * @return prime p
+      */
+      const BigInt& group_p() const { return m_group.get_p(); }
+
+      /**
+      * Get the prime q of the underlying DL group.
+      * @return prime q
+      */
+      const BigInt& group_q() const { return m_group.get_q(); }
+
+      /**
+      * Get the generator g of the underlying DL group.
+      * @return generator g
+      */
+      const BigInt& group_g() const { return m_group.get_g(); }
+
+      /**
+      * Get the underlying groups encoding format.
+      * @return encoding format
+      */
+      virtual DL_Group::Format group_format() const = 0;
+
+      size_t key_length() const override;
+      size_t estimated_strength() const override;
+
+      DL_Scheme_PublicKey& operator=(const DL_Scheme_PublicKey& other) = default;
+
+   protected:
+      DL_Scheme_PublicKey() = default;
+
+      /**
+      * Create a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      * @param group_format the underlying groups encoding format
+      */
+      DL_Scheme_PublicKey(const AlgorithmIdentifier& alg_id,
+                          const std::vector<uint8_t>& key_bits,
+                          DL_Group::Format group_format);
+
+      DL_Scheme_PublicKey(const DL_Group& group, const BigInt& y);
+
+      /**
+      * The DL public key
+      */
+      BigInt m_y;
+
+      /**
+      * The DL group
+      */
+      DL_Group m_group;
+   };
+
+/**
+* This class represents discrete logarithm (DL) private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Scheme_PrivateKey : public virtual DL_Scheme_PublicKey,
+                                       public virtual Private_Key
+   {
+   public:
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      /**
+      * Get the secret key x.
+      * @return secret key
+      */
+      const BigInt& get_x() const { return m_x; }
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      DL_Scheme_PrivateKey& operator=(const DL_Scheme_PrivateKey& other) = default;
+
+   protected:
+      /**
+      * Create a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded private key bits
+      * @param group_format the underlying groups encoding format
+      */
+      DL_Scheme_PrivateKey(const AlgorithmIdentifier& alg_id,
+                           const secure_vector<uint8_t>& key_bits,
+                           DL_Group::Format group_format);
+
+      DL_Scheme_PrivateKey() = default;
+
+      /**
+      * The DL private key
+      */
+      BigInt m_x;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp b/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp
@@ -0,0 +1,644 @@
+/*
+* Discrete Logarithm Parameters
+* (C) 1999-2008,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_group.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/pem.h>
+#include <botan/workfactor.h>
+#include <botan/internal/monty_exp.h>
+
+namespace Botan {
+
+class DL_Group_Data final
+   {
+   public:
+      DL_Group_Data(const BigInt& p, const BigInt& q, const BigInt& g, DL_Group_Source source) :
+         m_p(p), m_q(q), m_g(g),
+         m_mod_p(p),
+         m_mod_q(q),
+         m_monty_params(std::make_shared<Montgomery_Params>(m_p, m_mod_p)),
+         m_monty(monty_precompute(m_monty_params, m_g, /*window bits=*/4)),
+         m_p_bits(p.bits()),
+         m_q_bits(q.bits()),
+         m_estimated_strength(dl_work_factor(m_p_bits)),
+         m_exponent_bits(dl_exponent_size(m_p_bits)),
+         m_source(source)
+         {
+         }
+
+      ~DL_Group_Data() = default;
+
+      DL_Group_Data(const DL_Group_Data& other) = delete;
+      DL_Group_Data& operator=(const DL_Group_Data& other) = delete;
+
+      const BigInt& p() const { return m_p; }
+      const BigInt& q() const { return m_q; }
+      const BigInt& g() const { return m_g; }
+
+      BigInt mod_p(const BigInt& x) const { return m_mod_p.reduce(x); }
+
+      BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const
+         {
+         return m_mod_p.multiply(x, y);
+         }
+
+      BigInt mod_q(const BigInt& x) const { return m_mod_q.reduce(x); }
+
+      BigInt multiply_mod_q(const BigInt& x, const BigInt& y) const
+         {
+         return m_mod_q.multiply(x, y);
+         }
+
+      BigInt square_mod_q(const BigInt& x) const
+         {
+         return m_mod_q.square(x);
+         }
+
+      std::shared_ptr<const Montgomery_Params> monty_params_p() const
+         { return m_monty_params; }
+
+      size_t p_bits() const { return m_p_bits; }
+      size_t q_bits() const { return m_q_bits; }
+      size_t p_bytes() const { return (m_p_bits + 7) / 8; }
+      size_t q_bytes() const { return (m_q_bits + 7) / 8; }
+
+      size_t estimated_strength() const { return m_estimated_strength; }
+
+      size_t exponent_bits() const { return m_exponent_bits; }
+
+      BigInt power_g_p(const BigInt& k, size_t max_k_bits) const
+         {
+         return monty_execute(*m_monty, k, max_k_bits);
+         }
+
+      bool q_is_set() const { return m_q_bits > 0; }
+
+      void assert_q_is_set(const std::string& function) const
+         {
+         if(q_is_set() == false)
+            throw Invalid_State("DL_Group::" + function + " q is not set for this group");
+         }
+
+      DL_Group_Source source() const { return m_source; }
+
+   private:
+      BigInt m_p;
+      BigInt m_q;
+      BigInt m_g;
+      Modular_Reducer m_mod_p;
+      Modular_Reducer m_mod_q;
+      std::shared_ptr<const Montgomery_Params> m_monty_params;
+      std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
+      size_t m_p_bits;
+      size_t m_q_bits;
+      size_t m_estimated_strength;
+      size_t m_exponent_bits;
+      DL_Group_Source m_source;
+   };
+
+//static
+std::shared_ptr<DL_Group_Data> DL_Group::BER_decode_DL_group(const uint8_t data[], size_t data_len,
+                                                             DL_Group::Format format,
+                                                             DL_Group_Source source)
+   {
+   BigInt p, q, g;
+
+   BER_Decoder decoder(data, data_len);
+   BER_Decoder ber = decoder.start_cons(SEQUENCE);
+
+   if(format == DL_Group::ANSI_X9_57)
+      {
+      ber.decode(p)
+         .decode(q)
+         .decode(g)
+         .verify_end();
+      }
+   else if(format == DL_Group::ANSI_X9_42)
+      {
+      ber.decode(p)
+         .decode(g)
+         .decode(q)
+         .discard_remaining();
+      }
+   else if(format == DL_Group::PKCS_3)
+      {
+      // q is left as zero
+      ber.decode(p)
+         .decode(g)
+         .discard_remaining();
+      }
+   else
+      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+
+   return std::make_shared<DL_Group_Data>(p, q, g, source);
+   }
+
+//static
+std::shared_ptr<DL_Group_Data>
+DL_Group::load_DL_group_info(const char* p_str,
+                             const char* q_str,
+                             const char* g_str)
+   {
+   const BigInt p(p_str);
+   const BigInt q(q_str);
+   const BigInt g(g_str);
+
+   return std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::Builtin);
+   }
+
+//static
+std::shared_ptr<DL_Group_Data>
+DL_Group::load_DL_group_info(const char* p_str,
+                             const char* g_str)
+   {
+   const BigInt p(p_str);
+   const BigInt q = (p - 1) / 2;
+   const BigInt g(g_str);
+
+   return std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::Builtin);
+   }
+
+namespace {
+
+DL_Group::Format pem_label_to_dl_format(const std::string& label)
+   {
+   if(label == "DH PARAMETERS")
+      return DL_Group::PKCS_3;
+   else if(label == "DSA PARAMETERS")
+      return DL_Group::ANSI_X9_57;
+   else if(label == "X942 DH PARAMETERS" || label == "X9.42 DH PARAMETERS")
+      return DL_Group::ANSI_X9_42;
+   else
+      throw Decoding_Error("DL_Group: Invalid PEM label " + label);
+   }
+
+}
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(const std::string& str)
+   {
+   // Either a name or a PEM block, try name first
+   m_data = DL_group_info(str);
+
+   if(m_data == nullptr)
+      {
+      try
+         {
+         std::string label;
+         const std::vector<uint8_t> ber = unlock(PEM_Code::decode(str, label));
+         Format format = pem_label_to_dl_format(label);
+
+         m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+         }
+      catch(...) {}
+      }
+
+   if(m_data == nullptr)
+      throw Invalid_Argument("DL_Group: Unknown group " + str);
+   }
+
+namespace {
+
+/*
+* Create generator of the q-sized subgroup (DSA style generator)
+*/
+BigInt make_dsa_generator(const BigInt& p, const BigInt& q)
+   {
+   const BigInt e = (p - 1) / q;
+
+   if(e == 0 || (p - 1) % q > 0)
+      throw Invalid_Argument("make_dsa_generator q does not divide p-1");
+
+   for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
+      {
+      // TODO precompute!
+      BigInt g = power_mod(PRIMES[i], e, p);
+      if(g > 1)
+         return g;
+      }
+
+   throw Internal_Error("DL_Group: Couldn't create a suitable generator");
+   }
+
+}
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(RandomNumberGenerator& rng,
+                   PrimeType type, size_t pbits, size_t qbits)
+   {
+   if(pbits < 1024)
+      throw Invalid_Argument("DL_Group: prime size " + std::to_string(pbits) + " is too small");
+
+   if(type == Strong)
+      {
+      if(qbits != 0 && qbits != pbits - 1)
+         throw Invalid_Argument("Cannot create strong-prime DL_Group with specified q bits");
+
+      const BigInt p = random_safe_prime(rng, pbits);
+      const BigInt q = (p - 1) / 2;
+
+      /*
+      Always choose a generator that is quadratic reside mod p,
+      this forces g to be a generator of the subgroup of size q.
+      */
+      BigInt g = 2;
+      if(jacobi(g, p) != 1)
+         {
+         // prime table does not contain 2
+         for(size_t i = 0; i < PRIME_TABLE_SIZE; ++i)
+            {
+            g = PRIMES[i];
+            if(jacobi(g, p) == 1)
+               break;
+            }
+         }
+
+      m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+      }
+   else if(type == Prime_Subgroup)
+      {
+      if(qbits == 0)
+         qbits = dl_exponent_size(pbits);
+
+      const BigInt q = random_prime(rng, qbits);
+      Modular_Reducer mod_2q(2*q);
+      BigInt X;
+      BigInt p;
+      while(p.bits() != pbits || !is_prime(p, rng, 128, true))
+         {
+         X.randomize(rng, pbits);
+         p = X - mod_2q.reduce(X) + 1;
+         }
+
+      const BigInt g = make_dsa_generator(p, q);
+      m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+      }
+   else if(type == DSA_Kosherizer)
+      {
+      if(qbits == 0)
+         qbits = ((pbits <= 1024) ? 160 : 256);
+
+      BigInt p, q;
+      generate_dsa_primes(rng, p, q, pbits, qbits);
+      const BigInt g = make_dsa_generator(p, q);
+      m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+      }
+   else
+      {
+      throw Invalid_Argument("DL_Group unknown PrimeType");
+      }
+   }
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(RandomNumberGenerator& rng,
+                   const std::vector<uint8_t>& seed,
+                   size_t pbits, size_t qbits)
+   {
+   BigInt p, q;
+
+   if(!generate_dsa_primes(rng, p, q, pbits, qbits, seed))
+      throw Invalid_Argument("DL_Group: The seed given does not generate a DSA group");
+
+   BigInt g = make_dsa_generator(p, q);
+
+   m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+   }
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(const BigInt& p, const BigInt& g)
+   {
+   m_data = std::make_shared<DL_Group_Data>(p, 0, g, DL_Group_Source::ExternalSource);
+   }
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(const BigInt& p, const BigInt& q, const BigInt& g)
+   {
+   m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::ExternalSource);
+   }
+
+const DL_Group_Data& DL_Group::data() const
+   {
+   if(m_data)
+      return *m_data;
+
+   throw Invalid_State("DL_Group uninitialized");
+   }
+
+bool DL_Group::verify_public_element(const BigInt& y) const
+   {
+   const BigInt& p = get_p();
+   const BigInt& q = get_q();
+
+   if(y <= 1 || y >= p)
+      return false;
+
+   if(q.is_zero() == false)
+      {
+      if(power_mod(y, q, p) != 1)
+         return false;
+      }
+
+   return true;
+   }
+
+bool DL_Group::verify_element_pair(const BigInt& y, const BigInt& x) const
+   {
+   const BigInt& p = get_p();
+
+   if(y <= 1 || y >= p || x <= 1 || x >= p)
+      return false;
+
+   if(y != power_g_p(x))
+      return false;
+
+   return true;
+   }
+
+/*
+* Verify the parameters
+*/
+bool DL_Group::verify_group(RandomNumberGenerator& rng,
+                            bool strong) const
+   {
+   const bool from_builtin = (source() == DL_Group_Source::Builtin);
+
+   if(!strong && from_builtin)
+      return true;
+
+   const BigInt& p = get_p();
+   const BigInt& q = get_q();
+   const BigInt& g = get_g();
+
+   if(g < 2 || p < 3 || q < 0)
+      return false;
+
+   const size_t test_prob = 128;
+   const bool is_randomly_generated = (source() != DL_Group_Source::ExternalSource);
+
+   if(q != 0)
+      {
+      if((p - 1) % q != 0)
+         {
+         return false;
+         }
+      if(this->power_g_p(q) != 1)
+         {
+         return false;
+         }
+      if(!is_prime(q, rng, test_prob, is_randomly_generated))
+         {
+         return false;
+         }
+      }
+
+   if(!is_prime(p, rng, test_prob, is_randomly_generated))
+      {
+      return false;
+      }
+
+   return true;
+   }
+
+/*
+* Return the prime
+*/
+const BigInt& DL_Group::get_p() const
+   {
+   return data().p();
+   }
+
+/*
+* Return the generator
+*/
+const BigInt& DL_Group::get_g() const
+   {
+   return data().g();
+   }
+
+/*
+* Return the subgroup
+*/
+const BigInt& DL_Group::get_q() const
+   {
+   return data().q();
+   }
+
+std::shared_ptr<const Montgomery_Params> DL_Group::monty_params_p() const
+   {
+   return data().monty_params_p();
+   }
+
+size_t DL_Group::p_bits() const
+   {
+   return data().p_bits();
+   }
+
+size_t DL_Group::p_bytes() const
+   {
+   return data().p_bytes();
+   }
+
+size_t DL_Group::q_bits() const
+   {
+   data().assert_q_is_set("q_bits");
+   return data().q_bits();
+   }
+
+size_t DL_Group::q_bytes() const
+   {
+   data().assert_q_is_set("q_bytes");
+   return data().q_bytes();
+   }
+
+size_t DL_Group::estimated_strength() const
+   {
+   return data().estimated_strength();
+   }
+
+size_t DL_Group::exponent_bits() const
+   {
+   return data().exponent_bits();
+   }
+
+BigInt DL_Group::inverse_mod_p(const BigInt& x) const
+   {
+   // precompute??
+   return inverse_mod(x, get_p());
+   }
+
+BigInt DL_Group::mod_p(const BigInt& x) const
+   {
+   return data().mod_p(x);
+   }
+
+BigInt DL_Group::multiply_mod_p(const BigInt& x, const BigInt& y) const
+   {
+   return data().multiply_mod_p(x, y);
+   }
+
+BigInt DL_Group::inverse_mod_q(const BigInt& x) const
+   {
+   data().assert_q_is_set("inverse_mod_q");
+   // precompute??
+   return inverse_mod(x, get_q());
+   }
+
+BigInt DL_Group::mod_q(const BigInt& x) const
+   {
+   data().assert_q_is_set("mod_q");
+   return data().mod_q(x);
+   }
+
+BigInt DL_Group::multiply_mod_q(const BigInt& x, const BigInt& y) const
+   {
+   data().assert_q_is_set("multiply_mod_q");
+   return data().multiply_mod_q(x, y);
+   }
+
+BigInt DL_Group::multiply_mod_q(const BigInt& x, const BigInt& y, const BigInt& z) const
+   {
+   data().assert_q_is_set("multiply_mod_q");
+   return data().multiply_mod_q(data().multiply_mod_q(x, y), z);
+   }
+
+BigInt DL_Group::square_mod_q(const BigInt& x) const
+   {
+   data().assert_q_is_set("square_mod_q");
+   return data().square_mod_q(x);
+   }
+
+BigInt DL_Group::multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const
+   {
+   return monty_multi_exp(data().monty_params_p(), get_g(), x, y, z);
+   }
+
+BigInt DL_Group::power_g_p(const BigInt& x) const
+   {
+   return data().power_g_p(x, x.bits());
+   }
+
+BigInt DL_Group::power_g_p(const BigInt& x, size_t max_x_bits) const
+   {
+   return data().power_g_p(x, max_x_bits);
+   }
+
+DL_Group_Source DL_Group::source() const
+   {
+   return data().source();
+   }
+
+/*
+* DER encode the parameters
+*/
+std::vector<uint8_t> DL_Group::DER_encode(Format format) const
+   {
+   if(get_q().is_zero() && (format == ANSI_X9_57 || format == ANSI_X9_42))
+      throw Encoding_Error("Cannot encode DL_Group in ANSI formats when q param is missing");
+
+   std::vector<uint8_t> output;
+   DER_Encoder der(output);
+
+   if(format == ANSI_X9_57)
+      {
+      der.start_cons(SEQUENCE)
+            .encode(get_p())
+            .encode(get_q())
+            .encode(get_g())
+         .end_cons();
+      }
+   else if(format == ANSI_X9_42)
+      {
+      der.start_cons(SEQUENCE)
+            .encode(get_p())
+            .encode(get_g())
+            .encode(get_q())
+         .end_cons();
+      }
+   else if(format == PKCS_3)
+      {
+      der.start_cons(SEQUENCE)
+            .encode(get_p())
+            .encode(get_g())
+         .end_cons();
+      }
+   else
+      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+
+   return output;
+   }
+
+/*
+* PEM encode the parameters
+*/
+std::string DL_Group::PEM_encode(Format format) const
+   {
+   const std::vector<uint8_t> encoding = DER_encode(format);
+
+   if(format == PKCS_3)
+      return PEM_Code::encode(encoding, "DH PARAMETERS");
+   else if(format == ANSI_X9_57)
+      return PEM_Code::encode(encoding, "DSA PARAMETERS");
+   else if(format == ANSI_X9_42)
+      return PEM_Code::encode(encoding, "X9.42 DH PARAMETERS");
+   else
+      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+   }
+
+DL_Group::DL_Group(const uint8_t ber[], size_t ber_len, Format format)
+   {
+   m_data = BER_decode_DL_group(ber, ber_len, format, DL_Group_Source::ExternalSource);
+   }
+
+void DL_Group::BER_decode(const std::vector<uint8_t>& ber, Format format)
+   {
+   m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+   }
+
+//static
+DL_Group DL_Group::DL_Group_from_PEM(const std::string& pem)
+   {
+   std::string label;
+   const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
+   Format format = pem_label_to_dl_format(label);
+   return DL_Group(ber, format);
+   }
+
+/*
+* Decode PEM encoded parameters
+*/
+void DL_Group::PEM_decode(const std::string& pem)
+   {
+   std::string label;
+   const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
+   Format format = pem_label_to_dl_format(label);
+
+   m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+   }
+
+//static
+std::string DL_Group::PEM_for_named_group(const std::string& name)
+   {
+   DL_Group group(name);
+   DL_Group::Format format = group.get_q().is_zero() ? DL_Group::PKCS_3 : DL_Group::ANSI_X9_42;
+   return group.PEM_encode(format);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dl_group/dl_group.h b/third_party/botan/src/lib/pubkey/dl_group/dl_group.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dl_group/dl_group.h
@@ -0,0 +1,357 @@
+/*
+* Discrete Logarithm Group
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DL_PARAM_H_
+#define BOTAN_DL_PARAM_H_
+
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class Montgomery_Params;
+class DL_Group_Data;
+
+enum class DL_Group_Source {
+   Builtin,
+   RandomlyGenerated,
+   ExternalSource,
+};
+
+/**
+* This class represents discrete logarithm groups. It holds a prime
+* modulus p, a generator g, and (optionally) a prime q which is a
+* factor of (p-1). In most cases g generates the order-q subgroup.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Group final
+   {
+   public:
+      /**
+      * Determine the prime creation for DL groups.
+      */
+      enum PrimeType { Strong, Prime_Subgroup, DSA_Kosherizer };
+
+      /**
+      * The DL group encoding format variants.
+      */
+      enum Format {
+         ANSI_X9_42,
+         ANSI_X9_57,
+         PKCS_3,
+
+         DSA_PARAMETERS = ANSI_X9_57,
+         DH_PARAMETERS = ANSI_X9_42,
+         ANSI_X9_42_DH_PARAMETERS = ANSI_X9_42,
+         PKCS3_DH_PARAMETERS = PKCS_3
+      };
+
+      /**
+      * Construct a DL group with uninitialized internal value.
+      * Use this constructor is you wish to set the groups values
+      * from a DER or PEM encoded group.
+      */
+      DL_Group() = default;
+
+      /**
+      * Construct a DL group that is registered in the configuration.
+      * @param name the name of the group, for example "modp/ietf/3072"
+      *
+      * @warning This constructor also accepts PEM inputs. This behavior is
+      * deprecated and will be removed in a future major release. Instead
+      * use DL_Group_from_PEM function
+      */
+      explicit DL_Group(const std::string& name);
+
+      /*
+      * Read a PEM representation
+      */
+      static DL_Group DL_Group_from_PEM(const std::string& pem);
+
+      /**
+      * Create a new group randomly.
+      * @param rng the random number generator to use
+      * @param type specifies how the creation of primes p and q shall
+      * be performed. If type=Strong, then p will be determined as a
+      * safe prime, and q will be chosen as (p-1)/2. If
+      * type=Prime_Subgroup and qbits = 0, then the size of q will be
+      * determined according to the estimated difficulty of the DL
+      * problem. If type=DSA_Kosherizer, DSA primes will be created.
+      * @param pbits the number of bits of p
+      * @param qbits the number of bits of q. Leave it as 0 to have
+      * the value determined according to pbits.
+      */
+      DL_Group(RandomNumberGenerator& rng, PrimeType type,
+               size_t pbits, size_t qbits = 0);
+
+      /**
+      * Create a DSA group with a given seed.
+      * @param rng the random number generator to use
+      * @param seed the seed to use to create the random primes
+      * @param pbits the desired bit size of the prime p
+      * @param qbits the desired bit size of the prime q.
+      */
+      DL_Group(RandomNumberGenerator& rng,
+               const std::vector<uint8_t>& seed,
+               size_t pbits = 1024, size_t qbits = 0);
+
+      /**
+      * Create a DL group.
+      * @param p the prime p
+      * @param g the base g
+      */
+      DL_Group(const BigInt& p, const BigInt& g);
+
+      /**
+      * Create a DL group.
+      * @param p the prime p
+      * @param q the prime q
+      * @param g the base g
+      */
+      DL_Group(const BigInt& p, const BigInt& q, const BigInt& g);
+
+      /**
+      * Decode a BER-encoded DL group param
+      */
+      DL_Group(const uint8_t ber[], size_t ber_len, Format format);
+
+      /**
+      * Decode a BER-encoded DL group param
+      */
+      template<typename Alloc>
+         DL_Group(const std::vector<uint8_t, Alloc>& ber, Format format) :
+         DL_Group(ber.data(), ber.size(), format) {}
+
+      /**
+      * Get the prime p.
+      * @return prime p
+      */
+      const BigInt& get_p() const;
+
+      /**
+      * Get the prime q, returns zero if q is not used
+      * @return prime q
+      */
+      const BigInt& get_q() const;
+
+      /**
+      * Get the base g.
+      * @return base g
+      */
+      const BigInt& get_g() const;
+
+      /**
+      * Perform validity checks on the group.
+      * @param rng the rng to use
+      * @param strong whether to perform stronger by lengthier tests
+      * @return true if the object is consistent, false otherwise
+      */
+      bool verify_group(RandomNumberGenerator& rng, bool strong = true) const;
+
+      /**
+      * Verify a public element, ie check if y = g^x for some x.
+      *
+      * This is not a perfect test. It verifies that 1 < y < p and (if q is set)
+      * that y is in the subgroup of size q.
+      */
+      bool verify_public_element(const BigInt& y) const;
+
+      /**
+      * Verify a pair of elements y = g^x
+      *
+      * This verifies that 1 < x,y < p and that y=g^x mod p
+      */
+      bool verify_element_pair(const BigInt& y, const BigInt& x) const;
+
+      /**
+      * Encode this group into a string using PEM encoding.
+      * @param format the encoding format
+      * @return string holding the PEM encoded group
+      */
+      std::string PEM_encode(Format format) const;
+
+      /**
+      * Encode this group into a string using DER encoding.
+      * @param format the encoding format
+      * @return string holding the DER encoded group
+      */
+      std::vector<uint8_t> DER_encode(Format format) const;
+
+      /**
+      * Reduce an integer modulo p
+      * @return x % p
+      */
+      BigInt mod_p(const BigInt& x) const;
+
+      /**
+      * Multiply and reduce an integer modulo p
+      * @return (x*y) % p
+      */
+      BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const;
+
+      /**
+      * Return the inverse of x mod p
+      */
+      BigInt inverse_mod_p(const BigInt& x) const;
+
+      /**
+      * Reduce an integer modulo q
+      * Throws if q is unset on this DL_Group
+      * @return x % q
+      */
+      BigInt mod_q(const BigInt& x) const;
+
+      /**
+      * Multiply and reduce an integer modulo q
+      * Throws if q is unset on this DL_Group
+      * @return (x*y) % q
+      */
+      BigInt multiply_mod_q(const BigInt& x, const BigInt& y) const;
+
+      /**
+      * Multiply and reduce an integer modulo q
+      * Throws if q is unset on this DL_Group
+      * @return (x*y*z) % q
+      */
+      BigInt multiply_mod_q(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+      /**
+      * Square and reduce an integer modulo q
+      * Throws if q is unset on this DL_Group
+      * @return (x*x) % q
+      */
+      BigInt square_mod_q(const BigInt& x) const;
+
+      /**
+      * Return the inverse of x mod q
+      * Throws if q is unset on this DL_Group
+      */
+      BigInt inverse_mod_q(const BigInt& x) const;
+
+      /**
+      * Modular exponentiation
+      *
+      * @warning this function leaks the size of x via the number of
+      * loop iterations. Use the version taking the maximum size to
+      * avoid this.
+      *
+      * @return (g^x) % p
+      */
+      BigInt power_g_p(const BigInt& x) const;
+
+      /**
+      * Modular exponentiation
+      * @param x the exponent
+      * @param max_x_bits x is assumed to be at most this many bits long.
+      *
+      * @return (g^x) % p
+      */
+      BigInt power_g_p(const BigInt& x, size_t max_x_bits) const;
+
+      /**
+      * Multi-exponentiate
+      * Return (g^x * y^z) % p
+      */
+      BigInt multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+      /**
+      * Return parameters for Montgomery reduction/exponentiation mod p
+      */
+      std::shared_ptr<const Montgomery_Params> monty_params_p() const;
+
+      /**
+      * Return the size of p in bits
+      * Same as get_p().bits()
+      */
+      size_t p_bits() const;
+
+      /**
+      * Return the size of p in bytes
+      * Same as get_p().bytes()
+      */
+      size_t p_bytes() const;
+
+      /**
+      * Return the size of q in bits
+      * Same as get_q().bits()
+      * Throws if q is unset
+      */
+      size_t q_bits() const;
+
+      /**
+      * Return the size of q in bytes
+      * Same as get_q().bytes()
+      * Throws if q is unset
+      */
+      size_t q_bytes() const;
+
+      /**
+      * Return size in bits of a secret exponent
+      *
+      * This attempts to balance between the attack costs of NFS
+      * (which depends on the size of the modulus) and Pollard's rho
+      * (which depends on the size of the exponent).
+      *
+      * It may vary over time for a particular group, if the attack
+      * costs change.
+      */
+      size_t exponent_bits() const;
+
+      /**
+      * Return an estimate of the strength of this group against
+      * discrete logarithm attacks (eg NFS). Warning: since this only
+      * takes into account known attacks it is by necessity an
+      * overestimate of the actual strength.
+      */
+      size_t estimated_strength() const;
+
+      /**
+      * Decode a DER/BER encoded group into this instance.
+      * @param ber a vector containing the DER/BER encoded group
+      * @param format the format of the encoded group
+      *
+      * @warning avoid this. Instead use the DL_Group constructor
+      */
+      void BER_decode(const std::vector<uint8_t>& ber, Format format);
+
+      /**
+      * Decode a PEM encoded group into this instance.
+      * @param pem the PEM encoding of the group
+      */
+      void BOTAN_DEPRECATED("Use DL_Group_from_PEM") PEM_decode(const std::string& pem);
+
+      DL_Group_Source source() const;
+
+      /**
+      * Return PEM representation of named DL group
+      */
+      static std::string BOTAN_DEPRECATED("Use DL_Group(name).PEM_encode()")
+         PEM_for_named_group(const std::string& name);
+
+      /*
+      * For internal use only
+      */
+      static std::shared_ptr<DL_Group_Data> DL_group_info(const std::string& name);
+
+   private:
+      static std::shared_ptr<DL_Group_Data> load_DL_group_info(const char* p_str,
+                                                               const char* q_str,
+                                                               const char* g_str);
+
+      static std::shared_ptr<DL_Group_Data> load_DL_group_info(const char* p_str,
+                                                               const char* g_str);
+
+      static std::shared_ptr<DL_Group_Data>
+         BER_decode_DL_group(const uint8_t data[], size_t data_len,
+                             DL_Group::Format format,
+                             DL_Group_Source source);
+
+      const DL_Group_Data& data() const;
+      std::shared_ptr<DL_Group_Data> m_data;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp b/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp
@@ -0,0 +1,175 @@
+/*
+* List of discrete log groups
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_group.h>
+
+namespace Botan {
+
+//static
+std::shared_ptr<DL_Group_Data> DL_Group::DL_group_info(const std::string& name)
+   {
+   /* TLS FFDHE groups */
+
+   if(name == "ffdhe/ietf/2048")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "ffdhe/ietf/3072")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "ffdhe/ietf/4096")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "ffdhe/ietf/6144")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "ffdhe/ietf/8192")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   /* IETF IPsec groups */
+
+   if(name == "modp/ietf/1024")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/1536")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/2048")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/3072")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/4096")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/6144")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   if(name == "modp/ietf/8192")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+                                "0x2");
+      }
+
+   /* SRP groups
+
+   SRP groups have a p st (p-1)/2 is prime, but g is not a generator
+   of subgroup of size q, so set q == 0 to bypass generator check
+
+   Missing q doesn't matter for SRP, and nothing but SRP should be
+   using these parameters.
+   */
+
+   if(name == "modp/srp/1024")
+      {
+      return load_DL_group_info("0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3",
+                                "0",
+                                "0x2");
+      }
+
+   if(name == "modp/srp/1536")
+      {
+      return load_DL_group_info("0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB",
+                                "0",
+                                "0x2");
+      }
+
+   if(name == "modp/srp/2048")
+      {
+      return load_DL_group_info("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
+                                "0",
+                                "0x2");
+      }
+
+   if(name == "modp/srp/3072")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+                                "0",
+                                "0x5");
+      }
+
+   if(name == "modp/srp/4096")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF",
+                                "0",
+                                "0x5");
+      }
+
+   if(name == "modp/srp/6144")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF",
+                                "0",
+                                "0x5");
+      }
+
+   if(name == "modp/srp/8192")
+      {
+      return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+                                "0",
+                                "0x13");
+      }
+
+   /* DSA groups */
+
+   if(name == "dsa/jce/1024")
+      {
+      return load_DL_group_info("0xFD7F53811D75122952DF4A9C2EECE4E7F611B7523CEF4400C31E3F80B6512669455D402251FB593D8D58FABFC5F5BA30F6CB9B556CD7813B801D346FF26660B76B9950A5A49F9FE8047B1022C24FBBA9D7FEB7C61BF83B57E7C6A8A6150F04FB83F6D3C51EC3023554135A169132F675F3AE2B61D72AEFF22203199DD14801C7",
+                                "0x9760508F15230BCCB292B982A2EB840BF0581CF5",
+                                "0x469603512E30278CD3947595DB22EEC9826A6322ADC97344F41D740C325724C8F9EFBAA7D4D803FF8C609DCD100EBC5BDFCFAD7C6A425FAEA786EA2050EBE98351EA1FDA1FDF24D6947AA6B9AA23766953802F4D7D4A8ECBA06D19768A2491FFB16D0EF9C43A99B5F71672FF6F0A24B444D0736D04D38A1A1322DAF6CDD88C9D");
+      }
+
+   if(name == "dsa/botan/2048")
+      {
+      return load_DL_group_info("0x91C48A4FDFBCF7C02AE95E7DA126122B5DD2864F559B87E8E74A286D52F59BD1DE68DFD645D0E00C60C080031891980374EEB594A532BFD67B9A09EAC4B8663A07910E68F39465FB7040D25DF13932EBAC4347A530ECBA61C854F9B880D3C0C3660080587C45566DADE26BD5A394BE093B4C0F24B5AFFEF8EC6C5B3E57FB89025A9BC16769932131E16D3C94EFCAB18D0DF061203CC53E6103BC72D5594BFD40CA65380F44A9A851DCB075495FC033A8A58071A1BD78FE052F66555648EB4B719D2AFE8B4880F8DAD6F15818BA178F89274C870BE9B96EB08C46C40040CC2EFE1DFB1B1868DD319DE3C34A32A63AB6EB1224209A419680CC7902D1728D4DF9E1",
+                                "0x8CD7D450F86F0AD94EEE4CE469A8756D1EBD1058241943EAFFB0B354585E924D",
+                                "0xD9F5E0761B4DBD1833D6AB1A961A0996C5F22303F72D84C140F67C431D94AB5715BEA81A0C98D39CE4BCF78D6B9EBC895D34FE89D94091D5848615EF15F5E86F11D96F6C969E203DDFA58356420A49CB444B595B901A933CFE0767B594F18A07B7F91DECDBA446B88990F78F2FF91F2FE7CD43FD2E46D18EADA1F7BB6602C617F6EF3A4B284F2FD9BA10A36042DE8FA87A2CA36597FEC81157A1485E44041DF02830111CB880BBE6ED494814886F965CDC3135F5CCF1383728BF65B806F9692C0B10D6C4C09C75A6CA3B4013CB16AB2C105F6BE23AEA9000EAB2178985F972C98057E1C86E44E7218688EA4AE0F3636DCCA745C9DCD4E6AFFB67CCBC13D6131");
+      }
+
+   if(name == "dsa/botan/3072")
+      {
+      return load_DL_group_info("0xE4B50880759663585E142460CA2D9DFF132F8AE4C840DDA3A2666889124FE5638B84E8A29B7AF3FA1209BE6BFC4B5072ED3B2B7387BAF3F857F478A80228EF3600B76B3DCFB61D20D34465B2506D2CAF87DF6E7DC0CE91BD2D167A46F6ADCC31C531E4F9C7ABBDB92ADDF35B0A806C66292A5F5E17E964DD099903733AC428AB35D80EA6F685BFBA8BE4068E5418AE5ECAD9E8FF073DE2B63E4E7EAD35C8A9B70B5BD47CFB88D373B66F37931939B0AB71BD5595809086DA0155337D185A0E4FB36A519B1B6202B8591E6002449CF1CD3A66384F6D2073B1CD73BECA93BAF1E1A6117D0238F222AE1ED7FED185A890E7F67FAB8FEB9753CC134A5183DFE87AE2595F7B5C2D9FBB42249FDD59513E1D3396B3EB2FD86684F285A8448FE757A029881C40760B94EF919BDF9740C38389599EC51A6E9BB519A8E068491E9CE0A2FCFE3CB60D66CF0DFAD20A8EC684048684A61444575BD1724D7352B44A760077B3BD6BD385CE5B0A7250CC0BF768DA82923806EB9CFBB138843731B618208C759B",
+                                "0xB3EBD364EC69EF8CF3BAF643B75734B16339B2E49E5CDE1B59C1E9FB40EE0C5B",
+                                "0x2BED21EEF83964A230AE89BBA71D9F7C39C52FC8229B4E3BC7E5944D329DA10F010EAC9E7BAF6C009FC4EB2960723E2B56DF4663E4C3AC800E9258DE2F7649D206782893F865EFCA498D2EEF30074EA5E8A7AB262712A4D94A2F3B0B9A92EE400FB38A3CC59A5DC7E436D5C004B22E35028381B51C93407EB32D4AE0FD42CB45E12D0ECEE8A26238EDE2082A7B1522113C66CEF8D745C6CF3CB945F84D2F4DE16D44A71DE198270E13F03553C88B8D323AD0B948A1BF2103A949979B6ED16FB5F3C953D95B7C8E88CA67DCF5A636FB9CA39D924215F7A884ED6C7EE3C96D8D9715427974B7C4351282E13D3773F7D28B452F10892A13C7587328DEA4827B6B369B2A8DC172ADC583F51F2A6598C5483E5BC467B02F91D059C402D18E2C2680F776AA06F49280A2C72C17CC42D5B6E740C5C4B1AB3C51C2ED092BE2A2D8B053AE5773D1425ED2B08F06E2DD50592DF1A478C15591CDFD11564FF88FF38B721D42392FDA473212DCFD8D2D88A976A00AFFE6FFFB430A359E64CA2B351CA2412394");
+      }
+
+   return std::shared_ptr<DL_Group_Data>();
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dlies/dlies.cpp b/third_party/botan/src/lib/pubkey/dlies/dlies.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dlies/dlies.cpp
@@ -0,0 +1,219 @@
+/*
+* DLIES
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dlies.h>
+#include <limits>
+
+namespace Botan {
+
+DLIES_Encryptor::DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+                                 RandomNumberGenerator& rng,
+                                 KDF* kdf,
+                                 MessageAuthenticationCode* mac,
+                                 size_t mac_key_length) :
+   DLIES_Encryptor(own_priv_key, rng, kdf, nullptr, 0, mac, mac_key_length)
+   {
+   }
+
+DLIES_Encryptor::DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+                                 RandomNumberGenerator& rng,
+                                 KDF* kdf,
+                                 Cipher_Mode* cipher,
+                                 size_t cipher_key_len,
+                                 MessageAuthenticationCode* mac,
+                                 size_t mac_key_length) :
+   m_other_pub_key(),
+   m_own_pub_key(own_priv_key.public_value()),
+   m_ka(own_priv_key, rng, "Raw"),
+   m_kdf(kdf),
+   m_cipher(cipher),
+   m_cipher_key_len(cipher_key_len),
+   m_mac(mac),
+   m_mac_keylen(mac_key_length),
+   m_iv()
+   {
+   BOTAN_ASSERT_NONNULL(kdf);
+   BOTAN_ASSERT_NONNULL(mac);
+   }
+
+std::vector<uint8_t> DLIES_Encryptor::enc(const uint8_t in[], size_t length,
+                                       RandomNumberGenerator&) const
+   {
+   if(m_other_pub_key.empty())
+      {
+      throw Invalid_State("DLIES: The other key was never set");
+      }
+
+   // calculate secret value
+   const SymmetricKey secret_value = m_ka.derive_key(0, m_other_pub_key);
+
+   // derive secret key from secret value
+   const size_t required_key_length = m_cipher ? m_cipher_key_len + m_mac_keylen : length + m_mac_keylen;
+   const secure_vector<uint8_t> secret_keys = m_kdf->derive_key(required_key_length, secret_value.bits_of());
+
+   if(secret_keys.size() != required_key_length)
+      {
+      throw Encoding_Error("DLIES: KDF did not provide sufficient output");
+      }
+
+   secure_vector<uint8_t> ciphertext(in, in + length);
+   const size_t cipher_key_len = m_cipher ? m_cipher_key_len : length;
+
+   if(m_cipher)
+      {
+      SymmetricKey enc_key(secret_keys.data(), cipher_key_len);
+      m_cipher->set_key(enc_key);
+
+      if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+         throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+      m_cipher->start(m_iv.bits_of());
+      m_cipher->finish(ciphertext);
+      }
+   else
+      {
+      xor_buf(ciphertext, secret_keys, cipher_key_len);
+      }
+
+   // calculate MAC
+   m_mac->set_key(secret_keys.data() + cipher_key_len, m_mac_keylen);
+   secure_vector<uint8_t> tag = m_mac->process(ciphertext);
+
+   // out = (ephemeral) public key + ciphertext + tag
+   secure_vector<uint8_t> out(m_own_pub_key.size() + ciphertext.size() + tag.size());
+   buffer_insert(out, 0, m_own_pub_key);
+   buffer_insert(out, 0 + m_own_pub_key.size(), ciphertext);
+   buffer_insert(out, 0 + m_own_pub_key.size() + ciphertext.size(), tag);
+
+   return unlock(out);
+   }
+
+/**
+* Return the max size, in bytes, of a message
+* We assume DLIES is only used for key transport and limit the maximum size
+* to 512 bits
+*/
+size_t DLIES_Encryptor::maximum_input_size() const
+   {
+   return 64;
+   }
+
+size_t DLIES_Encryptor::ciphertext_length(size_t ptext_len) const
+   {
+   return m_own_pub_key.size() + m_mac->output_length() + m_cipher->output_length(ptext_len);
+   }
+
+DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+                                 RandomNumberGenerator& rng,
+                                 KDF* kdf,
+                                 Cipher_Mode* cipher,
+                                 size_t cipher_key_len,
+                                 MessageAuthenticationCode* mac,
+                                 size_t mac_key_length) :
+   m_pub_key_size(own_priv_key.public_value().size()),
+   m_ka(own_priv_key, rng, "Raw"),
+   m_kdf(kdf),
+   m_cipher(cipher),
+   m_cipher_key_len(cipher_key_len),
+   m_mac(mac),
+   m_mac_keylen(mac_key_length),
+   m_iv()
+   {
+   BOTAN_ASSERT_NONNULL(kdf);
+   BOTAN_ASSERT_NONNULL(mac);
+   }
+
+DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+                                 RandomNumberGenerator& rng,
+                                 KDF* kdf,
+                                 MessageAuthenticationCode* mac,
+                                 size_t mac_key_length) :
+   DLIES_Decryptor(own_priv_key, rng, kdf, nullptr, 0, mac, mac_key_length)
+   {}
+
+size_t DLIES_Decryptor::plaintext_length(size_t ctext_len) const
+   {
+   if(ctext_len < m_pub_key_size + m_mac->output_length())
+      return 0; // will throw if attempted
+
+   return ctext_len - (m_pub_key_size + m_mac->output_length());
+   }
+
+secure_vector<uint8_t> DLIES_Decryptor::do_decrypt(uint8_t& valid_mask,
+      const uint8_t msg[], size_t length) const
+   {
+   if(length < m_pub_key_size + m_mac->output_length())
+      {
+      throw Decoding_Error("DLIES decryption: ciphertext is too short");
+      }
+
+   // calculate secret value
+   std::vector<uint8_t> other_pub_key(msg, msg + m_pub_key_size);
+   const SymmetricKey secret_value = m_ka.derive_key(0, other_pub_key);
+
+   const size_t ciphertext_len = length - m_pub_key_size - m_mac->output_length();
+   size_t cipher_key_len = m_cipher ? m_cipher_key_len : ciphertext_len;
+
+   // derive secret key from secret value
+   const size_t required_key_length = cipher_key_len + m_mac_keylen;
+   secure_vector<uint8_t> secret_keys = m_kdf->derive_key(required_key_length, secret_value.bits_of());
+
+   if(secret_keys.size() != required_key_length)
+      {
+      throw Encoding_Error("DLIES: KDF did not provide sufficient output");
+      }
+
+   secure_vector<uint8_t> ciphertext(msg + m_pub_key_size, msg + m_pub_key_size + ciphertext_len);
+
+   // calculate MAC
+   m_mac->set_key(secret_keys.data() + cipher_key_len, m_mac_keylen);
+   secure_vector<uint8_t> calculated_tag = m_mac->process(ciphertext);
+
+   // calculated tag == received tag ?
+   secure_vector<uint8_t> tag(msg + m_pub_key_size + ciphertext_len,
+                           msg + m_pub_key_size + ciphertext_len + m_mac->output_length());
+
+   valid_mask = ct_compare_u8(tag.data(), calculated_tag.data(), tag.size());
+
+   // decrypt
+   if(m_cipher)
+      {
+      if(valid_mask)
+         {
+         SymmetricKey dec_key(secret_keys.data(), cipher_key_len);
+         m_cipher->set_key(dec_key);
+
+         try
+            {
+            // the decryption can fail:
+            // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
+
+            if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+               throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+            m_cipher->start(m_iv.bits_of());
+            m_cipher->finish(ciphertext);
+            }
+         catch(...)
+            {
+            valid_mask = 0;
+            }
+
+         }
+      else
+         {
+         return secure_vector<uint8_t>();
+         }
+      }
+   else
+      {
+      xor_buf(ciphertext, secret_keys.data(), cipher_key_len);
+      }
+
+   return ciphertext;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dlies/dlies.h b/third_party/botan/src/lib/pubkey/dlies/dlies.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dlies/dlies.h
@@ -0,0 +1,163 @@
+/*
+* DLIES
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DLIES_H_
+#define BOTAN_DLIES_H_
+
+#include <botan/pubkey.h>
+#include <botan/mac.h>
+#include <botan/kdf.h>
+#include <botan/dh.h>
+#include <botan/cipher_mode.h>
+
+namespace Botan {
+
+/**
+* DLIES Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) DLIES_Encryptor final : public PK_Encryptor
+   {
+   public:
+      /**
+      * Stream mode: use KDF to provide a stream of bytes to xor with the message
+      *
+      * @param own_priv_key own (ephemeral) DH private key
+      * @param rng the RNG to use
+      * @param kdf the KDF that should be used
+      * @param mac the MAC function that should be used
+      * @param mac_key_len key length of the MAC function. Default = 20 bytes
+      *
+      * output = (ephemeral) public key + ciphertext + tag
+      */
+      DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+                      RandomNumberGenerator& rng,
+                      KDF* kdf,
+                      MessageAuthenticationCode* mac,
+                      size_t mac_key_len = 20);
+
+      /**
+      * Block cipher mode
+      *
+      * @param own_priv_key own (ephemeral) DH private key
+      * @param rng the RNG to use
+      * @param kdf the KDF that should be used
+      * @param cipher the block cipher that should be used
+      * @param cipher_key_len the key length of the block cipher
+      * @param mac the MAC function that should be used
+      * @param mac_key_len key length of the MAC function. Default = 20 bytes
+      *
+      * output = (ephemeral) public key + ciphertext + tag
+      */
+      DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+                      RandomNumberGenerator& rng,
+                      KDF* kdf,
+                      Cipher_Mode* cipher,
+                      size_t cipher_key_len,
+                      MessageAuthenticationCode* mac,
+                      size_t mac_key_len = 20);
+
+      // Set the other parties public key
+      inline void set_other_key(const std::vector<uint8_t>& other_pub_key)
+         {
+         m_other_pub_key = other_pub_key;
+         }
+
+      /// Set the initialization vector for the data encryption method
+      inline void set_initialization_vector(const InitializationVector& iv)
+         {
+         m_iv = iv;
+         }
+
+   private:
+      std::vector<uint8_t> enc(const uint8_t[], size_t,
+                            RandomNumberGenerator&) const override;
+
+      size_t maximum_input_size() const override;
+
+      size_t ciphertext_length(size_t ptext_len) const override;
+
+      std::vector<uint8_t> m_other_pub_key;
+      std::vector<uint8_t> m_own_pub_key;
+      PK_Key_Agreement m_ka;
+      std::unique_ptr<KDF> m_kdf;
+      std::unique_ptr<Cipher_Mode> m_cipher;
+      const size_t m_cipher_key_len;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      const size_t m_mac_keylen;
+      InitializationVector m_iv;
+   };
+
+/**
+* DLIES Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) DLIES_Decryptor final : public PK_Decryptor
+   {
+   public:
+      /**
+      * Stream mode: use KDF to provide a stream of bytes to xor with the message
+      *
+      * @param own_priv_key own (ephemeral) DH private key
+      * @param rng the RNG to use
+      * @param kdf the KDF that should be used
+      * @param mac the MAC function that should be used
+      * @param mac_key_len key length of the MAC function. Default = 20 bytes
+      *
+      * input = (ephemeral) public key + ciphertext + tag
+      */
+      DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+                      RandomNumberGenerator& rng,
+                      KDF* kdf,
+                      MessageAuthenticationCode* mac,
+                      size_t mac_key_len = 20);
+
+      /**
+      * Block cipher mode
+      *
+      * @param own_priv_key own (ephemeral) DH private key
+      * @param rng the RNG to use
+      * @param kdf the KDF that should be used
+      * @param cipher the block cipher that should be used
+      * @param cipher_key_len the key length of the block cipher
+      * @param mac the MAC function that should be used
+      * @param mac_key_len key length of the MAC function. Default = 20 bytes
+      *
+      * input = (ephemeral) public key + ciphertext + tag
+      */
+      DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+                      RandomNumberGenerator& rng,
+                      KDF* kdf,
+                      Cipher_Mode* cipher,
+                      size_t cipher_key_len,
+                      MessageAuthenticationCode* mac,
+                      size_t mac_key_len = 20);
+
+      /// Set the initialization vector for the data decryption method
+      inline void set_initialization_vector(const InitializationVector& iv)
+         {
+         m_iv = iv;
+         }
+
+   private:
+      secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+                                     const uint8_t in[], size_t in_len) const override;
+
+      size_t plaintext_length(size_t ctext_len) const override;
+
+      const size_t m_pub_key_size;
+      PK_Key_Agreement m_ka;
+      std::unique_ptr<KDF> m_kdf;
+      std::unique_ptr<Cipher_Mode> m_cipher;
+      const size_t m_cipher_key_len;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      const size_t m_mac_keylen;
+      InitializationVector m_iv;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/dsa/dsa.cpp b/third_party/botan/src/lib/pubkey/dsa/dsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dsa/dsa.cpp
@@ -0,0 +1,230 @@
+/*
+* DSA
+* (C) 1999-2010,2014,2016 Jack Lloyd
+* (C) 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dsa.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+#include <botan/divide.h>
+#include <botan/internal/pk_ops_impl.h>
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+  #include <botan/emsa.h>
+  #include <botan/rfc6979.h>
+#endif
+
+namespace Botan {
+
+/*
+* DSA_PublicKey Constructor
+*/
+DSA_PublicKey::DSA_PublicKey(const DL_Group& grp, const BigInt& y1)
+   {
+   m_group = grp;
+   m_y = y1;
+   }
+
+/*
+* Create a DSA private key
+*/
+DSA_PrivateKey::DSA_PrivateKey(RandomNumberGenerator& rng,
+                               const DL_Group& grp,
+                               const BigInt& x_arg)
+   {
+   m_group = grp;
+
+   if(x_arg == 0)
+      m_x = BigInt::random_integer(rng, 2, group_q());
+   else
+      m_x = x_arg;
+
+   m_y = m_group.power_g_p(m_x, m_group.q_bits());
+   }
+
+DSA_PrivateKey::DSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                               const secure_vector<uint8_t>& key_bits) :
+   DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_57)
+   {
+   m_y = m_group.power_g_p(m_x, m_group.q_bits());
+   }
+
+/*
+* Check Private DSA Parameters
+*/
+bool DSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+   {
+   if(!DL_Scheme_PrivateKey::check_key(rng, strong) || m_x >= group_q())
+      return false;
+
+   if(!strong)
+      return true;
+
+   return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+   }
+
+namespace {
+
+/**
+* Object that can create a DSA signature
+*/
+class DSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+      DSA_Signature_Operation(const DSA_PrivateKey& dsa,
+                              const std::string& emsa,
+                              RandomNumberGenerator& rng) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_group(dsa.get_group()),
+         m_x(dsa.get_x())
+         {
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+         m_rfc6979_hash = hash_for_emsa(emsa);
+#endif
+
+         m_b = BigInt::random_integer(rng, 2, dsa.group_q());
+         m_b_inv = m_group.inverse_mod_q(m_b);
+         }
+
+      size_t signature_length() const override { return 2*m_group.q_bytes(); }
+      size_t max_input_bits() const override { return m_group.q_bits(); }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                   RandomNumberGenerator& rng) override;
+   private:
+      const DL_Group m_group;
+      const BigInt& m_x;
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+      std::string m_rfc6979_hash;
+#endif
+
+      BigInt m_b, m_b_inv;
+   };
+
+secure_vector<uint8_t>
+DSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+                                  RandomNumberGenerator& rng)
+   {
+   const BigInt& q = m_group.get_q();
+
+   BigInt m(msg, msg_len, m_group.q_bits());
+
+   while(m >= q)
+      m -= q;
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+   BOTAN_UNUSED(rng);
+   const BigInt k = generate_rfc6979_nonce(m_x, q, m, m_rfc6979_hash);
+#else
+   const BigInt k = BigInt::random_integer(rng, 1, q);
+#endif
+
+   const BigInt k_inv = m_group.inverse_mod_q(k);
+
+   /*
+   * It may not be strictly necessary for the reduction (g^k mod p) mod q to be
+   * const time, since r is published as part of the signature, and deriving
+   * anything useful about k from g^k mod p would seem to require computing a
+   * discrete logarithm.
+   *
+   * However it only increases the cost of signatures by about 7-10%, and DSA is
+   * only for legacy use anyway so we don't care about the performance so much.
+   */
+   const BigInt r = ct_modulo(m_group.power_g_p(k, m_group.q_bits()), m_group.get_q());
+
+   /*
+   * Blind the input message and compute x*r+m as (x*r*b + m*b)/b
+   */
+   m_b = m_group.square_mod_q(m_b);
+   m_b_inv = m_group.square_mod_q(m_b_inv);
+
+   m = m_group.multiply_mod_q(m_b, m);
+   const BigInt xr = m_group.multiply_mod_q(m_b, m_x, r);
+
+   const BigInt s = m_group.multiply_mod_q(m_b_inv, k_inv, m_group.mod_q(xr+m));
+
+   // With overwhelming probability, a bug rather than actual zero r/s
+   if(r.is_zero() || s.is_zero())
+      throw Internal_Error("Computed zero r/s during DSA signature");
+
+   return BigInt::encode_fixed_length_int_pair(r, s, q.bytes());
+   }
+
+/**
+* Object that can verify a DSA signature
+*/
+class DSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+      DSA_Verification_Operation(const DSA_PublicKey& dsa,
+                                 const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_group(dsa.get_group()),
+         m_y(dsa.get_y())
+         {
+         }
+
+      size_t max_input_bits() const override { return m_group.q_bits(); }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig[], size_t sig_len) override;
+   private:
+      const DL_Group m_group;
+      const BigInt& m_y;
+   };
+
+bool DSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+                                        const uint8_t sig[], size_t sig_len)
+   {
+   const BigInt& q = m_group.get_q();
+   const size_t q_bytes = q.bytes();
+
+   if(sig_len != 2*q_bytes || msg_len > q_bytes)
+      return false;
+
+   BigInt r(sig, q_bytes);
+   BigInt s(sig + q_bytes, q_bytes);
+   BigInt i(msg, msg_len, q.bits());
+
+   if(r <= 0 || r >= q || s <= 0 || s >= q)
+      return false;
+
+   s = inverse_mod(s, q);
+
+   const BigInt sr = m_group.multiply_mod_q(s, r);
+   const BigInt si = m_group.multiply_mod_q(s, i);
+
+   s = m_group.multi_exponentiate(si, m_y, sr);
+
+   // s is too big for Barrett, and verification doesn't need to be const-time
+   return (s % m_group.get_q() == r);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+DSA_PublicKey::create_verification_op(const std::string& params,
+                                      const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new DSA_Verification_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+DSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+                                    const std::string& params,
+                                    const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new DSA_Signature_Operation(*this, params, rng));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/dsa/dsa.h b/third_party/botan/src/lib/pubkey/dsa/dsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/dsa/dsa.h
@@ -0,0 +1,87 @@
+/*
+* DSA
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DSA_H_
+#define BOTAN_DSA_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* DSA Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) DSA_PublicKey : public virtual DL_Scheme_PublicKey
+   {
+   public:
+      std::string algo_name() const override { return "DSA"; }
+
+      DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_57; }
+      size_t message_parts() const override { return 2; }
+      size_t message_part_size() const override { return group_q().bytes(); }
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      DSA_PublicKey(const AlgorithmIdentifier& alg_id,
+                    const std::vector<uint8_t>& key_bits) :
+         DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_57)
+         {
+         }
+
+      /**
+      * Create a public key.
+      * @param group the underlying DL group
+      * @param y the public value y = g^x mod p
+      */
+      DSA_PublicKey(const DL_Group& group, const BigInt& y);
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   protected:
+      DSA_PublicKey() = default;
+   };
+
+/**
+* DSA Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) DSA_PrivateKey final : public DSA_PublicKey,
+                                 public virtual DL_Scheme_PrivateKey
+   {
+   public:
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded key bits in ANSI X9.57 format
+      */
+      DSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                     const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Create a private key.
+      * @param rng the RNG to use
+      * @param group the underlying DL group
+      * @param private_key the private key (if zero, a new random key is generated)
+      */
+      DSA_PrivateKey(RandomNumberGenerator& rng,
+                     const DL_Group& group,
+                     const BigInt& private_key = 0);
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp b/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp
@@ -0,0 +1,568 @@
+/*
+* Elliptic curves over GF(p) Montgomery Representation
+* (C) 2014,2015,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve_gfp.h>
+#include <botan/curve_nistp.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+
+namespace Botan {
+
+namespace {
+
+class CurveGFp_Montgomery final : public CurveGFp_Repr
+   {
+   public:
+      CurveGFp_Montgomery(const BigInt& p, const BigInt& a, const BigInt& b) :
+         m_p(p), m_a(a), m_b(b),
+         m_p_words(m_p.sig_words()),
+         m_p_dash(monty_inverse(m_p.word_at(0)))
+         {
+         Modular_Reducer mod_p(m_p);
+
+         m_r.set_bit(m_p_words * BOTAN_MP_WORD_BITS);
+         m_r = mod_p.reduce(m_r);
+
+         m_r2  = mod_p.square(m_r);
+         m_r3  = mod_p.multiply(m_r, m_r2);
+         m_a_r = mod_p.multiply(m_r, m_a);
+         m_b_r = mod_p.multiply(m_r, m_b);
+
+         m_a_is_zero = m_a.is_zero();
+         m_a_is_minus_3 = (m_a + 3 == m_p);
+         }
+
+      bool a_is_zero() const override { return m_a_is_zero; }
+      bool a_is_minus_3() const override { return m_a_is_minus_3; }
+
+      const BigInt& get_a() const override { return m_a; }
+
+      const BigInt& get_b() const override { return m_b; }
+
+      const BigInt& get_p() const override { return m_p; }
+
+      const BigInt& get_a_rep() const override { return m_a_r; }
+
+      const BigInt& get_b_rep() const override { return m_b_r; }
+
+      const BigInt& get_1_rep() const override { return m_r; }
+
+      bool is_one(const BigInt& x) const override { return x == m_r; }
+
+      size_t get_p_words() const override { return m_p_words; }
+
+      size_t get_ws_size() const override { return 2*m_p_words + 4; }
+
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+
+      void to_curve_rep(BigInt& x, secure_vector<word>& ws) const override;
+
+      void from_curve_rep(BigInt& x, secure_vector<word>& ws) const override;
+
+      void curve_mul_words(BigInt& z,
+                           const word x_words[],
+                           const size_t x_size,
+                           const BigInt& y,
+                           secure_vector<word>& ws) const override;
+
+      void curve_sqr_words(BigInt& z,
+                           const word x_words[],
+                           size_t x_size,
+                           secure_vector<word>& ws) const override;
+
+   private:
+      BigInt m_p;
+      BigInt m_a, m_b;
+      BigInt m_a_r, m_b_r;
+      size_t m_p_words; // cache of m_p.sig_words()
+
+      // Montgomery parameters
+      BigInt m_r, m_r2, m_r3;
+      word m_p_dash;
+
+      bool m_a_is_zero;
+      bool m_a_is_minus_3;
+   };
+
+BigInt CurveGFp_Montgomery::invert_element(const BigInt& x, secure_vector<word>& ws) const
+   {
+   // Should we use Montgomery inverse instead?
+   const BigInt inv = inverse_mod(x, m_p);
+   BigInt res;
+   curve_mul(res, inv, m_r3, ws);
+   return res;
+   }
+
+void CurveGFp_Montgomery::to_curve_rep(BigInt& x, secure_vector<word>& ws) const
+   {
+   const BigInt tx = x;
+   curve_mul(x, tx, m_r2, ws);
+   }
+
+void CurveGFp_Montgomery::from_curve_rep(BigInt& z, secure_vector<word>& ws) const
+   {
+   if(ws.size() < get_ws_size())
+      ws.resize(get_ws_size());
+
+   const size_t output_size = 2*m_p_words + 2;
+   if(z.size() < output_size)
+      z.grow_to(output_size);
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+   }
+
+void CurveGFp_Montgomery::curve_mul_words(BigInt& z,
+                                          const word x_w[],
+                                          size_t x_size,
+                                          const BigInt& y,
+                                          secure_vector<word>& ws) const
+   {
+   BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+   if(ws.size() < get_ws_size())
+      ws.resize(get_ws_size());
+
+   const size_t output_size = 2*m_p_words + 2;
+   if(z.size() < output_size)
+      z.grow_to(output_size);
+
+   bigint_mul(z.mutable_data(), z.size(),
+              x_w, x_size, std::min(m_p_words, x_size),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws.data(), ws.size());
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+   }
+
+void CurveGFp_Montgomery::curve_sqr_words(BigInt& z,
+                                          const word x[],
+                                          size_t x_size,
+                                          secure_vector<word>& ws) const
+   {
+   if(ws.size() < get_ws_size())
+      ws.resize(get_ws_size());
+
+   const size_t output_size = 2*m_p_words + 2;
+   if(z.size() < output_size)
+      z.grow_to(output_size);
+
+   bigint_sqr(z.mutable_data(), z.size(),
+              x, x_size, std::min(m_p_words, x_size),
+              ws.data(), ws.size());
+
+   bigint_monty_redc(z.mutable_data(),
+                     m_p.data(), m_p_words, m_p_dash,
+                     ws.data(), ws.size());
+   }
+
+class CurveGFp_NIST : public CurveGFp_Repr
+   {
+   public:
+      CurveGFp_NIST(size_t p_bits, const BigInt& a, const BigInt& b) :
+         m_1(1), m_a(a), m_b(b), m_p_words((p_bits + BOTAN_MP_WORD_BITS - 1) / BOTAN_MP_WORD_BITS)
+         {
+         // All Solinas prime curves are assumed a == -3
+         }
+
+      bool a_is_zero() const override { return false; }
+      bool a_is_minus_3() const override { return true; }
+
+      const BigInt& get_a() const override { return m_a; }
+
+      const BigInt& get_b() const override { return m_b; }
+
+      const BigInt& get_1_rep() const override { return m_1; }
+
+      size_t get_p_words() const override { return m_p_words; }
+
+      size_t get_ws_size() const override { return 2*m_p_words + 4; }
+
+      const BigInt& get_a_rep() const override { return m_a; }
+
+      const BigInt& get_b_rep() const override { return m_b; }
+
+      bool is_one(const BigInt& x) const override { return x == 1; }
+
+      void to_curve_rep(BigInt& x, secure_vector<word>& ws) const override
+         { redc_mod_p(x, ws); }
+
+      void from_curve_rep(BigInt& x, secure_vector<word>& ws) const override
+         { redc_mod_p(x, ws); }
+
+      virtual void redc_mod_p(BigInt& z, secure_vector<word>& ws) const = 0;
+
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+
+      void curve_mul_words(BigInt& z,
+                           const word x_words[],
+                           const size_t x_size,
+                           const BigInt& y,
+                           secure_vector<word>& ws) const override;
+
+      void curve_mul_tmp(BigInt& x, const BigInt& y, BigInt& tmp, secure_vector<word>& ws) const
+         {
+         curve_mul(tmp, x, y, ws);
+         x.swap(tmp);
+         }
+
+      void curve_sqr_tmp(BigInt& x, BigInt& tmp, secure_vector<word>& ws) const
+         {
+         curve_sqr(tmp, x, ws);
+         x.swap(tmp);
+         }
+
+      void curve_sqr_words(BigInt& z,
+                           const word x_words[],
+                           size_t x_size,
+                           secure_vector<word>& ws) const override;
+   private:
+      // Curve parameters
+      BigInt m_1;
+      BigInt m_a, m_b;
+      size_t m_p_words; // cache of m_p.sig_words()
+   };
+
+BigInt CurveGFp_NIST::invert_element(const BigInt& x, secure_vector<word>& ws) const
+   {
+   BOTAN_UNUSED(ws);
+   return inverse_mod(x, get_p());
+   }
+
+void CurveGFp_NIST::curve_mul_words(BigInt& z,
+                                    const word x_w[],
+                                    size_t x_size,
+                                    const BigInt& y,
+                                    secure_vector<word>& ws) const
+   {
+   BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+   if(ws.size() < get_ws_size())
+      ws.resize(get_ws_size());
+
+   const size_t output_size = 2*m_p_words + 2;
+   if(z.size() < output_size)
+      z.grow_to(output_size);
+
+   bigint_mul(z.mutable_data(), z.size(),
+              x_w, x_size, std::min(m_p_words, x_size),
+              y.data(), y.size(), std::min(m_p_words, y.size()),
+              ws.data(), ws.size());
+
+   this->redc_mod_p(z, ws);
+   }
+
+void CurveGFp_NIST::curve_sqr_words(BigInt& z, const word x[], size_t x_size,
+                                    secure_vector<word>& ws) const
+   {
+   if(ws.size() < get_ws_size())
+      ws.resize(get_ws_size());
+
+   const size_t output_size = 2*m_p_words + 2;
+   if(z.size() < output_size)
+      z.grow_to(output_size);
+
+   bigint_sqr(z.mutable_data(), output_size,
+              x, x_size, std::min(m_p_words, x_size),
+              ws.data(), ws.size());
+
+   this->redc_mod_p(z, ws);
+   }
+
+/**
+* The NIST P-192 curve
+*/
+class CurveGFp_P192 final : public CurveGFp_NIST
+   {
+   public:
+      CurveGFp_P192(const BigInt& a, const BigInt& b) : CurveGFp_NIST(192, a, b) {}
+      const BigInt& get_p() const override { return prime_p192(); }
+   private:
+      void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p192(x, ws); }
+   };
+
+/**
+* The NIST P-224 curve
+*/
+class CurveGFp_P224 final : public CurveGFp_NIST
+   {
+   public:
+      CurveGFp_P224(const BigInt& a, const BigInt& b) : CurveGFp_NIST(224, a, b) {}
+      const BigInt& get_p() const override { return prime_p224(); }
+   private:
+      void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p224(x, ws); }
+   };
+
+/**
+* The NIST P-256 curve
+*/
+class CurveGFp_P256 final : public CurveGFp_NIST
+   {
+   public:
+      CurveGFp_P256(const BigInt& a, const BigInt& b) : CurveGFp_NIST(256, a, b) {}
+      const BigInt& get_p() const override { return prime_p256(); }
+   private:
+      void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p256(x, ws); }
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+   };
+
+BigInt CurveGFp_P256::invert_element(const BigInt& x, secure_vector<word>& ws) const
+   {
+   BigInt r, p2, p4, p8, p16, p32, tmp;
+
+   curve_sqr(r, x, ws);
+
+   curve_mul(p2, r, x, ws);
+   curve_sqr(r, p2, ws);
+   curve_sqr_tmp(r, tmp, ws);
+
+   curve_mul(p4, r, p2, ws);
+
+   curve_sqr(r, p4, ws);
+   for(size_t i = 0; i != 3; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul(p8, r, p4, ws);
+
+   curve_sqr(r, p8, ws);
+   for(size_t i = 0; i != 7; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul(p16, r, p8, ws);
+
+   curve_sqr(r, p16, ws);
+   for(size_t i = 0; i != 15; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul(p32, r, p16, ws);
+
+   curve_sqr(r, p32, ws);
+   for(size_t i = 0; i != 31; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   for(size_t i = 0; i != 32*4; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p32, tmp, ws);
+
+   for(size_t i = 0; i != 32; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p32, tmp, ws);
+
+   for(size_t i = 0; i != 16; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p16, tmp, ws);
+   for(size_t i = 0; i != 8; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p8, tmp, ws);
+
+   for(size_t i = 0; i != 4; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p4, tmp, ws);
+
+   for(size_t i = 0; i != 2; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, p2, tmp, ws);
+
+   for(size_t i = 0; i != 2; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   return r;
+   }
+
+/**
+* The NIST P-384 curve
+*/
+class CurveGFp_P384 final : public CurveGFp_NIST
+   {
+   public:
+      CurveGFp_P384(const BigInt& a, const BigInt& b) : CurveGFp_NIST(384, a, b) {}
+      const BigInt& get_p() const override { return prime_p384(); }
+   private:
+      void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p384(x, ws); }
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+   };
+
+BigInt CurveGFp_P384::invert_element(const BigInt& x, secure_vector<word>& ws) const
+   {
+   // From https://briansmith.org/ecc-inversion-addition-chains-01
+
+   BigInt r, x2, x3, x15, x30, tmp, rl;
+
+   r = x;
+   curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+   x2 = r;
+
+   curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   x3 = r;
+
+   for(size_t i = 0; i != 3; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x3, tmp, ws);
+
+   rl = r;
+   for(size_t i = 0; i != 6; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+   for(size_t i = 0; i != 3; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x3, tmp, ws);
+
+   x15 = r;
+   for(size_t i = 0; i != 15; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x15, tmp, ws);
+
+   x30 = r;
+   for(size_t i = 0; i != 30; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x30, tmp, ws);
+
+   rl = r;
+   for(size_t i = 0; i != 60; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+   rl = r;
+   for(size_t i = 0; i != 120; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+   for(size_t i = 0; i != 15; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x15, tmp, ws);
+
+   for(size_t i = 0; i != 31; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x30, tmp, ws);
+
+   for(size_t i = 0; i != 2; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x2, tmp, ws);
+
+   for(size_t i = 0; i != 94; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x30, tmp, ws);
+
+   for(size_t i = 0; i != 2; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+
+   curve_mul_tmp(r, x, tmp, ws);
+
+   return r;
+   }
+
+/**
+* The NIST P-521 curve
+*/
+class CurveGFp_P521 final : public CurveGFp_NIST
+   {
+   public:
+      CurveGFp_P521(const BigInt& a, const BigInt& b) : CurveGFp_NIST(521, a, b) {}
+      const BigInt& get_p() const override { return prime_p521(); }
+   private:
+      void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p521(x, ws); }
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+   };
+
+BigInt CurveGFp_P521::invert_element(const BigInt& x, secure_vector<word>& ws) const
+   {
+   // Addition chain from https://eprint.iacr.org/2014/852.pdf section
+
+   BigInt r;
+   BigInt rl;
+   BigInt a7;
+   BigInt tmp;
+
+   curve_sqr(r, x, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   rl = r;
+
+   for(size_t i = 0; i != 3; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+   curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+   a7 = r; // need this value later
+
+   curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, x, tmp, ws);
+
+   rl = r;
+   for(size_t i = 0; i != 8; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+   rl = r;
+   for(size_t i = 0; i != 16; ++i)
+      curve_sqr_tmp(r, tmp, ws);
+   curve_mul_tmp(r, rl, tmp, ws);
+
+    rl = r;
+    for(size_t i = 0; i != 32; ++i)
+        curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, rl, tmp, ws);
+
+    rl = r;
+    for(size_t i = 0; i != 64; ++i)
+        curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, rl, tmp, ws);
+
+    rl = r;
+    for(size_t i = 0; i != 128; ++i)
+        curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, rl, tmp, ws);
+
+    rl = r;
+    for(size_t i = 0; i != 256; ++i)
+        curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, rl, tmp, ws);
+
+    for(size_t i = 0; i != 7; ++i)
+        curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, a7, tmp, ws);
+
+    for(size_t i = 0; i != 2; ++i)
+       curve_sqr_tmp(r, tmp, ws);
+    curve_mul_tmp(r, x, tmp, ws);
+
+    return r;
+   }
+
+}
+
+std::shared_ptr<CurveGFp_Repr>
+CurveGFp::choose_repr(const BigInt& p, const BigInt& a, const BigInt& b)
+   {
+   if(p == prime_p192())
+      return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P192(a, b));
+   if(p == prime_p224())
+      return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P224(a, b));
+   if(p == prime_p256())
+      return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P256(a, b));
+   if(p == prime_p384())
+      return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P384(a, b));
+   if(p == prime_p521())
+      return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P521(a, b));
+
+   return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_Montgomery(p, a, b));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h b/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h
@@ -0,0 +1,265 @@
+/*
+* Elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+*     2010-2011,2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GFP_CURVE_H_
+#define BOTAN_GFP_CURVE_H_
+
+#include <botan/bigint.h>
+#include <memory>
+
+// Currently exposed in PointGFp
+//BOTAN_FUTURE_INTERNAL_HEADER(curve_gfp.h)
+
+namespace Botan {
+
+class BOTAN_UNSTABLE_API CurveGFp_Repr
+   {
+   public:
+      virtual ~CurveGFp_Repr() = default;
+
+      virtual const BigInt& get_p() const = 0;
+      virtual const BigInt& get_a() const = 0;
+      virtual const BigInt& get_b() const = 0;
+
+      virtual size_t get_p_words() const = 0;
+
+      virtual size_t get_ws_size() const = 0;
+
+      virtual bool is_one(const BigInt& x) const = 0;
+
+      virtual bool a_is_zero() const = 0;
+
+      virtual bool a_is_minus_3() const = 0;
+
+      /*
+      * Returns to_curve_rep(get_a())
+      */
+      virtual const BigInt& get_a_rep() const = 0;
+
+      /*
+      * Returns to_curve_rep(get_b())
+      */
+      virtual const BigInt& get_b_rep() const = 0;
+
+      /*
+      * Returns to_curve_rep(1)
+      */
+      virtual const BigInt& get_1_rep() const = 0;
+
+      virtual BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const = 0;
+
+      virtual void to_curve_rep(BigInt& x, secure_vector<word>& ws) const = 0;
+
+      virtual void from_curve_rep(BigInt& x, secure_vector<word>& ws) const = 0;
+
+      void curve_mul(BigInt& z, const BigInt& x, const BigInt& y,
+                     secure_vector<word>& ws) const
+         {
+         BOTAN_DEBUG_ASSERT(x.sig_words() <= get_p_words());
+         curve_mul_words(z, x.data(), x.size(), y, ws);
+         }
+
+      virtual void curve_mul_words(BigInt& z,
+                                   const word x_words[],
+                                   const size_t x_size,
+                                   const BigInt& y,
+                                   secure_vector<word>& ws) const = 0;
+
+      void curve_sqr(BigInt& z, const BigInt& x,
+                             secure_vector<word>& ws) const
+         {
+         BOTAN_DEBUG_ASSERT(x.sig_words() <= get_p_words());
+         curve_sqr_words(z, x.data(), x.size(), ws);
+         }
+
+      virtual void curve_sqr_words(BigInt& z,
+                                   const word x_words[],
+                                   size_t x_size,
+                                   secure_vector<word>& ws) const = 0;
+   };
+
+/**
+* This class represents an elliptic curve over GF(p)
+*
+* There should not be any reason for applications to use this type.
+* If you need EC primitives use the interfaces EC_Group and PointGFp
+*
+* It is likely this class will be removed entirely in a future major
+* release.
+*/
+class BOTAN_UNSTABLE_API CurveGFp final
+   {
+   public:
+
+      /**
+      * Create an uninitialized CurveGFp
+      */
+      CurveGFp() = default;
+
+      /**
+      * Construct the elliptic curve E: y^2 = x^3 + ax + b over GF(p)
+      * @param p prime number of the field
+      * @param a first coefficient
+      * @param b second coefficient
+      */
+      CurveGFp(const BigInt& p, const BigInt& a, const BigInt& b) :
+         m_repr(choose_repr(p, a, b))
+         {
+         }
+
+      CurveGFp(const CurveGFp&) = default;
+
+      CurveGFp& operator=(const CurveGFp&) = default;
+
+      /**
+      * @return curve coefficient a
+      */
+      const BigInt& get_a() const { return m_repr->get_a(); }
+
+      /**
+      * @return curve coefficient b
+      */
+      const BigInt& get_b() const { return m_repr->get_b(); }
+
+      /**
+      * Get prime modulus of the field of the curve
+      * @return prime modulus of the field of the curve
+      */
+      const BigInt& get_p() const { return m_repr->get_p(); }
+
+      size_t get_p_words() const { return m_repr->get_p_words(); }
+
+      size_t get_ws_size() const { return m_repr->get_ws_size(); }
+
+      const BigInt& get_a_rep() const { return m_repr->get_a_rep(); }
+
+      const BigInt& get_b_rep() const { return m_repr->get_b_rep(); }
+
+      const BigInt& get_1_rep() const { return m_repr->get_1_rep(); }
+
+      bool a_is_minus_3() const { return m_repr->a_is_minus_3(); }
+      bool a_is_zero() const { return m_repr->a_is_zero(); }
+
+      bool is_one(const BigInt& x) const { return m_repr->is_one(x); }
+
+      BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const
+         {
+         return m_repr->invert_element(x, ws);
+         }
+
+      void to_rep(BigInt& x, secure_vector<word>& ws) const
+         {
+         m_repr->to_curve_rep(x, ws);
+         }
+
+      void from_rep(BigInt& x, secure_vector<word>& ws) const
+         {
+         m_repr->from_curve_rep(x, ws);
+         }
+
+      BigInt from_rep_to_tmp(const BigInt& x, secure_vector<word>& ws) const
+         {
+         BigInt xt(x);
+         m_repr->from_curve_rep(xt, ws);
+         return xt;
+         }
+
+      // TODO: from_rep taking && ref
+
+      void mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+         {
+         m_repr->curve_mul(z, x, y, ws);
+         }
+
+      void mul(BigInt& z, const word x_w[], size_t x_size,
+               const BigInt& y, secure_vector<word>& ws) const
+         {
+         m_repr->curve_mul_words(z, x_w, x_size, y, ws);
+         }
+
+      void sqr(BigInt& z, const BigInt& x, secure_vector<word>& ws) const
+         {
+         m_repr->curve_sqr(z, x, ws);
+         }
+
+      void sqr(BigInt& z, const word x_w[], size_t x_size, secure_vector<word>& ws) const
+         {
+         m_repr->curve_sqr_words(z, x_w, x_size, ws);
+         }
+
+      BigInt mul(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+         {
+         return mul_to_tmp(x, y, ws);
+         }
+
+      BigInt sqr(const BigInt& x, secure_vector<word>& ws) const
+         {
+         return sqr_to_tmp(x, ws);
+         }
+
+      BigInt mul_to_tmp(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+         {
+         BigInt z;
+         m_repr->curve_mul(z, x, y, ws);
+         return z;
+         }
+
+      BigInt sqr_to_tmp(const BigInt& x, secure_vector<word>& ws) const
+         {
+         BigInt z;
+         m_repr->curve_sqr(z, x, ws);
+         return z;
+         }
+
+      void swap(CurveGFp& other)
+         {
+         std::swap(m_repr, other.m_repr);
+         }
+
+      /**
+      * Equality operator
+      * @param other a curve
+      * @return true iff *this is the same as other
+      */
+      inline bool operator==(const CurveGFp& other) const
+         {
+         if(m_repr.get() == other.m_repr.get())
+            return true;
+
+         return (get_p() == other.get_p()) &&
+                (get_a() == other.get_a()) &&
+                (get_b() == other.get_b());
+         }
+
+   private:
+      static std::shared_ptr<CurveGFp_Repr>
+         choose_repr(const BigInt& p, const BigInt& a, const BigInt& b);
+
+      std::shared_ptr<CurveGFp_Repr> m_repr;
+   };
+
+inline bool operator!=(const CurveGFp& lhs, const CurveGFp& rhs)
+   {
+   return !(lhs == rhs);
+   }
+
+}
+
+namespace std {
+
+template<> inline
+void swap<Botan::CurveGFp>(Botan::CurveGFp& curve1,
+                           Botan::CurveGFp& curve2) noexcept
+   {
+   curve1.swap(curve2);
+   }
+
+} // namespace std
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp b/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp
@@ -0,0 +1,796 @@
+/*
+* ECC Domain Parameters
+*
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* (C) 2008,2018 Jack Lloyd
+* (C) 2018 Tobias Niemann
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ec_group.h>
+#include <botan/internal/point_mul.h>
+#include <botan/internal/primality.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/pem.h>
+#include <botan/reducer.h>
+#include <botan/mutex.h>
+#include <botan/rng.h>
+#include <vector>
+
+namespace Botan {
+
+class EC_Group_Data final
+   {
+   public:
+
+      EC_Group_Data(const BigInt& p,
+                    const BigInt& a,
+                    const BigInt& b,
+                    const BigInt& g_x,
+                    const BigInt& g_y,
+                    const BigInt& order,
+                    const BigInt& cofactor,
+                    const OID& oid,
+                    EC_Group_Source source) :
+         m_curve(p, a, b),
+         m_base_point(m_curve, g_x, g_y),
+         m_g_x(g_x),
+         m_g_y(g_y),
+         m_order(order),
+         m_cofactor(cofactor),
+         m_mod_order(order),
+         m_base_mult(m_base_point, m_mod_order),
+         m_oid(oid),
+         m_p_bits(p.bits()),
+         m_order_bits(order.bits()),
+         m_a_is_minus_3(a == p - 3),
+         m_a_is_zero(a.is_zero()),
+         m_source(source)
+         {
+         }
+
+      bool match(const BigInt& p, const BigInt& a, const BigInt& b,
+                 const BigInt& g_x, const BigInt& g_y,
+                 const BigInt& order, const BigInt& cofactor) const
+         {
+         return (this->p() == p &&
+                 this->a() == a &&
+                 this->b() == b &&
+                 this->order() == order &&
+                 this->cofactor() == cofactor &&
+                 this->g_x() == g_x &&
+                 this->g_y() == g_y);
+         }
+
+      void set_oid(const OID& oid)
+         {
+         BOTAN_STATE_CHECK(m_oid.empty());
+         m_oid = oid;
+         }
+
+      const OID& oid() const { return m_oid; }
+      const BigInt& p() const { return m_curve.get_p(); }
+      const BigInt& a() const { return m_curve.get_a(); }
+      const BigInt& b() const { return m_curve.get_b(); }
+      const BigInt& order() const { return m_order; }
+      const BigInt& cofactor() const { return m_cofactor; }
+      const BigInt& g_x() const { return m_g_x; }
+      const BigInt& g_y() const { return m_g_y; }
+
+      size_t p_bits() const { return m_p_bits; }
+      size_t p_bytes() const { return (m_p_bits + 7) / 8; }
+
+      size_t order_bits() const { return m_order_bits; }
+      size_t order_bytes() const { return (m_order_bits + 7) / 8; }
+
+      const CurveGFp& curve() const { return m_curve; }
+      const PointGFp& base_point() const { return m_base_point; }
+
+      bool a_is_minus_3() const { return m_a_is_minus_3; }
+      bool a_is_zero() const { return m_a_is_zero; }
+
+      BigInt mod_order(const BigInt& x) const { return m_mod_order.reduce(x); }
+
+      BigInt square_mod_order(const BigInt& x) const
+         {
+         return m_mod_order.square(x);
+         }
+
+      BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const
+         {
+         return m_mod_order.multiply(x, y);
+         }
+
+      BigInt multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const
+         {
+         return m_mod_order.multiply(m_mod_order.multiply(x, y), z);
+         }
+
+      BigInt inverse_mod_order(const BigInt& x) const
+         {
+         return inverse_mod(x, m_order);
+         }
+
+      PointGFp blinded_base_point_multiply(const BigInt& k,
+                                           RandomNumberGenerator& rng,
+                                           std::vector<BigInt>& ws) const
+         {
+         return m_base_mult.mul(k, rng, m_order, ws);
+         }
+
+      EC_Group_Source source() const { return m_source; }
+
+   private:
+      CurveGFp m_curve;
+      PointGFp m_base_point;
+
+      BigInt m_g_x;
+      BigInt m_g_y;
+      BigInt m_order;
+      BigInt m_cofactor;
+      Modular_Reducer m_mod_order;
+      PointGFp_Base_Point_Precompute m_base_mult;
+      OID m_oid;
+      size_t m_p_bits;
+      size_t m_order_bits;
+      bool m_a_is_minus_3;
+      bool m_a_is_zero;
+      EC_Group_Source m_source;
+   };
+
+class EC_Group_Data_Map final
+   {
+   public:
+      EC_Group_Data_Map() {}
+
+      size_t clear()
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         size_t count = m_registered_curves.size();
+         m_registered_curves.clear();
+         return count;
+         }
+
+      std::shared_ptr<EC_Group_Data> lookup(const OID& oid)
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+
+         for(auto i : m_registered_curves)
+            {
+            if(i->oid() == oid)
+               return i;
+            }
+
+         // Not found, check hardcoded data
+         std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
+
+         if(data)
+            {
+            m_registered_curves.push_back(data);
+            return data;
+            }
+
+         // Nope, unknown curve
+         return std::shared_ptr<EC_Group_Data>();
+         }
+
+      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
+                                                      const BigInt& a,
+                                                      const BigInt& b,
+                                                      const BigInt& g_x,
+                                                      const BigInt& g_y,
+                                                      const BigInt& order,
+                                                      const BigInt& cofactor,
+                                                      const OID& oid,
+                                                      EC_Group_Source source)
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+
+         for(auto i : m_registered_curves)
+            {
+            if(!oid.empty())
+               {
+               if(i->oid() == oid)
+                  {
+                  if(!i->match(p, a, b, g_x, g_y, order, cofactor))
+                     throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
+                                            " but another curve is already registered using that OID");
+                  return i;
+                  }
+               else if(i->oid().has_value())
+                  continue; // distinct OIDs so not a match
+               }
+
+            if(i->match(p, a, b, g_x, g_y, order, cofactor))
+               {
+               /*
+               * If the same curve was previously created without an OID
+               * but has been registered again using an OID, save that OID.
+               */
+               if(oid.empty() == false)
+                  {
+                  if(i->oid().empty() == true)
+                     {
+                     i->set_oid(oid);
+                     }
+                  else
+                     {
+                     throw Invalid_Argument("Cannot register ECC group with OID " + oid.to_string() +
+                                            " already registered using " + i->oid().to_string());
+                     }
+                  }
+               return i;
+               }
+            }
+
+         // Not found - if OID is set try looking up that way
+
+         if(oid.has_value())
+            {
+            // Not located in existing store - try hardcoded data set
+            std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
+
+            if(data)
+               {
+               m_registered_curves.push_back(data);
+               return data;
+               }
+            }
+
+         // Not found or no OID, add data and return
+         std::shared_ptr<EC_Group_Data> d =
+            std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source);
+
+         m_registered_curves.push_back(d);
+         return d;
+         }
+
+   private:
+      mutex_type m_mutex;
+      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
+   };
+
+//static
+EC_Group_Data_Map& EC_Group::ec_group_data()
+   {
+   /*
+   * This exists purely to ensure the allocator is constructed before g_ec_data,
+   * which ensures that its destructor runs after ~g_ec_data is complete.
+   */
+
+   static Allocator_Initializer g_init_allocator;
+   static EC_Group_Data_Map g_ec_data;
+   return g_ec_data;
+   }
+
+//static
+size_t EC_Group::clear_registered_curve_data()
+   {
+   return ec_group_data().clear();
+   }
+
+//static
+std::shared_ptr<EC_Group_Data>
+EC_Group::load_EC_group_info(const char* p_str,
+                             const char* a_str,
+                             const char* b_str,
+                             const char* g_x_str,
+                             const char* g_y_str,
+                             const char* order_str,
+                             const OID& oid)
+   {
+   const BigInt p(p_str);
+   const BigInt a(a_str);
+   const BigInt b(b_str);
+   const BigInt g_x(g_x_str);
+   const BigInt g_y(g_y_str);
+   const BigInt order(order_str);
+   const BigInt cofactor(1); // implicit
+
+   return std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
+   }
+
+//static
+std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[], size_t len,
+                                                             EC_Group_Source source)
+   {
+   BER_Decoder ber(bits, len);
+   BER_Object obj = ber.get_next_object();
+
+   if(obj.type() == NULL_TAG)
+      {
+      throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
+      }
+   else if(obj.type() == OBJECT_ID)
+      {
+      OID dom_par_oid;
+      BER_Decoder(bits, len).decode(dom_par_oid);
+      return ec_group_data().lookup(dom_par_oid);
+      }
+   else if(obj.type() == SEQUENCE)
+      {
+      BigInt p, a, b, order, cofactor;
+      std::vector<uint8_t> base_pt;
+      std::vector<uint8_t> seed;
+
+      BER_Decoder(bits, len)
+         .start_cons(SEQUENCE)
+           .decode_and_check<size_t>(1, "Unknown ECC param version code")
+           .start_cons(SEQUENCE)
+            .decode_and_check(OID("1.2.840.10045.1.1"),
+                              "Only prime ECC fields supported")
+             .decode(p)
+           .end_cons()
+           .start_cons(SEQUENCE)
+             .decode_octet_string_bigint(a)
+             .decode_octet_string_bigint(b)
+             .decode_optional_string(seed, BIT_STRING, BIT_STRING)
+           .end_cons()
+           .decode(base_pt, OCTET_STRING)
+           .decode(order)
+           .decode(cofactor)
+         .end_cons()
+         .verify_end();
+
+      if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p))
+         throw Decoding_Error("Invalid ECC p parameter");
+
+      if(a.is_negative() || a >= p)
+         throw Decoding_Error("Invalid ECC a parameter");
+
+      if(b <= 0 || b >= p)
+         throw Decoding_Error("Invalid ECC b parameter");
+
+      if(order <= 0 || !is_bailie_psw_probable_prime(order))
+         throw Decoding_Error("Invalid ECC order parameter");
+
+      if(cofactor <= 0 || cofactor >= 16)
+         throw Decoding_Error("Invalid ECC cofactor parameter");
+
+      std::pair<BigInt, BigInt> base_xy = Botan::OS2ECP(base_pt.data(), base_pt.size(), p, a, b);
+
+      return ec_group_data().lookup_or_create(p, a, b, base_xy.first, base_xy.second,
+                                              order, cofactor, OID(), source);
+      }
+   else
+      {
+      throw Decoding_Error("Unexpected tag while decoding ECC domain params");
+      }
+   }
+
+EC_Group::EC_Group()
+   {
+   }
+
+EC_Group::~EC_Group()
+   {
+   // shared_ptr possibly freed here
+   }
+
+EC_Group::EC_Group(const OID& domain_oid)
+   {
+   this->m_data = ec_group_data().lookup(domain_oid);
+   if(!this->m_data)
+      throw Invalid_Argument("Unknown EC_Group " + domain_oid.to_string());
+   }
+
+EC_Group::EC_Group(const std::string& str)
+   {
+   if(str == "")
+      return; // no initialization / uninitialized
+
+   try
+      {
+      const OID oid = OID::from_string(str);
+      if(oid.has_value())
+         m_data = ec_group_data().lookup(oid);
+      }
+   catch(...)
+      {
+      }
+
+   if(m_data == nullptr)
+      {
+      if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----")
+         {
+         // OK try it as PEM ...
+         secure_vector<uint8_t> ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
+         this->m_data = BER_decode_EC_group(ber.data(), ber.size(), EC_Group_Source::ExternalSource);
+         }
+      }
+
+   if(m_data == nullptr)
+      throw Invalid_Argument("Unknown ECC group '" + str + "'");
+   }
+
+//static
+EC_Group EC_Group::EC_Group_from_PEM(const std::string& pem)
+   {
+   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
+   return EC_Group(ber.data(), ber.size());
+   }
+
+//static
+std::string EC_Group::PEM_for_named_group(const std::string& name)
+   {
+   try
+      {
+      EC_Group group(name);
+      return group.PEM_encode();
+      }
+   catch(...)
+      {
+      return "";
+      }
+   }
+
+EC_Group::EC_Group(const BigInt& p,
+                   const BigInt& a,
+                   const BigInt& b,
+                   const BigInt& base_x,
+                   const BigInt& base_y,
+                   const BigInt& order,
+                   const BigInt& cofactor,
+                   const OID& oid)
+   {
+   m_data = ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid,
+                                             EC_Group_Source::ExternalSource);
+   }
+
+EC_Group::EC_Group(const uint8_t ber[], size_t ber_len)
+   {
+   m_data = BER_decode_EC_group(ber, ber_len, EC_Group_Source::ExternalSource);
+   }
+
+const EC_Group_Data& EC_Group::data() const
+   {
+   if(m_data == nullptr)
+      throw Invalid_State("EC_Group uninitialized");
+   return *m_data;
+   }
+
+const CurveGFp& EC_Group::get_curve() const
+   {
+   return data().curve();
+   }
+
+bool EC_Group::a_is_minus_3() const
+   {
+   return data().a_is_minus_3();
+   }
+
+bool EC_Group::a_is_zero() const
+   {
+   return data().a_is_zero();
+   }
+
+size_t EC_Group::get_p_bits() const
+   {
+   return data().p_bits();
+   }
+
+size_t EC_Group::get_p_bytes() const
+   {
+   return data().p_bytes();
+   }
+
+size_t EC_Group::get_order_bits() const
+   {
+   return data().order_bits();
+   }
+
+size_t EC_Group::get_order_bytes() const
+   {
+   return data().order_bytes();
+   }
+
+const BigInt& EC_Group::get_p() const
+   {
+   return data().p();
+   }
+
+const BigInt& EC_Group::get_a() const
+   {
+   return data().a();
+   }
+
+const BigInt& EC_Group::get_b() const
+   {
+   return data().b();
+   }
+
+const PointGFp& EC_Group::get_base_point() const
+   {
+   return data().base_point();
+   }
+
+const BigInt& EC_Group::get_order() const
+   {
+   return data().order();
+   }
+
+const BigInt& EC_Group::get_g_x() const
+   {
+   return data().g_x();
+   }
+
+const BigInt& EC_Group::get_g_y() const
+   {
+   return data().g_y();
+   }
+
+const BigInt& EC_Group::get_cofactor() const
+   {
+   return data().cofactor();
+   }
+
+BigInt EC_Group::mod_order(const BigInt& k) const
+   {
+   return data().mod_order(k);
+   }
+
+BigInt EC_Group::square_mod_order(const BigInt& x) const
+   {
+   return data().square_mod_order(x);
+   }
+
+BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y) const
+   {
+   return data().multiply_mod_order(x, y);
+   }
+
+BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const
+   {
+   return data().multiply_mod_order(x, y, z);
+   }
+
+BigInt EC_Group::inverse_mod_order(const BigInt& x) const
+   {
+   return data().inverse_mod_order(x);
+   }
+
+const OID& EC_Group::get_curve_oid() const
+   {
+   return data().oid();
+   }
+
+EC_Group_Source EC_Group::source() const
+   {
+   return data().source();
+   }
+
+size_t EC_Group::point_size(PointGFp::Compression_Type format) const
+   {
+   // Hybrid and standard format are (x,y), compressed is y, +1 format byte
+   if(format == PointGFp::COMPRESSED)
+      return (1 + get_p_bytes());
+   else
+      return (1 + 2*get_p_bytes());
+   }
+
+PointGFp EC_Group::OS2ECP(const uint8_t bits[], size_t len) const
+   {
+   return Botan::OS2ECP(bits, len, data().curve());
+   }
+
+PointGFp EC_Group::point(const BigInt& x, const BigInt& y) const
+   {
+   // TODO: randomize the representation?
+   return PointGFp(data().curve(), x, y);
+   }
+
+PointGFp EC_Group::point_multiply(const BigInt& x, const PointGFp& pt, const BigInt& y) const
+   {
+   PointGFp_Multi_Point_Precompute xy_mul(get_base_point(), pt);
+   return xy_mul.multi_exp(x, y);
+   }
+
+PointGFp EC_Group::blinded_base_point_multiply(const BigInt& k,
+                                               RandomNumberGenerator& rng,
+                                               std::vector<BigInt>& ws) const
+   {
+   return data().blinded_base_point_multiply(k, rng, ws);
+   }
+
+BigInt EC_Group::blinded_base_point_multiply_x(const BigInt& k,
+                                               RandomNumberGenerator& rng,
+                                               std::vector<BigInt>& ws) const
+   {
+   const PointGFp pt = data().blinded_base_point_multiply(k, rng, ws);
+
+   if(pt.is_zero())
+      return 0;
+   return pt.get_affine_x();
+   }
+
+BigInt EC_Group::random_scalar(RandomNumberGenerator& rng) const
+   {
+   return BigInt::random_integer(rng, 1, get_order());
+   }
+
+PointGFp EC_Group::blinded_var_point_multiply(const PointGFp& point,
+                                              const BigInt& k,
+                                              RandomNumberGenerator& rng,
+                                              std::vector<BigInt>& ws) const
+   {
+   PointGFp_Var_Point_Precompute mul(point, rng, ws);
+   return mul.mul(k, rng, get_order(), ws);
+   }
+
+PointGFp EC_Group::zero_point() const
+   {
+   return PointGFp(data().curve());
+   }
+
+std::vector<uint8_t>
+EC_Group::DER_encode(EC_Group_Encoding form) const
+   {
+   std::vector<uint8_t> output;
+
+   DER_Encoder der(output);
+
+   if(form == EC_DOMPAR_ENC_EXPLICIT)
+      {
+      const size_t ecpVers1 = 1;
+      const OID curve_type("1.2.840.10045.1.1"); // prime field
+
+      const size_t p_bytes = get_p_bytes();
+
+      der.start_cons(SEQUENCE)
+            .encode(ecpVers1)
+            .start_cons(SEQUENCE)
+               .encode(curve_type)
+               .encode(get_p())
+            .end_cons()
+            .start_cons(SEQUENCE)
+               .encode(BigInt::encode_1363(get_a(), p_bytes),
+                       OCTET_STRING)
+               .encode(BigInt::encode_1363(get_b(), p_bytes),
+                       OCTET_STRING)
+            .end_cons()
+              .encode(get_base_point().encode(PointGFp::UNCOMPRESSED), OCTET_STRING)
+            .encode(get_order())
+            .encode(get_cofactor())
+         .end_cons();
+      }
+   else if(form == EC_DOMPAR_ENC_OID)
+      {
+      const OID oid = get_curve_oid();
+      if(oid.empty())
+         {
+         throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
+         }
+      der.encode(oid);
+      }
+   else if(form == EC_DOMPAR_ENC_IMPLICITCA)
+      {
+      der.encode_null();
+      }
+   else
+      {
+      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
+      }
+
+   return output;
+   }
+
+std::string EC_Group::PEM_encode() const
+   {
+   const std::vector<uint8_t> der = DER_encode(EC_DOMPAR_ENC_EXPLICIT);
+   return PEM_Code::encode(der, "EC PARAMETERS");
+   }
+
+bool EC_Group::operator==(const EC_Group& other) const
+   {
+   if(m_data == other.m_data)
+      return true; // same shared rep
+
+   /*
+   * No point comparing order/cofactor as they are uniquely determined
+   * by the curve equation (p,a,b) and the base point.
+   */
+   return (get_p() == other.get_p() &&
+           get_a() == other.get_a() &&
+           get_b() == other.get_b() &&
+           get_g_x() == other.get_g_x() &&
+           get_g_y() == other.get_g_y());
+   }
+
+bool EC_Group::verify_public_element(const PointGFp& point) const
+   {
+   //check that public point is not at infinity
+   if(point.is_zero())
+      return false;
+
+   //check that public point is on the curve
+   if(point.on_the_curve() == false)
+      return false;
+
+   //check that public point has order q
+   if((point * get_order()).is_zero() == false)
+      return false;
+
+   if(get_cofactor() > 1)
+      {
+      if((point * get_cofactor()).is_zero())
+         return false;
+      }
+
+   return true;
+   }
+
+bool EC_Group::verify_group(RandomNumberGenerator& rng,
+                            bool strong) const
+   {
+   const bool is_builtin = source() == EC_Group_Source::Builtin;
+
+   if(is_builtin && !strong)
+      return true;
+
+   const BigInt& p = get_p();
+   const BigInt& a = get_a();
+   const BigInt& b = get_b();
+   const BigInt& order = get_order();
+   const PointGFp& base_point = get_base_point();
+
+   if(p <= 3 || order <= 0)
+      return false;
+   if(a < 0 || a >= p)
+      return false;
+   if(b <= 0 || b >= p)
+      return false;
+
+   const size_t test_prob = 128;
+   const bool is_randomly_generated = is_builtin;
+
+   //check if field modulus is prime
+   if(!is_prime(p, rng, test_prob, is_randomly_generated))
+      {
+      return false;
+      }
+
+   //check if order is prime
+   if(!is_prime(order, rng, test_prob, is_randomly_generated))
+      {
+      return false;
+      }
+
+   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
+   const Modular_Reducer mod_p(p);
+
+   const BigInt discriminant = mod_p.reduce(
+      mod_p.multiply(4, mod_p.cube(a)) +
+      mod_p.multiply(27, mod_p.square(b)));
+
+   if(discriminant == 0)
+      {
+      return false;
+      }
+
+   //check for valid cofactor
+   if(get_cofactor() < 1)
+      {
+      return false;
+      }
+
+   //check if the base point is on the curve
+   if(!base_point.on_the_curve())
+      {
+      return false;
+      }
+   if((base_point * get_cofactor()).is_zero())
+      {
+      return false;
+      }
+   //check if order of the base point is correct
+   if(!(base_point * order).is_zero())
+      {
+      return false;
+      }
+
+   return true;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ec_group/ec_group.h b/third_party/botan/src/lib/pubkey/ec_group/ec_group.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/ec_group.h
@@ -0,0 +1,398 @@
+/*
+* ECC Domain Parameters
+*
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*     2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECC_DOMAIN_PARAMETERS_H_
+#define BOTAN_ECC_DOMAIN_PARAMETERS_H_
+
+#include <botan/point_gfp.h>
+#include <botan/asn1_oid.h>
+#include <memory>
+#include <set>
+
+namespace Botan {
+
+/**
+* This class represents elliptic curce domain parameters
+*/
+enum EC_Group_Encoding {
+   EC_DOMPAR_ENC_EXPLICIT = 0,
+   EC_DOMPAR_ENC_IMPLICITCA = 1,
+   EC_DOMPAR_ENC_OID = 2
+};
+
+enum class EC_Group_Source {
+   Builtin,
+   ExternalSource,
+};
+
+class CurveGFp;
+
+class EC_Group_Data;
+class EC_Group_Data_Map;
+
+/**
+* Class representing an elliptic curve
+*
+* The internal representation is stored in a shared_ptr, so copying an
+* EC_Group is inexpensive.
+*/
+class BOTAN_PUBLIC_API(2,0) EC_Group final
+   {
+   public:
+
+      /**
+      * Construct Domain paramers from specified parameters
+      * @param curve elliptic curve
+      * @param base_point a base point
+      * @param order the order of the base point
+      * @param cofactor the cofactor
+      */
+      BOTAN_DEPRECATED("Use version taking all BigInts")
+      EC_Group(const CurveGFp& curve,
+               const PointGFp& base_point,
+               const BigInt& order,
+               const BigInt& cofactor) :
+         EC_Group(curve.get_p(),
+                  curve.get_a(),
+                  curve.get_b(),
+                  base_point.get_affine_x(),
+                  base_point.get_affine_y(),
+                  order,
+                  cofactor) {}
+
+      /**
+      * Construct Domain paramers from specified parameters
+      * @param p the elliptic curve p
+      * @param a the elliptic curve a param
+      * @param b the elliptic curve b param
+      * @param base_x the x coordinate of the base point
+      * @param base_y the y coordinate of the base point
+      * @param order the order of the base point
+      * @param cofactor the cofactor
+      * @param oid an optional OID used to identify this curve
+      */
+      EC_Group(const BigInt& p,
+               const BigInt& a,
+               const BigInt& b,
+               const BigInt& base_x,
+               const BigInt& base_y,
+               const BigInt& order,
+               const BigInt& cofactor,
+               const OID& oid = OID());
+
+      /**
+      * Decode a BER encoded ECC domain parameter set
+      * @param ber the bytes of the BER encoding
+      * @param ber_len the length of ber
+      */
+      explicit EC_Group(const uint8_t ber[], size_t ber_len);
+
+      template<typename Alloc>
+         EC_Group(const std::vector<uint8_t, Alloc>& ber) :
+         EC_Group(ber.data(), ber.size()) {}
+
+      /**
+      * Create an EC domain by OID (or throw if unknown)
+      * @param oid the OID of the EC domain to create
+      */
+      explicit EC_Group(const OID& oid);
+
+      /**
+      * Create an EC domain from PEM encoding (as from PEM_encode), or
+      * from an OID name (eg "secp256r1", or "1.2.840.10045.3.1.7")
+      * @param pem_or_oid PEM-encoded data, or an OID
+
+      * @warning Support for PEM in this function is deprecated. Use
+      * EC_Group_from_PEM
+      */
+      explicit EC_Group(const std::string& pem_or_oid);
+
+      static EC_Group EC_Group_from_PEM(const std::string& pem);
+
+      /**
+      * Create an uninitialized EC_Group
+      */
+      EC_Group();
+
+      ~EC_Group();
+
+      EC_Group(const EC_Group&) = default;
+      EC_Group(EC_Group&&) = default;
+
+      EC_Group& operator=(const EC_Group&) = default;
+      EC_Group& operator=(EC_Group&&) = default;
+
+      /**
+      * Create the DER encoding of this domain
+      * @param form of encoding to use
+      * @returns bytes encododed as DER
+      */
+      std::vector<uint8_t> DER_encode(EC_Group_Encoding form) const;
+
+      /**
+      * Return the PEM encoding (always in explicit form)
+      * @return string containing PEM data
+      */
+      std::string PEM_encode() const;
+
+      /**
+      * Return domain parameter curve
+      * @result domain parameter curve
+      */
+      BOTAN_DEPRECATED("Avoid CurveGFp") const CurveGFp& get_curve() const;
+
+      /**
+      * Return if a == -3 mod p
+      */
+      bool a_is_minus_3() const;
+
+      /**
+      * Return if a == 0 mod p
+      */
+      bool a_is_zero() const;
+
+      /**
+      * Return the size of p in bits (same as get_p().bits())
+      */
+      size_t get_p_bits() const;
+
+      /**
+      * Return the size of p in bits (same as get_p().bytes())
+      */
+      size_t get_p_bytes() const;
+
+      /**
+      * Return the size of group order in bits (same as get_order().bits())
+      */
+      size_t get_order_bits() const;
+
+      /**
+      * Return the size of p in bytes (same as get_order().bytes())
+      */
+      size_t get_order_bytes() const;
+
+      /**
+      * Return the prime modulus of the field
+      */
+      const BigInt& get_p() const;
+
+      /**
+      * Return the a parameter of the elliptic curve equation
+      */
+      const BigInt& get_a() const;
+
+      /**
+      * Return the b parameter of the elliptic curve equation
+      */
+      const BigInt& get_b() const;
+
+      /**
+      * Return group base point
+      * @result base point
+      */
+      const PointGFp& get_base_point() const;
+
+      /**
+      * Return the x coordinate of the base point
+      */
+      const BigInt& get_g_x() const;
+
+      /**
+      * Return the y coordinate of the base point
+      */
+      const BigInt& get_g_y() const;
+
+      /**
+      * Return the order of the base point
+      * @result order of the base point
+      */
+      const BigInt& get_order() const;
+
+      /*
+      * Reduce x modulo the order
+      */
+      BigInt mod_order(const BigInt& x) const;
+
+      /*
+      * Return inverse of x modulo the order
+      */
+      BigInt inverse_mod_order(const BigInt& x) const;
+
+      /*
+      * Reduce (x*x) modulo the order
+      */
+      BigInt square_mod_order(const BigInt& x) const;
+
+      /*
+      * Reduce (x*y) modulo the order
+      */
+      BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const;
+
+      /*
+      * Reduce (x*y*z) modulo the order
+      */
+      BigInt multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+      /**
+      * Return the cofactor
+      * @result the cofactor
+      */
+      const BigInt& get_cofactor() const;
+
+      /**
+      * Check if y is a plausible point on the curve
+      *
+      * In particular, checks that it is a point on the curve, not infinity,
+      * and that it has order matching the group.
+      */
+      bool verify_public_element(const PointGFp& y) const;
+
+      /**
+      * Return the OID of these domain parameters
+      * @result the OID as a string
+      */
+      std::string BOTAN_DEPRECATED("Use get_curve_oid") get_oid() const { return get_curve_oid().to_string(); }
+
+      /**
+      * Return the OID of these domain parameters
+      * @result the OID
+      */
+      const OID& get_curve_oid() const;
+
+      /**
+      * Return a point on this curve with the affine values x, y
+      */
+      PointGFp point(const BigInt& x, const BigInt& y) const;
+
+      /**
+      * Multi exponentiate. Not constant time.
+      * @return base_point*x + pt*y
+      */
+      PointGFp point_multiply(const BigInt& x, const PointGFp& pt, const BigInt& y) const;
+
+      /**
+      * Blinded point multiplication, attempts resistance to side channels
+      * @param k the scalar
+      * @param rng a random number generator
+      * @param ws a temp workspace
+      * @return base_point*k
+      */
+      PointGFp blinded_base_point_multiply(const BigInt& k,
+                                           RandomNumberGenerator& rng,
+                                           std::vector<BigInt>& ws) const;
+
+      /**
+      * Blinded point multiplication, attempts resistance to side channels
+      * Returns just the x coordinate of the point
+      *
+      * @param k the scalar
+      * @param rng a random number generator
+      * @param ws a temp workspace
+      * @return x coordinate of base_point*k
+      */
+      BigInt blinded_base_point_multiply_x(const BigInt& k,
+                                           RandomNumberGenerator& rng,
+                                           std::vector<BigInt>& ws) const;
+
+      /**
+      * Blinded point multiplication, attempts resistance to side channels
+      * @param point input point
+      * @param k the scalar
+      * @param rng a random number generator
+      * @param ws a temp workspace
+      * @return point*k
+      */
+      PointGFp blinded_var_point_multiply(const PointGFp& point,
+                                          const BigInt& k,
+                                          RandomNumberGenerator& rng,
+                                          std::vector<BigInt>& ws) const;
+
+      /**
+      * Return a random scalar ie an integer in [1,order)
+      */
+      BigInt random_scalar(RandomNumberGenerator& rng) const;
+
+      /**
+      * Return the zero (or infinite) point on this curve
+      */
+      PointGFp zero_point() const;
+
+      size_t point_size(PointGFp::Compression_Type format) const;
+
+      PointGFp OS2ECP(const uint8_t bits[], size_t len) const;
+
+      template<typename Alloc>
+      PointGFp OS2ECP(const std::vector<uint8_t, Alloc>& vec) const
+         {
+         return this->OS2ECP(vec.data(), vec.size());
+         }
+
+      bool initialized() const { return (m_data != nullptr); }
+
+      /**
+       * Verify EC_Group domain
+       * @returns true if group is valid. false otherwise
+       */
+      bool verify_group(RandomNumberGenerator& rng,
+                        bool strong = false) const;
+
+      bool operator==(const EC_Group& other) const;
+
+      EC_Group_Source source() const;
+
+      /**
+      * Return PEM representation of named EC group
+      * Deprecated: Use EC_Group(name).PEM_encode() if this is needed
+      */
+      static std::string BOTAN_DEPRECATED("See header comment") PEM_for_named_group(const std::string& name);
+
+      /**
+      * Return a set of known named EC groups
+      */
+      static const std::set<std::string>& known_named_groups();
+
+      /*
+      * For internal use only
+      */
+      static std::shared_ptr<EC_Group_Data> EC_group_info(const OID& oid);
+
+      static size_t clear_registered_curve_data();
+
+   private:
+      static EC_Group_Data_Map& ec_group_data();
+
+      static std::shared_ptr<EC_Group_Data> BER_decode_EC_group(const uint8_t bits[], size_t len,
+                                                                EC_Group_Source source);
+
+      static std::shared_ptr<EC_Group_Data>
+         load_EC_group_info(const char* p,
+                            const char* a,
+                            const char* b,
+                            const char* g_x,
+                            const char* g_y,
+                            const char* order,
+                            const OID& oid);
+
+      // Member data
+      const EC_Group_Data& data() const;
+      std::shared_ptr<EC_Group_Data> m_data;
+   };
+
+inline bool operator!=(const EC_Group& lhs,
+                       const EC_Group& rhs)
+   {
+   return !(lhs == rhs);
+   }
+
+// For compatibility with 1.8
+typedef EC_Group EC_Domain_Params;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp b/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp
@@ -0,0 +1,301 @@
+/*
+* List of ECC groups
+* (C) 2013,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ec_group.h>
+
+namespace Botan {
+
+//static
+std::shared_ptr<EC_Group_Data> EC_Group::EC_group_info(const OID& oid)
+   {
+   // P-256
+   if(oid == OID{1,2,840,10045,3,1,7})
+      return load_EC_group_info("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+                                "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+                                "0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+                                "0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+                                "0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+                                "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+                                oid);
+
+   // P-384
+   if(oid == OID{1,3,132,0,34})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
+                                "0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+                                "0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+                                "0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+                                oid);
+   // P-521
+   if(oid == OID{1,3,132,0,35})
+      return load_EC_group_info("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+                                "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
+                                "0x51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+                                "0xC6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+                                "0x11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+                                "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+                                oid);
+
+   // brainpool160r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,1})
+      return load_EC_group_info("0xE95E4A5F737059DC60DFC7AD95B3D8139515620F",
+                                "0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300",
+                                "0x1E589A8595423412134FAA2DBDEC95C8D8675E58",
+                                "0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3",
+                                "0x1667CB477A1A8EC338F94741669C976316DA6321",
+                                "0xE95E4A5F737059DC60DF5991D45029409E60FC09",
+                                oid);
+   // brainpool192r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,3})
+      return load_EC_group_info("0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297",
+                                "0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF",
+                                "0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9",
+                                "0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6",
+                                "0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F",
+                                "0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1",
+                                oid);
+   // brainpool224r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,5})
+      return load_EC_group_info("0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF",
+                                "0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43",
+                                "0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B",
+                                "0xD9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D",
+                                "0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD",
+                                "0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F",
+                                oid);
+   // brainpool256r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,7})
+      return load_EC_group_info("0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377",
+                                "0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9",
+                                "0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6",
+                                "0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262",
+                                "0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997",
+                                "0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7",
+                                oid);
+   // brainpool320r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,9})
+      return load_EC_group_info("0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27",
+                                "0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4",
+                                "0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6",
+                                "0x43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611",
+                                "0x14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1",
+                                "0xD35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311",
+                                oid);
+   // brainpool384r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,11})
+      return load_EC_group_info("0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
+                                "0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
+                                "0x4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
+                                "0x1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
+                                "0x8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
+                                "0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
+                                oid);
+   // brainpool512r1
+   if(oid == OID{1,3,36,3,3,2,8,1,1,13})
+      return load_EC_group_info("0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
+                                "0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
+                                "0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
+                                "0x81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
+                                "0x7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
+                                "0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
+                                oid);
+   // frp256v1
+   if(oid == OID{1,2,250,1,223,101,256,1})
+      return load_EC_group_info("0xF1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03",
+                                "0xF1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00",
+                                "0xEE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F",
+                                "0xB6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF",
+                                "0x6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB",
+                                "0xF1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1",
+                                oid);
+   // gost_256A
+   if(oid == OID{1,2,643,2,2,35,1} || oid == OID{1,2,643,2,2,36,0} || oid == OID{1,2,643,7,1,2,1,1,1})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
+                                "0xA6",
+                                "0x1",
+                                "0x8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
+                                OID{1,2,643,7,1,2,1,1,1});
+
+   // gost_512A
+   if(oid == OID{1,2,643,7,1,2,1,2,1})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",
+                                "0xE8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",
+                                "3",
+                                "0x7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",
+                                oid);
+
+   // secp160k1
+   if(oid == OID{1,3,132,0,9})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+                                "0x0",
+                                "0x7",
+                                "0x3B4C382CE37AA192A4019E763036F4F5DD4D7EBB",
+                                "0x938CF935318FDCED6BC28286531733C3F03C4FEE",
+                                "0x100000000000000000001B8FA16DFAB9ACA16B6B3",
+                                oid);
+   // secp160r1
+   if(oid == OID{1,3,132,0,8})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+                                "0x1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+                                "0x4A96B5688EF573284664698968C38BB913CBFC82",
+                                "0x23A628553168947D59DCC912042351377AC5FB32",
+                                "0x100000000000000000001F4C8F927AED3CA752257",
+                                oid);
+   // secp160r2
+   if(oid == OID{1,3,132,0,30})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70",
+                                "0xB4E134D3FB59EB8BAB57274904664D5AF50388BA",
+                                "0x52DCB034293A117E1F4FF11B30F7199D3144CE6D",
+                                "0xFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E",
+                                "0x100000000000000000000351EE786A818F3A1A16B",
+                                oid);
+   // secp192k1
+   if(oid == OID{1,3,132,0,31})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37",
+                                "0x0",
+                                "0x3",
+                                "0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D",
+                                "0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D",
+                                oid);
+   // secp192r1
+   if(oid == OID{1,2,840,10045,3,1,1})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+                                "0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
+                                "0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+                                "0x7192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
+                                oid);
+   // secp224k1
+   if(oid == OID{1,3,132,0,32})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D",
+                                "0x0",
+                                "0x5",
+                                "0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C",
+                                "0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5",
+                                "0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7",
+                                oid);
+   // secp224r1
+   if(oid == OID{1,3,132,0,33})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE",
+                                "0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
+                                "0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
+                                "0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
+                                oid);
+   // secp256k1
+   if(oid == OID{1,3,132,0,10})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
+                                "0x0",
+                                "0x7",
+                                "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
+                                "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
+                                oid);
+
+   // sm2p256v1
+   if(oid == OID{1,2,156,10197,1,301})
+      return load_EC_group_info("0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
+                                "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
+                                "0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
+                                "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
+                                "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
+                                "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
+                                oid);
+   // x962_p192v2
+   if(oid == OID{1,2,840,10045,3,1,2})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+                                "0xCC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953",
+                                "0xEEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A",
+                                "0x6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31",
+                                oid);
+   // x962_p192v3
+   if(oid == OID{1,2,840,10045,3,1,3})
+      return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+                                "0x22123DC2395A05CAA7423DAECCC94760A7D462256BD56916",
+                                "0x7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896",
+                                "0x38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0",
+                                "0xFFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13",
+                                oid);
+   // x962_p239v1
+   if(oid == OID{1,2,840,10045,3,1,4})
+      return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+                                "0x6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A",
+                                "0xFFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF",
+                                "0x7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B",
+                                oid);
+   // x962_p239v2
+   if(oid == OID{1,2,840,10045,3,1,5})
+      return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+                                "0x617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C",
+                                "0x38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7",
+                                "0x5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063",
+                                oid);
+   // x962_p239v3
+   if(oid == OID{1,2,840,10045,3,1,6})
+      return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+                                "0x255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E",
+                                "0x6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A",
+                                "0x1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3",
+                                "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551",
+                                oid);
+
+   return std::shared_ptr<EC_Group_Data>();
+   }
+
+//static
+const std::set<std::string>& EC_Group::known_named_groups()
+   {
+   static const std::set<std::string> named_groups = {
+      "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"
+      };
+   return named_groups;
+   }
+}
diff --git a/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp b/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
@@ -0,0 +1,725 @@
+/*
+* Point arithmetic on elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+*     2008-2011,2012,2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/point_gfp.h>
+#include <botan/numthry.h>
+#include <botan/rng.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+PointGFp::PointGFp(const CurveGFp& curve) :
+   m_curve(curve),
+   m_coord_x(0),
+   m_coord_y(curve.get_1_rep()),
+   m_coord_z(0)
+   {
+   // Assumes Montgomery rep of zero is zero
+   }
+
+PointGFp::PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y) :
+   m_curve(curve),
+   m_coord_x(x),
+   m_coord_y(y),
+   m_coord_z(m_curve.get_1_rep())
+   {
+   if(x < 0 || x >= curve.get_p())
+      throw Invalid_Argument("Invalid PointGFp affine x");
+   if(y < 0 || y >= curve.get_p())
+      throw Invalid_Argument("Invalid PointGFp affine y");
+
+   secure_vector<word> monty_ws(m_curve.get_ws_size());
+   m_curve.to_rep(m_coord_x, monty_ws);
+   m_curve.to_rep(m_coord_y, monty_ws);
+   }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng)
+   {
+   secure_vector<word> ws(m_curve.get_ws_size());
+   randomize_repr(rng, ws);
+   }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws)
+   {
+   const BigInt mask = BigInt::random_integer(rng, 2, m_curve.get_p());
+
+   /*
+   * No reason to convert this to Montgomery representation first,
+   * just pretend the random mask was chosen as Redc(mask) and the
+   * random mask we generated above is in the Montgomery
+   * representation.
+   * //m_curve.to_rep(mask, ws);
+   */
+   const BigInt mask2 = m_curve.sqr_to_tmp(mask, ws);
+   const BigInt mask3 = m_curve.mul_to_tmp(mask2, mask, ws);
+
+   m_coord_x = m_curve.mul_to_tmp(m_coord_x, mask2, ws);
+   m_coord_y = m_curve.mul_to_tmp(m_coord_y, mask3, ws);
+   m_coord_z = m_curve.mul_to_tmp(m_coord_z, mask, ws);
+   }
+
+namespace {
+
+inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size)
+   {
+   BOTAN_ASSERT(ws_bn.size() >= PointGFp::WORKSPACE_SIZE,
+                "Expected size for PointGFp workspace");
+
+   for(size_t i = 0; i != ws_bn.size(); ++i)
+      if(ws_bn[i].size() < cap_size)
+         ws_bn[i].get_word_vector().resize(cap_size);
+   }
+
+inline word all_zeros(const word x[], size_t len)
+   {
+   word z = 0;
+   for(size_t i = 0; i != len; ++i)
+      z |= x[i];
+   return CT::Mask<word>::is_zero(z).value();
+   }
+
+}
+
+void PointGFp::add_affine(const word x_words[], size_t x_size,
+                          const word y_words[], size_t y_size,
+                          std::vector<BigInt>& ws_bn)
+   {
+   if(all_zeros(x_words, x_size) & all_zeros(y_words, y_size))
+      {
+      return;
+      }
+
+   if(is_zero())
+      {
+      m_coord_x.set_words(x_words, x_size);
+      m_coord_y.set_words(y_words, y_size);
+      m_coord_z = m_curve.get_1_rep();
+      return;
+      }
+
+   resize_ws(ws_bn, m_curve.get_ws_size());
+
+   secure_vector<word>& ws = ws_bn[0].get_word_vector();
+   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+   BigInt& T0 = ws_bn[2];
+   BigInt& T1 = ws_bn[3];
+   BigInt& T2 = ws_bn[4];
+   BigInt& T3 = ws_bn[5];
+   BigInt& T4 = ws_bn[6];
+
+   /*
+   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+   simplified with Z2 = 1
+   */
+
+   const BigInt& p = m_curve.get_p();
+
+   m_curve.sqr(T3, m_coord_z, ws); // z1^2
+   m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+   m_curve.mul(T2, m_coord_z, T3, ws); // z1^3
+   m_curve.mul(T0, y_words, y_size, T2, ws); // y2*z1^3
+
+   T4.mod_sub(m_coord_x, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+   T0.mod_sub(m_coord_y, p, sub_ws);
+
+   if(T4.is_zero())
+      {
+      if(T0.is_zero())
+         {
+         mult2(ws_bn);
+         return;
+         }
+
+      // setting to zero:
+      m_coord_x.clear();
+      m_coord_y = m_curve.get_1_rep();
+      m_coord_z.clear();
+      return;
+      }
+
+   m_curve.sqr(T2, T4, ws);
+
+   m_curve.mul(T3, m_coord_x, T2, ws);
+
+   m_curve.mul(T1, T2, T4, ws);
+
+   m_curve.sqr(m_coord_x, T0, ws);
+   m_coord_x.mod_sub(T1, p, sub_ws);
+
+   m_coord_x.mod_sub(T3, p, sub_ws);
+   m_coord_x.mod_sub(T3, p, sub_ws);
+
+   T3.mod_sub(m_coord_x, p, sub_ws);
+
+   m_curve.mul(T2, T0, T3, ws);
+   m_curve.mul(T0, m_coord_y, T1, ws);
+   T2.mod_sub(T0, p, sub_ws);
+   m_coord_y.swap(T2);
+
+   m_curve.mul(T0, m_coord_z, T4, ws);
+   m_coord_z.swap(T0);
+   }
+
+void PointGFp::add(const word x_words[], size_t x_size,
+                   const word y_words[], size_t y_size,
+                   const word z_words[], size_t z_size,
+                   std::vector<BigInt>& ws_bn)
+   {
+   if(all_zeros(x_words, x_size) & all_zeros(z_words, z_size))
+      return;
+
+   if(is_zero())
+      {
+      m_coord_x.set_words(x_words, x_size);
+      m_coord_y.set_words(y_words, y_size);
+      m_coord_z.set_words(z_words, z_size);
+      return;
+      }
+
+   resize_ws(ws_bn, m_curve.get_ws_size());
+
+   secure_vector<word>& ws = ws_bn[0].get_word_vector();
+   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+   BigInt& T0 = ws_bn[2];
+   BigInt& T1 = ws_bn[3];
+   BigInt& T2 = ws_bn[4];
+   BigInt& T3 = ws_bn[5];
+   BigInt& T4 = ws_bn[6];
+   BigInt& T5 = ws_bn[7];
+
+   /*
+   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+   */
+
+   const BigInt& p = m_curve.get_p();
+
+   m_curve.sqr(T0, z_words, z_size, ws); // z2^2
+   m_curve.mul(T1, m_coord_x, T0, ws); // x1*z2^2
+   m_curve.mul(T3, z_words, z_size, T0, ws); // z2^3
+   m_curve.mul(T2, m_coord_y, T3, ws); // y1*z2^3
+
+   m_curve.sqr(T3, m_coord_z, ws); // z1^2
+   m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+   m_curve.mul(T5, m_coord_z, T3, ws); // z1^3
+   m_curve.mul(T0, y_words, y_size, T5, ws); // y2*z1^3
+
+   T4.mod_sub(T1, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+   T0.mod_sub(T2, p, sub_ws);
+
+   if(T4.is_zero())
+      {
+      if(T0.is_zero())
+         {
+         mult2(ws_bn);
+         return;
+         }
+
+      // setting to zero:
+      m_coord_x.clear();
+      m_coord_y = m_curve.get_1_rep();
+      m_coord_z.clear();
+      return;
+      }
+
+   m_curve.sqr(T5, T4, ws);
+
+   m_curve.mul(T3, T1, T5, ws);
+
+   m_curve.mul(T1, T5, T4, ws);
+
+   m_curve.sqr(m_coord_x, T0, ws);
+   m_coord_x.mod_sub(T1, p, sub_ws);
+   m_coord_x.mod_sub(T3, p, sub_ws);
+   m_coord_x.mod_sub(T3, p, sub_ws);
+
+   T3.mod_sub(m_coord_x, p, sub_ws);
+
+   m_curve.mul(m_coord_y, T0, T3, ws);
+   m_curve.mul(T3, T2, T1, ws);
+
+   m_coord_y.mod_sub(T3, p, sub_ws);
+
+   m_curve.mul(T3, z_words, z_size, m_coord_z, ws);
+   m_curve.mul(m_coord_z, T3, T4, ws);
+   }
+
+void PointGFp::mult2i(size_t iterations, std::vector<BigInt>& ws_bn)
+   {
+   if(iterations == 0)
+      return;
+
+   if(m_coord_y.is_zero())
+      {
+      *this = PointGFp(m_curve); // setting myself to zero
+      return;
+      }
+
+   /*
+   TODO we can save 2 squarings per iteration by computing
+   a*Z^4 using values cached from previous iteration
+   */
+   for(size_t i = 0; i != iterations; ++i)
+      mult2(ws_bn);
+   }
+
+// *this *= 2
+void PointGFp::mult2(std::vector<BigInt>& ws_bn)
+   {
+   if(is_zero())
+      return;
+
+   if(m_coord_y.is_zero())
+      {
+      *this = PointGFp(m_curve); // setting myself to zero
+      return;
+      }
+
+   resize_ws(ws_bn, m_curve.get_ws_size());
+
+   secure_vector<word>& ws = ws_bn[0].get_word_vector();
+   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+   BigInt& T0 = ws_bn[2];
+   BigInt& T1 = ws_bn[3];
+   BigInt& T2 = ws_bn[4];
+   BigInt& T3 = ws_bn[5];
+   BigInt& T4 = ws_bn[6];
+
+   /*
+   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc
+   */
+   const BigInt& p = m_curve.get_p();
+
+   m_curve.sqr(T0, m_coord_y, ws);
+
+   m_curve.mul(T1, m_coord_x, T0, ws);
+   T1.mod_mul(4, p, sub_ws);
+
+   if(m_curve.a_is_zero())
+      {
+      // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2
+      m_curve.sqr(T4, m_coord_x, ws); // x^2
+      T4.mod_mul(3, p, sub_ws); // 3*x^2
+      }
+   else if(m_curve.a_is_minus_3())
+      {
+      /*
+      if a == -3 then
+        3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2)
+      */
+      m_curve.sqr(T3, m_coord_z, ws); // z^2
+
+      // (x-z^2)
+      T2 = m_coord_x;
+      T2.mod_sub(T3, p, sub_ws);
+
+      // (x+z^2)
+      T3.mod_add(m_coord_x, p, sub_ws);
+
+      m_curve.mul(T4, T2, T3, ws); // (x-z^2)*(x+z^2)
+
+      T4.mod_mul(3, p, sub_ws); // 3*(x-z^2)*(x+z^2)
+      }
+   else
+      {
+      m_curve.sqr(T3, m_coord_z, ws); // z^2
+      m_curve.sqr(T4, T3, ws); // z^4
+      m_curve.mul(T3, m_curve.get_a_rep(), T4, ws); // a*z^4
+
+      m_curve.sqr(T4, m_coord_x, ws); // x^2
+      T4.mod_mul(3, p, sub_ws);
+      T4.mod_add(T3, p, sub_ws); // 3*x^2 + a*z^4
+      }
+
+   m_curve.sqr(T2, T4, ws);
+   T2.mod_sub(T1, p, sub_ws);
+   T2.mod_sub(T1, p, sub_ws);
+
+   m_curve.sqr(T3, T0, ws);
+   T3.mod_mul(8, p, sub_ws);
+
+   T1.mod_sub(T2, p, sub_ws);
+
+   m_curve.mul(T0, T4, T1, ws);
+   T0.mod_sub(T3, p, sub_ws);
+
+   m_coord_x.swap(T2);
+
+   m_curve.mul(T2, m_coord_y, m_coord_z, ws);
+   T2.mod_mul(2, p, sub_ws);
+
+   m_coord_y.swap(T0);
+   m_coord_z.swap(T2);
+   }
+
+// arithmetic operators
+PointGFp& PointGFp::operator+=(const PointGFp& rhs)
+   {
+   std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+   add(rhs, ws);
+   return *this;
+   }
+
+PointGFp& PointGFp::operator-=(const PointGFp& rhs)
+   {
+   PointGFp minus_rhs = PointGFp(rhs).negate();
+
+   if(is_zero())
+      *this = minus_rhs;
+   else
+      *this += minus_rhs;
+
+   return *this;
+   }
+
+PointGFp& PointGFp::operator*=(const BigInt& scalar)
+   {
+   *this = scalar * *this;
+   return *this;
+   }
+
+PointGFp operator*(const BigInt& scalar, const PointGFp& point)
+   {
+   BOTAN_DEBUG_ASSERT(point.on_the_curve());
+
+   const size_t scalar_bits = scalar.bits();
+
+   std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+   PointGFp R[2] = { point.zero(), point };
+
+   for(size_t i = scalar_bits; i > 0; i--)
+      {
+      const size_t b = scalar.get_bit(i - 1);
+      R[b ^ 1].add(R[b], ws);
+      R[b].mult2(ws);
+      }
+
+   if(scalar.is_negative())
+      R[0].negate();
+
+   BOTAN_DEBUG_ASSERT(R[0].on_the_curve());
+
+   return R[0];
+   }
+
+//static
+void PointGFp::force_all_affine(std::vector<PointGFp>& points,
+                                secure_vector<word>& ws)
+   {
+   if(points.size() <= 1)
+      {
+      for(size_t i = 0; i != points.size(); ++i)
+         points[i].force_affine();
+      return;
+      }
+
+   /*
+   For >= 2 points use Montgomery's trick
+
+   See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
+   (Hankerson, Menezes, Vanstone)
+
+   TODO is it really necessary to save all k points in c?
+   */
+
+   const CurveGFp& curve = points[0].m_curve;
+   const BigInt& rep_1 = curve.get_1_rep();
+
+   if(ws.size() < curve.get_ws_size())
+      ws.resize(curve.get_ws_size());
+
+   std::vector<BigInt> c(points.size());
+   c[0] = points[0].m_coord_z;
+
+   for(size_t i = 1; i != points.size(); ++i)
+      {
+      curve.mul(c[i], c[i-1], points[i].m_coord_z, ws);
+      }
+
+   BigInt s_inv = curve.invert_element(c[c.size()-1], ws);
+
+   BigInt z_inv, z2_inv, z3_inv;
+
+   for(size_t i = points.size() - 1; i != 0; i--)
+      {
+      PointGFp& point = points[i];
+
+      curve.mul(z_inv, s_inv, c[i-1], ws);
+
+      s_inv = curve.mul_to_tmp(s_inv, point.m_coord_z, ws);
+
+      curve.sqr(z2_inv, z_inv, ws);
+      curve.mul(z3_inv, z2_inv, z_inv, ws);
+      point.m_coord_x = curve.mul_to_tmp(point.m_coord_x, z2_inv, ws);
+      point.m_coord_y = curve.mul_to_tmp(point.m_coord_y, z3_inv, ws);
+      point.m_coord_z = rep_1;
+      }
+
+   curve.sqr(z2_inv, s_inv, ws);
+   curve.mul(z3_inv, z2_inv, s_inv, ws);
+   points[0].m_coord_x = curve.mul_to_tmp(points[0].m_coord_x, z2_inv, ws);
+   points[0].m_coord_y = curve.mul_to_tmp(points[0].m_coord_y, z3_inv, ws);
+   points[0].m_coord_z = rep_1;
+   }
+
+void PointGFp::force_affine()
+   {
+   if(is_zero())
+      throw Invalid_State("Cannot convert zero ECC point to affine");
+
+   secure_vector<word> ws;
+
+   const BigInt z_inv = m_curve.invert_element(m_coord_z, ws);
+   const BigInt z2_inv = m_curve.sqr_to_tmp(z_inv, ws);
+   const BigInt z3_inv = m_curve.mul_to_tmp(z_inv, z2_inv, ws);
+   m_coord_x = m_curve.mul_to_tmp(m_coord_x, z2_inv, ws);
+   m_coord_y = m_curve.mul_to_tmp(m_coord_y, z3_inv, ws);
+   m_coord_z = m_curve.get_1_rep();
+   }
+
+bool PointGFp::is_affine() const
+   {
+   return m_curve.is_one(m_coord_z);
+   }
+
+BigInt PointGFp::get_affine_x() const
+   {
+   if(is_zero())
+      throw Illegal_Transformation("Cannot convert zero point to affine");
+
+   secure_vector<word> monty_ws;
+
+   if(is_affine())
+      return m_curve.from_rep_to_tmp(m_coord_x, monty_ws);
+
+   BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+   z2 = m_curve.invert_element(z2, monty_ws);
+
+   BigInt r;
+   m_curve.mul(r, m_coord_x, z2, monty_ws);
+   m_curve.from_rep(r, monty_ws);
+   return r;
+   }
+
+BigInt PointGFp::get_affine_y() const
+   {
+   if(is_zero())
+      throw Illegal_Transformation("Cannot convert zero point to affine");
+
+   secure_vector<word> monty_ws;
+
+   if(is_affine())
+      return m_curve.from_rep_to_tmp(m_coord_y, monty_ws);
+
+   const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+   const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+   const BigInt z3_inv = m_curve.invert_element(z3, monty_ws);
+
+   BigInt r;
+   m_curve.mul(r, m_coord_y, z3_inv, monty_ws);
+   m_curve.from_rep(r, monty_ws);
+   return r;
+   }
+
+bool PointGFp::on_the_curve() const
+   {
+   /*
+   Is the point still on the curve?? (If everything is correct, the
+   point is always on its curve; then the function will return true.
+   If somehow the state is corrupted, which suggests a fault attack
+   (or internal computational error), then return false.
+   */
+   if(is_zero())
+      return true;
+
+   secure_vector<word> monty_ws;
+
+   const BigInt y2 = m_curve.from_rep_to_tmp(m_curve.sqr_to_tmp(m_coord_y, monty_ws), monty_ws);
+   const BigInt x3 = m_curve.mul_to_tmp(m_coord_x, m_curve.sqr_to_tmp(m_coord_x, monty_ws), monty_ws);
+   const BigInt ax = m_curve.mul_to_tmp(m_coord_x, m_curve.get_a_rep(), monty_ws);
+   const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+
+   if(m_coord_z == z2) // Is z equal to 1 (in Montgomery form)?
+      {
+      if(y2 != m_curve.from_rep_to_tmp(x3 + ax + m_curve.get_b_rep(), monty_ws))
+         return false;
+      }
+
+   const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+   const BigInt ax_z4 = m_curve.mul_to_tmp(ax, m_curve.sqr_to_tmp(z2, monty_ws), monty_ws);
+   const BigInt b_z6 = m_curve.mul_to_tmp(m_curve.get_b_rep(), m_curve.sqr_to_tmp(z3, monty_ws), monty_ws);
+
+   if(y2 != m_curve.from_rep_to_tmp(x3 + ax_z4 + b_z6, monty_ws))
+      return false;
+
+   return true;
+   }
+
+// swaps the states of *this and other, does not throw!
+void PointGFp::swap(PointGFp& other)
+   {
+   m_curve.swap(other.m_curve);
+   m_coord_x.swap(other.m_coord_x);
+   m_coord_y.swap(other.m_coord_y);
+   m_coord_z.swap(other.m_coord_z);
+   }
+
+bool PointGFp::operator==(const PointGFp& other) const
+   {
+   if(m_curve != other.m_curve)
+      return false;
+
+   // If this is zero, only equal if other is also zero
+   if(is_zero())
+      return other.is_zero();
+
+   return (get_affine_x() == other.get_affine_x() &&
+           get_affine_y() == other.get_affine_y());
+   }
+
+// encoding and decoding
+std::vector<uint8_t> PointGFp::encode(PointGFp::Compression_Type format) const
+   {
+   if(is_zero())
+      return std::vector<uint8_t>(1); // single 0 byte
+
+   const size_t p_bytes = m_curve.get_p().bytes();
+
+   const BigInt x = get_affine_x();
+   const BigInt y = get_affine_y();
+
+   std::vector<uint8_t> result;
+
+   if(format == PointGFp::UNCOMPRESSED)
+      {
+      result.resize(1 + 2*p_bytes);
+      result[0] = 0x04;
+      BigInt::encode_1363(&result[1], p_bytes, x);
+      BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+      }
+   else if(format == PointGFp::COMPRESSED)
+      {
+      result.resize(1 + p_bytes);
+      result[0] = 0x02 | static_cast<uint8_t>(y.get_bit(0));
+      BigInt::encode_1363(&result[1], p_bytes, x);
+      }
+   else if(format == PointGFp::HYBRID)
+      {
+      result.resize(1 + 2*p_bytes);
+      result[0] = 0x06 | static_cast<uint8_t>(y.get_bit(0));
+      BigInt::encode_1363(&result[1], p_bytes, x);
+      BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+      }
+   else
+      throw Invalid_Argument("EC2OSP illegal point encoding");
+
+   return result;
+   }
+
+namespace {
+
+BigInt decompress_point(bool yMod2,
+                        const BigInt& x,
+                        const BigInt& curve_p,
+                        const BigInt& curve_a,
+                        const BigInt& curve_b)
+   {
+   BigInt xpow3 = x * x * x;
+
+   BigInt g = curve_a * x;
+   g += xpow3;
+   g += curve_b;
+   g = g % curve_p;
+
+   BigInt z = ressol(g, curve_p);
+
+   if(z < 0)
+      throw Illegal_Point("error during EC point decompression");
+
+   if(z.get_bit(0) != yMod2)
+      z = curve_p - z;
+
+   return z;
+   }
+
+}
+
+PointGFp OS2ECP(const uint8_t data[], size_t data_len,
+                const CurveGFp& curve)
+   {
+   // Should we really be doing this?
+   if(data_len <= 1)
+      return PointGFp(curve); // return zero
+
+   std::pair<BigInt, BigInt> xy = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b());
+
+   PointGFp point(curve, xy.first, xy.second);
+
+   if(!point.on_the_curve())
+      throw Illegal_Point("OS2ECP: Decoded point was not on the curve");
+
+   return point;
+   }
+
+std::pair<BigInt, BigInt> OS2ECP(const uint8_t data[], size_t data_len,
+                                 const BigInt& curve_p,
+                                 const BigInt& curve_a,
+                                 const BigInt& curve_b)
+   {
+   if(data_len <= 1)
+      throw Decoding_Error("OS2ECP invalid point");
+
+   const uint8_t pc = data[0];
+
+   BigInt x, y;
+
+   if(pc == 2 || pc == 3)
+      {
+      //compressed form
+      x = BigInt::decode(&data[1], data_len - 1);
+
+      const bool y_mod_2 = ((pc & 0x01) == 1);
+      y = decompress_point(y_mod_2, x, curve_p, curve_a, curve_b);
+      }
+   else if(pc == 4)
+      {
+      const size_t l = (data_len - 1) / 2;
+
+      // uncompressed form
+      x = BigInt::decode(&data[1], l);
+      y = BigInt::decode(&data[l+1], l);
+      }
+   else if(pc == 6 || pc == 7)
+      {
+      const size_t l = (data_len - 1) / 2;
+
+      // hybrid form
+      x = BigInt::decode(&data[1], l);
+      y = BigInt::decode(&data[l+1], l);
+
+      const bool y_mod_2 = ((pc & 0x01) == 1);
+
+      if(decompress_point(y_mod_2, x, curve_p, curve_a, curve_b) != y)
+         throw Illegal_Point("OS2ECP: Decoding error in hybrid format");
+      }
+   else
+      throw Invalid_Argument("OS2ECP: Unknown format type " + std::to_string(pc));
+
+   return std::make_pair(x, y);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h b/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h
@@ -0,0 +1,447 @@
+/*
+* Point arithmetic on elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+*     2008-2011,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POINT_GFP_H_
+#define BOTAN_POINT_GFP_H_
+
+#include <botan/curve_gfp.h>
+#include <botan/exceptn.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Exception thrown if you try to convert a zero point to an affine
+* coordinate
+*
+* In a future major release this exception type will be removed and its
+* usage replaced by Invalid_State
+*/
+class BOTAN_PUBLIC_API(2,0) Illegal_Transformation final : public Invalid_State
+   {
+   public:
+      explicit Illegal_Transformation(const std::string& err) : Invalid_State(err) {}
+   };
+
+/**
+* Exception thrown if some form of illegal point is decoded
+*
+* In a future major release this exception type will be removed and its
+* usage replaced by Decoding_Error
+*/
+class BOTAN_PUBLIC_API(2,0) Illegal_Point final : public Decoding_Error
+   {
+   public:
+      explicit Illegal_Point(const std::string& err) : Decoding_Error(err) {}
+   };
+
+/**
+* This class represents one point on a curve of GF(p)
+*/
+class BOTAN_PUBLIC_API(2,0) PointGFp final
+   {
+   public:
+      enum Compression_Type {
+         UNCOMPRESSED = 0,
+         COMPRESSED   = 1,
+         HYBRID       = 2
+      };
+
+      enum { WORKSPACE_SIZE = 8 };
+
+      /**
+      * Construct an uninitialized PointGFp
+      */
+      PointGFp() = default;
+
+      /**
+      * Construct the zero point
+      * @param curve The base curve
+      */
+      explicit PointGFp(const CurveGFp& curve);
+
+      /**
+      * Copy constructor
+      */
+      PointGFp(const PointGFp&) = default;
+
+      /**
+      * Move Constructor
+      */
+      PointGFp(PointGFp&& other)
+         {
+         this->swap(other);
+         }
+
+      /**
+      * Standard Assignment
+      */
+      PointGFp& operator=(const PointGFp&) = default;
+
+      /**
+      * Move Assignment
+      */
+      PointGFp& operator=(PointGFp&& other)
+         {
+         if(this != &other)
+            this->swap(other);
+         return (*this);
+         }
+
+      /**
+      * Construct a point from its affine coordinates
+      * Prefer EC_Group::point(x,y) for this operation.
+      * @param curve the base curve
+      * @param x affine x coordinate
+      * @param y affine y coordinate
+      */
+      PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y);
+
+      /**
+      * EC2OSP - elliptic curve to octet string primitive
+      * @param format which format to encode using
+      */
+      std::vector<uint8_t> encode(PointGFp::Compression_Type format) const;
+
+      /**
+      * += Operator
+      * @param rhs the PointGFp to add to the local value
+      * @result resulting PointGFp
+      */
+      PointGFp& operator+=(const PointGFp& rhs);
+
+      /**
+      * -= Operator
+      * @param rhs the PointGFp to subtract from the local value
+      * @result resulting PointGFp
+      */
+      PointGFp& operator-=(const PointGFp& rhs);
+
+      /**
+      * *= Operator
+      * @param scalar the PointGFp to multiply with *this
+      * @result resulting PointGFp
+      */
+      PointGFp& operator*=(const BigInt& scalar);
+
+      /**
+      * Negate this point
+      * @return *this
+      */
+      PointGFp& negate()
+         {
+         if(!is_zero())
+            m_coord_y = m_curve.get_p() - m_coord_y;
+         return *this;
+         }
+
+      /**
+      * get affine x coordinate
+      * @result affine x coordinate
+      */
+      BigInt get_affine_x() const;
+
+      /**
+      * get affine y coordinate
+      * @result affine y coordinate
+      */
+      BigInt get_affine_y() const;
+
+      const BigInt& get_x() const { return m_coord_x; }
+      const BigInt& get_y() const { return m_coord_y; }
+      const BigInt& get_z() const { return m_coord_z; }
+
+      void swap_coords(BigInt& new_x, BigInt& new_y, BigInt& new_z)
+         {
+         m_coord_x.swap(new_x);
+         m_coord_y.swap(new_y);
+         m_coord_z.swap(new_z);
+         }
+
+      /**
+      * Force this point to affine coordinates
+      */
+      void force_affine();
+
+      /**
+      * Force all points on the list to affine coordinates
+      */
+      static void force_all_affine(std::vector<PointGFp>& points,
+                                   secure_vector<word>& ws);
+
+      bool is_affine() const;
+
+      /**
+      * Is this the point at infinity?
+      * @result true, if this point is at infinity, false otherwise.
+      */
+      bool is_zero() const { return m_coord_z.is_zero(); }
+
+      /**
+      * Checks whether the point is to be found on the underlying
+      * curve; used to prevent fault attacks.
+      * @return if the point is on the curve
+      */
+      bool on_the_curve() const;
+
+      /**
+      * swaps the states of *this and other, does not throw!
+      * @param other the object to swap values with
+      */
+      void swap(PointGFp& other);
+
+      /**
+      * Randomize the point representation
+      * The actual value (get_affine_x, get_affine_y) does not change
+      */
+      void randomize_repr(RandomNumberGenerator& rng);
+
+      /**
+      * Randomize the point representation
+      * The actual value (get_affine_x, get_affine_y) does not change
+      */
+      void randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws);
+
+      /**
+      * Equality operator
+      */
+      bool operator==(const PointGFp& other) const;
+
+      /**
+      * Point addition
+      * @param other the point to add to *this
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void add(const PointGFp& other, std::vector<BigInt>& workspace)
+         {
+         BOTAN_ASSERT_NOMSG(m_curve == other.m_curve);
+
+         const size_t p_words = m_curve.get_p_words();
+
+         add(other.m_coord_x.data(), std::min(p_words, other.m_coord_x.size()),
+             other.m_coord_y.data(), std::min(p_words, other.m_coord_y.size()),
+             other.m_coord_z.data(), std::min(p_words, other.m_coord_z.size()),
+             workspace);
+         }
+
+      /**
+      * Point addition. Array version.
+      *
+      * @param x_words the words of the x coordinate of the other point
+      * @param x_size size of x_words
+      * @param y_words the words of the y coordinate of the other point
+      * @param y_size size of y_words
+      * @param z_words the words of the z coordinate of the other point
+      * @param z_size size of z_words
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void add(const word x_words[], size_t x_size,
+               const word y_words[], size_t y_size,
+               const word z_words[], size_t z_size,
+               std::vector<BigInt>& workspace);
+
+      /**
+      * Point addition - mixed J+A
+      * @param other affine point to add - assumed to be affine!
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void add_affine(const PointGFp& other, std::vector<BigInt>& workspace)
+         {
+         BOTAN_ASSERT_NOMSG(m_curve == other.m_curve);
+         BOTAN_DEBUG_ASSERT(other.is_affine());
+
+         const size_t p_words = m_curve.get_p_words();
+         add_affine(other.m_coord_x.data(), std::min(p_words, other.m_coord_x.size()),
+                    other.m_coord_y.data(), std::min(p_words, other.m_coord_y.size()),
+                    workspace);
+         }
+
+      /**
+      * Point addition - mixed J+A. Array version.
+      *
+      * @param x_words the words of the x coordinate of the other point
+      * @param x_size size of x_words
+      * @param y_words the words of the y coordinate of the other point
+      * @param y_size size of y_words
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void add_affine(const word x_words[], size_t x_size,
+                      const word y_words[], size_t y_size,
+                      std::vector<BigInt>& workspace);
+
+      /**
+      * Point doubling
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void mult2(std::vector<BigInt>& workspace);
+
+      /**
+      * Repeated point doubling
+      * @param i number of doublings to perform
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      */
+      void mult2i(size_t i, std::vector<BigInt>& workspace);
+
+      /**
+      * Point addition
+      * @param other the point to add to *this
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      * @return other plus *this
+      */
+      PointGFp plus(const PointGFp& other, std::vector<BigInt>& workspace) const
+         {
+         PointGFp x = (*this);
+         x.add(other, workspace);
+         return x;
+         }
+
+      /**
+      * Point doubling
+      * @param workspace temp space, at least WORKSPACE_SIZE elements
+      * @return *this doubled
+      */
+      PointGFp double_of(std::vector<BigInt>& workspace) const
+         {
+         PointGFp x = (*this);
+         x.mult2(workspace);
+         return x;
+         }
+
+      /**
+      * Return the zero (aka infinite) point associated with this curve
+      */
+      PointGFp zero() const { return PointGFp(m_curve); }
+
+      /**
+      * Return base curve of this point
+      * @result the curve over GF(p) of this point
+      *
+      * You should not need to use this
+      */
+      const CurveGFp& get_curve() const { return m_curve; }
+
+   private:
+      CurveGFp m_curve;
+      BigInt m_coord_x, m_coord_y, m_coord_z;
+   };
+
+/**
+* Point multiplication operator
+* @param scalar the scalar value
+* @param point the point value
+* @return scalar*point on the curve
+*/
+BOTAN_PUBLIC_API(2,0) PointGFp operator*(const BigInt& scalar, const PointGFp& point);
+
+/**
+* ECC point multiexponentiation - not constant time!
+* @param p1 a point
+* @param z1 a scalar
+* @param p2 a point
+* @param z2 a scalar
+* @result (p1 * z1 + p2 * z2)
+*/
+BOTAN_PUBLIC_API(2,0) PointGFp multi_exponentiate(
+   const PointGFp& p1, const BigInt& z1,
+   const PointGFp& p2, const BigInt& z2);
+
+// relational operators
+inline bool operator!=(const PointGFp& lhs, const PointGFp& rhs)
+   {
+   return !(rhs == lhs);
+   }
+
+// arithmetic operators
+inline PointGFp operator-(const PointGFp& lhs)
+   {
+   return PointGFp(lhs).negate();
+   }
+
+inline PointGFp operator+(const PointGFp& lhs, const PointGFp& rhs)
+   {
+   PointGFp tmp(lhs);
+   return tmp += rhs;
+   }
+
+inline PointGFp operator-(const PointGFp& lhs, const PointGFp& rhs)
+   {
+   PointGFp tmp(lhs);
+   return tmp -= rhs;
+   }
+
+inline PointGFp operator*(const PointGFp& point, const BigInt& scalar)
+   {
+   return scalar * point;
+   }
+
+// encoding and decoding
+inline secure_vector<uint8_t> BOTAN_DEPRECATED("Use PointGFp::encode")
+   EC2OSP(const PointGFp& point, uint8_t format)
+   {
+   std::vector<uint8_t> enc = point.encode(static_cast<PointGFp::Compression_Type>(format));
+   return secure_vector<uint8_t>(enc.begin(), enc.end());
+   }
+
+/**
+* Perform point decoding
+* Use EC_Group::OS2ECP instead
+*/
+PointGFp BOTAN_PUBLIC_API(2,0) OS2ECP(const uint8_t data[], size_t data_len,
+                                      const CurveGFp& curve);
+
+/**
+* Perform point decoding
+* Use EC_Group::OS2ECP instead
+*
+* @param data the encoded point
+* @param data_len length of data in bytes
+* @param curve_p the curve equation prime
+* @param curve_a the curve equation a parameter
+* @param curve_b the curve equation b parameter
+*/
+std::pair<BigInt, BigInt> BOTAN_UNSTABLE_API OS2ECP(const uint8_t data[], size_t data_len,
+                                                    const BigInt& curve_p,
+                                                    const BigInt& curve_a,
+                                                    const BigInt& curve_b);
+
+template<typename Alloc>
+PointGFp OS2ECP(const std::vector<uint8_t, Alloc>& data, const CurveGFp& curve)
+   { return OS2ECP(data.data(), data.size(), curve); }
+
+class PointGFp_Var_Point_Precompute;
+
+/**
+* Deprecated API for point multiplication
+* Use EC_Group::blinded_base_point_multiply or EC_Group::blinded_var_point_multiply
+*/
+class BOTAN_PUBLIC_API(2,0) Blinded_Point_Multiply final
+   {
+   public:
+      Blinded_Point_Multiply(const PointGFp& base, const BigInt& order, size_t h = 0);
+
+      ~Blinded_Point_Multiply();
+
+      PointGFp BOTAN_DEPRECATED("Use alternative APIs") blinded_multiply(const BigInt& scalar, RandomNumberGenerator& rng);
+   private:
+      std::vector<BigInt> m_ws;
+      const BigInt& m_order;
+      std::unique_ptr<PointGFp_Var_Point_Precompute> m_point_mul;
+   };
+
+}
+
+namespace std {
+
+template<>
+inline void swap<Botan::PointGFp>(Botan::PointGFp& x, Botan::PointGFp& y)
+   { x.swap(y); }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp b/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp
@@ -0,0 +1,415 @@
+/*
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/point_mul.h>
+#include <botan/rng.h>
+#include <botan/reducer.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+size_t blinding_size(const BigInt& group_order)
+   {
+   return (group_order.bits() + 1) / 2;
+   }
+
+}
+
+PointGFp multi_exponentiate(const PointGFp& x, const BigInt& z1,
+                            const PointGFp& y, const BigInt& z2)
+   {
+   PointGFp_Multi_Point_Precompute xy_mul(x, y);
+   return xy_mul.multi_exp(z1, z2);
+   }
+
+Blinded_Point_Multiply::Blinded_Point_Multiply(const PointGFp& base,
+                                               const BigInt& order,
+                                               size_t h) :
+   m_ws(PointGFp::WORKSPACE_SIZE),
+   m_order(order)
+   {
+   BOTAN_UNUSED(h);
+   Null_RNG null_rng;
+   m_point_mul.reset(new PointGFp_Var_Point_Precompute(base, null_rng, m_ws));
+   }
+
+Blinded_Point_Multiply::~Blinded_Point_Multiply()
+   {
+   /* for ~unique_ptr */
+   }
+
+PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar,
+                                                  RandomNumberGenerator& rng)
+   {
+   return m_point_mul->mul(scalar, rng, m_order, m_ws);
+   }
+
+PointGFp_Base_Point_Precompute::PointGFp_Base_Point_Precompute(const PointGFp& base,
+                                                               const Modular_Reducer& mod_order) :
+   m_base_point(base),
+   m_mod_order(mod_order),
+   m_p_words(base.get_curve().get_p().sig_words())
+   {
+   std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+   const size_t p_bits = base.get_curve().get_p().bits();
+
+   /*
+   * Some of the curves (eg secp160k1) have an order slightly larger than
+   * the size of the prime modulus. In all cases they are at most 1 bit
+   * longer. The +1 compensates for this.
+   */
+   const size_t T_bits = round_up(p_bits + blinding_size(mod_order.get_modulus()) + 1, WINDOW_BITS) / WINDOW_BITS;
+
+   std::vector<PointGFp> T(WINDOW_SIZE*T_bits);
+
+   PointGFp g = base;
+   PointGFp g2, g4;
+
+   for(size_t i = 0; i != T_bits; i++)
+      {
+      g2 = g;
+      g2.mult2(ws);
+      g4 = g2;
+      g4.mult2(ws);
+
+      T[7*i+0] = g;
+      T[7*i+1] = std::move(g2);
+      T[7*i+2] = T[7*i+1].plus(T[7*i+0], ws); // g2+g
+      T[7*i+3] = g4;
+      T[7*i+4] = T[7*i+3].plus(T[7*i+0], ws); // g4+g
+      T[7*i+5] = T[7*i+3].plus(T[7*i+1], ws); // g4+g2
+      T[7*i+6] = T[7*i+3].plus(T[7*i+2], ws); // g4+g2+g
+
+      g.swap(g4);
+      g.mult2(ws);
+      }
+
+   PointGFp::force_all_affine(T, ws[0].get_word_vector());
+
+   m_W.resize(T.size() * 2 * m_p_words);
+
+   word* p = &m_W[0];
+   for(size_t i = 0; i != T.size(); ++i)
+      {
+      T[i].get_x().encode_words(p, m_p_words);
+      p += m_p_words;
+      T[i].get_y().encode_words(p, m_p_words);
+      p += m_p_words;
+      }
+   }
+
+PointGFp PointGFp_Base_Point_Precompute::mul(const BigInt& k,
+                                             RandomNumberGenerator& rng,
+                                             const BigInt& group_order,
+                                             std::vector<BigInt>& ws) const
+   {
+   if(k.is_negative())
+      throw Invalid_Argument("PointGFp_Base_Point_Precompute scalar must be positive");
+
+   // Instead of reducing k mod group order should we alter the mask size??
+   BigInt scalar = m_mod_order.reduce(k);
+
+   if(rng.is_seeded())
+      {
+      // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
+      const BigInt mask(rng, blinding_size(group_order));
+      scalar += group_order * mask;
+      }
+   else
+      {
+      /*
+      When we don't have an RNG we cannot do scalar blinding. Instead use the
+      same trick as OpenSSL and add one or two copies of the order to normalize
+      the length of the scalar at order.bits()+1. This at least ensures the loop
+      bound does not leak information about the high bits of the scalar.
+      */
+      scalar += group_order;
+      if(scalar.bits() == group_order.bits())
+         scalar += group_order;
+      BOTAN_DEBUG_ASSERT(scalar.bits() == group_order.bits() + 1);
+      }
+
+   const size_t windows = round_up(scalar.bits(), WINDOW_BITS) / WINDOW_BITS;
+
+   const size_t elem_size = 2*m_p_words;
+
+   BOTAN_ASSERT(windows <= m_W.size() / (3*elem_size),
+                "Precomputed sufficient values for scalar mult");
+
+   PointGFp R = m_base_point.zero();
+
+   if(ws.size() < PointGFp::WORKSPACE_SIZE)
+      ws.resize(PointGFp::WORKSPACE_SIZE);
+
+   // the precomputed multiples are not secret so use std::vector
+   std::vector<word> Wt(elem_size);
+
+   for(size_t i = 0; i != windows; ++i)
+      {
+      const size_t window = windows - i - 1;
+      const size_t base_addr = (WINDOW_SIZE*window)*elem_size;
+
+      const word w = scalar.get_substring(WINDOW_BITS*window, WINDOW_BITS);
+
+      const auto w_is_1 = CT::Mask<word>::is_equal(w, 1);
+      const auto w_is_2 = CT::Mask<word>::is_equal(w, 2);
+      const auto w_is_3 = CT::Mask<word>::is_equal(w, 3);
+      const auto w_is_4 = CT::Mask<word>::is_equal(w, 4);
+      const auto w_is_5 = CT::Mask<word>::is_equal(w, 5);
+      const auto w_is_6 = CT::Mask<word>::is_equal(w, 6);
+      const auto w_is_7 = CT::Mask<word>::is_equal(w, 7);
+
+      for(size_t j = 0; j != elem_size; ++j)
+         {
+         const word w1 = w_is_1.if_set_return(m_W[base_addr + 0*elem_size + j]);
+         const word w2 = w_is_2.if_set_return(m_W[base_addr + 1*elem_size + j]);
+         const word w3 = w_is_3.if_set_return(m_W[base_addr + 2*elem_size + j]);
+         const word w4 = w_is_4.if_set_return(m_W[base_addr + 3*elem_size + j]);
+         const word w5 = w_is_5.if_set_return(m_W[base_addr + 4*elem_size + j]);
+         const word w6 = w_is_6.if_set_return(m_W[base_addr + 5*elem_size + j]);
+         const word w7 = w_is_7.if_set_return(m_W[base_addr + 6*elem_size + j]);
+
+         Wt[j] = w1 | w2 | w3 | w4 | w5 | w6 | w7;
+         }
+
+      R.add_affine(&Wt[0], m_p_words, &Wt[m_p_words], m_p_words, ws);
+
+      if(i == 0 && rng.is_seeded())
+         {
+         /*
+         * Since we start with the top bit of the exponent we know the
+         * first window must have a non-zero element, and thus R is
+         * now a point other than the point at infinity.
+         */
+         BOTAN_DEBUG_ASSERT(w != 0);
+         R.randomize_repr(rng, ws[0].get_word_vector());
+         }
+      }
+
+   BOTAN_DEBUG_ASSERT(R.on_the_curve());
+
+   return R;
+   }
+
+PointGFp_Var_Point_Precompute::PointGFp_Var_Point_Precompute(const PointGFp& point,
+                                                             RandomNumberGenerator& rng,
+                                                             std::vector<BigInt>& ws) :
+   m_curve(point.get_curve()),
+   m_p_words(m_curve.get_p().sig_words()),
+   m_window_bits(4)
+   {
+   if(ws.size() < PointGFp::WORKSPACE_SIZE)
+      ws.resize(PointGFp::WORKSPACE_SIZE);
+
+   std::vector<PointGFp> U(static_cast<size_t>(1) << m_window_bits);
+   U[0] = point.zero();
+   U[1] = point;
+
+   for(size_t i = 2; i < U.size(); i += 2)
+      {
+      U[i] = U[i/2].double_of(ws);
+      U[i+1] = U[i].plus(point, ws);
+      }
+
+   // Hack to handle Blinded_Point_Multiply
+   if(rng.is_seeded())
+      {
+      BigInt& mask = ws[0];
+      BigInt& mask2 = ws[1];
+      BigInt& mask3 = ws[2];
+      BigInt& new_x = ws[3];
+      BigInt& new_y = ws[4];
+      BigInt& new_z = ws[5];
+      secure_vector<word>& tmp = ws[6].get_word_vector();
+
+      const CurveGFp& curve = U[0].get_curve();
+
+      const size_t p_bits = curve.get_p().bits();
+
+      // Skipping zero point since it can't be randomized
+      for(size_t i = 1; i != U.size(); ++i)
+         {
+         mask.randomize(rng, p_bits - 1, false);
+         // Easy way of ensuring mask != 0
+         mask.set_bit(0);
+
+         curve.sqr(mask2, mask, tmp);
+         curve.mul(mask3, mask, mask2, tmp);
+
+         curve.mul(new_x, U[i].get_x(), mask2, tmp);
+         curve.mul(new_y, U[i].get_y(), mask3, tmp);
+         curve.mul(new_z, U[i].get_z(), mask, tmp);
+
+         U[i].swap_coords(new_x, new_y, new_z);
+         }
+      }
+
+   m_T.resize(U.size() * 3 * m_p_words);
+
+   word* p = &m_T[0];
+   for(size_t i = 0; i != U.size(); ++i)
+      {
+      U[i].get_x().encode_words(p              , m_p_words);
+      U[i].get_y().encode_words(p +   m_p_words, m_p_words);
+      U[i].get_z().encode_words(p + 2*m_p_words, m_p_words);
+      p += 3*m_p_words;
+      }
+   }
+
+PointGFp PointGFp_Var_Point_Precompute::mul(const BigInt& k,
+                                            RandomNumberGenerator& rng,
+                                            const BigInt& group_order,
+                                            std::vector<BigInt>& ws) const
+   {
+   if(k.is_negative())
+      throw Invalid_Argument("PointGFp_Var_Point_Precompute scalar must be positive");
+   if(ws.size() < PointGFp::WORKSPACE_SIZE)
+      ws.resize(PointGFp::WORKSPACE_SIZE);
+
+   // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
+   const BigInt mask(rng, blinding_size(group_order), false);
+   const BigInt scalar = k + group_order * mask;
+
+   const size_t elem_size = 3*m_p_words;
+   const size_t window_elems = (1ULL << m_window_bits);
+
+   size_t windows = round_up(scalar.bits(), m_window_bits) / m_window_bits;
+   PointGFp R(m_curve);
+   secure_vector<word> e(elem_size);
+
+   if(windows > 0)
+      {
+      windows--;
+
+      const uint32_t w = scalar.get_substring(windows*m_window_bits, m_window_bits);
+
+      clear_mem(e.data(), e.size());
+      for(size_t i = 1; i != window_elems; ++i)
+         {
+         const auto wmask = CT::Mask<word>::is_equal(w, i);
+
+         for(size_t j = 0; j != elem_size; ++j)
+            {
+            e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
+            }
+         }
+
+      R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2*m_p_words], m_p_words, ws);
+
+      /*
+      Randomize after adding the first nibble as before the addition R
+      is zero, and we cannot effectively randomize the point
+      representation of the zero point.
+      */
+      R.randomize_repr(rng, ws[0].get_word_vector());
+      }
+
+   while(windows)
+      {
+      R.mult2i(m_window_bits, ws);
+
+      const uint32_t w = scalar.get_substring((windows-1)*m_window_bits, m_window_bits);
+
+      clear_mem(e.data(), e.size());
+      for(size_t i = 1; i != window_elems; ++i)
+         {
+         const auto wmask = CT::Mask<word>::is_equal(w, i);
+
+         for(size_t j = 0; j != elem_size; ++j)
+            {
+            e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
+            }
+         }
+
+      R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2*m_p_words], m_p_words, ws);
+
+      windows--;
+      }
+
+   BOTAN_DEBUG_ASSERT(R.on_the_curve());
+
+   return R;
+   }
+
+
+PointGFp_Multi_Point_Precompute::PointGFp_Multi_Point_Precompute(const PointGFp& x,
+                                                                 const PointGFp& y)
+   {
+   std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+   PointGFp x2 = x;
+   x2.mult2(ws);
+
+   const PointGFp x3(x2.plus(x, ws));
+
+   PointGFp y2 = y;
+   y2.mult2(ws);
+
+   const PointGFp y3(y2.plus(y, ws));
+
+   m_M.reserve(15);
+
+   m_M.push_back(x);
+   m_M.push_back(x2);
+   m_M.push_back(x3);
+
+   m_M.push_back(y);
+   m_M.push_back(y.plus(x, ws));
+   m_M.push_back(y.plus(x2, ws));
+   m_M.push_back(y.plus(x3, ws));
+
+   m_M.push_back(y2);
+   m_M.push_back(y2.plus(x, ws));
+   m_M.push_back(y2.plus(x2, ws));
+   m_M.push_back(y2.plus(x3, ws));
+
+   m_M.push_back(y3);
+   m_M.push_back(y3.plus(x, ws));
+   m_M.push_back(y3.plus(x2, ws));
+   m_M.push_back(y3.plus(x3, ws));
+
+   PointGFp::force_all_affine(m_M, ws[0].get_word_vector());
+   }
+
+PointGFp PointGFp_Multi_Point_Precompute::multi_exp(const BigInt& z1,
+                                                    const BigInt& z2) const
+   {
+   std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+   const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);
+
+   PointGFp H = m_M[0].zero();
+
+   for(size_t i = 0; i != z_bits; i += 2)
+      {
+      if(i > 0)
+         {
+         H.mult2i(2, ws);
+         }
+
+      const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
+      const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
+
+      const uint32_t z12 = (4*z2_b) + z1_b;
+
+      // This function is not intended to be const time
+      if(z12)
+         {
+         H.add_affine(m_M[z12-1], ws);
+         }
+      }
+
+   if(z1.is_negative() != z2.is_negative())
+      H.negate();
+
+   return H;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ec_group/point_mul.h b/third_party/botan/src/lib/pubkey/ec_group/point_mul.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ec_group/point_mul.h
@@ -0,0 +1,84 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POINT_MUL_H_
+#define BOTAN_POINT_MUL_H_
+
+#include <botan/point_gfp.h>
+
+namespace Botan {
+
+class Modular_Reducer;
+
+class PointGFp_Base_Point_Precompute final
+   {
+   public:
+      PointGFp_Base_Point_Precompute(const PointGFp& base_point,
+                                     const Modular_Reducer& mod_order);
+
+      PointGFp mul(const BigInt& k,
+                   RandomNumberGenerator& rng,
+                   const BigInt& group_order,
+                   std::vector<BigInt>& ws) const;
+   private:
+      const PointGFp& m_base_point;
+      const Modular_Reducer& m_mod_order;
+
+      enum { WINDOW_BITS = 3 };
+      enum { WINDOW_SIZE = (1 << WINDOW_BITS) - 1 };
+
+      const size_t m_p_words;
+
+      /*
+      * This is a table of T_size * 3*p_word words
+      */
+      std::vector<word> m_W;
+   };
+
+class PointGFp_Var_Point_Precompute final
+   {
+   public:
+      PointGFp_Var_Point_Precompute(const PointGFp& point,
+                                    RandomNumberGenerator& rng,
+                                    std::vector<BigInt>& ws);
+
+      PointGFp mul(const BigInt& k,
+                   RandomNumberGenerator& rng,
+                   const BigInt& group_order,
+                   std::vector<BigInt>& ws) const;
+   private:
+      const CurveGFp m_curve;
+      const size_t m_p_words;
+      const size_t m_window_bits;
+
+      /*
+      * Table of 2^window_bits * 3*2*p_word words
+      * Kept in locked vector since the base point might be sensitive
+      * (normally isn't in most protocols but hard to say anything
+      * categorically.)
+      */
+      secure_vector<word> m_T;
+   };
+
+class PointGFp_Multi_Point_Precompute final
+   {
+   public:
+      PointGFp_Multi_Point_Precompute(const PointGFp& g1,
+                                      const PointGFp& g2);
+
+      /*
+      * Return (g1*k1 + g2*k2)
+      * Not constant time, intended to use with public inputs
+      */
+      PointGFp multi_exp(const BigInt& k1,
+                         const BigInt& k2) const;
+   private:
+      std::vector<PointGFp> m_M;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp b/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp
@@ -0,0 +1,205 @@
+/*
+* ECC Key implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+*          Falko Strenzke, FlexSecure GmbH
+*     2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecc_key.h>
+#include <botan/numthry.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/secmem.h>
+#include <botan/point_gfp.h>
+#include <botan/workfactor.h>
+
+namespace Botan {
+
+size_t EC_PublicKey::key_length() const
+   {
+   return domain().get_p_bits();
+   }
+
+size_t EC_PublicKey::estimated_strength() const
+   {
+   return ecp_work_factor(key_length());
+   }
+
+EC_PublicKey::EC_PublicKey(const EC_Group& dom_par,
+                           const PointGFp& pub_point) :
+   m_domain_params(dom_par), m_public_key(pub_point)
+   {
+   if (!dom_par.get_curve_oid().empty())
+      m_domain_encoding = EC_DOMPAR_ENC_OID;
+   else
+      m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+#if 0
+   if(domain().get_curve() != public_point().get_curve())
+      throw Invalid_Argument("EC_PublicKey: curve mismatch in constructor");
+#endif
+   }
+
+EC_PublicKey::EC_PublicKey(const AlgorithmIdentifier& alg_id,
+                           const std::vector<uint8_t>& key_bits) :
+   m_domain_params{EC_Group(alg_id.get_parameters())},
+   m_public_key{domain().OS2ECP(key_bits)}
+   {
+   if (!domain().get_curve_oid().empty())
+      m_domain_encoding = EC_DOMPAR_ENC_OID;
+   else
+      m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+   }
+
+bool EC_PublicKey::check_key(RandomNumberGenerator& rng,
+                             bool) const
+   {
+   return m_domain_params.verify_group(rng) &&
+          m_domain_params.verify_public_element(public_point());
+   }
+
+
+AlgorithmIdentifier EC_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), DER_domain());
+   }
+
+std::vector<uint8_t> EC_PublicKey::public_key_bits() const
+   {
+   return public_point().encode(point_encoding());
+   }
+
+void EC_PublicKey::set_point_encoding(PointGFp::Compression_Type enc)
+   {
+   if(enc != PointGFp::COMPRESSED &&
+      enc != PointGFp::UNCOMPRESSED &&
+      enc != PointGFp::HYBRID)
+      throw Invalid_Argument("Invalid point encoding for EC_PublicKey");
+
+   m_point_encoding = enc;
+   }
+
+void EC_PublicKey::set_parameter_encoding(EC_Group_Encoding form)
+   {
+   if(form != EC_DOMPAR_ENC_EXPLICIT &&
+      form != EC_DOMPAR_ENC_IMPLICITCA &&
+      form != EC_DOMPAR_ENC_OID)
+      throw Invalid_Argument("Invalid encoding form for EC-key object specified");
+
+   if((form == EC_DOMPAR_ENC_OID) && (m_domain_params.get_curve_oid().empty()))
+      throw Invalid_Argument("Invalid encoding form OID specified for "
+                             "EC-key object whose corresponding domain "
+                             "parameters are without oid");
+
+   m_domain_encoding = form;
+   }
+
+const BigInt& EC_PrivateKey::private_value() const
+   {
+   if(m_private_key == 0)
+      throw Invalid_State("EC_PrivateKey::private_value - uninitialized");
+
+   return m_private_key;
+   }
+
+/**
+* EC_PrivateKey constructor
+*/
+EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng,
+                             const EC_Group& ec_group,
+                             const BigInt& x,
+                             bool with_modular_inverse)
+   {
+   m_domain_params = ec_group;
+   if (!ec_group.get_curve_oid().empty())
+      m_domain_encoding = EC_DOMPAR_ENC_OID;
+   else
+      m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+   if(x == 0)
+      {
+      m_private_key = ec_group.random_scalar(rng);
+      }
+   else
+      {
+      m_private_key = x;
+      }
+
+   std::vector<BigInt> ws;
+
+   if(with_modular_inverse)
+      {
+      // ECKCDSA
+      m_public_key = domain().blinded_base_point_multiply(
+         m_domain_params.inverse_mod_order(m_private_key), rng, ws);
+      }
+   else
+      {
+      m_public_key = domain().blinded_base_point_multiply(m_private_key, rng, ws);
+      }
+
+   BOTAN_ASSERT(m_public_key.on_the_curve(),
+                "Generated public key point was on the curve");
+   }
+
+secure_vector<uint8_t> EC_PrivateKey::private_key_bits() const
+   {
+   return DER_Encoder()
+      .start_cons(SEQUENCE)
+         .encode(static_cast<size_t>(1))
+         .encode(BigInt::encode_1363(m_private_key, m_private_key.bytes()), OCTET_STRING)
+         .start_cons(ASN1_Tag(1), PRIVATE)
+            .encode(m_public_key.encode(PointGFp::Compression_Type::UNCOMPRESSED), BIT_STRING)
+         .end_cons()
+      .end_cons()
+      .get_contents();
+   }
+
+EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id,
+                             const secure_vector<uint8_t>& key_bits,
+                             bool with_modular_inverse)
+   {
+   m_domain_params = EC_Group(alg_id.get_parameters());
+   m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+   if (!domain().get_curve_oid().empty())
+      m_domain_encoding = EC_DOMPAR_ENC_OID;
+   else
+      m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+   OID key_parameters;
+   secure_vector<uint8_t> public_key_bits;
+
+   BER_Decoder(key_bits)
+      .start_cons(SEQUENCE)
+         .decode_and_check<size_t>(1, "Unknown version code for ECC key")
+         .decode_octet_string_bigint(m_private_key)
+         .decode_optional(key_parameters, ASN1_Tag(0), PRIVATE)
+         .decode_optional_string(public_key_bits, BIT_STRING, 1, PRIVATE)
+      .end_cons();
+
+   if(public_key_bits.empty())
+      {
+      if(with_modular_inverse)
+         {
+         // ECKCDSA
+         m_public_key = domain().get_base_point() * m_domain_params.inverse_mod_order(m_private_key);
+         }
+      else
+         {
+         m_public_key = domain().get_base_point() * m_private_key;
+         }
+
+      BOTAN_ASSERT(m_public_key.on_the_curve(),
+                   "Public point derived from loaded key was on the curve");
+      }
+   else
+      {
+      m_public_key = domain().OS2ECP(public_key_bits);
+      // OS2ECP verifies that the point is on the curve
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h b/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h
@@ -0,0 +1,172 @@
+/*
+* ECDSA
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*          Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECC_PUBLIC_KEY_BASE_H_
+#define BOTAN_ECC_PUBLIC_KEY_BASE_H_
+
+#include <botan/ec_group.h>
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+/**
+* This class represents abstract ECC public keys. When encoding a key
+* via an encoder that can be accessed via the corresponding member
+* functions, the key will decide upon its internally stored encoding
+* information whether to encode itself with or without domain
+* parameters, or using the domain parameter oid. Furthermore, a public
+* key without domain parameters can be decoded. In that case, it
+* cannot be used for verification until its domain parameters are set
+* by calling the corresponding member function.
+*/
+class BOTAN_PUBLIC_API(2,0) EC_PublicKey : public virtual Public_Key
+   {
+   public:
+      /**
+      * Create a public key.
+      * @param dom_par EC domain parameters
+      * @param pub_point public point on the curve
+      */
+      EC_PublicKey(const EC_Group& dom_par,
+                   const PointGFp& pub_point);
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      EC_PublicKey(const AlgorithmIdentifier& alg_id,
+                   const std::vector<uint8_t>& key_bits);
+
+      EC_PublicKey(const EC_PublicKey& other) = default;
+      EC_PublicKey& operator=(const EC_PublicKey& other) = default;
+      virtual ~EC_PublicKey() = default;
+
+      /**
+      * Get the public point of this key.
+      * @throw Invalid_State is thrown if the
+      * domain parameters of this point are not set
+      * @result the public point of this key
+      */
+      const PointGFp& public_point() const { return m_public_key; }
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator& rng,
+                     bool strong) const override;
+
+      /**
+      * Get the domain parameters of this key.
+      * @throw Invalid_State is thrown if the
+      * domain parameters of this point are not set
+      * @result the domain parameters of this key
+      */
+      const EC_Group& domain() const { return m_domain_params; }
+
+      /**
+      * Set the domain parameter encoding to be used when encoding this key.
+      * @param enc the encoding to use
+      */
+      void set_parameter_encoding(EC_Group_Encoding enc);
+
+      /**
+      * Set the point encoding method to be used when encoding this key.
+      * @param enc the encoding to use
+      */
+      void set_point_encoding(PointGFp::Compression_Type enc);
+
+      /**
+      * Return the DER encoding of this keys domain in whatever format
+      * is preset for this particular key
+      */
+      std::vector<uint8_t> DER_domain() const
+         { return domain().DER_encode(domain_format()); }
+
+      /**
+      * Get the domain parameter encoding to be used when encoding this key.
+      * @result the encoding to use
+      */
+      EC_Group_Encoding domain_format() const
+         { return m_domain_encoding; }
+
+      /**
+      * Get the point encoding method to be used when encoding this key.
+      * @result the encoding to use
+      */
+      PointGFp::Compression_Type point_encoding() const
+         { return m_point_encoding; }
+
+      size_t key_length() const override;
+      size_t estimated_strength() const override;
+
+   protected:
+      EC_PublicKey() : m_domain_params{}, m_public_key{}, m_domain_encoding(EC_DOMPAR_ENC_EXPLICIT)
+      {}
+
+      EC_Group m_domain_params;
+      PointGFp m_public_key;
+      EC_Group_Encoding m_domain_encoding;
+      PointGFp::Compression_Type m_point_encoding = PointGFp::UNCOMPRESSED;
+   };
+
+/**
+* This abstract class represents ECC private keys
+*/
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKey : public virtual EC_PublicKey,
+                                public virtual Private_Key
+   {
+   public:
+      /*
+      * If x=0, creates a new private key in the domain
+      * using the given rng. If with_modular_inverse is set,
+      * the public key will be calculated by multiplying
+      * the base point with the modular inverse of
+      * x (as in ECGDSA and ECKCDSA), otherwise by
+      * multiplying directly with x (as in ECDSA).
+      */
+      EC_PrivateKey(RandomNumberGenerator& rng,
+                    const EC_Group& domain,
+                    const BigInt& x,
+                    bool with_modular_inverse=false);
+
+      /*
+      * Creates a new private key object from the
+      * ECPrivateKey structure given in key_bits.
+      * If with_modular_inverse is set,
+      * the public key will be calculated by multiplying
+      * the base point with the modular inverse of
+      * x (as in ECGDSA and ECKCDSA), otherwise by
+      * multiplying directly with x (as in ECDSA).
+      */
+      EC_PrivateKey(const AlgorithmIdentifier& alg_id,
+                    const secure_vector<uint8_t>& key_bits,
+                    bool with_modular_inverse=false);
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      /**
+      * Get the private key value of this key object.
+      * @result the private key value of this key object
+      */
+      const BigInt& private_value() const;
+
+      EC_PrivateKey(const EC_PrivateKey& other) = default;
+      EC_PrivateKey& operator=(const EC_PrivateKey& other) = default;
+      ~EC_PrivateKey() = default;
+   protected:
+      EC_PrivateKey() = default;
+
+      BigInt m_private_key;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp b/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp
@@ -0,0 +1,87 @@
+/*
+* ECDH implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+*     2007 Falko Strenzke, FlexSecure GmbH
+*     2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecdh.h>
+#include <botan/numthry.h>
+#include <botan/internal/pk_ops_impl.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+/**
+* ECDH operation
+*/
+class ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+   {
+   public:
+
+      ECDH_KA_Operation(const ECDH_PrivateKey& key, const std::string& kdf, RandomNumberGenerator& rng) :
+         PK_Ops::Key_Agreement_with_KDF(kdf),
+         m_group(key.domain()),
+         m_rng(rng)
+         {
+         m_l_times_priv = m_group.inverse_mod_order(m_group.get_cofactor()) * key.private_value();
+         }
+
+      size_t agreed_value_size() const override { return m_group.get_p_bytes(); }
+
+      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+         {
+         PointGFp input_point = m_group.get_cofactor() * m_group.OS2ECP(w, w_len);
+         input_point.randomize_repr(m_rng);
+
+         const PointGFp S = m_group.blinded_var_point_multiply(
+            input_point, m_l_times_priv, m_rng, m_ws);
+
+         if(S.on_the_curve() == false)
+            throw Internal_Error("ECDH agreed value was not on the curve");
+         return BigInt::encode_1363(S.get_affine_x(), m_group.get_p_bytes());
+         }
+   private:
+      const EC_Group m_group;
+      BigInt m_l_times_priv;
+      RandomNumberGenerator& m_rng;
+      std::vector<BigInt> m_ws;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+ECDH_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+                                         const std::string& params,
+                                         const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      try
+         {
+         return make_openssl_ecdh_ka_op(*this, params);
+         }
+      catch(Lookup_Error&)
+         {
+         if(provider == "openssl")
+            throw;
+         }
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Key_Agreement>(new ECDH_KA_Operation(*this, params, rng));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ecdh/ecdh.h b/third_party/botan/src/lib/pubkey/ecdh/ecdh.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecdh/ecdh.h
@@ -0,0 +1,106 @@
+/*
+* ECDH
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*          Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECDH_KEY_H_
+#define BOTAN_ECDH_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECDH Public Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDH_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+      /**
+      * Create an ECDH public key.
+      * @param alg_id algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      ECDH_PublicKey(const AlgorithmIdentifier& alg_id,
+                     const std::vector<uint8_t>& key_bits) :
+         EC_PublicKey(alg_id, key_bits) {}
+
+      /**
+      * Construct a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      ECDH_PublicKey(const EC_Group& dom_par,
+                     const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Get this keys algorithm name.
+      * @return this keys algorithm name
+      */
+      std::string algo_name() const override { return "ECDH"; }
+
+      /**
+      * @return public point value
+      */
+      std::vector<uint8_t> public_value() const
+         { return public_point().encode(PointGFp::UNCOMPRESSED); }
+
+      /**
+      * @return public point value
+      */
+      std::vector<uint8_t> public_value(PointGFp::Compression_Type format) const
+         { return public_point().encode(format); }
+
+   protected:
+      ECDH_PublicKey() = default;
+   };
+
+/**
+* This class represents ECDH Private Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDH_PrivateKey final : public ECDH_PublicKey,
+                                  public EC_PrivateKey,
+                                  public PK_Key_Agreement_Key
+   {
+   public:
+
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      ECDH_PrivateKey(const AlgorithmIdentifier& alg_id,
+                      const secure_vector<uint8_t>& key_bits) :
+         EC_PrivateKey(alg_id, key_bits) {}
+
+      /**
+      * Generate a new private key
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key; if zero, a new random key is generated
+      */
+      ECDH_PrivateKey(RandomNumberGenerator& rng,
+                      const EC_Group& domain,
+                      const BigInt& x = 0) :
+         EC_PrivateKey(rng, domain, x) {}
+
+      std::vector<uint8_t> public_value() const override
+         { return ECDH_PublicKey::public_value(PointGFp::UNCOMPRESSED); }
+
+      std::vector<uint8_t> public_value(PointGFp::Compression_Type type) const
+         { return ECDH_PublicKey::public_value(type); }
+
+      std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp b/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
@@ -0,0 +1,308 @@
+/*
+* ECDSA implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+*     2007 Falko Strenzke, FlexSecure GmbH
+*     2008-2010,2015,2016,2018 Jack Lloyd
+*     2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecdsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/emsa.h>
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+  #include <botan/rfc6979.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+PointGFp recover_ecdsa_public_key(const EC_Group& group,
+                                  const std::vector<uint8_t>& msg,
+                                  const BigInt& r,
+                                  const BigInt& s,
+                                  uint8_t v)
+   {
+   if(group.get_cofactor() != 1)
+      throw Invalid_Argument("ECDSA public key recovery only supported for prime order groups");
+
+   if(v > 4)
+      throw Invalid_Argument("Unexpected v param for ECDSA public key recovery");
+
+   const uint8_t y_odd = v % 2;
+   const uint8_t add_order = v >> 1;
+
+   const BigInt& group_order = group.get_order();
+   const size_t p_bytes = group.get_p_bytes();
+
+   try
+      {
+      const BigInt e(msg.data(), msg.size(), group.get_order_bits());
+      const BigInt r_inv = group.inverse_mod_order(r);
+
+      BigInt x = r + add_order*group_order;
+
+      std::vector<uint8_t> X(p_bytes + 1);
+
+      X[0] = 0x02 | y_odd;
+      BigInt::encode_1363(&X[1], p_bytes, x);
+
+      const PointGFp R = group.OS2ECP(X);
+
+      if((R*group_order).is_zero() == false)
+         throw Decoding_Error("Unable to recover ECDSA public key");
+
+      // Compute r_inv * (s*R - eG)
+      PointGFp_Multi_Point_Precompute RG_mul(R, group.get_base_point());
+      const BigInt ne = group.mod_order(group_order - e);
+      return r_inv * RG_mul.multi_exp(s, ne);
+      }
+   catch(...)
+      {
+      // continue on and throw
+      }
+
+   throw Decoding_Error("Failed to recover ECDSA public key from signature/msg pair");
+   }
+
+}
+
+ECDSA_PublicKey::ECDSA_PublicKey(const EC_Group& group,
+                                 const std::vector<uint8_t>& msg,
+                                 const BigInt& r,
+                                 const BigInt& s,
+                                 uint8_t v) :
+   EC_PublicKey(group, recover_ecdsa_public_key(group, msg, r, s, v)) {}
+
+
+uint8_t ECDSA_PublicKey::recovery_param(const std::vector<uint8_t>& msg,
+                                        const BigInt& r,
+                                        const BigInt& s) const
+   {
+   for(uint8_t v = 0; v != 4; ++v)
+      {
+      try
+         {
+         PointGFp R = recover_ecdsa_public_key(this->domain(), msg, r, s, v);
+
+         if(R == this->public_point())
+            {
+            return v;
+            }
+         }
+      catch(Decoding_Error&)
+         {
+         // try the next v
+         }
+      }
+
+   throw Internal_Error("Could not determine ECDSA recovery parameter");
+   }
+
+bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+                                 bool strong) const
+   {
+   if(!public_point().on_the_curve())
+      return false;
+
+   if(!strong)
+      return true;
+
+   return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+   }
+
+namespace {
+
+/**
+* ECDSA signature operation
+*/
+class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+
+      ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa,
+                                const std::string& emsa,
+                                RandomNumberGenerator& rng) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_group(ecdsa.domain()),
+         m_x(ecdsa.private_value())
+         {
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+         m_rfc6979.reset(new RFC6979_Nonce_Generator(hash_for_emsa(emsa), m_group.get_order(), m_x));
+#endif
+
+         m_b = m_group.random_scalar(rng);
+         m_b_inv = m_group.inverse_mod_order(m_b);
+         }
+
+      size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                      RandomNumberGenerator& rng) override;
+
+   private:
+      const EC_Group m_group;
+      const BigInt& m_x;
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+      std::unique_ptr<RFC6979_Nonce_Generator> m_rfc6979;
+#endif
+
+      std::vector<BigInt> m_ws;
+
+      BigInt m_b, m_b_inv;
+   };
+
+secure_vector<uint8_t>
+ECDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+                                    RandomNumberGenerator& rng)
+   {
+   BigInt m(msg, msg_len, m_group.get_order_bits());
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+   const BigInt k = m_rfc6979->nonce_for(m);
+#else
+   const BigInt k = m_group.random_scalar(rng);
+#endif
+
+   const BigInt r = m_group.mod_order(
+      m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+   const BigInt k_inv = m_group.inverse_mod_order(k);
+
+   /*
+   * Blind the input message and compute x*r+m as (x*r*b + m*b)/b
+   */
+   m_b = m_group.square_mod_order(m_b);
+   m_b_inv = m_group.square_mod_order(m_b_inv);
+
+   m = m_group.multiply_mod_order(m_b, m_group.mod_order(m));
+   const BigInt xr_m = m_group.mod_order(m_group.multiply_mod_order(m_x, m_b, r) + m);
+
+   const BigInt s = m_group.multiply_mod_order(k_inv, xr_m, m_b_inv);
+
+   // With overwhelming probability, a bug rather than actual zero r/s
+   if(r.is_zero() || s.is_zero())
+      throw Internal_Error("During ECDSA signature generated zero r/s");
+
+   return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order_bytes());
+   }
+
+/**
+* ECDSA verification operation
+*/
+class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+      ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa,
+                                   const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_group(ecdsa.domain()),
+         m_gy_mul(m_group.get_base_point(), ecdsa.public_point())
+         {
+         }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig[], size_t sig_len) override;
+   private:
+      const EC_Group m_group;
+      const PointGFp_Multi_Point_Precompute m_gy_mul;
+   };
+
+bool ECDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+                                          const uint8_t sig[], size_t sig_len)
+   {
+   if(sig_len != m_group.get_order_bytes() * 2)
+      return false;
+
+   const BigInt e(msg, msg_len, m_group.get_order_bits());
+
+   const BigInt r(sig, sig_len / 2);
+   const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+   if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+      return false;
+
+   const BigInt w = m_group.inverse_mod_order(s);
+
+   const BigInt u1 = m_group.multiply_mod_order(m_group.mod_order(e), w);
+   const BigInt u2 = m_group.multiply_mod_order(r, w);
+   const PointGFp R = m_gy_mul.multi_exp(u1, u2);
+
+   if(R.is_zero())
+      return false;
+
+   const BigInt v = m_group.mod_order(R.get_affine_x());
+   return (v == r);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECDSA_PublicKey::create_verification_op(const std::string& params,
+                                        const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      try
+         {
+         return make_openssl_ecdsa_ver_op(*this, params);
+         }
+      catch(Lookup_Error& e)
+         {
+         if(provider == "openssl")
+            throw;
+         }
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new ECDSA_Verification_Operation(*this, params));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+                                      const std::string& params,
+                                      const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      try
+         {
+         return make_openssl_ecdsa_sig_op(*this, params);
+         }
+      catch(Lookup_Error& e)
+         {
+         if(provider == "openssl")
+            throw;
+         }
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new ECDSA_Signature_Operation(*this, params, rng));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h b/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h
@@ -0,0 +1,117 @@
+/*
+* ECDSA
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*          Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECDSA_KEY_H_
+#define BOTAN_ECDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECDSA Public Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDSA_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+
+      /**
+      * Create a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      ECDSA_PublicKey(const EC_Group& dom_par,
+                      const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      ECDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+                      const std::vector<uint8_t>& key_bits) :
+         EC_PublicKey(alg_id, key_bits) {}
+
+      /**
+      * Recover a public key from a signature/msg pair
+      * See SEC section 4.6.1
+      * @param group the elliptic curve group
+      * @param msg the message
+      * @param r the r paramter of the signature
+      * @param s the s paramter of the signature
+      * @param v the recovery ID
+      */
+      ECDSA_PublicKey(const EC_Group& group,
+                      const std::vector<uint8_t>& msg,
+                      const BigInt& r,
+                      const BigInt& s,
+                      uint8_t v);
+
+      /**
+      * Get this keys algorithm name.
+      * @result this keys algorithm name ("ECDSA")
+      */
+      std::string algo_name() const override { return "ECDSA"; }
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      uint8_t recovery_param(const std::vector<uint8_t>& msg,
+                             const BigInt& r,
+                             const BigInt& s) const;
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   protected:
+      ECDSA_PublicKey() = default;
+   };
+
+/**
+* This class represents ECDSA Private Keys
+*/
+class BOTAN_PUBLIC_API(2,0) ECDSA_PrivateKey final : public ECDSA_PublicKey,
+                                   public EC_PrivateKey
+   {
+   public:
+
+      /**
+      * Load a private key
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      ECDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                       const secure_vector<uint8_t>& key_bits) :
+         EC_PrivateKey(alg_id, key_bits) {}
+
+      /**
+      * Create a private key.
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key (if zero, generate a new random key)
+      */
+      ECDSA_PrivateKey(RandomNumberGenerator& rng,
+                       const EC_Group& domain,
+                       const BigInt& x = 0) :
+         EC_PrivateKey(rng, domain, x) {}
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp b/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp
@@ -0,0 +1,155 @@
+/*
+* ECGDSA (BSI-TR-03111, version 2.0)
+* (C) 2016 René Korthaus
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecgdsa.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+
+namespace Botan {
+
+bool ECGDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+                                 bool strong) const
+   {
+   if(!public_point().on_the_curve())
+      return false;
+
+   if(!strong)
+      return true;
+
+   return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+   }
+
+namespace {
+
+/**
+* ECGDSA signature operation
+*/
+class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+
+      ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa,
+                                const std::string& emsa) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_group(ecgdsa.domain()),
+         m_x(ecgdsa.private_value())
+         {
+         }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                      RandomNumberGenerator& rng) override;
+
+      size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+   private:
+      const EC_Group m_group;
+      const BigInt& m_x;
+      std::vector<BigInt> m_ws;
+   };
+
+secure_vector<uint8_t>
+ECGDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+                                     RandomNumberGenerator& rng)
+   {
+   const BigInt m(msg, msg_len, m_group.get_order_bits());
+
+   const BigInt k = m_group.random_scalar(rng);
+
+   const BigInt r = m_group.mod_order(
+      m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+   const BigInt kr = m_group.multiply_mod_order(k, r);
+
+   const BigInt s = m_group.multiply_mod_order(m_x, kr - m);
+
+   // With overwhelming probability, a bug rather than actual zero r/s
+   if(r.is_zero() || s.is_zero())
+      throw Internal_Error("During ECGDSA signature generated zero r/s");
+
+   return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order_bytes());
+   }
+
+/**
+* ECGDSA verification operation
+*/
+class ECGDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+
+      ECGDSA_Verification_Operation(const ECGDSA_PublicKey& ecgdsa,
+                                   const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_group(ecgdsa.domain()),
+         m_gy_mul(m_group.get_base_point(), ecgdsa.public_point())
+         {
+         }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig[], size_t sig_len) override;
+   private:
+      const EC_Group m_group;
+      const PointGFp_Multi_Point_Precompute m_gy_mul;
+   };
+
+bool ECGDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+                                           const uint8_t sig[], size_t sig_len)
+   {
+   if(sig_len != m_group.get_order_bytes() * 2)
+      return false;
+
+   const BigInt e(msg, msg_len, m_group.get_order_bits());
+
+   const BigInt r(sig, sig_len / 2);
+   const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+   if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+      return false;
+
+   const BigInt w = m_group.inverse_mod_order(r);
+
+   const BigInt u1 = m_group.multiply_mod_order(e, w);
+   const BigInt u2 = m_group.multiply_mod_order(s, w);
+   const PointGFp R = m_gy_mul.multi_exp(u1, u2);
+
+   if(R.is_zero())
+      return false;
+
+   const BigInt v = m_group.mod_order(R.get_affine_x());
+   return (v == r);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECGDSA_PublicKey::create_verification_op(const std::string& params,
+                                         const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new ECGDSA_Verification_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+ECGDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                       const std::string& params,
+                                       const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new ECGDSA_Signature_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h b/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h
@@ -0,0 +1,96 @@
+/*
+* ECGDSA (BSI-TR-03111, version 2.0)
+* (C) 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECGDSA_KEY_H_
+#define BOTAN_ECGDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECGDSA public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECGDSA_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+
+      /**
+      * Construct a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      ECGDSA_PublicKey(const EC_Group& dom_par,
+                      const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      ECGDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+                      const std::vector<uint8_t>& key_bits) :
+         EC_PublicKey(alg_id, key_bits) {}
+
+      /**
+      * Get this keys algorithm name.
+      * @result this keys algorithm name ("ECGDSA")
+      */
+      std::string algo_name() const override { return "ECGDSA"; }
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   protected:
+      ECGDSA_PublicKey() = default;
+   };
+
+/**
+* This class represents ECGDSA private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECGDSA_PrivateKey final : public ECGDSA_PublicKey,
+                                    public EC_PrivateKey
+   {
+   public:
+
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      ECGDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                       const secure_vector<uint8_t>& key_bits) :
+         EC_PrivateKey(alg_id, key_bits, true) {}
+
+      /**
+      * Generate a new private key.
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key (if zero, generate a new random key)
+      */
+      ECGDSA_PrivateKey(RandomNumberGenerator& rng,
+                       const EC_Group& domain,
+                       const BigInt& x = 0) :
+         EC_PrivateKey(rng, domain, x, true) {}
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ecies/ecies.cpp b/third_party/botan/src/lib/pubkey/ecies/ecies.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecies/ecies.cpp
@@ -0,0 +1,415 @@
+/*
+* ECIES
+* (C) 2016 Philipp Weber
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecies.h>
+#include <botan/numthry.h>
+#include <botan/cipher_mode.h>
+#include <botan/mac.h>
+#include <botan/internal/pk_ops_impl.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+* Private key type for ECIES_ECDH_KA_Operation
+*/
+class ECIES_PrivateKey final : public EC_PrivateKey, public PK_Key_Agreement_Key
+   {
+   public:
+      explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) :
+         EC_PublicKey(private_key),
+         EC_PrivateKey(private_key),
+         PK_Key_Agreement_Key(),
+         m_key(private_key)
+         {
+         }
+
+      std::vector<uint8_t> public_value() const override
+         {
+         return m_key.public_value();
+         }
+
+      std::string algo_name() const override
+         {
+         return "ECIES";
+         }
+
+      std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const override;
+
+   private:
+      ECDH_PrivateKey m_key;
+   };
+
+/**
+* Implements ECDH key agreement without using the cofactor mode
+*/
+class ECIES_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+   {
+   public:
+      ECIES_ECDH_KA_Operation(const ECIES_PrivateKey& private_key, RandomNumberGenerator& rng) :
+         PK_Ops::Key_Agreement_with_KDF("Raw"),
+         m_key(private_key),
+         m_rng(rng)
+         {
+         }
+
+      size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
+
+      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+         {
+         const EC_Group& group = m_key.domain();
+
+         PointGFp input_point = group.OS2ECP(w, w_len);
+         input_point.randomize_repr(m_rng);
+
+         const PointGFp S = group.blinded_var_point_multiply(
+            input_point, m_key.private_value(), m_rng, m_ws);
+
+         if(S.on_the_curve() == false)
+            throw Internal_Error("ECDH agreed value was not on the curve");
+         return BigInt::encode_1363(S.get_affine_x(), group.get_p_bytes());
+         }
+
+   private:
+      ECIES_PrivateKey m_key;
+      RandomNumberGenerator& m_rng;
+      std::vector<BigInt> m_ws;
+   };
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+                                          const std::string& /*params*/,
+                                          const std::string& /*provider*/) const
+   {
+   return std::unique_ptr<PK_Ops::Key_Agreement>(new ECIES_ECDH_KA_Operation(*this, rng));
+   }
+
+/**
+* Creates a PK_Key_Agreement instance for the given key and ecies_params
+* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
+* depending on the key and ecies_params
+* @param private_key the private key used for the key agreement
+* @param ecies_params settings for ecies
+* @param for_encryption disable cofactor mode if the secret will be used for encryption
+* (according to ISO 18033 cofactor mode is only used during decryption)
+*/
+PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
+                                      const ECIES_KA_Params& ecies_params,
+                                      bool for_encryption,
+                                      RandomNumberGenerator& rng)
+   {
+   const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
+
+   if(ecdh_key == nullptr && (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode()
+                              || ecies_params.check_mode()))
+      {
+      // assume we have a private key from an external provider (e.g. pkcs#11):
+      // there is no way to determine or control whether the provider uses cofactor mode or not.
+      // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
+      // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
+      throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey");
+      }
+
+   if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode()))
+      {
+      // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.
+      return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw");
+      }
+
+   return PK_Key_Agreement(private_key, rng, "Raw");        // use default implementation
+   }
+}
+
+ECIES_KA_Operation::ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
+                                       const ECIES_KA_Params& ecies_params,
+                                       bool for_encryption,
+                                       RandomNumberGenerator& rng) :
+   m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)),
+   m_params(ecies_params)
+   {
+   }
+
+/**
+* ECIES secret derivation according to ISO 18033-2
+*/
+SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
+      const PointGFp& other_public_key_point) const
+   {
+   if(other_public_key_point.is_zero())
+      {
+      throw Invalid_Argument("ECIES: other public key point is zero");
+      }
+
+   std::unique_ptr<KDF> kdf = Botan::KDF::create_or_throw(m_params.kdf_spec());
+
+   PointGFp other_point = other_public_key_point;
+
+   // ISO 18033: step b
+   if(m_params.old_cofactor_mode())
+      {
+      other_point *= m_params.domain().get_cofactor();
+      }
+
+   secure_vector<uint8_t> derivation_input;
+
+   // ISO 18033: encryption step e / decryption step g
+   if(!m_params.single_hash_mode())
+      {
+      derivation_input += eph_public_key_bin;
+      }
+
+   // ISO 18033: encryption step f / decryption step h
+   std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
+    // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because
+   // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used.
+   const SymmetricKey peh = m_ka.derive_key(m_params.domain().get_order().bytes(), other_public_key_bin.data(), other_public_key_bin.size());
+   derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
+
+   // ISO 18033: encryption step g / decryption step i
+   return kdf->derive_key(m_params.secret_length(), derivation_input);
+   }
+
+
+ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,
+                                 PointGFp::Compression_Type compression_type, ECIES_Flags flags) :
+   m_domain(domain),
+   m_kdf_spec(kdf_spec),
+   m_length(length),
+   m_compression_mode(compression_type),
+   m_flags(flags)
+   {
+   }
+
+ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
+                                         const std::string& dem_algo_spec, size_t dem_key_len,
+                                         const std::string& mac_spec, size_t mac_key_len,
+                                         PointGFp::Compression_Type compression_type, ECIES_Flags flags) :
+   ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
+   m_dem_spec(dem_algo_spec),
+   m_dem_keylen(dem_key_len),
+   m_mac_spec(mac_spec),
+   m_mac_keylen(mac_key_len)
+   {
+   // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
+   if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1)
+      {
+      throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
+      }
+   }
+
+ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
+                                         const std::string& dem_algo_spec, size_t dem_key_len,
+                                         const std::string& mac_spec, size_t mac_key_len) :
+   ECIES_System_Params(domain, kdf_spec, dem_algo_spec, dem_key_len, mac_spec, mac_key_len, PointGFp::UNCOMPRESSED,
+                         ECIES_Flags::NONE)
+   {
+   }
+
+std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const
+   {
+   return Botan::MessageAuthenticationCode::create_or_throw(m_mac_spec);
+   }
+
+std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Botan::Cipher_Dir direction) const
+   {
+   return Cipher_Mode::create_or_throw(m_dem_spec, direction);
+   }
+
+
+/*
+* ECIES_Encryptor Constructor
+*/
+ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
+                                 const ECIES_System_Params& ecies_params,
+                                 RandomNumberGenerator& rng) :
+   m_ka(private_key, ecies_params, true, rng),
+   m_params(ecies_params),
+   m_eph_public_key_bin(private_key.public_value()),    // returns the uncompressed public key, see conversion below
+   m_iv(),
+   m_other_point(),
+   m_label()
+   {
+   if(ecies_params.compression_type() != PointGFp::UNCOMPRESSED)
+      {
+      // ISO 18033: step d
+      // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format
+      m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
+      }
+   m_mac = m_params.create_mac();
+   m_cipher = m_params.create_cipher(ENCRYPTION);
+   }
+
+/*
+* ECIES_Encryptor Constructor
+*/
+ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params) :
+   ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng)
+   {
+   }
+
+size_t ECIES_Encryptor::maximum_input_size() const
+   {
+   /*
+   ECIES should just be used for key transport so this (arbitrary) limit
+   seems sufficient
+   */
+   return 64;
+   }
+
+size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const
+   {
+   return m_eph_public_key_bin.size() +
+          m_mac->output_length() +
+          m_cipher->output_length(ptext_len);
+   }
+
+/*
+* ECIES Encryption according to ISO 18033-2
+*/
+std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const
+   {
+   if(m_other_point.is_zero())
+      {
+      throw Invalid_State("ECIES: the other key is zero");
+      }
+
+   const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);
+
+   // encryption
+
+   m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
+   if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+      throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+
+   m_cipher->start(m_iv.bits_of());
+
+   secure_vector<uint8_t> encrypted_data(data, data + length);
+   m_cipher->finish(encrypted_data);
+
+   // concat elements
+
+   std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
+   buffer_insert(out, 0, m_eph_public_key_bin);
+   buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
+
+   // mac
+   m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
+   m_mac->update(encrypted_data);
+   if(!m_label.empty())
+      {
+      m_mac->update(m_label);
+      }
+   m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
+
+   return out;
+   }
+
+
+ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key,
+                                 const ECIES_System_Params& ecies_params,
+                                 RandomNumberGenerator& rng) :
+   m_ka(key, ecies_params, false, rng),
+   m_params(ecies_params),
+   m_iv(),
+   m_label()
+   {
+   // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
+   if(!ecies_params.check_mode())
+      {
+      const Botan::BigInt& cofactor = m_params.domain().get_cofactor();
+      if(cofactor > 1 && Botan::gcd(cofactor, m_params.domain().get_order()) != 1)
+         {
+         throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
+         }
+      }
+
+   m_mac = m_params.create_mac();
+   m_cipher = m_params.create_cipher(DECRYPTION);
+   }
+
+size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const
+   {
+   const size_t point_size = m_params.domain().point_size(m_params.compression_type());
+   const size_t overhead = point_size + m_mac->output_length();
+
+   if(ctext_len < overhead)
+      return 0;
+
+   return m_cipher->output_length(ctext_len - overhead);
+   }
+
+/**
+* ECIES Decryption according to ISO 18033-2
+*/
+secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const
+   {
+   const size_t point_size = m_params.domain().point_size(m_params.compression_type());
+
+   if(in_len < point_size + m_mac->output_length())
+      {
+      throw Decoding_Error("ECIES decryption: ciphertext is too short");
+      }
+
+   // extract data
+   const std::vector<uint8_t> other_public_key_bin(in, in + point_size);    // the received (ephemeral) public key
+   const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
+   const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
+
+   // ISO 18033: step a
+   PointGFp other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
+
+   // ISO 18033: step b
+   if(m_params.check_mode() && !other_public_key.on_the_curve())
+      {
+      throw Decoding_Error("ECIES decryption: received public key is not on the curve");
+      }
+
+   // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
+   // throws Illegal_Transformation if the point is zero)
+   const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
+
+   // validate mac
+   m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
+   m_mac->update(encrypted_data);
+   if(!m_label.empty())
+      {
+      m_mac->update(m_label);
+      }
+   const secure_vector<uint8_t> calculated_mac = m_mac->final();
+   valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
+
+   if(valid_mask)
+      {
+      // decrypt data
+
+      m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
+      if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+         throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+      m_cipher->start(m_iv.bits_of());
+
+      try
+         {
+         // the decryption can fail:
+         // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
+         secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
+         m_cipher->finish(decrypted_data);
+         return decrypted_data;
+         }
+      catch(...)
+         {
+         valid_mask = 0;
+         }
+      }
+   return secure_vector<uint8_t>();
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ecies/ecies.h b/third_party/botan/src/lib/pubkey/ecies/ecies.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ecies/ecies.h
@@ -0,0 +1,314 @@
+/*
+* ECIES
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECIES_H_
+#define BOTAN_ECIES_H_
+
+#include <botan/ecdh.h>
+#include <botan/ec_group.h>
+#include <botan/cipher_mode.h>
+#include <botan/point_gfp.h>
+#include <botan/pubkey.h>
+#include <botan/secmem.h>
+#include <botan/symkey.h>
+#include <botan/mac.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+enum class ECIES_Flags : uint32_t
+   {
+   NONE = 0,
+
+   /// if set: prefix the input of the (ecdh) key agreement with the encoded (ephemeral) public key
+   SINGLE_HASH_MODE = 1,
+
+   /// (decryption only) if set: use cofactor multiplication during (ecdh) key agreement
+   COFACTOR_MODE = 2,
+
+   /// if set: use ecdhc instead of ecdh
+   OLD_COFACTOR_MODE = 4,
+
+   /// (decryption only) if set: test if the (ephemeral) public key is on the curve
+   CHECK_MODE = 8
+   };
+
+inline ECIES_Flags operator |(ECIES_Flags a, ECIES_Flags b)
+   {
+   return static_cast<ECIES_Flags>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
+   }
+
+inline ECIES_Flags operator &(ECIES_Flags a, ECIES_Flags b)
+   {
+   return static_cast<ECIES_Flags>(static_cast<uint32_t>(a) & static_cast<uint32_t>(b));
+   }
+
+/**
+* Parameters for ECIES secret derivation
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_KA_Params
+   {
+   public:
+      /**
+      * @param domain ec domain parameters of the involved ec keys
+      * @param kdf_spec name of the key derivation function
+      * @param length length of the secret to be derived
+      * @param compression_type format of encoded keys (affects the secret derivation if single_hash_mode is used)
+      * @param flags options, see documentation of ECIES_Flags
+      */
+      ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,
+                      PointGFp::Compression_Type compression_type, ECIES_Flags flags);
+
+      ECIES_KA_Params(const ECIES_KA_Params&) = default;
+      ECIES_KA_Params& operator=(const ECIES_KA_Params&) = delete;
+
+      virtual ~ECIES_KA_Params() = default;
+
+      inline const EC_Group& domain() const
+         {
+         return m_domain;
+         }
+
+      inline size_t secret_length() const
+         {
+         return m_length;
+         }
+
+      inline bool single_hash_mode() const
+         {
+         return (m_flags & ECIES_Flags::SINGLE_HASH_MODE) == ECIES_Flags::SINGLE_HASH_MODE;
+         }
+
+      inline bool cofactor_mode() const
+         {
+         return (m_flags & ECIES_Flags::COFACTOR_MODE) == ECIES_Flags::COFACTOR_MODE;
+         }
+
+      inline bool old_cofactor_mode() const
+         {
+         return (m_flags & ECIES_Flags::OLD_COFACTOR_MODE) == ECIES_Flags::OLD_COFACTOR_MODE;
+         }
+
+      inline bool check_mode() const
+         {
+         return (m_flags & ECIES_Flags::CHECK_MODE) == ECIES_Flags::CHECK_MODE;
+         }
+
+      inline PointGFp::Compression_Type compression_type() const
+         {
+         return m_compression_mode;
+         }
+
+      const std::string& kdf_spec() const
+         {
+         return m_kdf_spec;
+         }
+
+   private:
+      const EC_Group m_domain;
+      const std::string m_kdf_spec;
+      const size_t m_length;
+      const PointGFp::Compression_Type m_compression_mode;
+      const ECIES_Flags m_flags;
+   };
+
+
+class BOTAN_PUBLIC_API(2,0) ECIES_System_Params final : public ECIES_KA_Params
+   {
+   public:
+      /**
+      * @param domain ec domain parameters of the involved ec keys
+      * @param kdf_spec name of the key derivation function
+      * @param dem_algo_spec name of the data encryption method
+      * @param dem_key_len length of the key used for the data encryption method
+      * @param mac_spec name of the message authentication code
+      * @param mac_key_len length of the key used for the message authentication code
+      */
+      ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec, const std::string& dem_algo_spec,
+                          size_t dem_key_len, const std::string& mac_spec, size_t mac_key_len);
+
+      /**
+      * @param domain ec domain parameters of the involved ec keys
+      * @param kdf_spec name of the key derivation function
+      * @param dem_algo_spec name of the data encryption method
+      * @param dem_key_len length of the key used for the data encryption method
+      * @param mac_spec name of the message authentication code
+      * @param mac_key_len length of the key used for the message authentication code
+      * @param compression_type format of encoded keys (affects the secret derivation if single_hash_mode is used)
+      * @param flags options, see documentation of ECIES_Flags
+      */
+      ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec, const std::string& dem_algo_spec,
+                          size_t dem_key_len, const std::string& mac_spec, size_t mac_key_len,
+                          PointGFp::Compression_Type compression_type, ECIES_Flags flags);
+
+      ECIES_System_Params(const ECIES_System_Params&) = default;
+      ECIES_System_Params& operator=(const ECIES_System_Params&) = delete;
+      virtual ~ECIES_System_Params() = default;
+
+      /// creates an instance of the message authentication code
+      std::unique_ptr<MessageAuthenticationCode> create_mac() const;
+
+      /// creates an instance of the data encryption method
+      std::unique_ptr<Cipher_Mode> create_cipher(Botan::Cipher_Dir direction) const;
+
+      /// returns the length of the key used by the data encryption method
+      inline size_t dem_keylen() const
+         {
+         return m_dem_keylen;
+         }
+
+      /// returns the length of the key used by the message authentication code
+      inline size_t mac_keylen() const
+         {
+         return m_mac_keylen;
+         }
+
+   private:
+      const std::string m_dem_spec;
+      const size_t m_dem_keylen;
+      const std::string m_mac_spec;
+      const size_t m_mac_keylen;
+   };
+
+
+/**
+* ECIES secret derivation according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_KA_Operation
+   {
+   public:
+      /**
+      * @param private_key the (ephemeral) private key which is used to derive the secret
+      * @param ecies_params settings for ecies
+      * @param for_encryption disable cofactor mode if the secret will be used for encryption
+      * (according to ISO 18033 cofactor mode is only used during decryption)
+      * @param rng the RNG to use
+      */
+      ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
+                         const ECIES_KA_Params& ecies_params,
+                         bool for_encryption,
+                         RandomNumberGenerator& rng);
+
+      /**
+      * Performs a key agreement with the provided keys and derives the secret from the result
+      * @param eph_public_key_bin the encoded (ephemeral) public key which belongs to the used (ephemeral) private key
+      * @param other_public_key_point public key point of the other party
+      */
+      SymmetricKey derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
+                                 const PointGFp& other_public_key_point) const;
+
+   private:
+      const PK_Key_Agreement m_ka;
+      const ECIES_KA_Params m_params;
+   };
+
+
+/**
+* ECIES Encryption according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_Encryptor final : public PK_Encryptor
+   {
+   public:
+      /**
+      * @param private_key the (ephemeral) private key which is used for the key agreement
+      * @param ecies_params settings for ecies
+      * @param rng random generator to use
+      */
+      ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
+                      const ECIES_System_Params& ecies_params,
+                      RandomNumberGenerator& rng);
+
+      /**
+      * Creates an ephemeral private key which is used for the key agreement
+      * @param rng random generator used during private key generation
+      * @param ecies_params settings for ecies
+      */
+      ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params);
+
+      /// Set the public key of the other party
+      inline void set_other_key(const Botan::PointGFp& public_point)
+         {
+         m_other_point = public_point;
+         }
+
+      /// Set the initialization vector for the data encryption method
+      inline void set_initialization_vector(const InitializationVector& iv)
+         {
+         m_iv = iv;
+         }
+
+      /// Set the label which is appended to the input for the message authentication code
+      inline void set_label(const std::string& label)
+         {
+         m_label = std::vector<uint8_t>(label.begin(), label.end());
+         }
+
+   private:
+      std::vector<uint8_t> enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const override;
+
+      size_t maximum_input_size() const override;
+
+      size_t ciphertext_length(size_t ptext_len) const override;
+
+      const ECIES_KA_Operation m_ka;
+      const ECIES_System_Params m_params;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      std::unique_ptr<Cipher_Mode> m_cipher;
+      std::vector<uint8_t> m_eph_public_key_bin;
+      InitializationVector m_iv;
+      PointGFp m_other_point;
+      std::vector<uint8_t> m_label;
+   };
+
+
+/**
+* ECIES Decryption according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_Decryptor final : public PK_Decryptor
+   {
+   public:
+      /**
+      * @param private_key the private key which is used for the key agreement
+      * @param ecies_params settings for ecies
+      * @param rng the random generator to use
+      */
+      ECIES_Decryptor(const PK_Key_Agreement_Key& private_key,
+                      const ECIES_System_Params& ecies_params,
+                      RandomNumberGenerator& rng);
+
+      /// Set the initialization vector for the data encryption method
+      inline void set_initialization_vector(const InitializationVector& iv)
+         {
+         m_iv = iv;
+         }
+
+      /// Set the label which is appended to the input for the message authentication code
+      inline void set_label(const std::string& label)
+         {
+         m_label = std::vector<uint8_t>(label.begin(), label.end());
+         }
+
+   private:
+      secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const override;
+
+      size_t plaintext_length(size_t ctext_len) const override;
+
+      const ECIES_KA_Operation m_ka;
+      const ECIES_System_Params m_params;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      std::unique_ptr<Cipher_Mode> m_cipher;
+      InitializationVector m_iv;
+      std::vector<uint8_t> m_label;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp
@@ -0,0 +1,208 @@
+/*
+* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
+* (C) 2016 René Korthaus, Sirrix AG
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eckcdsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/emsa.h>
+#include <botan/hash.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+bool ECKCDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+                                 bool strong) const
+   {
+   if(!public_point().on_the_curve())
+      {
+      return false;
+      }
+
+   if(!strong)
+      {
+      return true;
+      }
+
+   return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+   }
+
+namespace {
+
+/**
+* ECKCDSA signature operation
+*/
+class ECKCDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+
+      ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa,
+                                const std::string& emsa) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_group(eckcdsa.domain()),
+         m_x(eckcdsa.private_value()),
+         m_prefix()
+         {
+         const BigInt public_point_x = eckcdsa.public_point().get_affine_x();
+         const BigInt public_point_y = eckcdsa.public_point().get_affine_y();
+
+         m_prefix.resize(public_point_x.bytes() + public_point_y.bytes());
+         public_point_x.binary_encode(m_prefix.data());
+         public_point_y.binary_encode(&m_prefix[public_point_x.bytes()]);
+         m_prefix.resize(HashFunction::create(hash_for_signature())->hash_block_size()); // use only the "hash input block size" leftmost bits
+         }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                      RandomNumberGenerator& rng) override;
+
+      size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      bool has_prefix() override { return true; }
+      secure_vector<uint8_t> message_prefix() const override { return m_prefix; }
+
+   private:
+      const EC_Group m_group;
+      const BigInt& m_x;
+      secure_vector<uint8_t> m_prefix;
+      std::vector<BigInt> m_ws;
+   };
+
+secure_vector<uint8_t>
+ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t,
+                                     RandomNumberGenerator& rng)
+   {
+   const BigInt k = m_group.random_scalar(rng);
+   const BigInt k_times_P_x = m_group.blinded_base_point_multiply_x(k, rng, m_ws);
+
+   secure_vector<uint8_t> to_be_hashed(k_times_P_x.bytes());
+   k_times_P_x.binary_encode(to_be_hashed.data());
+
+   std::unique_ptr<EMSA> emsa = this->clone_emsa();
+   emsa->update(to_be_hashed.data(), to_be_hashed.size());
+   secure_vector<uint8_t> c = emsa->raw_data();
+   c = emsa->encoding_of(c, max_input_bits(), rng);
+
+   const BigInt r(c.data(), c.size());
+
+   xor_buf(c, msg, c.size());
+   BigInt w(c.data(), c.size());
+   w = m_group.mod_order(w);
+
+   const BigInt s = m_group.multiply_mod_order(m_x, k - w);
+   if(s.is_zero())
+      throw Internal_Error("During ECKCDSA signature generation created zero s");
+
+   secure_vector<uint8_t> output = BigInt::encode_1363(r, c.size());
+   output += BigInt::encode_1363(s, m_group.get_order_bytes());
+   return output;
+   }
+
+/**
+* ECKCDSA verification operation
+*/
+class ECKCDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+
+      ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa,
+                                   const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_group(eckcdsa.domain()),
+         m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
+         m_prefix()
+         {
+         const BigInt public_point_x = eckcdsa.public_point().get_affine_x();
+         const BigInt public_point_y = eckcdsa.public_point().get_affine_y();
+
+         m_prefix.resize(public_point_x.bytes() + public_point_y.bytes());
+         public_point_x.binary_encode(&m_prefix[0]);
+         public_point_y.binary_encode(&m_prefix[public_point_x.bytes()]);
+         m_prefix.resize(HashFunction::create(hash_for_signature())->hash_block_size()); // use only the "hash input block size" leftmost bits
+         }
+
+      bool has_prefix() override { return true; }
+      secure_vector<uint8_t> message_prefix() const override { return m_prefix; }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig[], size_t sig_len) override;
+   private:
+      const EC_Group m_group;
+      const PointGFp_Multi_Point_Precompute m_gy_mul;
+      secure_vector<uint8_t> m_prefix;
+   };
+
+bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t,
+                                           const uint8_t sig[], size_t sig_len)
+   {
+   const std::unique_ptr<HashFunction> hash = HashFunction::create(hash_for_signature());
+   //calculate size of r
+
+   const size_t order_bytes = m_group.get_order_bytes();
+
+   const size_t size_r = std::min(hash -> output_length(), order_bytes);
+   if(sig_len != size_r + order_bytes)
+      {
+      return false;
+      }
+
+   secure_vector<uint8_t> r(sig, sig + size_r);
+
+   // check that 0 < s < q
+   const BigInt s(sig + size_r, order_bytes);
+
+   if(s <= 0 || s >= m_group.get_order())
+      {
+      return false;
+      }
+
+   secure_vector<uint8_t> r_xor_e(r);
+   xor_buf(r_xor_e, msg, r.size());
+   BigInt w(r_xor_e.data(), r_xor_e.size());
+   w = m_group.mod_order(w);
+
+   const PointGFp q = m_gy_mul.multi_exp(w, s);
+   const BigInt q_x = q.get_affine_x();
+   secure_vector<uint8_t> c(q_x.bytes());
+   q_x.binary_encode(c.data());
+   std::unique_ptr<EMSA> emsa = this->clone_emsa();
+   emsa->update(c.data(), c.size());
+   secure_vector<uint8_t> v = emsa->raw_data();
+   Null_RNG rng;
+   v = emsa->encoding_of(v, max_input_bits(), rng);
+
+   return (v == r);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECKCDSA_PublicKey::create_verification_op(const std::string& params,
+                                         const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new ECKCDSA_Verification_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                        const std::string& params,
+                                        const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new ECKCDSA_Signature_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h b/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h
@@ -0,0 +1,96 @@
+/*
+* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
+* (C) 2016 René Korthaus, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECKCDSA_KEY_H_
+#define BOTAN_ECKCDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECKCDSA public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECKCDSA_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+
+      /**
+      * Construct a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      ECKCDSA_PublicKey(const EC_Group& dom_par,
+                      const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      ECKCDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+                      const std::vector<uint8_t>& key_bits) :
+         EC_PublicKey(alg_id, key_bits) {}
+
+      /**
+      * Get this keys algorithm name.
+      * @result this keys algorithm name ("ECGDSA")
+      */
+      std::string algo_name() const override { return "ECKCDSA"; }
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+   protected:
+      ECKCDSA_PublicKey() = default;
+   };
+
+/**
+* This class represents ECKCDSA private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECKCDSA_PrivateKey final : public ECKCDSA_PublicKey,
+                                    public EC_PrivateKey
+   {
+   public:
+
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      ECKCDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                       const secure_vector<uint8_t>& key_bits) :
+         EC_PrivateKey(alg_id, key_bits, true) {}
+
+      /**
+      * Create a private key.
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key (if zero, generate a new random key)
+      */
+      ECKCDSA_PrivateKey(RandomNumberGenerator& rng,
+                       const EC_Group& domain,
+                       const BigInt& x = 0) :
+         EC_PrivateKey(rng, domain, x, true) {}
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp b/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp
@@ -0,0 +1,102 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ed25519.h>
+#include <botan/internal/ed25519_internal.h>
+#include <botan/sha2_64.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void ed25519_gen_keypair(uint8_t* pk, uint8_t* sk, const uint8_t seed[32])
+   {
+   uint8_t az[64];
+
+   SHA_512 sha;
+   sha.update(seed, 32);
+   sha.final(az);
+   az[0] &= 248;
+   az[31] &= 63;
+   az[31] |= 64;
+
+   ge_scalarmult_base(pk, az);
+
+   // todo copy_mem
+   copy_mem(sk, seed, 32);
+   copy_mem(sk + 32, pk, 32);
+   }
+
+void ed25519_sign(uint8_t sig[64],
+                  const uint8_t m[], size_t mlen,
+                  const uint8_t sk[64],
+                  const uint8_t domain_sep[], size_t domain_sep_len)
+   {
+   uint8_t az[64];
+   uint8_t nonce[64];
+   uint8_t hram[64];
+
+   SHA_512 sha;
+
+   sha.update(sk, 32);
+   sha.final(az);
+   az[0] &= 248;
+   az[31] &= 63;
+   az[31] |= 64;
+
+   sha.update(domain_sep, domain_sep_len);
+   sha.update(az + 32, 32);
+   sha.update(m, mlen);
+   sha.final(nonce);
+
+   sc_reduce(nonce);
+   ge_scalarmult_base(sig, nonce);
+
+   sha.update(domain_sep, domain_sep_len);
+   sha.update(sig, 32);
+   sha.update(sk + 32, 32);
+   sha.update(m, mlen);
+   sha.final(hram);
+
+   sc_reduce(hram);
+   sc_muladd(sig + 32, hram, az, nonce);
+   }
+
+bool ed25519_verify(const uint8_t* m, size_t mlen,
+                    const uint8_t sig[64],
+                    const uint8_t* pk,
+                    const uint8_t domain_sep[], size_t domain_sep_len)
+   {
+   uint8_t h[64];
+   uint8_t rcheck[32];
+   ge_p3 A;
+   SHA_512 sha;
+
+   if(sig[63] & 224)
+      {
+      return false;
+      }
+   if(ge_frombytes_negate_vartime(&A, pk) != 0)
+      {
+      return false;
+      }
+
+   sha.update(domain_sep, domain_sep_len);
+   sha.update(sig, 32);
+   sha.update(pk, 32);
+   sha.update(m, mlen);
+   sha.final(h);
+   sc_reduce(h);
+
+   ge_double_scalarmult_vartime(rcheck, h, &A, sig + 32);
+
+   return constant_time_compare(rcheck, sig, 32);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519.h b/third_party/botan/src/lib/pubkey/ed25519/ed25519.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519.h
@@ -0,0 +1,113 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_H_
+#define BOTAN_ED25519_H_
+
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,2) Ed25519_PublicKey : public virtual Public_Key
+   {
+   public:
+      std::string algo_name() const override { return "Ed25519"; }
+
+      size_t estimated_strength() const override { return 128; }
+
+      size_t key_length() const override { return 255; }
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      /**
+      * Create a Ed25519 Public Key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      Ed25519_PublicKey(const AlgorithmIdentifier& alg_id,
+                        const std::vector<uint8_t>& key_bits);
+
+      template<typename Alloc>
+      Ed25519_PublicKey(const std::vector<uint8_t, Alloc>& pub) :
+         Ed25519_PublicKey(pub.data(), pub.size()) {}
+
+      Ed25519_PublicKey(const uint8_t pub_key[], size_t len);
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+
+      const std::vector<uint8_t>& get_public_key() const { return m_public; }
+
+   protected:
+      Ed25519_PublicKey() = default;
+      std::vector<uint8_t> m_public;
+   };
+
+class BOTAN_PUBLIC_API(2,2) Ed25519_PrivateKey final : public Ed25519_PublicKey,
+                                     public virtual Private_Key
+   {
+   public:
+      /**
+      * Construct a private key from the specified parameters.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits PKCS #8 structure
+      */
+      Ed25519_PrivateKey(const AlgorithmIdentifier& alg_id,
+                         const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Generate a private key.
+      * @param rng the RNG to use
+      */
+      explicit Ed25519_PrivateKey(RandomNumberGenerator& rng);
+
+      /**
+      * Construct a private key from the specified parameters.
+      * @param secret_key the private key
+      */
+      explicit Ed25519_PrivateKey(const secure_vector<uint8_t>& secret_key);
+
+      const secure_vector<uint8_t>& get_private_key() const { return m_private; }
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+
+   private:
+      secure_vector<uint8_t> m_private;
+   };
+
+void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32]);
+
+void ed25519_sign(uint8_t sig[64],
+                  const uint8_t msg[],
+                  size_t msg_len,
+                  const uint8_t sk[64],
+                  const uint8_t domain_sep[], size_t domain_sep_len);
+
+bool ed25519_verify(const uint8_t msg[],
+                    size_t msg_len,
+                    const uint8_t sig[64],
+                    const uint8_t pk[32],
+                    const uint8_t domain_sep[], size_t domain_sep_len);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp b/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp
@@ -0,0 +1,754 @@
+/*
+* Ed25519 field element
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_fe.h>
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+//static
+FE_25519 FE_25519::invert(const FE_25519& z)
+   {
+   fe t0;
+   fe t1;
+   fe t2;
+   fe t3;
+
+   fe_sq(t0, z);
+   fe_sq_iter(t1, t0, 2);
+   fe_mul(t1, z, t1);
+   fe_mul(t0, t0, t1);
+   fe_sq(t2, t0);
+   fe_mul(t1, t1, t2);
+   fe_sq_iter(t2, t1, 5);
+   fe_mul(t1, t2, t1);
+   fe_sq_iter(t2, t1, 10);
+   fe_mul(t2, t2, t1);
+   fe_sq_iter(t3, t2, 20);
+   fe_mul(t2, t3, t2);
+   fe_sq_iter(t2, t2, 10);
+   fe_mul(t1, t2, t1);
+   fe_sq_iter(t2, t1, 50);
+   fe_mul(t2, t2, t1);
+   fe_sq_iter(t3, t2, 100);
+   fe_mul(t2, t3, t2);
+   fe_sq_iter(t2, t2, 50);
+   fe_mul(t1, t2, t1);
+   fe_sq_iter(t1, t1, 5);
+
+   fe_mul(t0, t1, t0);
+   return t0;
+   }
+
+FE_25519 FE_25519::pow_22523(const fe& z)
+   {
+   fe t0;
+   fe t1;
+   fe t2;
+
+   fe_sq(t0, z);
+   fe_sq_iter(t1, t0, 2);
+   fe_mul(t1, z, t1);
+   fe_mul(t0, t0, t1);
+   fe_sq(t0, t0);
+   fe_mul(t0, t1, t0);
+   fe_sq_iter(t1, t0, 5);
+   fe_mul(t0, t1, t0);
+   fe_sq_iter(t1, t0, 10);
+   fe_mul(t1, t1, t0);
+   fe_sq_iter(t2, t1, 20);
+   fe_mul(t1, t2, t1);
+   fe_sq_iter(t1, t1, 10);
+   fe_mul(t0, t1, t0);
+   fe_sq_iter(t1, t0, 50);
+   fe_mul(t1, t1, t0);
+   fe_sq_iter(t2, t1, 100);
+   fe_mul(t1, t2, t1);
+   fe_sq_iter(t1, t1, 50);
+   fe_mul(t0, t1, t0);
+   fe_sq_iter(t0, t0, 2);
+
+   fe_mul(t0, t0, z);
+   return t0;
+   }
+
+/*
+h = f * g
+Can overlap h with f or g.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+Notes on implementation strategy:
+
+Using schoolbook multiplication.
+Karatsuba would save a little in some cost models.
+
+Most multiplications by 2 and 19 are 32-bit precomputations;
+cheaper than 64-bit postcomputations.
+
+There is one remaining multiplication by 19 in the carry chain;
+one *19 precomputation can be merged into this,
+but the resulting data flow is considerably less clean.
+
+There are 12 carries below.
+10 of them are 2-way parallelizable and vectorizable.
+Can get away with 11 carries, but then data flow is much deeper.
+
+With tighter constraints on inputs can squeeze carries into int32.
+*/
+
+//static
+FE_25519 FE_25519::mul(const FE_25519& f, const FE_25519& g)
+   {
+   const int32_t f0 = f[0];
+   const int32_t f1 = f[1];
+   const int32_t f2 = f[2];
+   const int32_t f3 = f[3];
+   const int32_t f4 = f[4];
+   const int32_t f5 = f[5];
+   const int32_t f6 = f[6];
+   const int32_t f7 = f[7];
+   const int32_t f8 = f[8];
+   const int32_t f9 = f[9];
+
+   const int32_t g0 = g[0];
+   const int32_t g1 = g[1];
+   const int32_t g2 = g[2];
+   const int32_t g3 = g[3];
+   const int32_t g4 = g[4];
+   const int32_t g5 = g[5];
+   const int32_t g6 = g[6];
+   const int32_t g7 = g[7];
+   const int32_t g8 = g[8];
+   const int32_t g9 = g[9];
+
+   const int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */
+   const int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
+   const int32_t g3_19 = 19 * g3;
+   const int32_t g4_19 = 19 * g4;
+   const int32_t g5_19 = 19 * g5;
+   const int32_t g6_19 = 19 * g6;
+   const int32_t g7_19 = 19 * g7;
+   const int32_t g8_19 = 19 * g8;
+   const int32_t g9_19 = 19 * g9;
+   const int32_t f1_2 = 2 * f1;
+   const int32_t f3_2 = 2 * f3;
+   const int32_t f5_2 = 2 * f5;
+   const int32_t f7_2 = 2 * f7;
+   const int32_t f9_2 = 2 * f9;
+
+   const int64_t f0g0    = f0   * static_cast<int64_t>(g0);
+   const int64_t f0g1    = f0   * static_cast<int64_t>(g1);
+   const int64_t f0g2    = f0   * static_cast<int64_t>(g2);
+   const int64_t f0g3    = f0   * static_cast<int64_t>(g3);
+   const int64_t f0g4    = f0   * static_cast<int64_t>(g4);
+   const int64_t f0g5    = f0   * static_cast<int64_t>(g5);
+   const int64_t f0g6    = f0   * static_cast<int64_t>(g6);
+   const int64_t f0g7    = f0   * static_cast<int64_t>(g7);
+   const int64_t f0g8    = f0   * static_cast<int64_t>(g8);
+   const int64_t f0g9    = f0   * static_cast<int64_t>(g9);
+   const int64_t f1g0    = f1   * static_cast<int64_t>(g0);
+   const int64_t f1g1_2  = f1_2 * static_cast<int64_t>(g1);
+   const int64_t f1g2    = f1   * static_cast<int64_t>(g2);
+   const int64_t f1g3_2  = f1_2 * static_cast<int64_t>(g3);
+   const int64_t f1g4    = f1   * static_cast<int64_t>(g4);
+   const int64_t f1g5_2  = f1_2 * static_cast<int64_t>(g5);
+   const int64_t f1g6    = f1   * static_cast<int64_t>(g6);
+   const int64_t f1g7_2  = f1_2 * static_cast<int64_t>(g7);
+   const int64_t f1g8    = f1   * static_cast<int64_t>(g8);
+   const int64_t f1g9_38 = f1_2 * static_cast<int64_t>(g9_19);
+   const int64_t f2g0    = f2   * static_cast<int64_t>(g0);
+   const int64_t f2g1    = f2   * static_cast<int64_t>(g1);
+   const int64_t f2g2    = f2   * static_cast<int64_t>(g2);
+   const int64_t f2g3    = f2   * static_cast<int64_t>(g3);
+   const int64_t f2g4    = f2   * static_cast<int64_t>(g4);
+   const int64_t f2g5    = f2   * static_cast<int64_t>(g5);
+   const int64_t f2g6    = f2   * static_cast<int64_t>(g6);
+   const int64_t f2g7    = f2   * static_cast<int64_t>(g7);
+   const int64_t f2g8_19 = f2   * static_cast<int64_t>(g8_19);
+   const int64_t f2g9_19 = f2   * static_cast<int64_t>(g9_19);
+   const int64_t f3g0    = f3   * static_cast<int64_t>(g0);
+   const int64_t f3g1_2  = f3_2 * static_cast<int64_t>(g1);
+   const int64_t f3g2    = f3   * static_cast<int64_t>(g2);
+   const int64_t f3g3_2  = f3_2 * static_cast<int64_t>(g3);
+   const int64_t f3g4    = f3   * static_cast<int64_t>(g4);
+   const int64_t f3g5_2  = f3_2 * static_cast<int64_t>(g5);
+   const int64_t f3g6    = f3   * static_cast<int64_t>(g6);
+   const int64_t f3g7_38 = f3_2 * static_cast<int64_t>(g7_19);
+   const int64_t f3g8_19 = f3   * static_cast<int64_t>(g8_19);
+   const int64_t f3g9_38 = f3_2 * static_cast<int64_t>(g9_19);
+   const int64_t f4g0    = f4   * static_cast<int64_t>(g0);
+   const int64_t f4g1    = f4   * static_cast<int64_t>(g1);
+   const int64_t f4g2    = f4   * static_cast<int64_t>(g2);
+   const int64_t f4g3    = f4   * static_cast<int64_t>(g3);
+   const int64_t f4g4    = f4   * static_cast<int64_t>(g4);
+   const int64_t f4g5    = f4   * static_cast<int64_t>(g5);
+   const int64_t f4g6_19 = f4   * static_cast<int64_t>(g6_19);
+   const int64_t f4g7_19 = f4   * static_cast<int64_t>(g7_19);
+   const int64_t f4g8_19 = f4   * static_cast<int64_t>(g8_19);
+   const int64_t f4g9_19 = f4   * static_cast<int64_t>(g9_19);
+   const int64_t f5g0    = f5   * static_cast<int64_t>(g0);
+   const int64_t f5g1_2  = f5_2 * static_cast<int64_t>(g1);
+   const int64_t f5g2    = f5   * static_cast<int64_t>(g2);
+   const int64_t f5g3_2  = f5_2 * static_cast<int64_t>(g3);
+   const int64_t f5g4    = f5   * static_cast<int64_t>(g4);
+   const int64_t f5g5_38 = f5_2 * static_cast<int64_t>(g5_19);
+   const int64_t f5g6_19 = f5   * static_cast<int64_t>(g6_19);
+   const int64_t f5g7_38 = f5_2 * static_cast<int64_t>(g7_19);
+   const int64_t f5g8_19 = f5   * static_cast<int64_t>(g8_19);
+   const int64_t f5g9_38 = f5_2 * static_cast<int64_t>(g9_19);
+   const int64_t f6g0    = f6   * static_cast<int64_t>(g0);
+   const int64_t f6g1    = f6   * static_cast<int64_t>(g1);
+   const int64_t f6g2    = f6   * static_cast<int64_t>(g2);
+   const int64_t f6g3    = f6   * static_cast<int64_t>(g3);
+   const int64_t f6g4_19 = f6   * static_cast<int64_t>(g4_19);
+   const int64_t f6g5_19 = f6   * static_cast<int64_t>(g5_19);
+   const int64_t f6g6_19 = f6   * static_cast<int64_t>(g6_19);
+   const int64_t f6g7_19 = f6   * static_cast<int64_t>(g7_19);
+   const int64_t f6g8_19 = f6   * static_cast<int64_t>(g8_19);
+   const int64_t f6g9_19 = f6   * static_cast<int64_t>(g9_19);
+   const int64_t f7g0    = f7   * static_cast<int64_t>(g0);
+   const int64_t f7g1_2  = f7_2 * static_cast<int64_t>(g1);
+   const int64_t f7g2    = f7   * static_cast<int64_t>(g2);
+   const int64_t f7g3_38 = f7_2 * static_cast<int64_t>(g3_19);
+   const int64_t f7g4_19 = f7   * static_cast<int64_t>(g4_19);
+   const int64_t f7g5_38 = f7_2 * static_cast<int64_t>(g5_19);
+   const int64_t f7g6_19 = f7   * static_cast<int64_t>(g6_19);
+   const int64_t f7g7_38 = f7_2 * static_cast<int64_t>(g7_19);
+   const int64_t f7g8_19 = f7   * static_cast<int64_t>(g8_19);
+   const int64_t f7g9_38 = f7_2 * static_cast<int64_t>(g9_19);
+   const int64_t f8g0    = f8   * static_cast<int64_t>(g0);
+   const int64_t f8g1    = f8   * static_cast<int64_t>(g1);
+   const int64_t f8g2_19 = f8   * static_cast<int64_t>(g2_19);
+   const int64_t f8g3_19 = f8   * static_cast<int64_t>(g3_19);
+   const int64_t f8g4_19 = f8   * static_cast<int64_t>(g4_19);
+   const int64_t f8g5_19 = f8   * static_cast<int64_t>(g5_19);
+   const int64_t f8g6_19 = f8   * static_cast<int64_t>(g6_19);
+   const int64_t f8g7_19 = f8   * static_cast<int64_t>(g7_19);
+   const int64_t f8g8_19 = f8   * static_cast<int64_t>(g8_19);
+   const int64_t f8g9_19 = f8   * static_cast<int64_t>(g9_19);
+   const int64_t f9g0    = f9   * static_cast<int64_t>(g0);
+   const int64_t f9g1_38 = f9_2 * static_cast<int64_t>(g1_19);
+   const int64_t f9g2_19 = f9   * static_cast<int64_t>(g2_19);
+   const int64_t f9g3_38 = f9_2 * static_cast<int64_t>(g3_19);
+   const int64_t f9g4_19 = f9   * static_cast<int64_t>(g4_19);
+   const int64_t f9g5_38 = f9_2 * static_cast<int64_t>(g5_19);
+   const int64_t f9g6_19 = f9   * static_cast<int64_t>(g6_19);
+   const int64_t f9g7_38 = f9_2 * static_cast<int64_t>(g7_19);
+   const int64_t f9g8_19 = f9   * static_cast<int64_t>(g8_19);
+   const int64_t f9g9_38 = f9_2 * static_cast<int64_t>(g9_19);
+
+   int64_t h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
+   int64_t h1 = f0g1+f1g0   +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
+   int64_t h2 = f0g2+f1g1_2 +f2g0   +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
+   int64_t h3 = f0g3+f1g2   +f2g1   +f3g0   +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
+   int64_t h4 = f0g4+f1g3_2 +f2g2   +f3g1_2 +f4g0   +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
+   int64_t h5 = f0g5+f1g4   +f2g3   +f3g2   +f4g1   +f5g0   +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
+   int64_t h6 = f0g6+f1g5_2 +f2g4   +f3g3_2 +f4g2   +f5g1_2 +f6g0   +f7g9_38+f8g8_19+f9g7_38;
+   int64_t h7 = f0g7+f1g6   +f2g5   +f3g4   +f4g3   +f5g2   +f6g1   +f7g0   +f8g9_19+f9g8_19;
+   int64_t h8 = f0g8+f1g7_2 +f2g6   +f3g5_2 +f4g4   +f5g3_2 +f6g2   +f7g1_2 +f8g0   +f9g9_38;
+   int64_t h9 = f0g9+f1g8   +f2g7   +f3g6   +f4g5   +f5g4   +f6g3   +f7g2   +f8g1   +f9g0   ;
+
+   /*
+   |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
+   i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
+   |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
+   i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
+   */
+   carry<26>(h0, h1);
+   carry<26>(h4, h5);
+
+   /* |h0| <= 2^25 */
+   /* |h4| <= 2^25 */
+   /* |h1| <= 1.71*2^59 */
+   /* |h5| <= 1.71*2^59 */
+
+   carry<25>(h1, h2);
+   carry<25>(h5, h6);
+
+   /* |h1| <= 2^24; from now on fits into int32 */
+   /* |h5| <= 2^24; from now on fits into int32 */
+   /* |h2| <= 1.41*2^60 */
+   /* |h6| <= 1.41*2^60 */
+
+   carry<26>(h2, h3);
+   carry<26>(h6, h7);
+   /* |h2| <= 2^25; from now on fits into int32 unchanged */
+   /* |h6| <= 2^25; from now on fits into int32 unchanged */
+   /* |h3| <= 1.71*2^59 */
+   /* |h7| <= 1.71*2^59 */
+
+   carry<25>(h3, h4);
+   carry<25>(h7, h8);
+   /* |h3| <= 2^24; from now on fits into int32 unchanged */
+   /* |h7| <= 2^24; from now on fits into int32 unchanged */
+   /* |h4| <= 1.72*2^34 */
+   /* |h8| <= 1.41*2^60 */
+
+   carry<26>(h4, h5);
+   carry<26>(h8, h9);
+   /* |h4| <= 2^25; from now on fits into int32 unchanged */
+   /* |h8| <= 2^25; from now on fits into int32 unchanged */
+   /* |h5| <= 1.01*2^24 */
+   /* |h9| <= 1.71*2^59 */
+
+   carry<25, 19>(h9, h0);
+
+   /* |h9| <= 2^24; from now on fits into int32 unchanged */
+   /* |h0| <= 1.1*2^39 */
+
+   carry<26>(h0, h1);
+   /* |h0| <= 2^25; from now on fits into int32 unchanged */
+   /* |h1| <= 1.01*2^24 */
+
+   return FE_25519(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9);
+   }
+
+/*
+h = f * f
+Can overlap h with f.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+//static
+FE_25519 FE_25519::sqr_iter(const FE_25519& f, size_t iter)
+   {
+   int32_t f0 = f[0];
+   int32_t f1 = f[1];
+   int32_t f2 = f[2];
+   int32_t f3 = f[3];
+   int32_t f4 = f[4];
+   int32_t f5 = f[5];
+   int32_t f6 = f[6];
+   int32_t f7 = f[7];
+   int32_t f8 = f[8];
+   int32_t f9 = f[9];
+
+   for(size_t i = 0; i != iter; ++i)
+      {
+      const int32_t f0_2 = 2 * f0;
+      const int32_t f1_2 = 2 * f1;
+      const int32_t f2_2 = 2 * f2;
+      const int32_t f3_2 = 2 * f3;
+      const int32_t f4_2 = 2 * f4;
+      const int32_t f5_2 = 2 * f5;
+      const int32_t f6_2 = 2 * f6;
+      const int32_t f7_2 = 2 * f7;
+      const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+      const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+      const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+      const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+      const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+
+      const int64_t f0f0    = f0   * static_cast<int64_t>(f0);
+      const int64_t f0f1_2  = f0_2 * static_cast<int64_t>(f1);
+      const int64_t f0f2_2  = f0_2 * static_cast<int64_t>(f2);
+      const int64_t f0f3_2  = f0_2 * static_cast<int64_t>(f3);
+      const int64_t f0f4_2  = f0_2 * static_cast<int64_t>(f4);
+      const int64_t f0f5_2  = f0_2 * static_cast<int64_t>(f5);
+      const int64_t f0f6_2  = f0_2 * static_cast<int64_t>(f6);
+      const int64_t f0f7_2  = f0_2 * static_cast<int64_t>(f7);
+      const int64_t f0f8_2  = f0_2 * static_cast<int64_t>(f8);
+      const int64_t f0f9_2  = f0_2 * static_cast<int64_t>(f9);
+      const int64_t f1f1_2  = f1_2 * static_cast<int64_t>(f1);
+      const int64_t f1f2_2  = f1_2 * static_cast<int64_t>(f2);
+      const int64_t f1f3_4  = f1_2 * static_cast<int64_t>(f3_2);
+      const int64_t f1f4_2  = f1_2 * static_cast<int64_t>(f4);
+      const int64_t f1f5_4  = f1_2 * static_cast<int64_t>(f5_2);
+      const int64_t f1f6_2  = f1_2 * static_cast<int64_t>(f6);
+      const int64_t f1f7_4  = f1_2 * static_cast<int64_t>(f7_2);
+      const int64_t f1f8_2  = f1_2 * static_cast<int64_t>(f8);
+      const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
+      const int64_t f2f2    = f2   * static_cast<int64_t>(f2);
+      const int64_t f2f3_2  = f2_2 * static_cast<int64_t>(f3);
+      const int64_t f2f4_2  = f2_2 * static_cast<int64_t>(f4);
+      const int64_t f2f5_2  = f2_2 * static_cast<int64_t>(f5);
+      const int64_t f2f6_2  = f2_2 * static_cast<int64_t>(f6);
+      const int64_t f2f7_2  = f2_2 * static_cast<int64_t>(f7);
+      const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
+      const int64_t f2f9_38 = f2   * static_cast<int64_t>(f9_38);
+      const int64_t f3f3_2  = f3_2 * static_cast<int64_t>(f3);
+      const int64_t f3f4_2  = f3_2 * static_cast<int64_t>(f4);
+      const int64_t f3f5_4  = f3_2 * static_cast<int64_t>(f5_2);
+      const int64_t f3f6_2  = f3_2 * static_cast<int64_t>(f6);
+      const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
+      const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
+      const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
+      const int64_t f4f4    = f4   * static_cast<int64_t>(f4);
+      const int64_t f4f5_2  = f4_2 * static_cast<int64_t>(f5);
+      const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
+      const int64_t f4f7_38 = f4   * static_cast<int64_t>(f7_38);
+      const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
+      const int64_t f4f9_38 = f4   * static_cast<int64_t>(f9_38);
+      const int64_t f5f5_38 = f5   * static_cast<int64_t>(f5_38);
+      const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
+      const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
+      const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
+      const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
+      const int64_t f6f6_19 = f6   * static_cast<int64_t>(f6_19);
+      const int64_t f6f7_38 = f6   * static_cast<int64_t>(f7_38);
+      const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
+      const int64_t f6f9_38 = f6   * static_cast<int64_t>(f9_38);
+      const int64_t f7f7_38 = f7   * static_cast<int64_t>(f7_38);
+      const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
+      const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
+      const int64_t f8f8_19 = f8   * static_cast<int64_t>(f8_19);
+      const int64_t f8f9_38 = f8   * static_cast<int64_t>(f9_38);
+      const int64_t f9f9_38 = f9   * static_cast<int64_t>(f9_38);
+
+      int64_t h0 = f0f0  +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+      int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+      int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+      int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+      int64_t h4 = f0f4_2+f1f3_4 +f2f2   +f5f9_76+f6f8_38+f7f7_38;
+      int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+      int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+      int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+      int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4   +f9f9_38;
+      int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+
+      carry<26>(h0, h1);
+      carry<26>(h4, h5);
+      carry<25>(h1, h2);
+      carry<25>(h5, h6);
+      carry<26>(h2, h3);
+      carry<26>(h6, h7);
+
+      carry<25>(h3, h4);
+      carry<25>(h7, h8);
+
+      carry<26>(h4, h5);
+      carry<26>(h8, h9);
+      carry<25,19>(h9, h0);
+      carry<26>(h0, h1);
+
+      f0 = static_cast<int32_t>(h0);
+      f1 = static_cast<int32_t>(h1);
+      f2 = static_cast<int32_t>(h2);
+      f3 = static_cast<int32_t>(h3);
+      f4 = static_cast<int32_t>(h4);
+      f5 = static_cast<int32_t>(h5);
+      f6 = static_cast<int32_t>(h6);
+      f7 = static_cast<int32_t>(h7);
+      f8 = static_cast<int32_t>(h8);
+      f9 = static_cast<int32_t>(h9);
+      }
+
+   return FE_25519(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+   }
+
+/*
+h = 2 * f * f
+Can overlap h with f.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+//static
+FE_25519 FE_25519::sqr2(const FE_25519& f)
+   {
+   const int32_t f0 = f[0];
+   const int32_t f1 = f[1];
+   const int32_t f2 = f[2];
+   const int32_t f3 = f[3];
+   const int32_t f4 = f[4];
+   const int32_t f5 = f[5];
+   const int32_t f6 = f[6];
+   const int32_t f7 = f[7];
+   const int32_t f8 = f[8];
+   const int32_t f9 = f[9];
+   const int32_t f0_2 = 2 * f0;
+   const int32_t f1_2 = 2 * f1;
+   const int32_t f2_2 = 2 * f2;
+   const int32_t f3_2 = 2 * f3;
+   const int32_t f4_2 = 2 * f4;
+   const int32_t f5_2 = 2 * f5;
+   const int32_t f6_2 = 2 * f6;
+   const int32_t f7_2 = 2 * f7;
+   const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+   const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+   const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+   const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+   const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+   const int64_t f0f0    = f0   * static_cast<int64_t>(f0);
+   const int64_t f0f1_2  = f0_2 * static_cast<int64_t>(f1);
+   const int64_t f0f2_2  = f0_2 * static_cast<int64_t>(f2);
+   const int64_t f0f3_2  = f0_2 * static_cast<int64_t>(f3);
+   const int64_t f0f4_2  = f0_2 * static_cast<int64_t>(f4);
+   const int64_t f0f5_2  = f0_2 * static_cast<int64_t>(f5);
+   const int64_t f0f6_2  = f0_2 * static_cast<int64_t>(f6);
+   const int64_t f0f7_2  = f0_2 * static_cast<int64_t>(f7);
+   const int64_t f0f8_2  = f0_2 * static_cast<int64_t>(f8);
+   const int64_t f0f9_2  = f0_2 * static_cast<int64_t>(f9);
+   const int64_t f1f1_2  = f1_2 * static_cast<int64_t>(f1);
+   const int64_t f1f2_2  = f1_2 * static_cast<int64_t>(f2);
+   const int64_t f1f3_4  = f1_2 * static_cast<int64_t>(f3_2);
+   const int64_t f1f4_2  = f1_2 * static_cast<int64_t>(f4);
+   const int64_t f1f5_4  = f1_2 * static_cast<int64_t>(f5_2);
+   const int64_t f1f6_2  = f1_2 * static_cast<int64_t>(f6);
+   const int64_t f1f7_4  = f1_2 * static_cast<int64_t>(f7_2);
+   const int64_t f1f8_2  = f1_2 * static_cast<int64_t>(f8);
+   const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
+   const int64_t f2f2    = f2   * static_cast<int64_t>(f2);
+   const int64_t f2f3_2  = f2_2 * static_cast<int64_t>(f3);
+   const int64_t f2f4_2  = f2_2 * static_cast<int64_t>(f4);
+   const int64_t f2f5_2  = f2_2 * static_cast<int64_t>(f5);
+   const int64_t f2f6_2  = f2_2 * static_cast<int64_t>(f6);
+   const int64_t f2f7_2  = f2_2 * static_cast<int64_t>(f7);
+   const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
+   const int64_t f2f9_38 = f2   * static_cast<int64_t>(f9_38);
+   const int64_t f3f3_2  = f3_2 * static_cast<int64_t>(f3);
+   const int64_t f3f4_2  = f3_2 * static_cast<int64_t>(f4);
+   const int64_t f3f5_4  = f3_2 * static_cast<int64_t>(f5_2);
+   const int64_t f3f6_2  = f3_2 * static_cast<int64_t>(f6);
+   const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
+   const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
+   const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
+   const int64_t f4f4    = f4   * static_cast<int64_t>(f4);
+   const int64_t f4f5_2  = f4_2 * static_cast<int64_t>(f5);
+   const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
+   const int64_t f4f7_38 = f4   * static_cast<int64_t>(f7_38);
+   const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
+   const int64_t f4f9_38 = f4   * static_cast<int64_t>(f9_38);
+   const int64_t f5f5_38 = f5   * static_cast<int64_t>(f5_38);
+   const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
+   const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
+   const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
+   const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
+   const int64_t f6f6_19 = f6   * static_cast<int64_t>(f6_19);
+   const int64_t f6f7_38 = f6   * static_cast<int64_t>(f7_38);
+   const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
+   const int64_t f6f9_38 = f6   * static_cast<int64_t>(f9_38);
+   const int64_t f7f7_38 = f7   * static_cast<int64_t>(f7_38);
+   const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
+   const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
+   const int64_t f8f8_19 = f8   * static_cast<int64_t>(f8_19);
+   const int64_t f8f9_38 = f8   * static_cast<int64_t>(f9_38);
+   const int64_t f9f9_38 = f9   * static_cast<int64_t>(f9_38);
+
+   int64_t h0 = f0f0  +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+   int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+   int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+   int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+   int64_t h4 = f0f4_2+f1f3_4 +f2f2   +f5f9_76+f6f8_38+f7f7_38;
+   int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+   int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+   int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+   int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4   +f9f9_38;
+   int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+
+   h0 += h0;
+   h1 += h1;
+   h2 += h2;
+   h3 += h3;
+   h4 += h4;
+   h5 += h5;
+   h6 += h6;
+   h7 += h7;
+   h8 += h8;
+   h9 += h9;
+
+   carry<26>(h0, h1);
+   carry<26>(h4, h5);
+
+   carry<25>(h1, h2);
+   carry<25>(h5, h6);
+
+   carry<26>(h2, h3);
+   carry<26>(h6, h7);
+
+   carry<25>(h3, h4);
+   carry<25>(h7, h8);
+   carry<26>(h4, h5);
+   carry<26>(h8, h9);
+   carry<25,19>(h9, h0);
+   carry<26>(h0, h1);
+
+   return FE_25519(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9);
+   }
+
+/*
+Ignores top bit of h.
+*/
+
+void FE_25519::from_bytes(const uint8_t s[32])
+   {
+   int64_t h0 = load_4(s);
+   int64_t h1 = load_3(s + 4) << 6;
+   int64_t h2 = load_3(s + 7) << 5;
+   int64_t h3 = load_3(s + 10) << 3;
+   int64_t h4 = load_3(s + 13) << 2;
+   int64_t h5 = load_4(s + 16);
+   int64_t h6 = load_3(s + 20) << 7;
+   int64_t h7 = load_3(s + 23) << 5;
+   int64_t h8 = load_3(s + 26) << 4;
+   int64_t h9 = (load_3(s + 29) & 0x7fffff) << 2;
+
+   carry<25,19>(h9, h0);
+   carry<25>(h1, h2);
+   carry<25>(h3, h4);
+   carry<25>(h5, h6);
+   carry<25>(h7, h8);
+
+   carry<26>(h0, h1);
+   carry<26>(h2, h3);
+   carry<26>(h4, h5);
+   carry<26>(h6, h7);
+   carry<26>(h8, h9);
+
+   m_fe[0] = static_cast<int32_t>(h0);
+   m_fe[1] = static_cast<int32_t>(h1);
+   m_fe[2] = static_cast<int32_t>(h2);
+   m_fe[3] = static_cast<int32_t>(h3);
+   m_fe[4] = static_cast<int32_t>(h4);
+   m_fe[5] = static_cast<int32_t>(h5);
+   m_fe[6] = static_cast<int32_t>(h6);
+   m_fe[7] = static_cast<int32_t>(h7);
+   m_fe[8] = static_cast<int32_t>(h8);
+   m_fe[9] = static_cast<int32_t>(h9);
+   }
+
+/*
+Preconditions:
+|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+
+Write p=2^255-19; q=floor(h/p).
+Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+
+Proof:
+Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
+
+Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+Then 0<y<1.
+
+Write r=h-pq.
+Have 0<=r<=p-1=2^255-20.
+Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+
+Write x=r+19(2^-255)r+y.
+Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+
+Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+*/
+
+void FE_25519::to_bytes(uint8_t s[32]) const
+   {
+   const int64_t X25 = (1 << 25);
+
+   int32_t h0 = m_fe[0];
+   int32_t h1 = m_fe[1];
+   int32_t h2 = m_fe[2];
+   int32_t h3 = m_fe[3];
+   int32_t h4 = m_fe[4];
+   int32_t h5 = m_fe[5];
+   int32_t h6 = m_fe[6];
+   int32_t h7 = m_fe[7];
+   int32_t h8 = m_fe[8];
+   int32_t h9 = m_fe[9];
+   int32_t q;
+
+   q = (19 * h9 + ((static_cast<int32_t>(1) << 24))) >> 25;
+   q = (h0 + q) >> 26;
+   q = (h1 + q) >> 25;
+   q = (h2 + q) >> 26;
+   q = (h3 + q) >> 25;
+   q = (h4 + q) >> 26;
+   q = (h5 + q) >> 25;
+   q = (h6 + q) >> 26;
+   q = (h7 + q) >> 25;
+   q = (h8 + q) >> 26;
+   q = (h9 + q) >> 25;
+
+   /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
+   h0 += 19 * q;
+   /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
+
+   carry0<26>(h0, h1);
+   carry0<25>(h1, h2);
+   carry0<26>(h2, h3);
+   carry0<25>(h3, h4);
+   carry0<26>(h4, h5);
+   carry0<25>(h5, h6);
+   carry0<26>(h6, h7);
+   carry0<25>(h7, h8);
+   carry0<26>(h8, h9);
+
+   int32_t carry9 = h9 >> 25;
+   h9 -= carry9 * X25;
+   /* h10 = carry9 */
+
+   /*
+   Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+   Have h0+...+2^230 h9 between 0 and 2^255-1;
+   evidently 2^255 h10-2^255 q = 0.
+   Goal: Output h0+...+2^230 h9.
+   */
+
+   s[0] = static_cast<uint8_t>(h0 >> 0);
+   s[1] = static_cast<uint8_t>(h0 >> 8);
+   s[2] = static_cast<uint8_t>(h0 >> 16);
+   s[3] = static_cast<uint8_t>((h0 >> 24) | (h1 << 2));
+   s[4] = static_cast<uint8_t>(h1 >> 6);
+   s[5] = static_cast<uint8_t>(h1 >> 14);
+   s[6] = static_cast<uint8_t>((h1 >> 22) | (h2 << 3));
+   s[7] = static_cast<uint8_t>(h2 >> 5);
+   s[8] = static_cast<uint8_t>(h2 >> 13);
+   s[9] = static_cast<uint8_t>((h2 >> 21) | (h3 << 5));
+   s[10] = static_cast<uint8_t>(h3 >> 3);
+   s[11] = static_cast<uint8_t>(h3 >> 11);
+   s[12] = static_cast<uint8_t>((h3 >> 19) | (h4 << 6));
+   s[13] = static_cast<uint8_t>(h4 >> 2);
+   s[14] = static_cast<uint8_t>(h4 >> 10);
+   s[15] = static_cast<uint8_t>(h4 >> 18);
+   s[16] = static_cast<uint8_t>(h5 >> 0);
+   s[17] = static_cast<uint8_t>(h5 >> 8);
+   s[18] = static_cast<uint8_t>(h5 >> 16);
+   s[19] = static_cast<uint8_t>((h5 >> 24) | (h6 << 1));
+   s[20] = static_cast<uint8_t>(h6 >> 7);
+   s[21] = static_cast<uint8_t>(h6 >> 15);
+   s[22] = static_cast<uint8_t>((h6 >> 23) | (h7 << 3));
+   s[23] = static_cast<uint8_t>(h7 >> 5);
+   s[24] = static_cast<uint8_t>(h7 >> 13);
+   s[25] = static_cast<uint8_t>((h7 >> 21) | (h8 << 4));
+   s[26] = static_cast<uint8_t>(h8 >> 4);
+   s[27] = static_cast<uint8_t>(h8 >> 12);
+   s[28] = static_cast<uint8_t>((h8 >> 20) | (h9 << 6));
+   s[29] = static_cast<uint8_t>(h9 >> 2);
+   s[30] = static_cast<uint8_t>(h9 >> 10);
+   s[31] = static_cast<uint8_t>(h9 >> 18);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h b/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h
@@ -0,0 +1,227 @@
+/*
+* Ed25519 field element
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_FE_H_
+#define BOTAN_ED25519_FE_H_
+
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/**
+* An element of the field \\Z/(2^255-19)
+*/
+class FE_25519
+   {
+   public:
+      ~FE_25519() { secure_scrub_memory(m_fe, sizeof(m_fe)); }
+
+      /**
+      * Zero element
+      */
+      FE_25519(int init = 0)
+         {
+         if(init != 0 && init != 1)
+            throw Invalid_Argument("Invalid FE_25519 initial value");
+         clear_mem(m_fe, 10);
+         m_fe[0] = init;
+         }
+
+      FE_25519(std::initializer_list<int32_t> x)
+         {
+         if(x.size() != 10)
+            throw Invalid_Argument("Invalid FE_25519 initializer list");
+         copy_mem(m_fe, x.begin(), 10);
+         }
+
+      FE_25519(int64_t h0, int64_t h1, int64_t h2, int64_t h3, int64_t h4,
+               int64_t h5, int64_t h6, int64_t h7, int64_t h8, int64_t h9)
+         {
+         m_fe[0] = static_cast<int32_t>(h0);
+         m_fe[1] = static_cast<int32_t>(h1);
+         m_fe[2] = static_cast<int32_t>(h2);
+         m_fe[3] = static_cast<int32_t>(h3);
+         m_fe[4] = static_cast<int32_t>(h4);
+         m_fe[5] = static_cast<int32_t>(h5);
+         m_fe[6] = static_cast<int32_t>(h6);
+         m_fe[7] = static_cast<int32_t>(h7);
+         m_fe[8] = static_cast<int32_t>(h8);
+         m_fe[9] = static_cast<int32_t>(h9);
+         }
+
+      FE_25519(const FE_25519& other) = default;
+      FE_25519& operator=(const FE_25519& other) = default;
+
+      FE_25519(FE_25519&& other) = default;
+      FE_25519& operator=(FE_25519&& other) = default;
+
+      void from_bytes(const uint8_t b[32]);
+      void to_bytes(uint8_t b[32]) const;
+
+      bool is_zero() const
+         {
+         uint8_t s[32];
+         to_bytes(s);
+
+         uint8_t sum = 0;
+         for(size_t i = 0; i != 32; ++i)
+            { sum |= s[i]; }
+
+         // TODO avoid ternary here
+         return (sum == 0) ? 1 : 0;
+         }
+
+      /*
+      return 1 if f is in {1,3,5,...,q-2}
+      return 0 if f is in {0,2,4,...,q-1}
+      */
+      bool is_negative() const
+         {
+         // TODO could avoid most of the to_bytes computation here
+         uint8_t s[32];
+         to_bytes(s);
+         return s[0] & 1;
+         }
+
+      static FE_25519 add(const FE_25519& a, const FE_25519& b)
+         {
+         FE_25519 z;
+         for(size_t i = 0; i != 10; ++i)
+            { z[i] = a[i] + b[i]; }
+         return z;
+         }
+
+      static FE_25519 sub(const FE_25519& a, const FE_25519& b)
+         {
+         FE_25519 z;
+         for(size_t i = 0; i != 10; ++i)
+            { z[i] = a[i] - b[i]; }
+         return z;
+         }
+
+      static FE_25519 negate(const FE_25519& a)
+         {
+         FE_25519 z;
+         for(size_t i = 0; i != 10; ++i)
+            { z[i] = -a[i]; }
+         return z;
+         }
+
+      static FE_25519 mul(const FE_25519& a, const FE_25519& b);
+      static FE_25519 sqr_iter(const FE_25519& a, size_t iter);
+      static FE_25519 sqr(const FE_25519& a) { return sqr_iter(a, 1); }
+      static FE_25519 sqr2(const FE_25519& a);
+      static FE_25519 pow_22523(const FE_25519& a);
+      static FE_25519 invert(const FE_25519& a);
+
+      // TODO remove
+      int32_t operator[](size_t i) const { return m_fe[i]; }
+      int32_t& operator[](size_t i) { return m_fe[i]; }
+
+   private:
+
+      int32_t m_fe[10];
+   };
+
+typedef FE_25519 fe;
+
+/*
+fe means field element.
+Here the field is
+An element t, entries t[0]...t[9], represents the integer
+t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
+Bounds on each t[i] vary depending on context.
+*/
+
+inline void fe_frombytes(fe& x, const uint8_t* b)
+   {
+   x.from_bytes(b);
+   }
+
+inline void fe_tobytes(uint8_t* b, const fe& x)
+   {
+   x.to_bytes(b);
+   }
+
+inline void fe_copy(fe& a, const fe& b)
+   {
+   a = b;
+   }
+
+inline int fe_isnonzero(const fe& x)
+   {
+   return x.is_zero() ? 0 : 1;
+   }
+
+inline int fe_isnegative(const fe& x)
+   {
+   return x.is_negative();
+   }
+
+
+inline void fe_0(fe& x)
+   {
+   x = FE_25519();
+   }
+
+inline void fe_1(fe& x)
+   {
+   x = FE_25519(1);
+   }
+
+inline void fe_add(fe& x, const fe& a, const fe& b)
+   {
+   x = FE_25519::add(a, b);
+   }
+
+inline void fe_sub(fe& x, const fe& a, const fe& b)
+   {
+   x = FE_25519::sub(a, b);
+   }
+
+inline void fe_neg(fe& x, const fe& z)
+   {
+   x = FE_25519::negate(z);
+   }
+
+inline void fe_mul(fe& x, const fe& a, const fe& b)
+   {
+   x = FE_25519::mul(a, b);
+   }
+
+inline void fe_sq(fe& x, const fe& z)
+   {
+   x = FE_25519::sqr(z);
+   }
+
+inline void fe_sq_iter(fe& x, const fe& z, size_t iter)
+   {
+   x = FE_25519::sqr_iter(z, iter);
+   }
+
+inline void fe_sq2(fe& x, const fe& z)
+   {
+   x = FE_25519::sqr2(z);
+   }
+
+inline void fe_invert(fe& x, const fe& z)
+   {
+   x = FE_25519::invert(z);
+   }
+
+inline void fe_pow22523(fe& x, const fe& y)
+   {
+   x = FE_25519::pow_22523(y);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h b/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h
@@ -0,0 +1,119 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_INT_H_
+#define BOTAN_ED25519_INT_H_
+
+#include <botan/internal/ed25519_fe.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+inline uint64_t load_3(const uint8_t in[3])
+   {
+   return static_cast<uint64_t>(in[0]) |
+      (static_cast<uint64_t>(in[1]) << 8) |
+      (static_cast<uint64_t>(in[2]) << 16);
+   }
+
+inline uint64_t load_4(const uint8_t* in)
+   {
+   return load_le<uint32_t>(in, 0);
+   }
+
+template<size_t S, int64_t MUL=1>
+inline void carry(int64_t& h0, int64_t& h1)
+   {
+   static_assert(S > 0 && S < 64, "Shift in range");
+
+   const int64_t X1 = (static_cast<int64_t>(1) << S);
+   const int64_t X2 = (static_cast<int64_t>(1) << (S - 1));
+   int64_t c = (h0 + X2)  >> S;
+   h1 += c * MUL;
+   h0 -= c * X1;
+   }
+
+template<size_t S>
+inline void carry0(int64_t& h0, int64_t& h1)
+   {
+   static_assert(S > 0 && S < 64, "Shift in range");
+
+   const int64_t X1 = (static_cast<int64_t>(1) << S);
+   int64_t c = h0 >> S;
+   h1 += c;
+   h0 -= c * X1;
+   }
+
+template<size_t S>
+inline void carry0(int32_t& h0, int32_t& h1)
+   {
+   static_assert(S > 0 && S < 32, "Shift in range");
+
+   const int32_t X1 = (static_cast<int64_t>(1) << S);
+   int32_t c = h0 >> S;
+   h1 += c;
+   h0 -= c * X1;
+   }
+
+inline void redc_mul(int64_t& s1,
+                     int64_t& s2,
+                     int64_t& s3,
+                     int64_t& s4,
+                     int64_t& s5,
+                     int64_t& s6,
+                     int64_t& X)
+   {
+   s1 += X * 666643;
+   s2 += X * 470296;
+   s3 += X * 654183;
+   s4 -= X * 997805;
+   s5 += X * 136657;
+   s6 -= X * 683901;
+   X = 0;
+   }
+
+/*
+ge means group element.
+
+Here the group is the set of pairs (x,y) of field elements (see fe.h)
+satisfying -x^2 + y^2 = 1 + d x^2y^2
+where d = -121665/121666.
+
+Representations:
+  ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+*/
+
+typedef struct
+   {
+   fe X;
+   fe Y;
+   fe Z;
+   fe T;
+   } ge_p3;
+
+int ge_frombytes_negate_vartime(ge_p3*, const uint8_t*);
+void ge_scalarmult_base(uint8_t out[32], const uint8_t in[32]);
+
+void ge_double_scalarmult_vartime(uint8_t out[32],
+                                  const uint8_t a[],
+                                  const ge_p3* A,
+                                  const uint8_t b[]);
+
+/*
+The set of scalars is \Z/l
+where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_reduce(uint8_t*);
+void sc_muladd(uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp b/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -0,0 +1,288 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ed25519.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/hash.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+AlgorithmIdentifier Ed25519_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   }
+
+bool Ed25519_PublicKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   return true; // no tests possible?
+   // TODO could check cofactor
+   }
+
+Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len)
+   {
+   if(pub_len != 32)
+      throw Decoding_Error("Invalid length for Ed25519 key");
+   m_public.assign(pub_key, pub_key + pub_len);
+   }
+
+Ed25519_PublicKey::Ed25519_PublicKey(const AlgorithmIdentifier&,
+                                     const std::vector<uint8_t>& key_bits)
+   {
+   m_public = key_bits;
+
+   if(m_public.size() != 32)
+      throw Decoding_Error("Invalid size for Ed25519 public key");
+   }
+
+std::vector<uint8_t> Ed25519_PublicKey::public_key_bits() const
+   {
+   return m_public;
+   }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(const secure_vector<uint8_t>& secret_key)
+   {
+   if(secret_key.size() == 64)
+      {
+      m_private = secret_key;
+      m_public.assign(m_private.begin() + 32, m_private.end());
+      }
+   else if(secret_key.size() == 32)
+      {
+      m_public.resize(32);
+      m_private.resize(64);
+      ed25519_gen_keypair(m_public.data(), m_private.data(), secret_key.data());
+      }
+   else
+      throw Decoding_Error("Invalid size for Ed25519 private key");
+   }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(RandomNumberGenerator& rng)
+   {
+   const secure_vector<uint8_t> seed = rng.random_vec(32);
+   m_public.resize(32);
+   m_private.resize(64);
+   ed25519_gen_keypair(m_public.data(), m_private.data(), seed.data());
+   }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier&,
+                                       const secure_vector<uint8_t>& key_bits)
+   {
+   secure_vector<uint8_t> bits;
+   BER_Decoder(key_bits).decode(bits, OCTET_STRING).discard_remaining();
+
+   if(bits.size() != 32)
+      throw Decoding_Error("Invalid size for Ed25519 private key");
+   m_public.resize(32);
+   m_private.resize(64);
+   ed25519_gen_keypair(m_public.data(), m_private.data(), bits.data());
+   }
+
+secure_vector<uint8_t> Ed25519_PrivateKey::private_key_bits() const
+   {
+   secure_vector<uint8_t> bits(&m_private[0], &m_private[32]);
+   return DER_Encoder().encode(bits, OCTET_STRING).get_contents();
+   }
+
+bool Ed25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   return true; // ???
+   }
+
+namespace {
+
+/**
+* Ed25519 verifying operation
+*/
+class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification
+   {
+   public:
+      Ed25519_Pure_Verify_Operation(const Ed25519_PublicKey& key) : m_key(key)
+         {
+         }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         m_msg.insert(m_msg.end(), msg, msg + msg_len);
+         }
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+         {
+         if(sig_len != 64)
+            return false;
+
+         const std::vector<uint8_t>& pub_key = m_key.get_public_key();
+         BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
+         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig, pub_key.data(), nullptr, 0);
+         m_msg.clear();
+         return ok;
+         }
+
+   private:
+      std::vector<uint8_t> m_msg;
+      const Ed25519_PublicKey& m_key;
+   };
+
+/**
+* Ed25519 verifying operation with pre-hash
+*/
+class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification
+   {
+   public:
+      Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, const std::string& hash, bool rfc8032) :
+         m_key(key)
+         {
+         m_hash = HashFunction::create_or_throw(hash);
+
+         if(rfc8032)
+            {
+            m_domain_sep = {
+               0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64,
+               0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+               0x01, 0x00 };
+            }
+         }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         m_hash->update(msg, msg_len);
+         }
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+         {
+         if(sig_len != 64)
+            return false;
+         std::vector<uint8_t> msg_hash(m_hash->output_length());
+         m_hash->final(msg_hash.data());
+
+         const std::vector<uint8_t>& pub_key = m_key.get_public_key();
+         BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
+         return ed25519_verify(msg_hash.data(), msg_hash.size(), sig, pub_key.data(), m_domain_sep.data(), m_domain_sep.size());
+         }
+
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+      const Ed25519_PublicKey& m_key;
+      std::vector<uint8_t> m_domain_sep;
+   };
+
+/**
+* Ed25519 signing operation ('pure' - signs message directly)
+*/
+class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature
+   {
+   public:
+      Ed25519_Pure_Sign_Operation(const Ed25519_PrivateKey& key) : m_key(key)
+         {
+         }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         m_msg.insert(m_msg.end(), msg, msg + msg_len);
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+         {
+         secure_vector<uint8_t> sig(64);
+         ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.get_private_key().data(), nullptr, 0);
+         m_msg.clear();
+         return sig;
+         }
+
+      size_t signature_length() const override { return 64; }
+
+   private:
+      std::vector<uint8_t> m_msg;
+      const Ed25519_PrivateKey& m_key;
+   };
+
+/**
+* Ed25519 signing operation with pre-hash
+*/
+class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature
+   {
+   public:
+      Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, const std::string& hash, bool rfc8032) :
+         m_key(key)
+         {
+         m_hash = HashFunction::create_or_throw(hash);
+
+         if(rfc8032)
+            {
+            m_domain_sep = std::vector<uint8_t>{
+               0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64,
+               0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+               0x01, 0x00 };
+            }
+         }
+
+      size_t signature_length() const override { return 64; }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         m_hash->update(msg, msg_len);
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+         {
+         secure_vector<uint8_t> sig(64);
+         std::vector<uint8_t> msg_hash(m_hash->output_length());
+         m_hash->final(msg_hash.data());
+         ed25519_sign(sig.data(),
+                      msg_hash.data(), msg_hash.size(),
+                      m_key.get_private_key().data(),
+                      m_domain_sep.data(), m_domain_sep.size());
+         return sig;
+         }
+
+   private:
+      std::unique_ptr<HashFunction> m_hash;
+      const Ed25519_PrivateKey& m_key;
+      std::vector<uint8_t> m_domain_sep;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+Ed25519_PublicKey::create_verification_op(const std::string& params,
+                                          const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      if(params == "" || params == "Identity" || params == "Pure")
+         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Pure_Verify_Operation(*this));
+      else if(params == "Ed25519ph")
+         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, "SHA-512", true));
+      else
+         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, params, false));
+      }
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator&,
+                                        const std::string& params,
+                                        const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      if(params == "" || params == "Identity" || params == "Pure")
+         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Pure_Sign_Operation(*this));
+      else if(params == "Ed25519ph")
+         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, "SHA-512", true));
+      else
+         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, params, false));
+      }
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ed25519/ge.cpp b/third_party/botan/src/lib/pubkey/ed25519/ge.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/ge.cpp
@@ -0,0 +1,2174 @@
+/*
+* Ed25519 group operations
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+#include <assert.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+Representations:
+  ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+  ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+  ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+  ge_precomp (Duif): (y+x,y-x,2dxy)
+*/
+typedef struct
+   {
+   fe X;
+   fe Y;
+   fe Z;
+   } ge_p2;
+
+typedef struct
+   {
+   fe X;
+   fe Y;
+   fe Z;
+   fe T;
+   } ge_p1p1;
+
+typedef struct
+   {
+   fe yplusx;
+   fe yminusx;
+   fe xy2d;
+   } ge_precomp;
+
+typedef struct
+   {
+   fe YplusX;
+   fe YminusX;
+   fe Z;
+   fe T2d;
+   } ge_cached;
+
+/*
+r = p + q
+*/
+
+void ge_add(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
+   {
+   fe t0;
+   /* qhasm: YpX1 = Y1+X1 */
+   /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+   fe_add(r->X, p->Y, p->X);
+
+   /* qhasm: YmX1 = Y1-X1 */
+   /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+   fe_sub(r->Y, p->Y, p->X);
+
+   /* qhasm: A = YpX1*YpX2 */
+   /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */
+   /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YpX2=q->YplusX); */
+   fe_mul(r->Z, r->X, q->YplusX);
+
+   /* qhasm: B = YmX1*YmX2 */
+   /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */
+   /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YmX2=q->YminusX); */
+   fe_mul(r->Y, r->Y, q->YminusX);
+
+   /* qhasm: C = T2d2*T1 */
+   /* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
+   /* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */
+   fe_mul(r->T, q->T2d, p->T);
+
+   /* qhasm: ZZ = Z1*Z2 */
+   /* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
+   /* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */
+   fe_mul(r->X, p->Z, q->Z);
+
+   /* qhasm: D = 2*ZZ */
+   /* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
+   /* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */
+   fe_add(t0, r->X, r->X);
+
+   /* qhasm: X3 = A-B */
+   /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+   fe_sub(r->X, r->Z, r->Y);
+
+   /* qhasm: Y3 = A+B */
+   /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+   fe_add(r->Y, r->Z, r->Y);
+
+   /* qhasm: Z3 = D+C */
+   /* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */
+   fe_add(r->Z, t0, r->T);
+
+   /* qhasm: T3 = D-C */
+   /* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */
+   fe_sub(r->T, t0, r->T);
+   }
+
+/*
+r = p + q
+*/
+
+void ge_madd(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
+   {
+   fe t0;
+   /* qhasm: YpX1 = Y1+X1 */
+   fe_add(r->X, p->Y, p->X);
+
+   /* qhasm: YmX1 = Y1-X1 */
+   fe_sub(r->Y, p->Y, p->X);
+
+   /* qhasm: A = YpX1*ypx2 */
+   fe_mul(r->Z, r->X, q->yplusx);
+
+   /* qhasm: B = YmX1*ymx2 */
+   fe_mul(r->Y, r->Y, q->yminusx);
+
+   /* qhasm: C = xy2d2*T1 */
+   fe_mul(r->T, q->xy2d, p->T);
+
+   /* qhasm: D = 2*Z1 */
+   fe_add(t0, p->Z, p->Z);
+
+   /* qhasm: X3 = A-B */
+   fe_sub(r->X, r->Z, r->Y);
+
+   /* qhasm: Y3 = A+B */
+   fe_add(r->Y, r->Z, r->Y);
+
+   /* qhasm: Z3 = D+C */
+   fe_add(r->Z, t0, r->T);
+
+   /* qhasm: T3 = D-C */
+   fe_sub(r->T, t0, r->T);
+   }
+
+/*
+r = p - q
+*/
+
+void ge_msub(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
+   {
+   fe t0;
+
+   /* qhasm: YpX1 = Y1+X1 */
+   /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+   fe_add(r->X, p->Y, p->X);
+
+   /* qhasm: YmX1 = Y1-X1 */
+   /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+   fe_sub(r->Y, p->Y, p->X);
+
+   /* qhasm: A = YpX1*ymx2 */
+   /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */
+   /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ymx2=q->yminusx); */
+   fe_mul(r->Z, r->X, q->yminusx);
+
+   /* qhasm: B = YmX1*ypx2 */
+   /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */
+   /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ypx2=q->yplusx); */
+   fe_mul(r->Y, r->Y, q->yplusx);
+
+   /* qhasm: C = xy2d2*T1 */
+   /* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
+   /* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */
+   fe_mul(r->T, q->xy2d, p->T);
+
+   /* qhasm: D = 2*Z1 */
+   /* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
+   /* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */
+   fe_add(t0, p->Z, p->Z);
+
+   /* qhasm: X3 = A-B */
+   /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+   fe_sub(r->X, r->Z, r->Y);
+
+   /* qhasm: Y3 = A+B */
+   /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+   fe_add(r->Y, r->Z, r->Y);
+
+   /* qhasm: Z3 = D-C */
+   /* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */
+   fe_sub(r->Z, t0, r->T);
+
+   /* qhasm: T3 = D+C */
+   /* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */
+   fe_add(r->T, t0, r->T);
+
+   }
+
+/*
+r = p
+*/
+
+void ge_p1p1_to_p2(ge_p2* r, const ge_p1p1* p)
+   {
+   fe_mul(r->X, p->X, p->T);
+   fe_mul(r->Y, p->Y, p->Z);
+   fe_mul(r->Z, p->Z, p->T);
+   }
+
+/*
+r = p
+*/
+
+void ge_p1p1_to_p3(ge_p3* r, const ge_p1p1* p)
+   {
+   fe_mul(r->X, p->X, p->T);
+   fe_mul(r->Y, p->Y, p->Z);
+   fe_mul(r->Z, p->Z, p->T);
+   fe_mul(r->T, p->X, p->Y);
+   }
+
+/*
+r = 2 * p
+*/
+
+void ge_p2_dbl(ge_p1p1* r, const ge_p2* p)
+   {
+   fe t0;
+   /* qhasm: XX=X1^2 */
+   /* asm 1: fe_sq(>XX=fe#1,<X1=fe#11); */
+   /* asm 2: fe_sq(>XX=r->X,<X1=p->X); */
+   fe_sq(r->X, p->X);
+
+   /* qhasm: YY=Y1^2 */
+   /* asm 1: fe_sq(>YY=fe#3,<Y1=fe#12); */
+   /* asm 2: fe_sq(>YY=r->Z,<Y1=p->Y); */
+   fe_sq(r->Z, p->Y);
+
+   /* qhasm: B=2*Z1^2 */
+   /* asm 1: fe_sq2(>B=fe#4,<Z1=fe#13); */
+   /* asm 2: fe_sq2(>B=r->T,<Z1=p->Z); */
+   fe_sq2(r->T, p->Z);
+
+   /* qhasm: A=X1+Y1 */
+   /* asm 1: fe_add(>A=fe#2,<X1=fe#11,<Y1=fe#12); */
+   /* asm 2: fe_add(>A=r->Y,<X1=p->X,<Y1=p->Y); */
+   fe_add(r->Y, p->X, p->Y);
+
+   /* qhasm: AA=A^2 */
+   /* asm 1: fe_sq(>AA=fe#5,<A=fe#2); */
+   /* asm 2: fe_sq(>AA=t0,<A=r->Y); */
+   fe_sq(t0, r->Y);
+
+   /* qhasm: Y3=YY+XX */
+   /* asm 1: fe_add(>Y3=fe#2,<YY=fe#3,<XX=fe#1); */
+   /* asm 2: fe_add(>Y3=r->Y,<YY=r->Z,<XX=r->X); */
+   fe_add(r->Y, r->Z, r->X);
+
+   /* qhasm: Z3=YY-XX */
+   /* asm 1: fe_sub(>Z3=fe#3,<YY=fe#3,<XX=fe#1); */
+   /* asm 2: fe_sub(>Z3=r->Z,<YY=r->Z,<XX=r->X); */
+   fe_sub(r->Z, r->Z, r->X);
+
+   /* qhasm: X3=AA-Y3 */
+   /* asm 1: fe_sub(>X3=fe#1,<AA=fe#5,<Y3=fe#2); */
+   /* asm 2: fe_sub(>X3=r->X,<AA=t0,<Y3=r->Y); */
+   fe_sub(r->X, t0, r->Y);
+
+   /* qhasm: T3=B-Z3 */
+   /* asm 1: fe_sub(>T3=fe#4,<B=fe#4,<Z3=fe#3); */
+   /* asm 2: fe_sub(>T3=r->T,<B=r->T,<Z3=r->Z); */
+   fe_sub(r->T, r->T, r->Z);
+   }
+
+void ge_p3_0(ge_p3* h)
+   {
+   fe_0(h->X);
+   fe_1(h->Y);
+   fe_1(h->Z);
+   fe_0(h->T);
+   }
+
+/*
+r = 2 * p
+*/
+
+void ge_p3_dbl(ge_p1p1* r, const ge_p3* p)
+   {
+   ge_p2 q;
+   // Convert to p2 rep
+   q.X = p->X;
+   q.Y = p->Y;
+   q.Z = p->Z;
+   ge_p2_dbl(r, &q);
+   }
+
+/*
+r = p
+*/
+
+
+void ge_p3_to_cached(ge_cached* r, const ge_p3* p)
+   {
+   static const fe d2 =
+      {
+      -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
+      } ;
+   fe_add(r->YplusX, p->Y, p->X);
+   fe_sub(r->YminusX, p->Y, p->X);
+   fe_copy(r->Z, p->Z);
+   fe_mul(r->T2d, p->T, d2);
+   }
+
+/*
+r = p - q
+*/
+
+void ge_sub(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
+   {
+   fe t0;
+   /* qhasm: YpX1 = Y1+X1 */
+   /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+   fe_add(r->X, p->Y, p->X);
+
+   /* qhasm: YmX1 = Y1-X1 */
+   /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+   /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+   fe_sub(r->Y, p->Y, p->X);
+
+   /* qhasm: A = YpX1*YmX2 */
+   /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YmX2=fe#16); */
+   /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YmX2=q->YminusX); */
+   fe_mul(r->Z, r->X, q->YminusX);
+
+   /* qhasm: B = YmX1*YpX2 */
+   /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YpX2=fe#15); */
+   /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YpX2=q->YplusX); */
+   fe_mul(r->Y, r->Y, q->YplusX);
+
+   /* qhasm: C = T2d2*T1 */
+   /* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
+   /* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */
+   fe_mul(r->T, q->T2d, p->T);
+
+   /* qhasm: ZZ = Z1*Z2 */
+   /* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
+   /* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */
+   fe_mul(r->X, p->Z, q->Z);
+
+   /* qhasm: D = 2*ZZ */
+   /* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
+   /* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */
+   fe_add(t0, r->X, r->X);
+
+   /* qhasm: X3 = A-B */
+   /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+   fe_sub(r->X, r->Z, r->Y);
+
+   /* qhasm: Y3 = A+B */
+   /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+   /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+   fe_add(r->Y, r->Z, r->Y);
+
+   /* qhasm: Z3 = D-C */
+   /* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */
+   fe_sub(r->Z, t0, r->T);
+
+   /* qhasm: T3 = D+C */
+   /* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
+   /* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */
+   fe_add(r->T, t0, r->T);
+
+   }
+
+void slide(int8_t* r, const uint8_t* a)
+   {
+   for(size_t i = 0; i < 256; ++i)
+      {
+      r[i] = 1 & (a[i >> 3] >> (i & 7));
+      }
+
+   for(size_t i = 0; i < 256; ++i)
+      {
+      if(r[i])
+         {
+         for(size_t b = 1; b <= 6 && i + b < 256; ++b)
+            {
+            if(r[i + b])
+               {
+               if(r[i] + (r[i + b] << b) <= 15)
+                  {
+                  r[i] += r[i + b] << b;
+                  r[i + b] = 0;
+                  }
+               else if(r[i] - (r[i + b] << b) >= -15)
+                  {
+                  r[i] -= r[i + b] << b;
+                  for(size_t k = i + b; k < 256; ++k)
+                     {
+                     if(!r[k])
+                        {
+                        r[k] = 1;
+                        break;
+                        }
+                     r[k] = 0;
+                     }
+                  }
+               else
+                  { break; }
+               }
+            }
+         }
+      }
+   }
+
+void ge_tobytes(uint8_t* s, const ge_p2* h)
+   {
+   fe recip;
+   fe x;
+   fe y;
+
+   fe_invert(recip, h->Z);
+   fe_mul(x, h->X, recip);
+   fe_mul(y, h->Y, recip);
+   fe_tobytes(s, y);
+   s[31] ^= fe_isnegative(x) << 7;
+   }
+
+void ge_p2_0(ge_p2* h)
+   {
+   fe_0(h->X);
+   fe_1(h->Y);
+   fe_1(h->Z);
+   }
+
+}
+
+int ge_frombytes_negate_vartime(ge_p3* h, const uint8_t* s)
+   {
+   static const fe d =
+      {
+      -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
+      } ;
+   static const fe sqrtm1 =
+      {
+      -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
+      } ;
+
+   fe u;
+   fe v;
+   fe v3;
+   fe vxx;
+   fe check;
+
+   fe_frombytes(h->Y, s);
+   fe_1(h->Z);
+   fe_sq(u, h->Y);
+   fe_mul(v, u, d);
+   fe_sub(u, u, h->Z);     /* u = y^2-1 */
+   fe_add(v, v, h->Z);     /* v = dy^2+1 */
+
+   fe_sq(v3, v);
+   fe_mul(v3, v3, v);      /* v3 = v^3 */
+   fe_sq(h->X, v3);
+   fe_mul(h->X, h->X, v);
+   fe_mul(h->X, h->X, u);  /* x = uv^7 */
+
+   fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
+   fe_mul(h->X, h->X, v3);
+   fe_mul(h->X, h->X, u);  /* x = uv^3(uv^7)^((q-5)/8) */
+
+   fe_sq(vxx, h->X);
+   fe_mul(vxx, vxx, v);
+   fe_sub(check, vxx, u);  /* vx^2-u */
+   if(fe_isnonzero(check))
+      {
+      fe_add(check, vxx, u); /* vx^2+u */
+      if(fe_isnonzero(check))
+         {
+         return -1;
+         }
+      fe_mul(h->X, h->X, sqrtm1);
+      }
+
+   if(fe_isnegative(h->X) == (s[31] >> 7))
+      { fe_neg(h->X, h->X); }
+
+   fe_mul(h->T, h->X, h->Y);
+   return 0;
+   }
+
+/*
+r = a * A + b * B
+where a = a[0]+256*a[1]+...+256^31 a[31].
+and b = b[0]+256*b[1]+...+256^31 b[31].
+B is the Ed25519 base point (x,4/5) with x positive.
+*/
+
+void ge_double_scalarmult_vartime(
+   uint8_t out[32],
+   const uint8_t* a, const ge_p3* A, const uint8_t* b)
+   {
+   static const ge_precomp Bi[8] =
+      {
+         {
+            { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+            { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+            { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+         },
+         {
+            { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+            { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+            { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+         },
+         {
+            { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+            { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+            { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+         },
+         {
+            { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+            { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+            { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+         },
+         {
+            { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 },
+            { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 },
+            { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 },
+         },
+         {
+            { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 },
+            { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 },
+            { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 },
+         },
+         {
+            { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 },
+            { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 },
+            { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 },
+         },
+         {
+            { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 },
+            { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 },
+            { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 },
+         },
+      } ;
+
+   int8_t aslide[256];
+   int8_t bslide[256];
+   ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
+   ge_p1p1 t;
+   ge_p3 u;
+   ge_p3 A2;
+   ge_p2 r;
+   int i;
+
+   slide(aslide, a);
+   slide(bslide, b);
+
+   ge_p3_to_cached(&Ai[0], A);
+   ge_p3_dbl(&t, A);
+   ge_p1p1_to_p3(&A2, &t);
+   ge_add(&t, &A2, &Ai[0]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[1], &u);
+   ge_add(&t, &A2, &Ai[1]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[2], &u);
+   ge_add(&t, &A2, &Ai[2]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[3], &u);
+   ge_add(&t, &A2, &Ai[3]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[4], &u);
+   ge_add(&t, &A2, &Ai[4]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[5], &u);
+   ge_add(&t, &A2, &Ai[5]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[6], &u);
+   ge_add(&t, &A2, &Ai[6]);
+   ge_p1p1_to_p3(&u, &t);
+   ge_p3_to_cached(&Ai[7], &u);
+
+   ge_p2_0(&r);
+
+   for(i = 255; i >= 0; --i)
+      {
+      if(aslide[i] || bslide[i])
+         {
+         break;
+         }
+      }
+
+   for(; i >= 0; --i)
+      {
+      ge_p2_dbl(&t, &r);
+
+      if(aslide[i] > 0)
+         {
+         ge_p1p1_to_p3(&u, &t);
+         ge_add(&t, &u, &Ai[aslide[i] >> 1]);
+         }
+      else if(aslide[i] < 0)
+         {
+         ge_p1p1_to_p3(&u, &t);
+         ge_sub(&t, &u, &Ai[(-aslide[i]) >> 1]);
+         }
+
+      if(bslide[i] > 0)
+         {
+         ge_p1p1_to_p3(&u, &t);
+         ge_madd(&t, &u, &Bi[bslide[i] >> 1]);
+         }
+      else if(bslide[i] < 0)
+         {
+         ge_p1p1_to_p3(&u, &t);
+         ge_msub(&t, &u, &Bi[(-bslide[i]) >> 1]);
+         }
+
+      ge_p1p1_to_p2(&r, &t);
+      }
+
+   ge_tobytes(out, &r);
+   }
+
+/* base[i][j] = (j+1)*256^i*B */
+static const ge_precomp B_precomp[32][8] =
+   {
+      {
+         {
+            { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+            { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+            { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+         },
+         {
+            { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 },
+            { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 },
+            { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 },
+         },
+         {
+            { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+            { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+            { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+         },
+         {
+            { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 },
+            { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 },
+            { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 },
+         },
+         {
+            { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+            { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+            { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+         },
+         {
+            { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 },
+            { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 },
+            { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 },
+         },
+         {
+            { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+            { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+            { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+         },
+         {
+            { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 },
+            { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 },
+            { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 },
+         },
+      },
+      {
+         {
+            { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 },
+            { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 },
+            { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 },
+         },
+         {
+            { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 },
+            { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 },
+            { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 },
+         },
+         {
+            { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 },
+            { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 },
+            { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 },
+         },
+         {
+            { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 },
+            { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 },
+            { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 },
+         },
+         {
+            { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 },
+            { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 },
+            { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 },
+         },
+         {
+            { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 },
+            { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 },
+            { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 },
+         },
+         {
+            { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 },
+            { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 },
+            { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 },
+         },
+         {
+            { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 },
+            { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 },
+            { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 },
+         },
+      },
+      {
+         {
+            { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 },
+            { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 },
+            { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 },
+         },
+         {
+            { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 },
+            { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 },
+            { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 },
+         },
+         {
+            { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 },
+            { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 },
+            { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 },
+         },
+         {
+            { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 },
+            { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 },
+            { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 },
+         },
+         {
+            { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 },
+            { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 },
+            { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 },
+         },
+         {
+            { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 },
+            { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 },
+            { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 },
+         },
+         {
+            { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 },
+            { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 },
+            { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 },
+         },
+         {
+            { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 },
+            { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 },
+            { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 },
+         },
+      },
+      {
+         {
+            { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 },
+            { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 },
+            { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 },
+         },
+         {
+            { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 },
+            { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 },
+            { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 },
+         },
+         {
+            { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 },
+            { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 },
+            { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 },
+         },
+         {
+            { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 },
+            { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 },
+            { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 },
+         },
+         {
+            { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 },
+            { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 },
+            { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 },
+         },
+         {
+            { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 },
+            { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 },
+            { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 },
+         },
+         {
+            { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 },
+            { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 },
+            { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 },
+         },
+         {
+            { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 },
+            { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 },
+            { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 },
+         },
+      },
+      {
+         {
+            { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 },
+            { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 },
+            { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 },
+         },
+         {
+            { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 },
+            { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 },
+            { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 },
+         },
+         {
+            { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 },
+            { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 },
+            { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 },
+         },
+         {
+            { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 },
+            { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 },
+            { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 },
+         },
+         {
+            { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 },
+            { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 },
+            { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 },
+         },
+         {
+            { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 },
+            { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 },
+            { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 },
+         },
+         {
+            { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 },
+            { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 },
+            { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 },
+         },
+         {
+            { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 },
+            { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 },
+            { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 },
+         },
+      },
+      {
+         {
+            { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 },
+            { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 },
+            { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 },
+         },
+         {
+            { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 },
+            { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 },
+            { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 },
+         },
+         {
+            { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 },
+            { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 },
+            { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 },
+         },
+         {
+            { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 },
+            { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 },
+            { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 },
+         },
+         {
+            { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 },
+            { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 },
+            { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 },
+         },
+         {
+            { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 },
+            { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 },
+            { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 },
+         },
+         {
+            { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 },
+            { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 },
+            { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 },
+         },
+         {
+            { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 },
+            { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 },
+            { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 },
+         },
+      },
+      {
+         {
+            { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 },
+            { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 },
+            { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 },
+         },
+         {
+            { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 },
+            { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 },
+            { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 },
+         },
+         {
+            { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 },
+            { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 },
+            { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 },
+         },
+         {
+            { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 },
+            { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 },
+            { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 },
+         },
+         {
+            { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 },
+            { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 },
+            { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 },
+         },
+         {
+            { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 },
+            { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 },
+            { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 },
+         },
+         {
+            { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 },
+            { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 },
+            { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 },
+         },
+         {
+            { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 },
+            { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 },
+            { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 },
+         },
+      },
+      {
+         {
+            { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 },
+            { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 },
+            { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 },
+         },
+         {
+            { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 },
+            { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 },
+            { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 },
+         },
+         {
+            { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 },
+            { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 },
+            { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 },
+         },
+         {
+            { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 },
+            { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 },
+            { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 },
+         },
+         {
+            { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 },
+            { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 },
+            { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 },
+         },
+         {
+            { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 },
+            { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 },
+            { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 },
+         },
+         {
+            { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 },
+            { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 },
+            { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 },
+         },
+         {
+            { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 },
+            { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 },
+            { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 },
+         },
+      },
+      {
+         {
+            { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 },
+            { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 },
+            { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 },
+         },
+         {
+            { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 },
+            { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 },
+            { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 },
+         },
+         {
+            { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 },
+            { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 },
+            { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 },
+         },
+         {
+            { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 },
+            { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 },
+            { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 },
+         },
+         {
+            { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 },
+            { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 },
+            { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 },
+         },
+         {
+            { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 },
+            { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 },
+            { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 },
+         },
+         {
+            { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 },
+            { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 },
+            { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 },
+         },
+         {
+            { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 },
+            { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 },
+            { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 },
+         },
+      },
+      {
+         {
+            { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 },
+            { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 },
+            { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 },
+         },
+         {
+            { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 },
+            { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 },
+            { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 },
+         },
+         {
+            { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 },
+            { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 },
+            { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 },
+         },
+         {
+            { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 },
+            { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 },
+            { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 },
+         },
+         {
+            { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 },
+            { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 },
+            { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 },
+         },
+         {
+            { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 },
+            { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 },
+            { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 },
+         },
+         {
+            { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 },
+            { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 },
+            { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 },
+         },
+         {
+            { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 },
+            { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 },
+            { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 },
+         },
+      },
+      {
+         {
+            { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 },
+            { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 },
+            { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 },
+         },
+         {
+            { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 },
+            { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 },
+            { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 },
+         },
+         {
+            { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 },
+            { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 },
+            { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 },
+         },
+         {
+            { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 },
+            { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 },
+            { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 },
+         },
+         {
+            { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 },
+            { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 },
+            { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 },
+         },
+         {
+            { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 },
+            { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 },
+            { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 },
+         },
+         {
+            { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 },
+            { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 },
+            { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 },
+         },
+         {
+            { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 },
+            { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 },
+            { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 },
+         },
+      },
+      {
+         {
+            { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 },
+            { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 },
+            { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 },
+         },
+         {
+            { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 },
+            { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 },
+            { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 },
+         },
+         {
+            { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 },
+            { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 },
+            { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 },
+         },
+         {
+            { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 },
+            { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 },
+            { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 },
+         },
+         {
+            { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 },
+            { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 },
+            { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 },
+         },
+         {
+            { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 },
+            { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 },
+            { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 },
+         },
+         {
+            { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 },
+            { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 },
+            { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 },
+         },
+         {
+            { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 },
+            { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 },
+            { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 },
+         },
+      },
+      {
+         {
+            { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 },
+            { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 },
+            { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 },
+         },
+         {
+            { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 },
+            { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 },
+            { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 },
+         },
+         {
+            { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 },
+            { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 },
+            { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 },
+         },
+         {
+            { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 },
+            { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 },
+            { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 },
+         },
+         {
+            { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 },
+            { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 },
+            { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 },
+         },
+         {
+            { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 },
+            { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 },
+            { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 },
+         },
+         {
+            { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 },
+            { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 },
+            { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 },
+         },
+         {
+            { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 },
+            { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 },
+            { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 },
+         },
+      },
+      {
+         {
+            { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 },
+            { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 },
+            { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 },
+         },
+         {
+            { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 },
+            { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 },
+            { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 },
+         },
+         {
+            { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 },
+            { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 },
+            { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 },
+         },
+         {
+            { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 },
+            { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 },
+            { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 },
+         },
+         {
+            { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 },
+            { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 },
+            { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 },
+         },
+         {
+            { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 },
+            { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 },
+            { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 },
+         },
+         {
+            { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 },
+            { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 },
+            { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 },
+         },
+         {
+            { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 },
+            { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 },
+            { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 },
+         },
+      },
+      {
+         {
+            { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 },
+            { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 },
+            { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 },
+         },
+         {
+            { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 },
+            { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 },
+            { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 },
+         },
+         {
+            { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 },
+            { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 },
+            { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 },
+         },
+         {
+            { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 },
+            { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 },
+            { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 },
+         },
+         {
+            { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 },
+            { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 },
+            { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 },
+         },
+         {
+            { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 },
+            { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 },
+            { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 },
+         },
+         {
+            { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 },
+            { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 },
+            { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 },
+         },
+         {
+            { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 },
+            { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 },
+            { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 },
+         },
+      },
+      {
+         {
+            { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 },
+            { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 },
+            { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 },
+         },
+         {
+            { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 },
+            { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 },
+            { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 },
+         },
+         {
+            { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 },
+            { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 },
+            { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 },
+         },
+         {
+            { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 },
+            { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 },
+            { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 },
+         },
+         {
+            { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 },
+            { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 },
+            { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 },
+         },
+         {
+            { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 },
+            { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 },
+            { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 },
+         },
+         {
+            { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 },
+            { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 },
+            { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 },
+         },
+         {
+            { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 },
+            { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 },
+            { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 },
+         },
+      },
+      {
+         {
+            { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 },
+            { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 },
+            { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 },
+         },
+         {
+            { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 },
+            { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 },
+            { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 },
+         },
+         {
+            { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 },
+            { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 },
+            { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 },
+         },
+         {
+            { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 },
+            { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 },
+            { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 },
+         },
+         {
+            { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 },
+            { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 },
+            { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 },
+         },
+         {
+            { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 },
+            { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 },
+            { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 },
+         },
+         {
+            { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 },
+            { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 },
+            { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 },
+         },
+         {
+            { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 },
+            { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 },
+            { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 },
+         },
+      },
+      {
+         {
+            { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 },
+            { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 },
+            { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 },
+         },
+         {
+            { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 },
+            { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 },
+            { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 },
+         },
+         {
+            { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 },
+            { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 },
+            { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 },
+         },
+         {
+            { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 },
+            { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 },
+            { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 },
+         },
+         {
+            { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 },
+            { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 },
+            { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 },
+         },
+         {
+            { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 },
+            { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 },
+            { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 },
+         },
+         {
+            { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 },
+            { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 },
+            { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 },
+         },
+         {
+            { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 },
+            { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 },
+            { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 },
+         },
+      },
+      {
+         {
+            { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 },
+            { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 },
+            { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 },
+         },
+         {
+            { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 },
+            { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 },
+            { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 },
+         },
+         {
+            { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 },
+            { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 },
+            { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 },
+         },
+         {
+            { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 },
+            { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 },
+            { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 },
+         },
+         {
+            { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 },
+            { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 },
+            { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 },
+         },
+         {
+            { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 },
+            { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 },
+            { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 },
+         },
+         {
+            { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 },
+            { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 },
+            { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 },
+         },
+         {
+            { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 },
+            { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 },
+            { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 },
+         },
+      },
+      {
+         {
+            { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 },
+            { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 },
+            { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 },
+         },
+         {
+            { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 },
+            { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 },
+            { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 },
+         },
+         {
+            { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 },
+            { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 },
+            { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 },
+         },
+         {
+            { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 },
+            { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 },
+            { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 },
+         },
+         {
+            { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 },
+            { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 },
+            { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 },
+         },
+         {
+            { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 },
+            { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 },
+            { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 },
+         },
+         {
+            { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 },
+            { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 },
+            { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 },
+         },
+         {
+            { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 },
+            { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 },
+            { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 },
+         },
+      },
+      {
+         {
+            { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 },
+            { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 },
+            { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 },
+         },
+         {
+            { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 },
+            { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 },
+            { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 },
+         },
+         {
+            { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 },
+            { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 },
+            { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 },
+         },
+         {
+            { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 },
+            { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 },
+            { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 },
+         },
+         {
+            { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 },
+            { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 },
+            { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 },
+         },
+         {
+            { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 },
+            { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 },
+            { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 },
+         },
+         {
+            { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 },
+            { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 },
+            { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 },
+         },
+         {
+            { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 },
+            { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 },
+            { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 },
+         },
+      },
+      {
+         {
+            { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 },
+            { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 },
+            { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 },
+         },
+         {
+            { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 },
+            { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 },
+            { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 },
+         },
+         {
+            { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 },
+            { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 },
+            { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 },
+         },
+         {
+            { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 },
+            { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 },
+            { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 },
+         },
+         {
+            { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 },
+            { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 },
+            { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 },
+         },
+         {
+            { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 },
+            { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 },
+            { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 },
+         },
+         {
+            { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 },
+            { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 },
+            { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 },
+         },
+         {
+            { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 },
+            { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 },
+            { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 },
+         },
+      },
+      {
+         {
+            { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 },
+            { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 },
+            { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 },
+         },
+         {
+            { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 },
+            { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 },
+            { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 },
+         },
+         {
+            { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 },
+            { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 },
+            { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 },
+         },
+         {
+            { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 },
+            { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 },
+            { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 },
+         },
+         {
+            { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 },
+            { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 },
+            { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 },
+         },
+         {
+            { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 },
+            { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 },
+            { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 },
+         },
+         {
+            { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 },
+            { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 },
+            { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 },
+         },
+         {
+            { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 },
+            { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 },
+            { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 },
+         },
+      },
+      {
+         {
+            { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 },
+            { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 },
+            { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 },
+         },
+         {
+            { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 },
+            { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 },
+            { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 },
+         },
+         {
+            { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 },
+            { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 },
+            { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 },
+         },
+         {
+            { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 },
+            { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 },
+            { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 },
+         },
+         {
+            { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 },
+            { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 },
+            { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 },
+         },
+         {
+            { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 },
+            { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 },
+            { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 },
+         },
+         {
+            { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 },
+            { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 },
+            { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 },
+         },
+         {
+            { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 },
+            { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 },
+            { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 },
+         },
+      },
+      {
+         {
+            { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 },
+            { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 },
+            { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 },
+         },
+         {
+            { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 },
+            { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 },
+            { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 },
+         },
+         {
+            { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 },
+            { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 },
+            { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 },
+         },
+         {
+            { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 },
+            { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 },
+            { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 },
+         },
+         {
+            { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 },
+            { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 },
+            { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 },
+         },
+         {
+            { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 },
+            { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 },
+            { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 },
+         },
+         {
+            { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 },
+            { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 },
+            { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 },
+         },
+         {
+            { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 },
+            { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 },
+            { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 },
+         },
+      },
+      {
+         {
+            { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 },
+            { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 },
+            { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 },
+         },
+         {
+            { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 },
+            { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 },
+            { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 },
+         },
+         {
+            { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 },
+            { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 },
+            { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 },
+         },
+         {
+            { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 },
+            { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 },
+            { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 },
+         },
+         {
+            { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 },
+            { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 },
+            { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 },
+         },
+         {
+            { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 },
+            { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 },
+            { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 },
+         },
+         {
+            { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 },
+            { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 },
+            { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 },
+         },
+         {
+            { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 },
+            { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 },
+            { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 },
+         },
+      },
+      {
+         {
+            { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 },
+            { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 },
+            { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 },
+         },
+         {
+            { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 },
+            { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 },
+            { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 },
+         },
+         {
+            { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 },
+            { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 },
+            { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 },
+         },
+         {
+            { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 },
+            { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 },
+            { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 },
+         },
+         {
+            { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 },
+            { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 },
+            { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 },
+         },
+         {
+            { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 },
+            { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 },
+            { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 },
+         },
+         {
+            { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 },
+            { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 },
+            { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 },
+         },
+         {
+            { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 },
+            { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 },
+            { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 },
+         },
+      },
+      {
+         {
+            { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 },
+            { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 },
+            { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 },
+         },
+         {
+            { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 },
+            { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 },
+            { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 },
+         },
+         {
+            { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 },
+            { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 },
+            { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 },
+         },
+         {
+            { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 },
+            { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 },
+            { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 },
+         },
+         {
+            { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 },
+            { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 },
+            { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 },
+         },
+         {
+            { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 },
+            { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 },
+            { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 },
+         },
+         {
+            { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 },
+            { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 },
+            { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 },
+         },
+         {
+            { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 },
+            { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 },
+            { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 },
+         },
+      },
+      {
+         {
+            { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 },
+            { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 },
+            { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 },
+         },
+         {
+            { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 },
+            { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 },
+            { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 },
+         },
+         {
+            { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 },
+            { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 },
+            { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 },
+         },
+         {
+            { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 },
+            { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 },
+            { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 },
+         },
+         {
+            { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 },
+            { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 },
+            { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 },
+         },
+         {
+            { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 },
+            { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 },
+            { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 },
+         },
+         {
+            { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 },
+            { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 },
+            { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 },
+         },
+         {
+            { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 },
+            { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 },
+            { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 },
+         },
+      },
+      {
+         {
+            { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 },
+            { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 },
+            { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 },
+         },
+         {
+            { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 },
+            { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 },
+            { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 },
+         },
+         {
+            { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 },
+            { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 },
+            { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 },
+         },
+         {
+            { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 },
+            { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 },
+            { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 },
+         },
+         {
+            { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 },
+            { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 },
+            { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 },
+         },
+         {
+            { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 },
+            { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 },
+            { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 },
+         },
+         {
+            { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 },
+            { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 },
+            { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 },
+         },
+         {
+            { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 },
+            { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 },
+            { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 },
+         },
+      },
+      {
+         {
+            { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 },
+            { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 },
+            { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 },
+         },
+         {
+            { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 },
+            { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 },
+            { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 },
+         },
+         {
+            { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 },
+            { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 },
+            { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 },
+         },
+         {
+            { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 },
+            { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 },
+            { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 },
+         },
+         {
+            { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 },
+            { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 },
+            { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 },
+         },
+         {
+            { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 },
+            { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 },
+            { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 },
+         },
+         {
+            { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 },
+            { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 },
+            { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 },
+         },
+         {
+            { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 },
+            { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 },
+            { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 },
+         },
+      },
+      {
+         {
+            { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 },
+            { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 },
+            { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 },
+         },
+         {
+            { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 },
+            { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 },
+            { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 },
+         },
+         {
+            { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 },
+            { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 },
+            { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 },
+         },
+         {
+            { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 },
+            { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 },
+            { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 },
+         },
+         {
+            { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 },
+            { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 },
+            { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 },
+         },
+         {
+            { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 },
+            { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 },
+            { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 },
+         },
+         {
+            { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 },
+            { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 },
+            { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 },
+         },
+         {
+            { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 },
+            { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 },
+            { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 },
+         },
+      },
+   } ;
+
+namespace {
+
+inline uint8_t equal(int8_t b, int8_t c)
+   {
+   uint8_t ub = b;
+   uint8_t uc = c;
+   uint8_t x = ub ^ uc; /* 0: yes; 1..255: no */
+   uint32_t y = x; /* 0: yes; 1..255: no */
+   y -= 1; /* 4294967295: yes; 0..254: no */
+   y >>= 31; /* 1: yes; 0: no */
+   return static_cast<uint8_t>(y);
+   }
+
+inline int32_t equal32(int8_t b, int8_t c)
+   {
+   return -static_cast<int32_t>(equal(b, c));
+   }
+
+inline uint8_t negative(int8_t b)
+   {
+   uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+   x >>= 63; /* 1: yes; 0: no */
+   return static_cast<uint8_t>(x);
+   }
+
+inline void ge_precomp_0(ge_precomp* h)
+   {
+   fe_1(h->yplusx);
+   fe_1(h->yminusx);
+   fe_0(h->xy2d);
+   }
+
+inline void select(ge_precomp* t,
+                   const ge_precomp* base,
+                   int8_t b)
+   {
+   const uint8_t bnegative = negative(b);
+   const uint8_t babs = b - ((-static_cast<int>(bnegative) & b) * 2);
+   const int32_t neg_mask = equal32(bnegative, 1);
+
+   const int32_t mask1 = equal32(babs, 1);
+   const int32_t mask2 = equal32(babs, 2);
+   const int32_t mask3 = equal32(babs, 3);
+   const int32_t mask4 = equal32(babs, 4);
+   const int32_t mask5 = equal32(babs, 5);
+   const int32_t mask6 = equal32(babs, 6);
+   const int32_t mask7 = equal32(babs, 7);
+   const int32_t mask8 = equal32(babs, 8);
+
+   ge_precomp_0(t);
+
+   for(size_t i = 0; i != 10; ++i)
+      {
+      t->yplusx[i] = t->yplusx[i] ^
+                     ((t->yplusx[i] ^ base[0].yplusx[i]) & mask1) ^
+                     ((t->yplusx[i] ^ base[1].yplusx[i]) & mask2) ^
+                     ((t->yplusx[i] ^ base[2].yplusx[i]) & mask3) ^
+                     ((t->yplusx[i] ^ base[3].yplusx[i]) & mask4) ^
+                     ((t->yplusx[i] ^ base[4].yplusx[i]) & mask5) ^
+                     ((t->yplusx[i] ^ base[5].yplusx[i]) & mask6) ^
+                     ((t->yplusx[i] ^ base[6].yplusx[i]) & mask7) ^
+                     ((t->yplusx[i] ^ base[7].yplusx[i]) & mask8);
+
+      t->yminusx[i] = t->yminusx[i] ^
+                      ((t->yminusx[i] ^ base[0].yminusx[i]) & mask1) ^
+                      ((t->yminusx[i] ^ base[1].yminusx[i]) & mask2) ^
+                      ((t->yminusx[i] ^ base[2].yminusx[i]) & mask3) ^
+                      ((t->yminusx[i] ^ base[3].yminusx[i]) & mask4) ^
+                      ((t->yminusx[i] ^ base[4].yminusx[i]) & mask5) ^
+                      ((t->yminusx[i] ^ base[5].yminusx[i]) & mask6) ^
+                      ((t->yminusx[i] ^ base[6].yminusx[i]) & mask7) ^
+                      ((t->yminusx[i] ^ base[7].yminusx[i]) & mask8);
+
+      t->xy2d[i] = t->xy2d[i] ^
+                   ((t->xy2d[i] ^ base[0].xy2d[i]) & mask1) ^
+                   ((t->xy2d[i] ^ base[1].xy2d[i]) & mask2) ^
+                   ((t->xy2d[i] ^ base[2].xy2d[i]) & mask3) ^
+                   ((t->xy2d[i] ^ base[3].xy2d[i]) & mask4) ^
+                   ((t->xy2d[i] ^ base[4].xy2d[i]) & mask5) ^
+                   ((t->xy2d[i] ^ base[5].xy2d[i]) & mask6) ^
+                   ((t->xy2d[i] ^ base[6].xy2d[i]) & mask7) ^
+                   ((t->xy2d[i] ^ base[7].xy2d[i]) & mask8);
+      }
+
+   fe minus_xy2d;
+   fe_neg(minus_xy2d, t->xy2d);
+
+   // If negative have to swap yminusx and yplusx
+   for(size_t i = 0; i != 10; ++i)
+      {
+      int32_t t_yplusx = t->yplusx[i] ^ ((t->yplusx[i] ^ t->yminusx[i]) & neg_mask);
+      int32_t t_yminusx = t->yminusx[i] ^ ((t->yminusx[i] ^ t->yplusx[i]) & neg_mask);
+
+      t->yplusx[i] = t_yplusx;
+      t->yminusx[i] = t_yminusx;
+      t->xy2d[i] = t->xy2d[i] ^ ((t->xy2d[i] ^ minus_xy2d[i]) & neg_mask);
+      }
+   }
+
+void ge_p3_tobytes(uint8_t* s, const ge_p3* h)
+   {
+   fe recip;
+   fe x;
+   fe y;
+
+   fe_invert(recip, h->Z);
+   fe_mul(x, h->X, recip);
+   fe_mul(y, h->Y, recip);
+   fe_tobytes(s, y);
+   s[31] ^= fe_isnegative(x) << 7;
+   }
+
+}
+
+/*
+h = a * B
+where a = a[0]+256*a[1]+...+256^31 a[31]
+B is the Ed25519 base point (x,4/5) with x positive.
+
+Preconditions:
+  a[31] <= 127
+*/
+
+void ge_scalarmult_base(uint8_t out[32], const uint8_t a[32])
+   {
+   int8_t e[64];
+   int8_t carry;
+   ge_p1p1 r;
+   ge_p2 s;
+   ge_p3 h;
+   ge_precomp t;
+   int i;
+
+   for(i = 0; i < 32; ++i)
+      {
+      e[2 * i + 0] = (a[i] >> 0) & 15;
+      e[2 * i + 1] = (a[i] >> 4) & 15;
+      }
+   /* each e[i] is between 0 and 15 */
+   /* e[63] is between 0 and 7 */
+
+   carry = 0;
+   for(i = 0; i < 63; ++i)
+      {
+      e[i] += carry;
+      carry = e[i] + 8;
+      carry >>= 4;
+      e[i] -= carry << 4;
+      }
+   e[63] += carry;
+   /* each e[i] is between -8 and 8 */
+
+   ge_p3_0(&h);
+   for(i = 1; i < 64; i += 2)
+      {
+      select(&t, B_precomp[i / 2], e[i]);
+      ge_madd(&r, &h, &t);
+      ge_p1p1_to_p3(&h, &r);
+      }
+
+   ge_p3_dbl(&r, &h);
+   ge_p1p1_to_p2(&s, &r);
+   ge_p2_dbl(&r, &s);
+   ge_p1p1_to_p2(&s, &r);
+   ge_p2_dbl(&r, &s);
+   ge_p1p1_to_p2(&s, &r);
+   ge_p2_dbl(&r, &s);
+   ge_p1p1_to_p3(&h, &r);
+
+   for(i = 0; i < 64; i += 2)
+      {
+      select(&t, B_precomp[i / 2], e[i]);
+      ge_madd(&r, &h, &t);
+      ge_p1p1_to_p3(&h, &r);
+      }
+
+   ge_p3_tobytes(out, &h);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp b/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp
@@ -0,0 +1,221 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+/*
+Input:
+  a[0]+256*a[1]+...+256^31*a[31] = a
+  b[0]+256*b[1]+...+256^31*b[31] = b
+  c[0]+256*c[1]+...+256^31*c[31] = c
+
+Output:
+  s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+  where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_muladd(uint8_t* s, const uint8_t* a, const uint8_t* b, const uint8_t* c)
+   {
+   const int32_t MASK = 0x1fffff;
+
+   const int64_t a0 = MASK & load_3(a);
+   const int64_t a1 = MASK & (load_4(a + 2) >> 5);
+   const int64_t a2 = MASK & (load_3(a + 5) >> 2);
+   const int64_t a3 = MASK & (load_4(a + 7) >> 7);
+   const int64_t a4 = MASK & (load_4(a + 10) >> 4);
+   const int64_t a5 = MASK & (load_3(a + 13) >> 1);
+   const int64_t a6 = MASK & (load_4(a + 15) >> 6);
+   const int64_t a7 = MASK & (load_3(a + 18) >> 3);
+   const int64_t a8 = MASK & load_3(a + 21);
+   const int64_t a9 = MASK & (load_4(a + 23) >> 5);
+   const int64_t a10 = MASK & (load_3(a + 26) >> 2);
+   const int64_t a11 = (load_4(a + 28) >> 7);
+   const int64_t b0 = MASK & load_3(b);
+   const int64_t b1 = MASK & (load_4(b + 2) >> 5);
+   const int64_t b2 = MASK & (load_3(b + 5) >> 2);
+   const int64_t b3 = MASK & (load_4(b + 7) >> 7);
+   const int64_t b4 = MASK & (load_4(b + 10) >> 4);
+   const int64_t b5 = MASK & (load_3(b + 13) >> 1);
+   const int64_t b6 = MASK & (load_4(b + 15) >> 6);
+   const int64_t b7 = MASK & (load_3(b + 18) >> 3);
+   const int64_t b8 = MASK & load_3(b + 21);
+   const int64_t b9 = MASK & (load_4(b + 23) >> 5);
+   const int64_t b10 = MASK & (load_3(b + 26) >> 2);
+   const int64_t b11 = (load_4(b + 28) >> 7);
+   const int64_t c0 = MASK & load_3(c);
+   const int64_t c1 = MASK & (load_4(c + 2) >> 5);
+   const int64_t c2 = MASK & (load_3(c + 5) >> 2);
+   const int64_t c3 = MASK & (load_4(c + 7) >> 7);
+   const int64_t c4 = MASK & (load_4(c + 10) >> 4);
+   const int64_t c5 = MASK & (load_3(c + 13) >> 1);
+   const int64_t c6 = MASK & (load_4(c + 15) >> 6);
+   const int64_t c7 = MASK & (load_3(c + 18) >> 3);
+   const int64_t c8 = MASK & load_3(c + 21);
+   const int64_t c9 = MASK & (load_4(c + 23) >> 5);
+   const int64_t c10 = MASK & (load_3(c + 26) >> 2);
+   const int64_t c11 = (load_4(c + 28) >> 7);
+
+   int64_t s0 = c0 + a0*b0;
+   int64_t s1 = c1 + a0*b1 + a1*b0;
+   int64_t s2 = c2 + a0*b2 + a1*b1 + a2*b0;
+   int64_t s3 = c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0;
+   int64_t s4 = c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0;
+   int64_t s5 = c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0;
+   int64_t s6 = c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0;
+   int64_t s7 = c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0;
+   int64_t s8 = c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0;
+   int64_t s9 = c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0;
+   int64_t s10 = c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0;
+   int64_t s11 = c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0;
+   int64_t s12 = a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1;
+   int64_t s13 = a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2;
+   int64_t s14 = a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3;
+   int64_t s15 = a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4;
+   int64_t s16 = a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5;
+   int64_t s17 = a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6;
+   int64_t s18 = a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7;
+   int64_t s19 = a8*b11 + a9*b10 + a10*b9 + a11*b8;
+   int64_t s20 = a9*b11 + a10*b10 + a11*b9;
+   int64_t s21 = a10*b11 + a11*b10;
+   int64_t s22 = a11*b11;
+   int64_t s23 = 0;
+
+   carry<21>(s0, s1);
+   carry<21>(s2, s3);
+   carry<21>(s4, s5);
+   carry<21>(s6, s7);
+   carry<21>(s8, s9);
+   carry<21>(s10, s11);
+   carry<21>(s12, s13);
+   carry<21>(s14, s15);
+   carry<21>(s16, s17);
+   carry<21>(s18, s19);
+   carry<21>(s20, s21);
+   carry<21>(s22, s23);
+
+   carry<21>(s1, s2);
+   carry<21>(s3, s4);
+   carry<21>(s5, s6);
+   carry<21>(s7, s8);
+   carry<21>(s9, s10);
+   carry<21>(s11, s12);
+   carry<21>(s13, s14);
+   carry<21>(s15, s16);
+   carry<21>(s17, s18);
+   carry<21>(s19, s20);
+   carry<21>(s21, s22);
+
+   redc_mul(s11, s12, s13, s14, s15, s16, s23);
+   redc_mul(s10, s11, s12, s13, s14, s15, s22);
+   redc_mul( s9, s10, s11, s12, s13, s14, s21);
+   redc_mul( s8,  s9, s10, s11, s12, s13, s20);
+   redc_mul( s7,  s8,  s9, s10, s11, s12, s19);
+   redc_mul( s6,  s7,  s8,  s9, s10, s11, s18);
+
+   carry<21>(s6, s7);
+   carry<21>(s8, s9);
+   carry<21>(s10, s11);
+   carry<21>(s12, s13);
+   carry<21>(s14, s15);
+   carry<21>(s16, s17);
+
+   carry<21>(s7, s8);
+   carry<21>(s9, s10);
+   carry<21>(s11, s12);
+   carry<21>(s13, s14);
+   carry<21>(s15, s16);
+
+   redc_mul(s5, s6, s7, s8, s9, s10, s17);
+   redc_mul(s4, s5, s6, s7, s8, s9, s16);
+   redc_mul(s3, s4, s5, s6, s7, s8, s15);
+   redc_mul(s2, s3, s4, s5, s6, s7, s14);
+   redc_mul(s1, s2, s3, s4, s5, s6, s13);
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry<21>(s0, s1);
+   carry<21>(s2, s3);
+   carry<21>(s4, s5);
+   carry<21>(s6, s7);
+   carry<21>(s8, s9);
+   carry<21>(s10, s11);
+
+   carry<21>(s1, s2);
+   carry<21>(s3, s4);
+   carry<21>(s5, s6);
+   carry<21>(s7, s8);
+   carry<21>(s9, s10);
+   carry<21>(s11, s12);
+
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry0<21>(s0, s1);
+   carry0<21>(s1, s2);
+   carry0<21>(s2, s3);
+   carry0<21>(s3, s4);
+   carry0<21>(s4, s5);
+   carry0<21>(s5, s6);
+   carry0<21>(s6, s7);
+   carry0<21>(s7, s8);
+   carry0<21>(s8, s9);
+   carry0<21>(s9, s10);
+   carry0<21>(s10, s11);
+   carry0<21>(s11, s12);
+
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry0<21>(s0, s1);
+   carry0<21>(s1, s2);
+   carry0<21>(s2, s3);
+   carry0<21>(s3, s4);
+   carry0<21>(s4, s5);
+   carry0<21>(s5, s6);
+   carry0<21>(s6, s7);
+   carry0<21>(s7, s8);
+   carry0<21>(s8, s9);
+   carry0<21>(s9, s10);
+   carry0<21>(s10, s11);
+
+   s[0] = static_cast<uint8_t>(s0 >> 0);
+   s[1] = static_cast<uint8_t>(s0 >> 8);
+   s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
+   s[3] = static_cast<uint8_t>(s1 >> 3);
+   s[4] = static_cast<uint8_t>(s1 >> 11);
+   s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
+   s[6] = static_cast<uint8_t>(s2 >> 6);
+   s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
+   s[8] = static_cast<uint8_t>(s3 >> 1);
+   s[9] = static_cast<uint8_t>(s3 >> 9);
+   s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
+   s[11] = static_cast<uint8_t>(s4 >> 4);
+   s[12] = static_cast<uint8_t>(s4 >> 12);
+   s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
+   s[14] = static_cast<uint8_t>(s5 >> 7);
+   s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
+   s[16] = static_cast<uint8_t>(s6 >> 2);
+   s[17] = static_cast<uint8_t>(s6 >> 10);
+   s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
+   s[19] = static_cast<uint8_t>(s7 >> 5);
+   s[20] = static_cast<uint8_t>(s7 >> 13);
+   s[21] = static_cast<uint8_t>(s8 >> 0);
+   s[22] = static_cast<uint8_t>(s8 >> 8);
+   s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
+   s[24] = static_cast<uint8_t>(s9 >> 3);
+   s[25] = static_cast<uint8_t>(s9 >> 11);
+   s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
+   s[27] = static_cast<uint8_t>(s10 >> 6);
+   s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
+   s[29] = static_cast<uint8_t>(s11 >> 1);
+   s[30] = static_cast<uint8_t>(s11 >> 9);
+   s[31] = static_cast<uint8_t>(s11 >> 17);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp b/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp
@@ -0,0 +1,159 @@
+/*
+* Ed25519
+* (C) 2017 Ribose Inc
+*
+* Based on the public domain code from SUPERCOP ref10 by
+* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+/*
+Input:
+  s[0]+256*s[1]+...+256^63*s[63] = s
+
+Output:
+  s[0]+256*s[1]+...+256^31*s[31] = s mod l
+  where l = 2^252 + 27742317777372353535851937790883648493.
+  Overwrites s in place.
+*/
+
+void sc_reduce(uint8_t* s)
+   {
+   const uint32_t MASK = 0x1fffff;
+
+   int64_t s0 = MASK & load_3(s);
+   int64_t s1 = MASK & (load_4(s + 2) >> 5);
+   int64_t s2 = MASK & (load_3(s + 5) >> 2);
+   int64_t s3 = MASK & (load_4(s + 7) >> 7);
+   int64_t s4 = MASK & (load_4(s + 10) >> 4);
+   int64_t s5 = MASK & (load_3(s + 13) >> 1);
+   int64_t s6 = MASK & (load_4(s + 15) >> 6);
+   int64_t s7 = MASK & (load_3(s + 18) >> 3);
+   int64_t s8 = MASK & load_3(s + 21);
+   int64_t s9 = MASK & (load_4(s + 23) >> 5);
+   int64_t s10 = MASK & (load_3(s + 26) >> 2);
+   int64_t s11 = MASK & (load_4(s + 28) >> 7);
+   int64_t s12 = MASK & (load_4(s + 31) >> 4);
+   int64_t s13 = MASK & (load_3(s + 34) >> 1);
+   int64_t s14 = MASK & (load_4(s + 36) >> 6);
+   int64_t s15 = MASK & (load_3(s + 39) >> 3);
+   int64_t s16 = MASK & load_3(s + 42);
+   int64_t s17 = MASK & (load_4(s + 44) >> 5);
+   int64_t s18 = MASK & (load_3(s + 47) >> 2);
+   int64_t s19 = MASK & (load_4(s + 49) >> 7);
+   int64_t s20 = MASK & (load_4(s + 52) >> 4);
+   int64_t s21 = MASK & (load_3(s + 55) >> 1);
+   int64_t s22 = MASK & (load_4(s + 57) >> 6);
+   int64_t s23 = (load_4(s + 60) >> 3);
+
+   redc_mul(s11, s12, s13, s14, s15, s16, s23);
+   redc_mul(s10, s11, s12, s13, s14, s15, s22);
+   redc_mul( s9, s10, s11, s12, s13, s14, s21);
+   redc_mul( s8,  s9, s10, s11, s12, s13, s20);
+   redc_mul( s7,  s8,  s9, s10, s11, s12, s19);
+   redc_mul( s6,  s7,  s8,  s9, s10, s11, s18);
+
+   carry<21>(s6, s7);
+   carry<21>(s8, s9);
+   carry<21>(s10, s11);
+   carry<21>(s12, s13);
+   carry<21>(s14, s15);
+   carry<21>(s16, s17);
+
+   carry<21>(s7, s8);
+   carry<21>(s9, s10);
+   carry<21>(s11, s12);
+   carry<21>(s13, s14);
+   carry<21>(s15, s16);
+
+   redc_mul(s5, s6, s7, s8, s9, s10, s17);
+   redc_mul(s4, s5, s6, s7, s8, s9, s16);
+   redc_mul(s3, s4, s5, s6, s7, s8, s15);
+   redc_mul(s2, s3, s4, s5, s6, s7, s14);
+   redc_mul(s1, s2, s3, s4, s5, s6, s13);
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry<21>(s0, s1);
+   carry<21>(s2, s3);
+   carry<21>(s4, s5);
+   carry<21>(s6, s7);
+   carry<21>(s8, s9);
+   carry<21>(s10, s11);
+
+   carry<21>(s1, s2);
+   carry<21>(s3, s4);
+   carry<21>(s5, s6);
+   carry<21>(s7, s8);
+   carry<21>(s9, s10);
+   carry<21>(s11, s12);
+
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry0<21>(s0, s1);
+   carry0<21>(s1, s2);
+   carry0<21>(s2, s3);
+   carry0<21>(s3, s4);
+   carry0<21>(s4, s5);
+   carry0<21>(s5, s6);
+   carry0<21>(s6, s7);
+   carry0<21>(s7, s8);
+   carry0<21>(s8, s9);
+   carry0<21>(s9, s10);
+   carry0<21>(s10, s11);
+   carry0<21>(s11, s12);
+
+   redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+   carry0<21>(s0, s1);
+   carry0<21>(s1, s2);
+   carry0<21>(s2, s3);
+   carry0<21>(s3, s4);
+   carry0<21>(s4, s5);
+   carry0<21>(s5, s6);
+   carry0<21>(s6, s7);
+   carry0<21>(s7, s8);
+   carry0<21>(s8, s9);
+   carry0<21>(s9, s10);
+   carry0<21>(s10, s11);
+   carry0<21>(s11, s12);
+
+   s[0] = static_cast<uint8_t>(s0 >> 0);
+   s[1] = static_cast<uint8_t>(s0 >> 8);
+   s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
+   s[3] = static_cast<uint8_t>(s1 >> 3);
+   s[4] = static_cast<uint8_t>(s1 >> 11);
+   s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
+   s[6] = static_cast<uint8_t>(s2 >> 6);
+   s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
+   s[8] = static_cast<uint8_t>(s3 >> 1);
+   s[9] = static_cast<uint8_t>(s3 >> 9);
+   s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
+   s[11] = static_cast<uint8_t>(s4 >> 4);
+   s[12] = static_cast<uint8_t>(s4 >> 12);
+   s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
+   s[14] = static_cast<uint8_t>(s5 >> 7);
+   s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
+   s[16] = static_cast<uint8_t>(s6 >> 2);
+   s[17] = static_cast<uint8_t>(s6 >> 10);
+   s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
+   s[19] = static_cast<uint8_t>(s7 >> 5);
+   s[20] = static_cast<uint8_t>(s7 >> 13);
+   s[21] = static_cast<uint8_t>(s8 >> 0);
+   s[22] = static_cast<uint8_t>(s8 >> 8);
+   s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
+   s[24] = static_cast<uint8_t>(s9 >> 3);
+   s[25] = static_cast<uint8_t>(s9 >> 11);
+   s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
+   s[27] = static_cast<uint8_t>(s10 >> 6);
+   s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
+   s[29] = static_cast<uint8_t>(s11 >> 1);
+   s[30] = static_cast<uint8_t>(s11 >> 9);
+   s[31] = static_cast<uint8_t>(s11 >> 17);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp b/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp
@@ -0,0 +1,207 @@
+/*
+* ElGamal
+* (C) 1999-2007,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/elgamal.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/keypair.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+/*
+* ElGamal_PublicKey Constructor
+*/
+ElGamal_PublicKey::ElGamal_PublicKey(const DL_Group& group, const BigInt& y) :
+   DL_Scheme_PublicKey(group, y)
+   {
+   }
+
+/*
+* ElGamal_PrivateKey Constructor
+*/
+ElGamal_PrivateKey::ElGamal_PrivateKey(RandomNumberGenerator& rng,
+                                       const DL_Group& group,
+                                       const BigInt& x)
+   {
+   m_x = x;
+   m_group = group;
+
+   if(m_x.is_zero())
+      {
+      const size_t exp_bits = m_group.exponent_bits();
+      m_x.randomize(rng, exp_bits);
+      m_y = m_group.power_g_p(m_x, exp_bits);
+      }
+   else
+      {
+      m_y = m_group.power_g_p(m_x, m_group.p_bits());
+      }
+   }
+
+ElGamal_PrivateKey::ElGamal_PrivateKey(const AlgorithmIdentifier& alg_id,
+                                       const secure_vector<uint8_t>& key_bits) :
+   DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+   {
+   m_y = m_group.power_g_p(m_x, m_group.p_bits());
+   }
+
+/*
+* Check Private ElGamal Parameters
+*/
+bool ElGamal_PrivateKey::check_key(RandomNumberGenerator& rng,
+                                   bool strong) const
+   {
+   if(!DL_Scheme_PrivateKey::check_key(rng, strong))
+      return false;
+
+   if(!strong)
+      return true;
+
+   return KeyPair::encryption_consistency_check(rng, *this, "OAEP(SHA-256)");
+   }
+
+namespace {
+
+/**
+* ElGamal encryption operation
+*/
+class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_EME
+   {
+   public:
+
+      size_t ciphertext_length(size_t) const override { return 2*m_group.p_bytes(); }
+
+      size_t max_raw_input_bits() const override { return m_group.p_bits() - 1; }
+
+      ElGamal_Encryption_Operation(const ElGamal_PublicKey& key, const std::string& eme);
+
+      secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t msg_len,
+                                      RandomNumberGenerator& rng) override;
+
+   private:
+      const DL_Group m_group;
+      std::shared_ptr<const Montgomery_Exponentation_State> m_monty_y_p;
+   };
+
+ElGamal_Encryption_Operation::ElGamal_Encryption_Operation(const ElGamal_PublicKey& key,
+                                                           const std::string& eme) :
+   PK_Ops::Encryption_with_EME(eme),
+   m_group(key.get_group())
+   {
+   const size_t powm_window = 4;
+   m_monty_y_p = monty_precompute(key.get_group().monty_params_p(),
+                                  key.get_y(),
+                                  powm_window);
+   }
+
+secure_vector<uint8_t>
+ElGamal_Encryption_Operation::raw_encrypt(const uint8_t msg[], size_t msg_len,
+                                          RandomNumberGenerator& rng)
+   {
+   BigInt m(msg, msg_len);
+
+   if(m >= m_group.get_p())
+      throw Invalid_Argument("ElGamal encryption: Input is too large");
+
+   const size_t k_bits = m_group.exponent_bits();
+   const BigInt k(rng, k_bits);
+
+   const BigInt a = m_group.power_g_p(k, k_bits);
+   const BigInt b = m_group.multiply_mod_p(m, monty_execute(*m_monty_y_p, k, k_bits));
+
+   return BigInt::encode_fixed_length_int_pair(a, b, m_group.p_bytes());
+   }
+
+/**
+* ElGamal decryption operation
+*/
+class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME
+   {
+   public:
+
+      ElGamal_Decryption_Operation(const ElGamal_PrivateKey& key,
+                                   const std::string& eme,
+                                   RandomNumberGenerator& rng);
+
+      size_t plaintext_length(size_t) const override { return m_group.p_bytes(); }
+
+      secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t msg_len) override;
+   private:
+      BigInt powermod_x_p(const BigInt& v) const
+         {
+         const size_t powm_window = 4;
+         auto powm_v_p = monty_precompute(m_monty_p, v, powm_window);
+         return monty_execute(*powm_v_p, m_x, m_x_bits);
+         }
+
+      const DL_Group m_group;
+      const BigInt& m_x;
+      const size_t m_x_bits;
+      std::shared_ptr<const Montgomery_Params> m_monty_p;
+      Blinder m_blinder;
+   };
+
+ElGamal_Decryption_Operation::ElGamal_Decryption_Operation(const ElGamal_PrivateKey& key,
+                                                           const std::string& eme,
+                                                           RandomNumberGenerator& rng) :
+   PK_Ops::Decryption_with_EME(eme),
+   m_group(key.get_group()),
+   m_x(key.get_x()),
+   m_x_bits(m_x.bits()),
+   m_monty_p(key.get_group().monty_params_p()),
+   m_blinder(m_group.get_p(),
+             rng,
+             [](const BigInt& k) { return k; },
+             [this](const BigInt& k) { return powermod_x_p(k); })
+   {
+   }
+
+secure_vector<uint8_t>
+ElGamal_Decryption_Operation::raw_decrypt(const uint8_t msg[], size_t msg_len)
+   {
+   const size_t p_bytes = m_group.p_bytes();
+
+   if(msg_len != 2 * p_bytes)
+      throw Invalid_Argument("ElGamal decryption: Invalid message");
+
+   BigInt a(msg, p_bytes);
+   const BigInt b(msg + p_bytes, p_bytes);
+
+   if(a >= m_group.get_p() || b >= m_group.get_p())
+      throw Invalid_Argument("ElGamal decryption: Invalid message");
+
+   a = m_blinder.blind(a);
+
+   const BigInt r = m_group.multiply_mod_p(m_group.inverse_mod_p(powermod_x_p(a)), b);
+
+   return BigInt::encode_1363(m_blinder.unblind(r), p_bytes);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+ElGamal_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+                                        const std::string& params,
+                                        const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Encryption>(new ElGamal_Encryption_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+ElGamal_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+                                         const std::string& params,
+                                         const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Decryption>(new ElGamal_Decryption_Operation(*this, params, rng));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/elgamal/elgamal.h b/third_party/botan/src/lib/pubkey/elgamal/elgamal.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/elgamal/elgamal.h
@@ -0,0 +1,85 @@
+/*
+* ElGamal
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ELGAMAL_H_
+#define BOTAN_ELGAMAL_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* ElGamal Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) ElGamal_PublicKey : public virtual DL_Scheme_PublicKey
+   {
+   public:
+      std::string algo_name() const override { return "ElGamal"; }
+      DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_42; }
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      ElGamal_PublicKey(const AlgorithmIdentifier& alg_id,
+                        const std::vector<uint8_t>& key_bits) :
+         DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+         {}
+
+      /**
+      * Create a public key.
+      * @param group the underlying DL group
+      * @param y the public value y = g^x mod p
+      */
+      ElGamal_PublicKey(const DL_Group& group, const BigInt& y);
+
+      std::unique_ptr<PK_Ops::Encryption>
+         create_encryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+   protected:
+      ElGamal_PublicKey() = default;
+   };
+
+/**
+* ElGamal Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) ElGamal_PrivateKey final : public ElGamal_PublicKey,
+                                     public virtual DL_Scheme_PrivateKey
+   {
+   public:
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded key bits in ANSI X9.42 format
+      */
+      ElGamal_PrivateKey(const AlgorithmIdentifier& alg_id,
+                         const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Create a private key.
+      * @param rng random number generator to use
+      * @param group the group to be used in the key
+      * @param priv_key the key's secret value (or if zero, generate a new key)
+      */
+      ElGamal_PrivateKey(RandomNumberGenerator& rng,
+                         const DL_Group& group,
+                         const BigInt& priv_key = 0);
+
+      std::unique_ptr<PK_Ops::Decryption>
+         create_decryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp b/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp
@@ -0,0 +1,253 @@
+/*
+* GOST 34.10-2012
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*          Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_3410.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/reducer.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+std::vector<uint8_t> GOST_3410_PublicKey::public_key_bits() const
+   {
+   const BigInt x = public_point().get_affine_x();
+   const BigInt y = public_point().get_affine_y();
+
+   size_t part_size = std::max(x.bytes(), y.bytes());
+
+   std::vector<uint8_t> bits(2*part_size);
+
+   x.binary_encode(&bits[part_size - x.bytes()]);
+   y.binary_encode(&bits[2*part_size - y.bytes()]);
+
+   // Keys are stored in little endian format (WTF)
+   for(size_t i = 0; i != part_size / 2; ++i)
+      {
+      std::swap(bits[i], bits[part_size-1-i]);
+      std::swap(bits[part_size+i], bits[2*part_size-1-i]);
+      }
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(bits, OCTET_STRING);
+   return output;
+   }
+
+std::string GOST_3410_PublicKey::algo_name() const
+   {
+   const size_t p_bits = domain().get_p_bits();
+
+   if(p_bits == 256 || p_bits == 512)
+      return "GOST-34.10-2012-" + std::to_string(p_bits);
+   else
+      throw Encoding_Error("GOST-34.10-2012 is not defined for parameters of this size");
+   }
+
+AlgorithmIdentifier GOST_3410_PublicKey::algorithm_identifier() const
+   {
+   std::vector<uint8_t> params;
+
+   const OID gost_oid = get_oid();
+   const OID domain_oid = domain().get_curve_oid();
+
+   DER_Encoder(params).start_cons(SEQUENCE).encode(domain_oid).end_cons();
+
+   return AlgorithmIdentifier(gost_oid, params);
+   }
+
+GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
+                                         const std::vector<uint8_t>& key_bits)
+   {
+   OID ecc_param_id;
+
+   // The parameters also includes hash and cipher OIDs
+   BER_Decoder(alg_id.get_parameters()).start_cons(SEQUENCE).decode(ecc_param_id);
+
+   m_domain_params = EC_Group(ecc_param_id);
+
+   const size_t p_bits = m_domain_params.get_p_bits();
+   if(p_bits != 256 && p_bits != 512)
+      throw Decoding_Error("GOST-34.10-2012 is not defined for parameters of size " +
+                           std::to_string(p_bits));
+
+   secure_vector<uint8_t> bits;
+   BER_Decoder(key_bits).decode(bits, OCTET_STRING);
+
+   const size_t part_size = bits.size() / 2;
+
+   // Keys are stored in little endian format (WTF)
+   for(size_t i = 0; i != part_size / 2; ++i)
+      {
+      std::swap(bits[i], bits[part_size-1-i]);
+      std::swap(bits[part_size+i], bits[2*part_size-1-i]);
+      }
+
+   BigInt x(bits.data(), part_size);
+   BigInt y(&bits[part_size], part_size);
+
+   m_public_key = domain().point(x, y);
+
+   BOTAN_ASSERT(m_public_key.on_the_curve(),
+                "Loaded GOST 34.10 public key is on the curve");
+   }
+
+GOST_3410_PrivateKey::GOST_3410_PrivateKey(RandomNumberGenerator& rng,
+                                           const EC_Group& domain,
+                                           const BigInt& x) :
+   EC_PrivateKey(rng, domain, x)
+   {
+   const size_t p_bits = m_domain_params.get_p_bits();
+   if(p_bits != 256 && p_bits != 512)
+      throw Decoding_Error("GOST-34.10-2012 is not defined for parameters of size " +
+                           std::to_string(p_bits));
+   }
+
+namespace {
+
+BigInt decode_le(const uint8_t msg[], size_t msg_len)
+   {
+   secure_vector<uint8_t> msg_le(msg, msg + msg_len);
+
+   for(size_t i = 0; i != msg_le.size() / 2; ++i)
+      std::swap(msg_le[i], msg_le[msg_le.size()-1-i]);
+
+   return BigInt(msg_le.data(), msg_le.size());
+   }
+
+/**
+* GOST-34.10 signature operation
+*/
+class GOST_3410_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+   {
+   public:
+      GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410,
+                                    const std::string& emsa) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         m_group(gost_3410.domain()),
+         m_x(gost_3410.private_value())
+         {}
+
+      size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                      RandomNumberGenerator& rng) override;
+
+   private:
+      const EC_Group m_group;
+      const BigInt& m_x;
+      std::vector<BigInt> m_ws;
+   };
+
+secure_vector<uint8_t>
+GOST_3410_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+                                        RandomNumberGenerator& rng)
+   {
+   const BigInt k = m_group.random_scalar(rng);
+
+   BigInt e = decode_le(msg, msg_len);
+
+   e = m_group.mod_order(e);
+   if(e == 0)
+      e = 1;
+
+   const BigInt r = m_group.mod_order(
+      m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+   const BigInt s = m_group.mod_order(
+      m_group.multiply_mod_order(r, m_x) +
+      m_group.multiply_mod_order(k, e));
+
+   if(r == 0 || s == 0)
+      throw Internal_Error("GOST 34.10 signature generation failed, r/s equal to zero");
+
+   return BigInt::encode_fixed_length_int_pair(s, r, m_group.get_order_bytes());
+   }
+
+/**
+* GOST-34.10 verification operation
+*/
+class GOST_3410_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+   {
+   public:
+
+      GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost,
+                                       const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         m_group(gost.domain()),
+         m_gy_mul(m_group.get_base_point(), gost.public_point())
+         {}
+
+      size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+      bool with_recovery() const override { return false; }
+
+      bool verify(const uint8_t msg[], size_t msg_len,
+                  const uint8_t sig[], size_t sig_len) override;
+   private:
+      const EC_Group m_group;
+      const PointGFp_Multi_Point_Precompute m_gy_mul;
+   };
+
+bool GOST_3410_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+                                              const uint8_t sig[], size_t sig_len)
+   {
+   if(sig_len != m_group.get_order_bytes() * 2)
+      return false;
+
+   const BigInt s(sig, sig_len / 2);
+   const BigInt r(sig + sig_len / 2, sig_len / 2);
+
+   const BigInt& order = m_group.get_order();
+
+   if(r <= 0 || r >= order || s <= 0 || s >= order)
+      return false;
+
+   BigInt e = decode_le(msg, msg_len);
+   e = m_group.mod_order(e);
+   if(e == 0)
+      e = 1;
+
+   const BigInt v = m_group.inverse_mod_order(e);
+
+   const BigInt z1 = m_group.multiply_mod_order(s, v);
+   const BigInt z2 = m_group.multiply_mod_order(-r, v);
+
+   const PointGFp R = m_gy_mul.multi_exp(z1, z2);
+
+   if(R.is_zero())
+     return false;
+
+   return (R.get_affine_x() == r);
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+GOST_3410_PublicKey::create_verification_op(const std::string& params,
+                                            const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new GOST_3410_Verification_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+GOST_3410_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                          const std::string& params,
+                                          const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new GOST_3410_Signature_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h b/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h
@@ -0,0 +1,104 @@
+/*
+* GOST 34.10-2001
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+*          Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_3410_KEY_H_
+#define BOTAN_GOST_3410_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* GOST-34.10 Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_3410_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+
+      /**
+      * Construct a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      GOST_3410_PublicKey(const EC_Group& dom_par,
+                          const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
+                          const std::vector<uint8_t>& key_bits);
+
+      /**
+      * Get this keys algorithm name.
+      * @result this keys algorithm name
+      */
+      std::string algo_name() const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      Signature_Format default_x509_signature_format() const override
+         { return IEEE_1363; }
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+
+   protected:
+      GOST_3410_PublicKey() = default;
+   };
+
+/**
+* GOST-34.10 Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_3410_PrivateKey final :
+   public GOST_3410_PublicKey, public EC_PrivateKey
+   {
+   public:
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      GOST_3410_PrivateKey(const AlgorithmIdentifier& alg_id,
+                           const secure_vector<uint8_t>& key_bits) :
+         EC_PrivateKey(alg_id, key_bits) {}
+
+      /**
+      * Generate a new private key
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key; if zero, a new random key is generated
+      */
+      GOST_3410_PrivateKey(RandomNumberGenerator& rng,
+                           const EC_Group& domain,
+                           const BigInt& x = 0);
+
+      AlgorithmIdentifier pkcs8_algorithm_identifier() const override
+         { return EC_PublicKey::algorithm_identifier(); }
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/keypair/keypair.cpp b/third_party/botan/src/lib/pubkey/keypair/keypair.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/keypair/keypair.cpp
@@ -0,0 +1,85 @@
+/*
+* Keypair Checks
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/keypair.h>
+#include <botan/pubkey.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace KeyPair {
+
+/*
+* Check an encryption key pair for consistency
+*/
+bool encryption_consistency_check(RandomNumberGenerator& rng,
+                                  const Private_Key& private_key,
+                                  const Public_Key& public_key,
+                                  const std::string& padding)
+   {
+   PK_Encryptor_EME encryptor(public_key, rng, padding);
+   PK_Decryptor_EME decryptor(private_key, rng, padding);
+
+   /*
+   Weird corner case, if the key is too small to encrypt anything at
+   all. This can happen with very small RSA keys with PSS
+   */
+   if(encryptor.maximum_input_size() == 0)
+      return true;
+
+   std::vector<uint8_t> plaintext;
+   rng.random_vec(plaintext, encryptor.maximum_input_size() - 1);
+
+   std::vector<uint8_t> ciphertext = encryptor.encrypt(plaintext, rng);
+   if(ciphertext == plaintext)
+      return false;
+
+   std::vector<uint8_t> decrypted = unlock(decryptor.decrypt(ciphertext));
+
+   return (plaintext == decrypted);
+   }
+
+/*
+* Check a signature key pair for consistency
+*/
+bool signature_consistency_check(RandomNumberGenerator& rng,
+                                 const Private_Key& private_key,
+                                 const Public_Key& public_key,
+                                 const std::string& padding)
+   {
+   PK_Signer signer(private_key, rng, padding);
+   PK_Verifier verifier(public_key, padding);
+
+   std::vector<uint8_t> message(32);
+   rng.randomize(message.data(), message.size());
+
+   std::vector<uint8_t> signature;
+
+   try
+      {
+      signature = signer.sign_message(message, rng);
+      }
+   catch(Encoding_Error&)
+      {
+      return false;
+      }
+
+   if(!verifier.verify_message(message, signature))
+      return false;
+
+   // Now try to check a corrupt signature, ensure it does not succeed
+   ++signature[0];
+
+   if(verifier.verify_message(message, signature))
+      return false;
+
+   return true;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/pubkey/keypair/keypair.h b/third_party/botan/src/lib/pubkey/keypair/keypair.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/keypair/keypair.h
@@ -0,0 +1,85 @@
+/*
+* Keypair Checks
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEYPAIR_CHECKS_H_
+#define BOTAN_KEYPAIR_CHECKS_H_
+
+#include <botan/pk_keys.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(keypair.h)
+
+namespace Botan {
+
+namespace KeyPair {
+
+/**
+* Tests whether the key is consistent for encryption; whether
+* encrypting and then decrypting gives to the original plaintext.
+* @param rng the rng to use
+* @param private_key the key to test
+* @param public_key the key to test
+* @param padding the encryption padding method to use
+* @return true if consistent otherwise false
+*/
+BOTAN_PUBLIC_API(2,0) bool
+encryption_consistency_check(RandomNumberGenerator& rng,
+                             const Private_Key& private_key,
+                             const Public_Key& public_key,
+                             const std::string& padding);
+
+/**
+* Tests whether the key is consistent for signatures; whether a
+* signature can be created and then verified
+* @param rng the rng to use
+* @param private_key the key to test
+* @param public_key the key to test
+* @param padding the signature padding method to use
+* @return true if consistent otherwise false
+*/
+BOTAN_PUBLIC_API(2,0) bool
+signature_consistency_check(RandomNumberGenerator& rng,
+                            const Private_Key& private_key,
+                            const Public_Key& public_key,
+                            const std::string& padding);
+
+/**
+* Tests whether the key is consistent for encryption; whether
+* encrypting and then decrypting gives to the original plaintext.
+* @param rng the rng to use
+* @param key the key to test
+* @param padding the encryption padding method to use
+* @return true if consistent otherwise false
+*/
+inline bool
+encryption_consistency_check(RandomNumberGenerator& rng,
+                             const Private_Key& key,
+                             const std::string& padding)
+   {
+   return encryption_consistency_check(rng, key, key, padding);
+   }
+
+/**
+* Tests whether the key is consistent for signatures; whether a
+* signature can be created and then verified
+* @param rng the rng to use
+* @param key the key to test
+* @param padding the signature padding method to use
+* @return true if consistent otherwise false
+*/
+inline bool
+signature_consistency_check(RandomNumberGenerator& rng,
+                            const Private_Key& key,
+                            const std::string& padding)
+   {
+   return signature_consistency_check(rng, key, key, padding);
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp b/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp
@@ -0,0 +1,297 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+class binary_matrix final
+   {
+   public:
+      binary_matrix(size_t m_rown, size_t m_coln);
+
+      void row_xor(size_t a, size_t b);
+      secure_vector<size_t> row_reduced_echelon_form();
+
+      /**
+      * return the coefficient out of F_2
+      */
+      uint32_t coef(size_t i, size_t j)
+         {
+         return (m_elem[(i) * m_rwdcnt + (j) / 32] >> (j % 32)) & 1;
+         }
+
+      void set_coef_to_one(size_t i, size_t j)
+         {
+         m_elem[(i) * m_rwdcnt + (j) / 32] |= (static_cast<uint32_t>(1) << ((j) % 32)) ;
+         }
+
+      void toggle_coeff(size_t i, size_t j)
+         {
+         m_elem[(i) * m_rwdcnt + (j) / 32] ^= (static_cast<uint32_t>(1) << ((j) % 32)) ;
+         }
+
+      size_t rows() const { return m_rown; }
+
+      size_t columns() const { return m_coln; }
+
+   private:
+      size_t m_rown;  // number of rows.
+      size_t m_coln; // number of columns.
+      size_t m_rwdcnt; // number of words in a row
+   public:
+      // TODO this should be private
+      std::vector<uint32_t> m_elem;
+   };
+
+binary_matrix::binary_matrix(size_t rown, size_t coln)
+   {
+   m_coln = coln;
+   m_rown = rown;
+   m_rwdcnt = 1 + ((m_coln - 1) / 32);
+   m_elem = std::vector<uint32_t>(m_rown * m_rwdcnt);
+   }
+
+void binary_matrix::row_xor(size_t a, size_t b)
+   {
+   for(size_t i = 0; i != m_rwdcnt; i++)
+      {
+      m_elem[a*m_rwdcnt+i] ^= m_elem[b*m_rwdcnt+i];
+      }
+   }
+
+//the matrix is reduced from LSB...(from right)
+secure_vector<size_t> binary_matrix::row_reduced_echelon_form()
+   {
+   secure_vector<size_t> perm(m_coln);
+   for(size_t i = 0; i != m_coln; i++)
+      {
+      perm[i] = i; // initialize permutation.
+      }
+
+   uint32_t failcnt = 0;
+
+   size_t max = m_coln - 1;
+   for(size_t i = 0; i != m_rown; i++, max--)
+      {
+      bool found_row = false;
+
+      for(size_t j = i; !found_row && j != m_rown; j++)
+         {
+         if(coef(j, max))
+            {
+            if(i != j) //not needed as ith row is 0 and jth row is 1.
+               {
+               row_xor(i, j);//xor to the row.(swap)?
+               }
+
+            found_row = true;
+            }
+         }
+
+      //if no row with a 1 found then swap last column and the column with no 1 down.
+      if(!found_row)
+         {
+         perm[m_coln - m_rown - 1 - failcnt] = static_cast<int>(max);
+         failcnt++;
+         if(!max)
+            {
+            perm.resize(0);
+            }
+         i--;
+         }
+      else
+         {
+         perm[i+m_coln - m_rown] = max;
+         for(size_t j=i+1;j<m_rown;j++)//fill the column downwards with 0's
+            {
+            if(coef(j, max))
+               {
+               row_xor(j,i);//check the arg. order.
+               }
+            }
+
+         //fill the column with 0's upwards too.
+         for(size_t j = i; j != 0; --j)
+            {
+            if(coef(j - 1, max))
+               {
+               row_xor(j - 1, i);
+               }
+            }
+         }
+      }//end for(i)
+   return perm;
+   }
+
+void randomize_support(std::vector<gf2m>& L, RandomNumberGenerator& rng)
+   {
+   for(size_t i = 0; i != L.size(); ++i)
+      {
+      gf2m rnd = random_gf2m(rng);
+
+       // no rejection sampling, but for useful code-based parameters with n <= 13 this seem tolerable
+      std::swap(L[i], L[rnd % L.size()]);
+      }
+   }
+
+std::unique_ptr<binary_matrix> generate_R(std::vector<gf2m> &L, polyn_gf2m* g, std::shared_ptr<GF2m_Field> sp_field, size_t code_length, size_t t)
+   {
+   //L- Support
+   //t- Number of errors
+   //n- Length of the Goppa code
+   //m- The extension degree of the GF
+   //g- The generator polynomial.
+
+   const size_t r = t * sp_field->get_extension_degree();
+
+   binary_matrix H(r, code_length);
+
+   for(size_t i = 0; i != code_length; i++)
+      {
+      gf2m x = g->eval(lex_to_gray(L[i]));//evaluate the polynomial at the point L[i].
+      x = sp_field->gf_inv(x);
+      gf2m y = x;
+      for(size_t j=0;j<t;j++)
+         {
+         for(size_t k=0;k<sp_field->get_extension_degree();k++)
+            {
+            if(y & (1<<k))
+               {
+               //the co-eff. are set in 2^0,...,2^11 ; 2^0,...,2^11 format along the rows/cols?
+               H.set_coef_to_one(j*sp_field->get_extension_degree()+ k,i);
+               }
+            }
+         y = sp_field->gf_mul(y,lex_to_gray(L[i]));
+         }
+      }//The H matrix is fed.
+
+   secure_vector<size_t> perm = H.row_reduced_echelon_form();
+   if(perm.size() == 0)
+      {
+      throw Invalid_State("McEliece keygen failed - could not bring matrix to row reduced echelon form");
+      }
+
+   std::unique_ptr<binary_matrix> result(new binary_matrix(code_length-r, r)) ;
+   for(size_t i = 0; i < result->rows(); ++i)
+      {
+      for(size_t j = 0; j < result->columns(); ++j)
+         {
+         if(H.coef(j, perm[i]))
+            {
+            result->toggle_coeff(i,j);
+            }
+         }
+      }
+
+   std::vector<gf2m> Laux(code_length);
+   for(size_t i = 0; i < code_length; ++i)
+      {
+      Laux[i] = L[perm[i]];
+      }
+
+   for(size_t i = 0; i < code_length; ++i)
+      {
+      L[i] = Laux[i];
+      }
+   return result;
+   }
+}
+
+McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator & rng, size_t ext_deg, size_t code_length, size_t t)
+   {
+   const size_t codimension = t * ext_deg;
+
+   if(code_length <= codimension)
+      {
+      throw Invalid_Argument("invalid McEliece parameters");
+      }
+
+   std::shared_ptr<GF2m_Field> sp_field(new GF2m_Field(ext_deg));
+
+   //pick the support.........
+   std::vector<gf2m> L(code_length);
+
+   for(size_t i = 0; i != L.size(); i++)
+      {
+      L[i] = static_cast<gf2m>(i);
+      }
+   randomize_support(L, rng);
+   polyn_gf2m g(sp_field); // create as zero
+
+   bool success = false;
+   std::unique_ptr<binary_matrix> R;
+
+   do
+      {
+      // create a random irreducible polynomial
+      g = polyn_gf2m(t, rng, sp_field);
+
+      try
+         {
+         R = generate_R(L, &g, sp_field, code_length, t);
+         success = true;
+         }
+      catch(const Invalid_State &)
+         {
+         }
+      } while (!success);
+
+   std::vector<polyn_gf2m> sqrtmod = polyn_gf2m::sqrt_mod_init( g);
+   std::vector<polyn_gf2m> F = syndrome_init(g, L, static_cast<int>(code_length));
+
+   // Each F[i] is the (precomputed) syndrome of the error vector with
+   // a single '1' in i-th position.
+   // We do not store the F[i] as polynomials of degree t , but
+   // as binary vectors of length ext_deg * t (this will
+   // speed up the syndrome computation)
+   //
+   std::vector<uint32_t> H(bit_size_to_32bit_size(codimension) * code_length);
+   uint32_t* sk = H.data();
+   for(size_t i = 0; i < code_length; ++i)
+      {
+      for(size_t l = 0; l < t; ++l)
+         {
+         const size_t k = (l * ext_deg) / 32;
+         const uint8_t j = (l * ext_deg) % 32;
+         sk[k] ^= static_cast<uint32_t>(F[i].get_coef(l)) << j;
+         if(j + ext_deg > 32)
+            {
+            sk[k + 1] ^= F[i].get_coef(l) >> (32 - j);
+            }
+         }
+      sk += bit_size_to_32bit_size(codimension);
+      }
+
+   // We need the support L for decoding (decryption). In fact the
+   // inverse is needed
+
+   std::vector<gf2m> Linv(code_length) ;
+   for(size_t i = 0; i != Linv.size(); ++i)
+      {
+      Linv[L[i]] = static_cast<gf2m>(i);
+      }
+   std::vector<uint8_t> pubmat(R->m_elem.size() * 4);
+   for(size_t i = 0; i < R->m_elem.size(); i++)
+      {
+      store_le(R->m_elem[i], &pubmat[i*4]);
+      }
+
+   return McEliece_PrivateKey(g, H, sqrtmod, Linv, pubmat);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/code_based_util.h b/third_party/botan/src/lib/pubkey/mce/code_based_util.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/code_based_util.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_CODE_BASED_UTIL_H_
+#define BOTAN_CODE_BASED_UTIL_H_
+
+#include <botan/gf2m_small_m.h>
+
+namespace Botan {
+
+/**
+* Expand an input to a bit mask depending on it being being zero or non-zero
+* @param tst the input
+* @return the mask 0xFFFF if tst is non-zero and 0 otherwise
+*/
+template<typename T>
+uint16_t expand_mask_16bit(T tst)
+   {
+   const uint16_t result = (tst != 0);
+   return ~(result - 1);
+   }
+
+inline gf2m gray_to_lex(gf2m gray)
+   {
+   gf2m result = gray ^ (gray >> 8);
+   result ^= (result >> 4);
+   result ^= (result >> 2);
+   result ^= (result >> 1);
+   return result;
+   }
+
+inline gf2m lex_to_gray(gf2m lex)
+   {
+   return (lex >> 1) ^ lex;
+   }
+
+inline size_t bit_size_to_byte_size(size_t bit_size)
+   {
+   return (bit_size - 1) / 8 + 1;
+   }
+
+inline size_t bit_size_to_32bit_size(size_t bit_size)
+   {
+   return (bit_size - 1) / 32 + 1;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp b/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp
@@ -0,0 +1,314 @@
+/*
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/polyn_gf2m.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+void patch_root_array(gf2m res_root_arr[],
+                      size_t res_root_arr_len,
+                      size_t root_pos)
+   {
+   volatile gf2m patch_elem = 0x01;
+   volatile gf2m cond_mask = (root_pos == res_root_arr_len);
+   cond_mask = expand_mask_16bit(cond_mask);
+   cond_mask = ~cond_mask; /* now cond = 1 if not enough roots */
+   patch_elem &= cond_mask;
+   for(size_t i = 0; i < res_root_arr_len; i++)
+      {
+      gf2m masked_patch_elem = (patch_elem++) & cond_mask;
+      res_root_arr[i] ^= masked_patch_elem++;
+      }
+   }
+
+class gf2m_decomp_rootfind_state
+   {
+   public:
+      gf2m_decomp_rootfind_state(const polyn_gf2m & p_polyn, size_t code_length);
+
+      void calc_LiK(const polyn_gf2m & sigma);
+      gf2m calc_Fxj_j_neq_0( const polyn_gf2m & sigma, gf2m j_gray);
+      void calc_next_Aij();
+      void calc_Ai_zero(const polyn_gf2m & sigma);
+      secure_vector<gf2m> find_roots(const polyn_gf2m & sigma);
+
+   private:
+      size_t m_code_length;
+      secure_vector<gf2m> m_Lik; // size is outer_summands * m
+      secure_vector<gf2m> m_Aij; // ...
+      uint32_t m_outer_summands;
+      gf2m m_j;
+      gf2m m_j_gray;
+      gf2m m_sigma_3_l;
+      gf2m m_sigma_3_neq_0_mask;
+   };
+
+/*
+* !! Attention: assumes gf2m is 16bit !!
+*/
+#if 0
+gf2m brootf_decomp_gray_to_lex(gf2m gray)
+   {
+   static_assert(sizeof(gf2m) == 2, "Expected size");
+   gf2m result = gray ^ (gray>>8);
+   result ^= (result >> 4);
+   result ^= (result >> 2);
+   result ^= (result >> 1);
+   return result;
+   }
+#endif
+
+/**
+* calculates ceil((t-4)/5) = outer_summands - 1
+*/
+uint32_t brootf_decomp_calc_sum_limit(uint32_t t)
+   {
+   uint32_t result;
+   if(t < 4)
+      {
+      return 0;
+      }
+   result = t - 4;
+   result += 4;
+   result /= 5;
+   return result;
+   }
+
+gf2m_decomp_rootfind_state::gf2m_decomp_rootfind_state(const polyn_gf2m & polyn, size_t code_length) :
+   m_code_length(code_length), m_j(0), m_j_gray(0)
+   {
+   gf2m coeff_3;
+   gf2m coeff_head;
+   std::shared_ptr<GF2m_Field> sp_field = polyn.get_sp_field();
+   int deg_sigma = polyn.get_degree();
+   if(deg_sigma <= 3)
+      {
+      throw Internal_Error("Unexpected degree in gf2m_decomp_rootfind_state");
+      }
+
+   coeff_3 = polyn.get_coef( 3);
+   coeff_head = polyn.get_coef( deg_sigma); /* dummy value for SCA CM */
+   if(coeff_3 != 0)
+      {
+      this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_3);
+      this->m_sigma_3_neq_0_mask = 0xFFFF;
+      }
+   else
+      {
+      // dummy value needed for timing countermeasure
+      this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_head);
+      this->m_sigma_3_neq_0_mask = 0 ;
+      }
+
+   this->m_outer_summands =  1 + brootf_decomp_calc_sum_limit(deg_sigma);
+   this->m_Lik.resize(this->m_outer_summands * sp_field->get_extension_degree());
+   this->m_Aij.resize(this->m_outer_summands);
+   }
+
+void gf2m_decomp_rootfind_state::calc_Ai_zero(const polyn_gf2m & sigma)
+   {
+   uint32_t i;
+   /*
+   * this function assumes this the first gray code element is zero
+   */
+   for(i = 0; i < this->m_outer_summands; i++)
+      {
+      this->m_Aij[i] = sigma.get_coef(5*i);
+      }
+   this->m_j = 0;
+   this->m_j_gray = 0;
+   }
+
+void gf2m_decomp_rootfind_state::calc_next_Aij()
+   {
+   /*
+   * upon function entry, we have in the state j, Aij.
+   * first thing, we declare Aij Aij_minusone and increase j.
+   * Case j=0 upon function entry also included, then Aij contains A_{i,j=0}.
+   */
+   uint32_t i;
+   gf2m diff, new_j_gray;
+   uint32_t Lik_pos_base;
+
+   this->m_j++;
+
+   new_j_gray =  lex_to_gray(this->m_j);
+
+   if(this->m_j & 1)  /* half of the times */
+      {
+      Lik_pos_base = 0;
+      }
+   else if(this->m_j & 2) /* one quarter of the times */
+      {
+      Lik_pos_base = this->m_outer_summands;
+      }
+   else if( this->m_j & 4) /* one eighth of the times */
+      {
+      Lik_pos_base = this->m_outer_summands * 2;
+      }
+   else if( this->m_j & 8) /* one sixteenth of the times */
+      {
+      Lik_pos_base = this->m_outer_summands * 3;
+      }
+   else if( this->m_j & 16) /* ... */
+      {
+      Lik_pos_base = this->m_outer_summands * 4;
+      }
+   else
+      {
+      gf2m delta_offs = 5;
+      diff = this->m_j_gray ^ new_j_gray;
+      while(((static_cast<gf2m>(1) << delta_offs) & diff) == 0)
+         {
+         delta_offs++;
+
+         }
+      Lik_pos_base = delta_offs * this->m_outer_summands;
+      }
+   this->m_j_gray = new_j_gray;
+
+   i = 0;
+   for(; i < this->m_outer_summands; i++)
+      {
+      this->m_Aij[i] ^= this->m_Lik[Lik_pos_base + i];
+      }
+
+   }
+
+void gf2m_decomp_rootfind_state::calc_LiK(const polyn_gf2m & sigma)
+   {
+   std::shared_ptr<GF2m_Field> sp_field = sigma.get_sp_field();
+   uint32_t i, k, d;
+   d = sigma.get_degree();
+   for(k = 0; k < sp_field->get_extension_degree(); k++)
+      {
+      uint32_t Lik_pos_base = k * this->m_outer_summands;
+      gf2m alpha_l_k_tt2_ttj[4];
+      alpha_l_k_tt2_ttj[0] = sp_field->gf_l_from_n(static_cast<gf2m>(1) << k);
+      alpha_l_k_tt2_ttj[1] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[0], alpha_l_k_tt2_ttj[0]);
+      alpha_l_k_tt2_ttj[2] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[1],alpha_l_k_tt2_ttj[1] );
+
+      alpha_l_k_tt2_ttj[3] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[2], alpha_l_k_tt2_ttj[2]);
+      for(i = 0; i < this->m_outer_summands; i++)
+         {
+         uint32_t j;
+         uint32_t five_i = 5*i;
+         uint32_t Lik_pos = Lik_pos_base + i;
+         this->m_Lik[Lik_pos] = 0;
+         for(j = 0; j <= 3; j++)
+            {
+            gf2m f, x;
+            uint32_t f_ind = five_i + (static_cast<uint32_t>(1) << j);
+            if(f_ind > d)
+               {
+               break;
+               }
+            f = sigma.get_coef( f_ind);
+
+            x = sp_field->gf_mul_zrz(alpha_l_k_tt2_ttj[j], f);
+            this->m_Lik[Lik_pos] ^= x;
+            }
+         }
+      }
+   }
+
+gf2m gf2m_decomp_rootfind_state::calc_Fxj_j_neq_0( const polyn_gf2m & sigma, gf2m j_gray)
+   {
+   //needs the A_{ij} to compute F(x)_j
+   gf2m sum = 0;
+   uint32_t i;
+   std::shared_ptr<GF2m_Field> sp_field = sigma.get_sp_field();
+   const gf2m jl_gray = sp_field->gf_l_from_n(j_gray);
+   gf2m xl_j_tt_5 = sp_field->gf_square_rr(jl_gray);
+   gf2m xl_gray_tt_3 = sp_field->gf_mul_rrr(xl_j_tt_5, jl_gray);
+   xl_j_tt_5 = sp_field->gf_mul_rrr(xl_j_tt_5, xl_gray_tt_3);
+
+
+   sum = sp_field->gf_mul_nrr(xl_gray_tt_3, this->m_sigma_3_l);
+   sum &= this->m_sigma_3_neq_0_mask;
+   /* here, we rely on compiler to be unable to optimize
+   * for the state->sigma_3_neq_0_mask value
+   */
+   /* treat i = 0 special: */
+   sum ^= this->m_Aij[0];
+   /* treat i = 1 special also */
+
+   if(this->m_outer_summands > 1)
+      {
+      gf2m x;
+      x = sp_field->gf_mul_zrz(xl_j_tt_5, this->m_Aij[1]); /* x_j^{5i} A_i^j */
+      sum ^= x;
+      }
+
+   gf2m xl_j_tt_5i = xl_j_tt_5;
+
+   for(i = 2; i < this->m_outer_summands; i++)
+      {
+      gf2m x;
+      xl_j_tt_5i = sp_field->gf_mul_rrr(xl_j_tt_5i, xl_j_tt_5);
+      // now x_j_tt_5i lives up to its name
+      x = sp_field->gf_mul_zrz(xl_j_tt_5i, this->m_Aij[i]); /* x_j^{5i} A_i^(j) */
+      sum ^= x;
+      }
+   return sum;
+   }
+
+secure_vector<gf2m> gf2m_decomp_rootfind_state::find_roots(const polyn_gf2m & sigma)
+   {
+   const int sigma_degree = sigma.get_degree();
+   BOTAN_ASSERT(sigma_degree > 0, "Valid sigma");
+   secure_vector<gf2m> result(sigma_degree);
+   uint32_t root_pos = 0;
+
+   this->calc_Ai_zero(sigma);
+   this->calc_LiK(sigma);
+   for(;;)
+      {
+      gf2m eval_result;
+
+      if(this->m_j_gray == 0)
+         {
+         eval_result = sigma.get_coef(0);
+         }
+      else
+         {
+         eval_result = this->calc_Fxj_j_neq_0(sigma, this->m_j_gray);
+         }
+
+      if(eval_result == 0)
+         {
+         result[root_pos] = this->m_j_gray;
+         root_pos++;
+         }
+
+      if(this->m_j + static_cast<uint32_t>(1) == m_code_length)
+         {
+         break;
+         }
+      this->calc_next_Aij();
+      }
+
+   // side channel / fault attack countermeasure:
+   patch_root_array(result.data(), result.size(), root_pos);
+   return result;
+   }
+
+} // end anonymous namespace
+
+secure_vector<gf2m> find_roots_gf2m_decomp(const polyn_gf2m & polyn, size_t code_length)
+   {
+   gf2m_decomp_rootfind_state state(polyn, code_length);
+   return state.find_roots(polyn);
+   }
+
+} // end namespace Botan
diff --git a/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp b/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp
@@ -0,0 +1,126 @@
+/*
+* (C) Copyright Projet SECRET, INRIA, Rocquencourt
+* (C) Bhaskar Biswas and  Nicolas Sendrier
+*
+* (C) 2014 cryptosource GmbH
+* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gf2m_small_m.h>
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+#define MAX_EXT_DEG 16
+
+namespace {
+
+gf2m prim_poly[MAX_EXT_DEG + 1] = {
+   01,       /* extension degree 0 (!) never used */
+   03,       /* extension degree 1 (!) never used */
+   07,       /* extension degree 2 */
+   013,      /* extension degree 3 */
+   023,      /* extension degree 4 */
+   045,      /* extension degree 5 */
+   0103,     /* extension degree 6 */
+   0203,     /* extension degree 7 */
+   0435,     /* extension degree 8 */
+   01041,    /* extension degree 9 */
+   02011,    /* extension degree 10 */
+   04005,    /* extension degree 11 */
+   010123,   /* extension degree 12 */
+   020033,   /* extension degree 13 */
+   042103,   /* extension degree 14 */
+   0100003,  /* extension degree 15 */
+};
+
+std::vector<gf2m> gf_exp_table(size_t deg, gf2m prime_poly)
+   {
+   // construct the table gf_exp[i]=alpha^i
+
+   std::vector<gf2m> tab((static_cast<size_t>(1) << deg) + 1);
+
+   tab[0] = 1;
+   for(size_t i = 1; i < tab.size(); ++i)
+      {
+      const gf2m overflow = tab[i-1] >> (deg - 1);
+      tab[i] = (tab[i-1] << 1) ^ (overflow * prime_poly);
+      }
+
+   return tab;
+   }
+
+const std::vector<gf2m>& exp_table(size_t deg)
+   {
+   static std::vector<gf2m> tabs[MAX_EXT_DEG + 1];
+
+   if(deg < 2 || deg > MAX_EXT_DEG)
+      throw Invalid_Argument("GF2m_Field does not support degree " + std::to_string(deg));
+
+   if(tabs[deg].empty())
+      tabs[deg] = gf_exp_table(deg, prim_poly[deg]);
+
+   return tabs[deg];
+   }
+
+std::vector<gf2m> gf_log_table(size_t deg, const std::vector<gf2m>& exp)
+   {
+   std::vector<gf2m> tab(static_cast<size_t>(1) << deg);
+
+   tab[0] = static_cast<gf2m>((static_cast<gf2m>(1) << deg) - 1); // log of 0 is the order by convention
+   for(size_t i = 0; i < tab.size(); ++i)
+      {
+      tab[exp[i]] = static_cast<gf2m>(i);
+      }
+   return tab;
+   }
+
+const std::vector<gf2m>& log_table(size_t deg)
+   {
+   static std::vector<gf2m> tabs[MAX_EXT_DEG + 1];
+
+   if(deg < 2 || deg > MAX_EXT_DEG)
+      throw Invalid_Argument("GF2m_Field does not support degree " + std::to_string(deg));
+
+   if(tabs[deg].empty())
+      tabs[deg] = gf_log_table(deg, exp_table(deg));
+
+   return tabs[deg];
+   }
+
+}
+
+uint32_t encode_gf2m(gf2m to_enc, uint8_t* mem)
+   {
+   mem[0] = to_enc >> 8;
+   mem[1] = to_enc & 0xFF;
+   return sizeof(to_enc);
+   }
+
+gf2m decode_gf2m(const uint8_t* mem)
+   {
+   gf2m result;
+   result = mem[0] << 8;
+   result |= mem[1];
+   return result;
+   }
+
+GF2m_Field::GF2m_Field(size_t extdeg) : m_gf_extension_degree(extdeg),
+                                        m_gf_multiplicative_order((1 << extdeg) - 1),
+                                        m_gf_log_table(log_table(m_gf_extension_degree)),
+                                        m_gf_exp_table(exp_table(m_gf_extension_degree))
+   {
+   }
+
+gf2m GF2m_Field::gf_div(gf2m x, gf2m y) const
+   {
+   const int32_t sub_res = static_cast<int32_t>(gf_log(x) - static_cast<int32_t>(gf_log(y)));
+   const gf2m modq_res = _gf_modq_1(sub_res);
+   const int32_t div_res = static_cast<int32_t>(x) ? static_cast<int32_t>(gf_exp(modq_res)) : 0;
+   return static_cast<gf2m>(div_res);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h b/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h
@@ -0,0 +1,222 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_GF2M_SMALL_M_H_
+#define BOTAN_GF2M_SMALL_M_H_
+
+#include <botan/types.h>
+#include <vector>
+
+// fixme - still used in mceliece.h
+//BOTAN_FUTURE_INTERNAL_HEADER(gf2m_small_m.h)
+
+namespace Botan {
+
+typedef uint16_t gf2m;
+
+/**
+* GF(2^m) field for m = [2...16]
+*/
+class BOTAN_PUBLIC_API(2,0) GF2m_Field
+   {
+   public:
+      explicit GF2m_Field(size_t extdeg);
+
+      gf2m gf_mul(gf2m x, gf2m y) const
+         {
+         return ((x) ? gf_mul_fast(x, y) : 0);
+         }
+
+      gf2m gf_square(gf2m x) const
+         {
+         return ((x) ? gf_exp(_gf_modq_1(gf_log(x) << 1)) : 0);
+         }
+
+      gf2m square_rr(gf2m x) const
+         {
+         return _gf_modq_1(x << 1);
+         }
+
+      gf2m gf_mul_fast(gf2m x, gf2m y) const
+         {
+         return ((y) ? gf_exp(_gf_modq_1(gf_log(x) + gf_log(y))) : 0);
+         }
+
+      /*
+      naming convention of GF(2^m) field operations:
+        l logarithmic, unreduced
+        r logarithmic, reduced
+        n normal, non-zero
+        z normal, might be zero
+      */
+
+      gf2m gf_mul_lll(gf2m a, gf2m b) const
+         {
+         return (a + b);
+         }
+
+      gf2m gf_mul_rrr(gf2m a, gf2m b) const
+         {
+         return (_gf_modq_1(gf_mul_lll(a, b)));
+         }
+
+      gf2m gf_mul_nrr(gf2m a, gf2m b) const
+         {
+         return (gf_exp(gf_mul_rrr(a, b)));
+         }
+
+      gf2m gf_mul_rrn(gf2m a, gf2m y) const
+         {
+         return _gf_modq_1(gf_mul_lll(a, gf_log(y)));
+         }
+
+      gf2m gf_mul_rnr(gf2m y, gf2m a) const
+         {
+         return gf_mul_rrn(a, y);
+         }
+
+      gf2m gf_mul_lnn(gf2m x, gf2m y) const
+         {
+         return (gf_log(x) + gf_log(y));
+         }
+
+      gf2m gf_mul_rnn(gf2m x, gf2m y) const
+         {
+         return _gf_modq_1(gf_mul_lnn(x, y));
+         }
+
+      gf2m gf_mul_nrn(gf2m a, gf2m y) const
+         {
+         return gf_exp(_gf_modq_1((a) + gf_log(y)));
+         }
+
+      /**
+      * zero operand allowed
+      */
+      gf2m gf_mul_zrz(gf2m a, gf2m y) const
+         {
+         return ( (y == 0) ? 0 : gf_mul_nrn(a, y) );
+         }
+
+      gf2m gf_mul_zzr(gf2m a, gf2m y) const
+         {
+         return gf_mul_zrz(y, a);
+         }
+
+      /**
+      * non-zero operand
+      */
+      gf2m gf_mul_nnr(gf2m y, gf2m a) const
+         {
+         return gf_mul_nrn(a, y);
+         }
+
+      gf2m gf_sqrt(gf2m x) const
+         {
+         return ((x) ? gf_exp(_gf_modq_1(gf_log(x) << (get_extension_degree()-1))) : 0);
+         }
+
+      gf2m gf_div_rnn(gf2m x, gf2m y) const
+         {
+         return _gf_modq_1(gf_log(x) - gf_log(y));
+         }
+
+      gf2m gf_div_rnr(gf2m x, gf2m b) const
+         {
+         return _gf_modq_1(gf_log(x) - b);
+         }
+
+      gf2m gf_div_nrr(gf2m a, gf2m b) const
+         {
+         return gf_exp(_gf_modq_1(a - b));
+         }
+
+      gf2m gf_div_zzr(gf2m x, gf2m b) const
+         {
+         return ((x) ? gf_exp(_gf_modq_1(gf_log(x) - b)) : 0);
+         }
+
+      gf2m gf_inv(gf2m x) const
+         {
+         return gf_exp(gf_ord() - gf_log(x));
+         }
+
+      gf2m gf_inv_rn(gf2m x) const
+         {
+         return (gf_ord() - gf_log(x));
+         }
+
+      gf2m gf_square_ln(gf2m x) const
+         {
+         return gf_log(x) << 1;
+         }
+
+      gf2m gf_square_rr(gf2m a) const
+         {
+         return a << 1;
+         }
+
+      gf2m gf_l_from_n(gf2m x) const
+         {
+         return gf_log(x);
+         }
+
+      gf2m gf_div(gf2m x, gf2m y) const;
+
+      gf2m gf_exp(gf2m i) const
+         {
+         return m_gf_exp_table.at(i); /* alpha^i */
+         }
+
+      gf2m gf_log(gf2m i) const
+         {
+         return m_gf_log_table.at(i); /* return i when x=alpha^i */
+         }
+
+      gf2m gf_ord() const
+         {
+         return m_gf_multiplicative_order;
+         }
+
+      size_t get_extension_degree() const
+         {
+         return m_gf_extension_degree;
+         }
+
+      gf2m get_cardinality() const
+         {
+         return static_cast<gf2m>(1 << get_extension_degree());
+         }
+
+   private:
+      gf2m _gf_modq_1(int32_t d) const
+         {
+         /* residual modulo q-1
+         when -q < d < 0, we get (q-1+d)
+         when 0 <= d < q, we get (d)
+         when q <= d < 2q-1, we get (d-q+1)
+         */
+         return static_cast<gf2m>(((d) & gf_ord()) + ((d) >> get_extension_degree()));
+         }
+
+      const size_t m_gf_extension_degree;
+      const gf2m m_gf_multiplicative_order;
+      const std::vector<gf2m>& m_gf_log_table;
+      const std::vector<gf2m>& m_gf_exp_table;
+   };
+
+uint32_t encode_gf2m(gf2m to_enc, uint8_t* mem);
+
+gf2m decode_gf2m(const uint8_t* mem);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp b/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp
@@ -0,0 +1,234 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/code_based_util.h>
+
+namespace Botan {
+
+namespace {
+
+void matrix_arr_mul(std::vector<uint32_t> matrix,
+                    size_t numo_rows,
+                    size_t words_per_row,
+                    const uint8_t input_vec[],
+                    uint32_t output_vec[],
+                    size_t output_vec_len)
+   {
+   for(size_t j = 0; j < numo_rows; j++)
+      {
+      if((input_vec[j / 8] >> (j % 8)) & 1)
+         {
+         for(size_t i = 0; i < output_vec_len; i++)
+            {
+            output_vec[i] ^= matrix[j * (words_per_row) + i];
+            }
+         }
+      }
+   }
+
+/**
+* returns the error vector to the syndrome
+*/
+secure_vector<gf2m> goppa_decode(const polyn_gf2m & syndrom_polyn,
+                                 const polyn_gf2m & g,
+                                 const std::vector<polyn_gf2m> & sqrtmod,
+                                 const std::vector<gf2m> & Linv)
+   {
+   const size_t code_length = Linv.size();
+   gf2m a;
+   uint32_t t = g.get_degree();
+
+   std::shared_ptr<GF2m_Field> sp_field = g.get_sp_field();
+
+   std::pair<polyn_gf2m, polyn_gf2m> h_aux = polyn_gf2m::eea_with_coefficients( syndrom_polyn, g, 1);
+   polyn_gf2m & h = h_aux.first;
+   polyn_gf2m & aux = h_aux.second;
+   a = sp_field->gf_inv(aux.get_coef(0));
+   gf2m log_a = sp_field->gf_log(a);
+   for(int i = 0; i <= h.get_degree(); ++i)
+      {
+      h.set_coef(i,sp_field->gf_mul_zrz(log_a,h.get_coef(i)));
+      }
+
+   //  compute h(z) += z
+   h.add_to_coef( 1, 1);
+   // compute S square root of h (using sqrtmod)
+   polyn_gf2m S(t - 1, g.get_sp_field());
+
+   for(uint32_t i=0;i<t;i++)
+      {
+      a = sp_field->gf_sqrt(h.get_coef(i));
+
+      if(i & 1)
+         {
+         for(uint32_t j=0;j<t;j++)
+            {
+            S.add_to_coef( j, sp_field->gf_mul(a, sqrtmod[i/2].get_coef(j)));
+            }
+         }
+      else
+         {
+         S.add_to_coef( i/2, a);
+         }
+      } /* end for loop (i) */
+
+
+   S.get_degree();
+
+   std::pair<polyn_gf2m, polyn_gf2m> v_u = polyn_gf2m::eea_with_coefficients(S, g, t/2+1);
+   polyn_gf2m & u = v_u.second;
+   polyn_gf2m & v = v_u.first;
+
+   // sigma = u^2+z*v^2
+   polyn_gf2m sigma ( t , g.get_sp_field());
+
+   const int u_deg = u.get_degree();
+   BOTAN_ASSERT(u_deg >= 0, "Valid degree");
+   for(int i = 0; i <= u_deg; ++i)
+      {
+      sigma.set_coef(2*i, sp_field->gf_square(u.get_coef(i)));
+      }
+
+   const int v_deg = v.get_degree();
+   BOTAN_ASSERT(v_deg >= 0, "Valid degree");
+   for(int i = 0; i <= v_deg; ++i)
+      {
+      sigma.set_coef(2*i+1, sp_field->gf_square(v.get_coef(i)));
+      }
+
+   secure_vector<gf2m> res = find_roots_gf2m_decomp(sigma, code_length);
+   size_t d = res.size();
+
+   secure_vector<gf2m> result(d);
+   for(uint32_t i = 0; i < d; ++i)
+      {
+      gf2m current = res[i];
+
+      gf2m tmp;
+      tmp = gray_to_lex(current);
+      /// XXX double assignment, possible bug?
+      if(tmp >= code_length) /* invalid root */
+         {
+         result[i] = static_cast<gf2m>(i);
+         }
+      result[i] = Linv[tmp];
+      }
+
+   return result;
+   }
+}
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+                      secure_vector<uint8_t>& error_mask_out,
+                      const secure_vector<uint8_t>& ciphertext,
+                      const McEliece_PrivateKey& key)
+   {
+   mceliece_decrypt(plaintext_out, error_mask_out, ciphertext.data(), ciphertext.size(), key);
+   }
+
+void mceliece_decrypt(
+   secure_vector<uint8_t>& plaintext,
+   secure_vector<uint8_t> & error_mask,
+   const uint8_t ciphertext[],
+   size_t ciphertext_len,
+   const McEliece_PrivateKey & key)
+   {
+   secure_vector<gf2m> error_pos;
+   plaintext = mceliece_decrypt(error_pos, ciphertext, ciphertext_len, key);
+
+   const size_t code_length = key.get_code_length();
+   secure_vector<uint8_t> result((code_length+7)/8);
+   for(auto&& pos : error_pos)
+      {
+      if(pos > code_length)
+         {
+         throw Invalid_Argument("error position larger than code size");
+         }
+      result[pos / 8] |= (1 << (pos % 8));
+      }
+
+   error_mask = result;
+   }
+
+/**
+* @p p_err_pos_len must point to the available length of @p error_pos on input, the
+* function will set it to the actual number of errors returned in the @p error_pos
+* array */
+secure_vector<uint8_t> mceliece_decrypt(
+   secure_vector<gf2m> & error_pos,
+   const uint8_t *ciphertext, size_t ciphertext_len,
+   const McEliece_PrivateKey & key)
+   {
+
+   const size_t dimension = key.get_dimension();
+   const size_t codimension = key.get_codimension();
+   const uint32_t t = key.get_goppa_polyn().get_degree();
+   polyn_gf2m syndrome_polyn(key.get_goppa_polyn().get_sp_field()); // init as zero polyn
+   const unsigned unused_pt_bits = dimension % 8;
+   const uint8_t unused_pt_bits_mask = (1 << unused_pt_bits) - 1;
+
+   if(ciphertext_len != (key.get_code_length()+7)/8)
+      {
+      throw Invalid_Argument("wrong size of McEliece ciphertext");
+      }
+   const size_t cleartext_len = (key.get_message_word_bit_length()+7)/8;
+
+   if(cleartext_len != bit_size_to_byte_size(dimension))
+      {
+      throw Invalid_Argument("mce-decryption: wrong length of cleartext buffer");
+      }
+
+   secure_vector<uint32_t> syndrome_vec(bit_size_to_32bit_size(codimension));
+   matrix_arr_mul(key.get_H_coeffs(),
+                  key.get_code_length(),
+                  bit_size_to_32bit_size(codimension),
+                  ciphertext,
+                  syndrome_vec.data(), syndrome_vec.size());
+
+   secure_vector<uint8_t> syndrome_byte_vec(bit_size_to_byte_size(codimension));
+   const size_t syndrome_byte_vec_size = syndrome_byte_vec.size();
+   for(size_t i = 0; i < syndrome_byte_vec_size; i++)
+      {
+      syndrome_byte_vec[i] = static_cast<uint8_t>(syndrome_vec[i/4] >> (8 * (i % 4)));
+      }
+
+   syndrome_polyn = polyn_gf2m(t-1, syndrome_byte_vec.data(), bit_size_to_byte_size(codimension), key.get_goppa_polyn().get_sp_field());
+
+   syndrome_polyn.get_degree();
+   error_pos = goppa_decode(syndrome_polyn, key.get_goppa_polyn(), key.get_sqrtmod(), key.get_Linv());
+
+   const size_t nb_err = error_pos.size();
+
+   secure_vector<uint8_t> cleartext(cleartext_len);
+   copy_mem(cleartext.data(), ciphertext, cleartext_len);
+
+   for(size_t i = 0; i < nb_err; i++)
+      {
+      gf2m current = error_pos[i];
+
+      if(current >= cleartext_len * 8)
+         {
+         // an invalid position, this shouldn't happen
+         continue;
+         }
+      cleartext[current / 8] ^= (1 << (current % 8));
+      }
+
+   if(unused_pt_bits)
+      {
+      cleartext[cleartext_len - 1] &= unused_pt_bits_mask;
+      }
+
+   return cleartext;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/mce_internal.h b/third_party/botan/src/lib/pubkey/mce/mce_internal.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/mce_internal.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_MCELIECE_INTERNAL_H_
+#define BOTAN_MCELIECE_INTERNAL_H_
+
+#include <botan/secmem.h>
+#include <botan/types.h>
+#include <botan/pk_ops.h>
+#include <botan/mceliece.h>
+
+namespace Botan {
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+                      secure_vector<uint8_t>& error_mask_out,
+                      const uint8_t ciphertext[],
+                      size_t ciphertext_len,
+                      const McEliece_PrivateKey& key);
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+                      secure_vector<uint8_t>& error_mask_out,
+                      const secure_vector<uint8_t>& ciphertext,
+                      const McEliece_PrivateKey& key);
+
+secure_vector<uint8_t> mceliece_decrypt(
+   secure_vector<gf2m> & error_pos,
+   const uint8_t *ciphertext, size_t ciphertext_len,
+   const McEliece_PrivateKey & key);
+
+void mceliece_encrypt(secure_vector<uint8_t>& ciphertext_out,
+                      secure_vector<uint8_t>& error_mask_out,
+                      const secure_vector<uint8_t>& plaintext,
+                      const McEliece_PublicKey& key,
+                      RandomNumberGenerator& rng);
+
+McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator &rng,
+                                          size_t ext_deg,
+                                          size_t code_length,
+                                          size_t t);
+
+}
+
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp b/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ * (C) 2014 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/internal/bit_ops.h>
+#include <cmath>
+
+namespace Botan {
+
+namespace {
+
+double binomial(size_t n, size_t k)
+   {
+   double x = 1;
+
+   for(size_t i = 0; i != k; ++i)
+      {
+      x *= n - i;
+      x /= k -i;
+      }
+
+   return x;
+   }
+
+double log_binomial(size_t n, size_t k)
+   {
+   double x = 0;
+
+   for(size_t i = 0; i != k; ++i)
+      {
+      x += std::log(n - i);
+      x -= std::log(k - i);
+      }
+
+   return x / std::log(2);
+   }
+
+double nb_iter(size_t n, size_t k, size_t w, size_t p, size_t l)
+   {
+   double x = 2 * log_binomial(k / 2, p);
+   x += log_binomial(n - k - l, w - 2 * p);
+   x = log_binomial(n, w) - x;
+   return x;
+   }
+
+double cout_iter(size_t n, size_t k, size_t p, size_t l)
+   {
+   double x = binomial(k / 2, p);
+   const size_t i = static_cast<size_t>(std::log(x) / std::log(2));
+   double res = 2 * p * (n - k - l) * std::ldexp(x * x, -static_cast<int>(l));
+
+   // x <- binomial(k/2,p)*2*(2*l+log[2](binomial(k/2,p)))
+   x *= 2 * (2 * l + i);
+
+   // res <- k*(n-k)/2 +
+   // binomial(k/2,p)*2*(2*l+log[2](binomial(k/2,p))) +
+   // 2*p*(n-k-l)*binomial(k/2,p)^2/2^l
+   res += x + k * ((n - k) / 2.0);
+
+   return std::log(res) / std::log(2); // convert to bits
+   }
+
+double cout_total(size_t n, size_t k, size_t w, size_t p, size_t l)
+   {
+   return nb_iter(n, k, w, p, l) + cout_iter(n, k, p, l);
+   }
+
+double best_wf(size_t n, size_t k, size_t w, size_t p)
+   {
+   if(p >= k / 2)
+      return -1;
+
+   double min = cout_total(n, k, w, p, 0);
+
+   for(size_t l = 1; l < n - k; ++l)
+      {
+      const double lwf = cout_total(n, k, w, p, l);
+      if(lwf < min)
+         min = lwf;
+      else
+         break;
+      }
+
+   return min;
+   }
+
+}
+
+size_t mceliece_work_factor(size_t n, size_t t)
+   {
+   const size_t k = n - ceil_log2(n) * t;
+
+   double min = cout_total(n, k, t, 0, 0); // correspond a p=1
+   for(size_t p = 0; p != t / 2; ++p)
+      {
+      double lwf = best_wf(n, k + 1, t, p);
+      if(lwf < 0)
+         break;
+
+      min = std::min(min, lwf);
+      }
+
+   return static_cast<size_t>(min);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/mceliece.cpp b/third_party/botan/src/lib/pubkey/mce/mceliece.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/mceliece.cpp
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/internal/mce_internal.h>
+#include <botan/mceliece.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> concat_vectors(const secure_vector<uint8_t>& a,
+                                      const secure_vector<uint8_t>& b,
+                                      size_t dimension,
+                                      size_t codimension)
+   {
+   secure_vector<uint8_t> x(bit_size_to_byte_size(dimension) + bit_size_to_byte_size(codimension));
+
+   const size_t final_bits = dimension % 8;
+
+   if(final_bits == 0)
+      {
+      const size_t dim_bytes = bit_size_to_byte_size(dimension);
+      copy_mem(&x[0], a.data(), dim_bytes);
+      copy_mem(&x[dim_bytes], b.data(), bit_size_to_byte_size(codimension));
+      }
+   else
+      {
+      copy_mem(&x[0], a.data(), (dimension / 8));
+      size_t l = dimension / 8;
+      x[l] = static_cast<uint8_t>(a[l] & ((1 << final_bits) - 1));
+
+      for(size_t k = 0; k < codimension / 8; ++k)
+         {
+         x[l] ^= static_cast<uint8_t>(b[k] << final_bits);
+         ++l;
+         x[l] = static_cast<uint8_t>(b[k] >> (8 - final_bits));
+         }
+      x[l] ^= static_cast<uint8_t>(b[codimension/8] << final_bits);
+      }
+
+   return x;
+   }
+
+secure_vector<uint8_t> mult_by_pubkey(const secure_vector<uint8_t>& cleartext,
+                                      std::vector<uint8_t> const& public_matrix,
+                                      size_t code_length, size_t t)
+   {
+   const size_t ext_deg = ceil_log2(code_length);
+   const size_t codimension = ext_deg * t;
+   const size_t dimension = code_length - codimension;
+   secure_vector<uint8_t> cR(bit_size_to_32bit_size(codimension) * sizeof(uint32_t));
+
+   const uint8_t* pt = public_matrix.data();
+
+   for(size_t i = 0; i < dimension / 8; ++i)
+      {
+      for(size_t j = 0; j < 8; ++j)
+         {
+         if(cleartext[i] & (1 << j))
+            {
+            xor_buf(cR.data(), pt, cR.size());
+            }
+         pt += cR.size();
+         }
+      }
+
+   for(size_t i = 0; i < dimension % 8 ; ++i)
+      {
+      if(cleartext[dimension/8] & (1 << i))
+         {
+         xor_buf(cR.data(), pt, cR.size());
+         }
+      pt += cR.size();
+      }
+
+   secure_vector<uint8_t> ciphertext = concat_vectors(cleartext, cR, dimension, codimension);
+   ciphertext.resize((code_length+7)/8);
+   return ciphertext;
+   }
+
+secure_vector<uint8_t> create_random_error_vector(size_t code_length,
+                                                  size_t error_weight,
+                                                  RandomNumberGenerator& rng)
+   {
+   secure_vector<uint8_t> result((code_length+7)/8);
+
+   size_t bits_set = 0;
+
+   while(bits_set < error_weight)
+      {
+      gf2m x = random_code_element(static_cast<uint16_t>(code_length), rng);
+
+      const size_t byte_pos = x / 8;
+      const size_t bit_pos = x % 8;
+
+      const uint8_t mask = (1 << bit_pos);
+
+      if(result[byte_pos] & mask)
+         continue; // already set this bit
+
+      result[byte_pos] |= mask;
+      bits_set++;
+      }
+
+   return result;
+   }
+
+}
+
+void mceliece_encrypt(secure_vector<uint8_t>& ciphertext_out,
+                      secure_vector<uint8_t>& error_mask_out,
+                      const secure_vector<uint8_t>& plaintext,
+                      const McEliece_PublicKey& key,
+                      RandomNumberGenerator& rng)
+   {
+   const uint16_t code_length = static_cast<uint16_t>(key.get_code_length());
+
+   secure_vector<uint8_t> error_mask = create_random_error_vector(code_length, key.get_t(), rng);
+
+   secure_vector<uint8_t> ciphertext = mult_by_pubkey(plaintext, key.get_public_matrix(),
+                                                      key.get_code_length(), key.get_t());
+
+   ciphertext ^= error_mask;
+
+   ciphertext_out.swap(ciphertext);
+   error_mask_out.swap(error_mask);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/mceliece.h b/third_party/botan/src/lib/pubkey/mce/mceliece.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/mceliece.h
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_MCELIECE_KEY_H_
+#define BOTAN_MCELIECE_KEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/polyn_gf2m.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) McEliece_PublicKey : public virtual Public_Key
+   {
+   public:
+      explicit McEliece_PublicKey(const std::vector<uint8_t>& key_bits);
+
+      McEliece_PublicKey(const std::vector<uint8_t>& pub_matrix, size_t t, size_t the_code_length) :
+         m_public_matrix(pub_matrix),
+         m_t(t),
+         m_code_length(the_code_length){}
+
+      McEliece_PublicKey(const McEliece_PublicKey& other) = default;
+      McEliece_PublicKey& operator=(const McEliece_PublicKey& other) = default;
+      virtual ~McEliece_PublicKey()= default;
+
+      secure_vector<uint8_t> random_plaintext_element(RandomNumberGenerator& rng) const;
+
+      std::string algo_name() const override { return "McEliece"; }
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      size_t key_length() const override;
+      size_t estimated_strength() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      bool check_key(RandomNumberGenerator&, bool) const override
+         { return true; }
+
+      size_t get_t() const { return m_t; }
+      size_t get_code_length() const { return m_code_length; }
+      size_t get_message_word_bit_length() const;
+      const std::vector<uint8_t>& get_public_matrix() const { return m_public_matrix; }
+
+      bool operator==(const McEliece_PublicKey& other) const;
+      bool operator!=(const McEliece_PublicKey& other) const { return !(*this == other); }
+
+      std::unique_ptr<PK_Ops::KEM_Encryption>
+         create_kem_encryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const override;
+
+   protected:
+      McEliece_PublicKey() : m_t(0), m_code_length(0) {}
+
+      std::vector<uint8_t> m_public_matrix;
+      size_t m_t;
+      size_t m_code_length;
+   };
+
+class BOTAN_PUBLIC_API(2,0) McEliece_PrivateKey final : public virtual McEliece_PublicKey,
+                                      public virtual Private_Key
+   {
+   public:
+
+      /**
+      Generate a McEliece key pair
+
+      Suggested parameters for a given security level (SL)
+
+      SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
+      SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
+      SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
+      SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
+      SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
+      SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
+      */
+      McEliece_PrivateKey(RandomNumberGenerator& rng, size_t code_length, size_t t);
+
+      explicit McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits);
+
+      McEliece_PrivateKey(polyn_gf2m const& goppa_polyn,
+                          std::vector<uint32_t> const& parity_check_matrix_coeffs,
+                          std::vector<polyn_gf2m> const& square_root_matrix,
+                          std::vector<gf2m> const& inverse_support,
+                          std::vector<uint8_t> const& public_matrix );
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+      polyn_gf2m const& get_goppa_polyn() const { return m_g; }
+      std::vector<uint32_t> const& get_H_coeffs() const { return m_coeffs; }
+      std::vector<gf2m> const& get_Linv() const { return m_Linv; }
+      std::vector<polyn_gf2m> const& get_sqrtmod() const { return m_sqrtmod; }
+
+      inline size_t get_dimension() const { return m_dimension; }
+
+      inline size_t get_codimension() const { return m_codimension; }
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      bool operator==(const McEliece_PrivateKey & other) const;
+
+      bool operator!=(const McEliece_PrivateKey& other) const { return !(*this == other); }
+
+      std::unique_ptr<PK_Ops::KEM_Decryption>
+         create_kem_decryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const override;
+   private:
+      polyn_gf2m m_g;
+      std::vector<polyn_gf2m> m_sqrtmod;
+      std::vector<gf2m> m_Linv;
+      std::vector<uint32_t> m_coeffs;
+
+      size_t m_codimension;
+      size_t m_dimension;
+   };
+
+/**
+* Estimate work factor for McEliece
+* @return estimated security level for these key parameters
+*/
+BOTAN_PUBLIC_API(2,0) size_t mceliece_work_factor(size_t code_size, size_t t);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp b/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp
@@ -0,0 +1,378 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/loadstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+McEliece_PrivateKey::McEliece_PrivateKey(polyn_gf2m const& goppa_polyn,
+                                         std::vector<uint32_t> const& parity_check_matrix_coeffs,
+                                         std::vector<polyn_gf2m> const& square_root_matrix,
+                                         std::vector<gf2m> const& inverse_support,
+                                         std::vector<uint8_t> const& public_matrix) :
+   McEliece_PublicKey(public_matrix, goppa_polyn.get_degree(), inverse_support.size()),
+   m_g(goppa_polyn),
+   m_sqrtmod(square_root_matrix),
+   m_Linv(inverse_support),
+   m_coeffs(parity_check_matrix_coeffs),
+   m_codimension(static_cast<size_t>(ceil_log2(inverse_support.size())) * goppa_polyn.get_degree()),
+   m_dimension(inverse_support.size() - m_codimension)
+   {
+   }
+
+McEliece_PrivateKey::McEliece_PrivateKey(RandomNumberGenerator& rng, size_t code_length, size_t t)
+   {
+   uint32_t ext_deg = ceil_log2(code_length);
+   *this = generate_mceliece_key(rng, ext_deg, code_length, t);
+   }
+
+size_t McEliece_PublicKey::get_message_word_bit_length() const
+   {
+   size_t codimension = ceil_log2(m_code_length) * m_t;
+   return m_code_length - codimension;
+   }
+
+secure_vector<uint8_t> McEliece_PublicKey::random_plaintext_element(RandomNumberGenerator& rng) const
+   {
+   const size_t bits = get_message_word_bit_length();
+
+   secure_vector<uint8_t> plaintext((bits+7)/8);
+   rng.randomize(plaintext.data(), plaintext.size());
+
+   // unset unused bits in the last plaintext byte
+   if(uint32_t used = bits % 8)
+      {
+      const uint8_t mask = (1 << used) - 1;
+      plaintext[plaintext.size() - 1] &= mask;
+      }
+
+   return plaintext;
+   }
+
+AlgorithmIdentifier McEliece_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   }
+
+std::vector<uint8_t> McEliece_PublicKey::public_key_bits() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .start_cons(SEQUENCE)
+         .encode(static_cast<size_t>(get_code_length()))
+         .encode(static_cast<size_t>(get_t()))
+         .end_cons()
+      .encode(m_public_matrix, OCTET_STRING)
+      .end_cons();
+   return output;
+   }
+
+size_t McEliece_PublicKey::key_length() const
+   {
+   return m_code_length;
+   }
+
+size_t McEliece_PublicKey::estimated_strength() const
+   {
+   return mceliece_work_factor(m_code_length, m_t);
+   }
+
+McEliece_PublicKey::McEliece_PublicKey(const std::vector<uint8_t>& key_bits)
+   {
+   BER_Decoder dec(key_bits);
+   size_t n;
+   size_t t;
+   dec.start_cons(SEQUENCE)
+      .start_cons(SEQUENCE)
+      .decode(n)
+      .decode(t)
+      .end_cons()
+      .decode(m_public_matrix, OCTET_STRING)
+      .end_cons();
+   m_t = t;
+   m_code_length = n;
+   }
+
+secure_vector<uint8_t> McEliece_PrivateKey::private_key_bits() const
+   {
+   DER_Encoder enc;
+   enc.start_cons(SEQUENCE)
+      .start_cons(SEQUENCE)
+      .encode(static_cast<size_t>(get_code_length()))
+      .encode(static_cast<size_t>(get_t()))
+      .end_cons()
+      .encode(m_public_matrix, OCTET_STRING)
+      .encode(m_g.encode(), OCTET_STRING); // g as octet string
+   enc.start_cons(SEQUENCE);
+   for(size_t i = 0; i < m_sqrtmod.size(); i++)
+      {
+      enc.encode(m_sqrtmod[i].encode(), OCTET_STRING);
+      }
+   enc.end_cons();
+   secure_vector<uint8_t> enc_support;
+
+   for(uint16_t Linv : m_Linv)
+      {
+      enc_support.push_back(get_byte(0, Linv));
+      enc_support.push_back(get_byte(1, Linv));
+      }
+   enc.encode(enc_support, OCTET_STRING);
+   secure_vector<uint8_t> enc_H;
+   for(uint32_t coef : m_coeffs)
+      {
+      enc_H.push_back(get_byte(0, coef));
+      enc_H.push_back(get_byte(1, coef));
+      enc_H.push_back(get_byte(2, coef));
+      enc_H.push_back(get_byte(3, coef));
+      }
+   enc.encode(enc_H, OCTET_STRING);
+   enc.end_cons();
+   return enc.get_contents();
+   }
+
+bool McEliece_PrivateKey::check_key(RandomNumberGenerator& rng, bool) const
+   {
+   const secure_vector<uint8_t> plaintext = this->random_plaintext_element(rng);
+
+   secure_vector<uint8_t> ciphertext;
+   secure_vector<uint8_t> errors;
+   mceliece_encrypt(ciphertext, errors, plaintext, *this, rng);
+
+   secure_vector<uint8_t> plaintext_out;
+   secure_vector<uint8_t> errors_out;
+   mceliece_decrypt(plaintext_out, errors_out, ciphertext, *this);
+
+   if(errors != errors_out || plaintext != plaintext_out)
+      return false;
+
+   return true;
+   }
+
+McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
+   {
+   size_t n, t;
+   secure_vector<uint8_t> enc_g;
+   BER_Decoder dec_base(key_bits);
+   BER_Decoder dec = dec_base.start_cons(SEQUENCE)
+      .start_cons(SEQUENCE)
+      .decode(n)
+      .decode(t)
+      .end_cons()
+      .decode(m_public_matrix, OCTET_STRING)
+      .decode(enc_g, OCTET_STRING);
+
+   if(t == 0 || n == 0)
+      throw Decoding_Error("invalid McEliece parameters");
+
+   uint32_t ext_deg = ceil_log2(n);
+   m_code_length = n;
+   m_t = t;
+   m_codimension = (ext_deg * t);
+   m_dimension = (n - m_codimension);
+
+   std::shared_ptr<GF2m_Field> sp_field(new GF2m_Field(ext_deg));
+   m_g = polyn_gf2m(enc_g, sp_field);
+   if(m_g.get_degree() != static_cast<int>(t))
+      {
+      throw Decoding_Error("degree of decoded Goppa polynomial is incorrect");
+      }
+   BER_Decoder dec2 = dec.start_cons(SEQUENCE);
+   for(uint32_t i = 0; i < t/2; i++)
+      {
+      secure_vector<uint8_t> sqrt_enc;
+      dec2.decode(sqrt_enc, OCTET_STRING);
+      while(sqrt_enc.size() < (t*2))
+         {
+         // ensure that the length is always t
+         sqrt_enc.push_back(0);
+         sqrt_enc.push_back(0);
+         }
+      if(sqrt_enc.size() != t*2)
+         {
+         throw Decoding_Error("length of square root polynomial entry is too large");
+         }
+      m_sqrtmod.push_back(polyn_gf2m(sqrt_enc, sp_field));
+      }
+   secure_vector<uint8_t> enc_support;
+   BER_Decoder dec3 = dec2.end_cons()
+      .decode(enc_support, OCTET_STRING);
+   if(enc_support.size() % 2)
+      {
+      throw Decoding_Error("encoded support has odd length");
+      }
+   if(enc_support.size() / 2 != n)
+      {
+      throw Decoding_Error("encoded support has length different from code length");
+      }
+   for(uint32_t i = 0; i < n*2; i+=2)
+      {
+      gf2m el = (enc_support[i] << 8) |  enc_support[i+1];
+      m_Linv.push_back(el);
+      }
+   secure_vector<uint8_t> enc_H;
+   dec3.decode(enc_H, OCTET_STRING)
+      .end_cons();
+   if(enc_H.size() % 4)
+      {
+      throw Decoding_Error("encoded parity check matrix has length which is not a multiple of four");
+      }
+   if(enc_H.size() / 4 != bit_size_to_32bit_size(m_codimension) * m_code_length)
+      {
+      throw Decoding_Error("encoded parity check matrix has wrong length");
+      }
+
+   for(uint32_t i = 0; i < enc_H.size(); i+=4)
+      {
+      uint32_t coeff = (enc_H[i] << 24) | (enc_H[i+1] << 16) | (enc_H[i+2] << 8) | enc_H[i+3];
+      m_coeffs.push_back(coeff);
+      }
+
+   }
+
+bool McEliece_PrivateKey::operator==(const McEliece_PrivateKey & other) const
+   {
+   if(*static_cast<const McEliece_PublicKey*>(this) != *static_cast<const McEliece_PublicKey*>(&other))
+      {
+      return false;
+      }
+   if(m_g != other.m_g)
+      {
+      return false;
+      }
+
+   if( m_sqrtmod != other.m_sqrtmod)
+      {
+      return false;
+      }
+   if( m_Linv != other.m_Linv)
+      {
+      return false;
+      }
+   if( m_coeffs != other.m_coeffs)
+      {
+      return false;
+      }
+
+   if(m_codimension != other.m_codimension || m_dimension != other.m_dimension)
+      {
+      return false;
+      }
+
+   return true;
+   }
+
+bool McEliece_PublicKey::operator==(const McEliece_PublicKey& other) const
+   {
+   if(m_public_matrix != other.m_public_matrix)
+      {
+      return false;
+      }
+   if(m_t != other.m_t)
+      {
+      return false;
+      }
+   if( m_code_length != other.m_code_length)
+      {
+      return false;
+      }
+   return true;
+   }
+
+namespace {
+
+class MCE_KEM_Encryptor final : public PK_Ops::KEM_Encryption_with_KDF
+   {
+   public:
+
+      MCE_KEM_Encryptor(const McEliece_PublicKey& key,
+                        const std::string& kdf) :
+         KEM_Encryption_with_KDF(kdf), m_key(key) {}
+
+   private:
+      void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                           secure_vector<uint8_t>& raw_shared_key,
+                           Botan::RandomNumberGenerator& rng) override
+         {
+         secure_vector<uint8_t> plaintext = m_key.random_plaintext_element(rng);
+
+         secure_vector<uint8_t> ciphertext, error_mask;
+         mceliece_encrypt(ciphertext, error_mask, plaintext, m_key, rng);
+
+         raw_shared_key.clear();
+         raw_shared_key += plaintext;
+         raw_shared_key += error_mask;
+
+         out_encapsulated_key.swap(ciphertext);
+         }
+
+      const McEliece_PublicKey& m_key;
+   };
+
+class MCE_KEM_Decryptor final : public PK_Ops::KEM_Decryption_with_KDF
+   {
+   public:
+
+      MCE_KEM_Decryptor(const McEliece_PrivateKey& key,
+                        const std::string& kdf) :
+         KEM_Decryption_with_KDF(kdf), m_key(key) {}
+
+   private:
+      secure_vector<uint8_t>
+      raw_kem_decrypt(const uint8_t encap_key[], size_t len) override
+         {
+         secure_vector<uint8_t> plaintext, error_mask;
+         mceliece_decrypt(plaintext, error_mask, encap_key, len, m_key);
+
+         secure_vector<uint8_t> output;
+         output.reserve(plaintext.size() + error_mask.size());
+         output.insert(output.end(), plaintext.begin(), plaintext.end());
+         output.insert(output.end(), error_mask.begin(), error_mask.end());
+         return output;
+         }
+
+      const McEliece_PrivateKey& m_key;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+McEliece_PublicKey::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+                                             const std::string& params,
+                                             const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::KEM_Encryption>(new MCE_KEM_Encryptor(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+McEliece_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& /*rng*/,
+                                              const std::string& params,
+                                              const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::KEM_Decryption>(new MCE_KEM_Decryptor(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
+
+
diff --git a/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp b/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp
@@ -0,0 +1,806 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/polyn_gf2m.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/rng.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+gf2m generate_gf2m_mask(gf2m a)
+   {
+   gf2m result =  (a != 0);
+   return ~(result - 1);
+   }
+
+/**
+* number of leading zeros
+*/
+unsigned nlz_16bit(uint16_t x)
+   {
+   unsigned n;
+   if(x == 0) return 16;
+   n = 0;
+   if(x <= 0x00FF) {n = n + 8; x = x << 8;}
+   if(x <= 0x0FFF) {n = n + 4; x = x << 4;}
+   if(x <= 0x3FFF) {n = n + 2; x = x << 2;}
+   if(x <= 0x7FFF) {n = n + 1;}
+   return n;
+   }
+}
+
+int polyn_gf2m::calc_degree_secure() const
+   {
+   int i = static_cast<int>(this->coeff.size()) - 1;
+   int result = 0;
+   uint32_t found_mask = 0;
+   uint32_t tracker_mask = 0xffff;
+   for( ; i >= 0; i--)
+      {
+      found_mask = expand_mask_16bit(this->coeff[i]);
+      result |= i & found_mask & tracker_mask;
+      // tracker mask shall become zero once found mask is set
+      // it shall remain zero from then on
+      tracker_mask = tracker_mask & ~found_mask;
+      }
+   const_cast<polyn_gf2m*>(this)->m_deg = result;
+   return result;
+   }
+
+gf2m random_gf2m(RandomNumberGenerator& rng)
+   {
+   uint8_t b[2];
+   rng.randomize(b, sizeof(b));
+   return make_uint16(b[1], b[0]);
+   }
+
+gf2m random_code_element(uint16_t code_length, RandomNumberGenerator& rng)
+   {
+   if(code_length == 0)
+      {
+      throw Invalid_Argument("random_code_element() was supplied a code length of zero");
+      }
+   const unsigned nlz = nlz_16bit(code_length-1);
+   const gf2m mask = (1 << (16-nlz)) - 1;
+
+   gf2m result;
+
+   do
+      {
+      result = random_gf2m(rng);
+      result &= mask;
+      } while(result >= code_length); // rejection sampling
+
+   return result;
+   }
+
+polyn_gf2m::polyn_gf2m(polyn_gf2m const& other)
+   :m_deg(other.m_deg),
+    coeff(other.coeff),
+    m_sp_field(other.m_sp_field)
+   { }
+
+polyn_gf2m::polyn_gf2m(int d, std::shared_ptr<GF2m_Field> sp_field)
+   :m_deg(-1),
+    coeff(d+1),
+    m_sp_field(sp_field)
+   {
+   }
+
+std::string polyn_gf2m::to_string() const
+   {
+   int d = get_degree();
+   std::string result;
+   for(int i = 0; i <= d; i ++)
+      {
+      result += std::to_string(this->coeff[i]);
+      if(i != d)
+         {
+         result += ", ";
+         }
+      }
+   return result;
+   }
+/**
+* doesn't save coefficients:
+*/
+void polyn_gf2m::realloc(uint32_t new_size)
+   {
+   this->coeff = secure_vector<gf2m>(new_size);
+   }
+
+polyn_gf2m::polyn_gf2m(const uint8_t* mem, uint32_t mem_len, std::shared_ptr<GF2m_Field> sp_field) :
+   m_deg(-1), m_sp_field(sp_field)
+   {
+   if(mem_len % sizeof(gf2m))
+      {
+      throw Decoding_Error("illegal length of memory to decode ");
+      }
+
+   uint32_t size = (mem_len / sizeof(this->coeff[0])) ;
+   this->coeff = secure_vector<gf2m>(size);
+   this->m_deg = -1;
+   for(uint32_t i = 0; i < size; i++)
+      {
+      this->coeff[i] = decode_gf2m(mem);
+      mem += sizeof(this->coeff[0]);
+      }
+   for(uint32_t i = 0; i < size; i++)
+      {
+      if(this->coeff[i] >= (1 << sp_field->get_extension_degree()))
+         {
+         throw Decoding_Error("error decoding polynomial");
+         }
+      }
+   this->get_degree();
+   }
+
+
+polyn_gf2m::polyn_gf2m( std::shared_ptr<GF2m_Field> sp_field) :
+   m_deg(-1), coeff(1), m_sp_field(sp_field)
+   {}
+
+polyn_gf2m::polyn_gf2m(int degree, const uint8_t* mem, size_t mem_byte_len, std::shared_ptr<GF2m_Field> sp_field)
+   :m_sp_field(sp_field)
+   {
+   uint32_t j, k, l;
+   gf2m a;
+   uint32_t polyn_size;
+   polyn_size = degree + 1;
+   if(polyn_size * sp_field->get_extension_degree() > 8 * mem_byte_len)
+      {
+      throw Decoding_Error("memory vector for polynomial has wrong size");
+      }
+   this->coeff = secure_vector<gf2m>(degree+1);
+   gf2m ext_deg = static_cast<gf2m>(this->m_sp_field->get_extension_degree());
+   for (l = 0; l < polyn_size; l++)
+      {
+      k = (l * ext_deg) / 8;
+
+      j = (l * ext_deg) % 8;
+      a = mem[k] >> j;
+      if (j + ext_deg > 8)
+         {
+         a ^= mem[k + 1] << (8- j);
+         }
+      if(j + ext_deg > 16)
+         {
+         a ^= mem[k + 2] << (16- j);
+         }
+      a &= ((1 << ext_deg) - 1);
+      (*this).set_coef( l, a);
+      }
+
+   this->get_degree();
+   }
+
+#if 0
+void polyn_gf2m::encode(uint32_t min_numo_coeffs, uint8_t* mem, uint32_t mem_len) const
+   {
+   uint32_t i;
+   uint32_t numo_coeffs, needed_size;
+   this->get_degree();
+   numo_coeffs = (min_numo_coeffs > static_cast<uint32_t>(this->m_deg+1)) ? min_numo_coeffs : this->m_deg+1;
+   needed_size = sizeof(this->coeff[0]) * numo_coeffs;
+   if(mem_len < needed_size)
+      {
+      Invalid_Argument("provided memory too small to encode polynomial");
+      }
+
+   for(i = 0; i < numo_coeffs; i++)
+      {
+      gf2m to_enc;
+      if(i >= static_cast<uint32_t>(this->m_deg+1))
+         {
+         /* encode a zero */
+         to_enc = 0;
+         }
+      else
+         {
+         to_enc = this->coeff[i];
+         }
+      mem += encode_gf2m(to_enc, mem);
+      }
+   }
+#endif
+
+
+void polyn_gf2m::set_to_zero()
+   {
+   clear_mem(&this->coeff[0], this->coeff.size());
+   this->m_deg = -1;
+   }
+
+int polyn_gf2m::get_degree() const
+   {
+   int d = static_cast<int>(this->coeff.size()) - 1;
+   while ((d >= 0) && (this->coeff[d] == 0))
+      --d;
+   const_cast<polyn_gf2m*>(this)->m_deg = d;
+   return d;
+   }
+
+
+static gf2m eval_aux(const gf2m * /*restrict*/ coeff, gf2m a, int d, std::shared_ptr<GF2m_Field> sp_field)
+   {
+   gf2m b;
+   b = coeff[d--];
+   for (; d >= 0; --d)
+      if (b != 0)
+         {
+         b = sp_field->gf_mul(b, a) ^ coeff[d];
+         }
+      else
+         {
+         b = coeff[d];
+         }
+   return b;
+   }
+
+gf2m polyn_gf2m::eval(gf2m a)
+   {
+   return eval_aux(&this->coeff[0], a, this->m_deg, this->m_sp_field);
+   }
+
+
+// p will contain it's remainder modulo g
+void polyn_gf2m::remainder(polyn_gf2m &p, const polyn_gf2m & g)
+   {
+   int i, j, d;
+   std::shared_ptr<GF2m_Field> m_sp_field = g.m_sp_field;
+   d = p.get_degree() - g.get_degree();
+   if (d >= 0) {
+   gf2m la = m_sp_field->gf_inv_rn(g.get_lead_coef());
+
+   const int p_degree = p.get_degree();
+
+   BOTAN_ASSERT(p_degree > 0, "Valid polynomial");
+
+   for (i = p_degree; d >= 0; --i, --d) {
+   if (p[i] != 0) {
+   gf2m lb = m_sp_field->gf_mul_rrn(la, p[i]);
+   for (j = 0; j < g.get_degree(); ++j)
+      {
+      p[j+d] ^= m_sp_field->gf_mul_zrz(lb, g[j]);
+      }
+   (*&p).set_coef( i, 0);
+   }
+   }
+   p.set_degree( g.get_degree() - 1);
+   while ((p.get_degree() >= 0) && (p[p.get_degree()] == 0))
+      p.set_degree( p.get_degree() - 1);
+   }
+   }
+
+std::vector<polyn_gf2m> polyn_gf2m::sqmod_init(const polyn_gf2m & g)
+   {
+   std::vector<polyn_gf2m> sq;
+   const int signed_deg = g.get_degree();
+   if(signed_deg <= 0)
+      throw Invalid_Argument("cannot compute sqmod for such low degree");
+
+   const uint32_t d = static_cast<uint32_t>(signed_deg);
+   uint32_t t = g.m_deg;
+   // create t zero polynomials
+   uint32_t i;
+   for (i = 0; i < t; ++i)
+      {
+      sq.push_back(polyn_gf2m(t+1, g.get_sp_field()));
+      }
+   for (i = 0; i < d / 2; ++i)
+      {
+      sq[i].set_degree( 2 * i);
+      (*&sq[i]).set_coef( 2 * i, 1);
+      }
+
+   for (; i < d; ++i)
+      {
+      clear_mem(&sq[i].coeff[0], 2);
+      copy_mem(&sq[i].coeff[0] + 2, &sq[i - 1].coeff[0], d);
+      sq[i].set_degree( sq[i - 1].get_degree() + 2);
+      polyn_gf2m::remainder(sq[i], g);
+      }
+   return sq;
+   }
+
+/*Modulo p square of a certain polynomial g, sq[] contains the square
+Modulo g of the base canonical polynomials of degree < d, where d is
+the degree of G. The table sq[] will be calculated by polyn_gf2m_sqmod_init*/
+polyn_gf2m polyn_gf2m::sqmod( const std::vector<polyn_gf2m> & sq, int d)
+   {
+   int i, j;
+   gf2m la;
+   std::shared_ptr<GF2m_Field> sp_field = this->m_sp_field;
+
+   polyn_gf2m result(d - 1, sp_field);
+   // terms of low degree
+   for (i = 0; i < d / 2; ++i)
+      {
+      (*&result).set_coef( i * 2, sp_field->gf_square((*this)[i]));
+      }
+
+   // terms of high degree
+   for (; i < d; ++i)
+      {
+      gf2m lpi = (*this)[i];
+      if (lpi != 0)
+         {
+         lpi = sp_field->gf_log(lpi);
+         la = sp_field->gf_mul_rrr(lpi, lpi);
+         for (j = 0; j < d; ++j)
+            {
+            result[j] ^= sp_field->gf_mul_zrz(la, sq[i][j]);
+            }
+         }
+      }
+
+   // Update degre
+   result.set_degree( d - 1);
+   while ((result.get_degree() >= 0) && (result[result.get_degree()] == 0))
+      result.set_degree( result.get_degree() - 1);
+   return result;
+   }
+
+
+// destructive
+polyn_gf2m polyn_gf2m::gcd_aux(polyn_gf2m& p1, polyn_gf2m& p2)
+   {
+   if (p2.get_degree() == -1)
+      return p1;
+   else {
+   polyn_gf2m::remainder(p1, p2);
+   return polyn_gf2m::gcd_aux(p2, p1);
+   }
+   }
+
+
+polyn_gf2m polyn_gf2m::gcd(polyn_gf2m const& p1, polyn_gf2m const& p2)
+   {
+   polyn_gf2m a(p1);
+   polyn_gf2m b(p2);
+   if (a.get_degree() < b.get_degree())
+      {
+      return polyn_gf2m(polyn_gf2m::gcd_aux(b, a));
+      }
+   else
+      {
+      return polyn_gf2m(polyn_gf2m::gcd_aux(a, b));
+      }
+   }
+
+
+
+
+
+// Returns the degree of the smallest factor
+size_t polyn_gf2m::degppf(const polyn_gf2m& g)
+   {
+   polyn_gf2m s(g.get_sp_field());
+
+   const size_t ext_deg = g.m_sp_field->get_extension_degree();
+   const int d = g.get_degree();
+   std::vector<polyn_gf2m> u = polyn_gf2m::sqmod_init(g);
+
+   polyn_gf2m p(d - 1, g.m_sp_field);
+
+   p.set_degree(1);
+   (*&p).set_coef(1, 1);
+   size_t result = static_cast<size_t>(d);
+   for(size_t i = 1; i <= (d / 2) * ext_deg; ++i)
+      {
+      polyn_gf2m r = p.sqmod(u, d);
+      if ((i % ext_deg) == 0)
+         {
+         r[1] ^= 1;
+         r.get_degree(); // The degree may change
+         s = polyn_gf2m::gcd( g, r);
+
+         if(s.get_degree() > 0)
+            {
+            result = i / ext_deg;
+            break;
+            }
+         r[1] ^= 1;
+         r.get_degree(); // The degree may change
+         }
+      // No need for the exchange s
+      s = p;
+      p = r;
+      r = s;
+      }
+
+   return result;
+   }
+
+void polyn_gf2m::patchup_deg_secure( uint32_t trgt_deg, volatile gf2m patch_elem)
+   {
+   uint32_t i;
+   if(this->coeff.size() < trgt_deg)
+      {
+      return;
+      }
+   for(i = 0; i < this->coeff.size(); i++)
+      {
+      uint32_t equal, equal_mask;
+      this->coeff[i] |= patch_elem;
+      equal = (i == trgt_deg);
+      equal_mask = expand_mask_16bit(equal);
+      patch_elem &= ~equal_mask;
+      }
+   this->calc_degree_secure();
+   }
+// We suppose m_deg(g) >= m_deg(p)
+// v is the problem
+std::pair<polyn_gf2m, polyn_gf2m> polyn_gf2m::eea_with_coefficients( const polyn_gf2m & p, const polyn_gf2m & g, int break_deg)
+   {
+
+   std::shared_ptr<GF2m_Field> m_sp_field = g.m_sp_field;
+   int i, j, dr, du, delta;
+   gf2m a;
+   polyn_gf2m aux;
+
+   // initialisation of the local variables
+   // r0 <- g, r1 <- p, u0 <- 0, u1 <- 1
+   dr = g.get_degree();
+
+   BOTAN_ASSERT(dr > 3, "Valid polynomial");
+
+   polyn_gf2m r0(dr, g.m_sp_field);
+   polyn_gf2m r1(dr - 1, g.m_sp_field);
+   polyn_gf2m u0(dr - 1, g.m_sp_field);
+   polyn_gf2m u1(dr - 1, g.m_sp_field);
+
+   r0 = g;
+   r1 = p;
+   u0.set_to_zero();
+   u1.set_to_zero();
+   (*&u1).set_coef( 0, 1);
+   u1.set_degree( 0);
+
+
+   // invariants:
+   // r1 = u1 * p + v1 * g
+   // r0 = u0 * p + v0 * g
+   // and m_deg(u1) = m_deg(g) - m_deg(r0)
+   // It stops when m_deg (r1) <t (m_deg (r0)> = t)
+   // And therefore m_deg (u1) = m_deg (g) - m_deg (r0) <m_deg (g) - break_deg
+   du = 0;
+   dr = r1.get_degree();
+   delta = r0.get_degree() - dr;
+
+
+   while (dr >= break_deg)
+      {
+
+      for (j = delta; j >= 0; --j)
+         {
+         a = m_sp_field->gf_div(r0[dr + j], r1[dr]);
+         if (a != 0)
+            {
+            gf2m la = m_sp_field->gf_log(a);
+            // u0(z) <- u0(z) + a * u1(z) * z^j
+            for (i = 0; i <= du; ++i)
+               {
+               u0[i + j] ^= m_sp_field->gf_mul_zrz(la, u1[i]);
+               }
+            // r0(z) <- r0(z) + a * r1(z) * z^j
+            for (i = 0; i <= dr; ++i)
+               {
+               r0[i + j] ^= m_sp_field->gf_mul_zrz(la, r1[i]);
+               }
+            }
+         } // end loop over j
+
+      if(break_deg != 1) /* key eq. solving */
+         {
+         /* [ssms_icisc09] Countermeasure
+         * d_break from paper equals break_deg - 1
+         * */
+
+         volatile gf2m fake_elem = 0x01;
+         volatile gf2m cond1, cond2;
+         int trgt_deg = r1.get_degree() - 1;
+         r0.calc_degree_secure();
+         u0.calc_degree_secure();
+         if(!(g.get_degree() % 2))
+            {
+            /* t even */
+            cond1 = r0.get_degree() < break_deg - 1;
+            }
+         else
+            {
+            /* t odd */
+            cond1 =  r0.get_degree() < break_deg;
+            cond2 =  u0.get_degree() < break_deg - 1;
+            cond1 &= cond2;
+            }
+         /* expand cond1 to a full mask */
+         gf2m mask = generate_gf2m_mask(cond1);
+         fake_elem &= mask;
+         r0.patchup_deg_secure(trgt_deg, fake_elem);
+         }
+      if(break_deg == 1) /* syndrome inversion */
+         {
+         volatile gf2m fake_elem = 0x00;
+         volatile uint32_t trgt_deg = 0;
+         r0.calc_degree_secure();
+         u0.calc_degree_secure();
+         /**
+         * countermeasure against the low weight attacks for w=4, w=6 and w=8.
+         * Higher values are not covered since for w=8 we already have a
+         * probability for a positive of 1/n^3 from random ciphertexts with the
+         * given weight. For w = 10 it would be 1/n^4 and so on. Thus attacks
+         * based on such high values of w are considered impractical.
+         *
+         * The outer test for the degree of u ( Omega in the paper ) needs not to
+         * be disguised. Each of the three is performed at most once per EEA
+         * (syndrome inversion) execution, the attacker knows this already when
+         * preparing the ciphertext with the given weight. Inside these three
+         * cases however, we must use timing neutral (branch free) operations to
+         * implement the condition detection and the counteractions.
+         *
+         */
+         if(u0.get_degree() == 4)
+            {
+            uint32_t mask = 0;
+            /**
+            * Condition that the EEA would break now
+            */
+            int cond_r = r0.get_degree() == 0;
+            /**
+            * Now come the conditions for all odd coefficients of this sigma
+            * candiate. If they are all fulfilled, then we know that we have a low
+            * weight error vector, since the key-equation solving EEA is skipped if
+            * the degree of tau^2 is low (=m_deg(u0)) and all its odd cofficients are
+            * zero (they would cause "full-length" contributions from the square
+            * root computation).
+            */
+            // Condition for the coefficient to Y to be cancelled out by the
+            // addition of Y before the square root computation:
+            int cond_u1 = m_sp_field->gf_mul(u0.coeff[1], m_sp_field->gf_inv(r0.coeff[0])) == 1;
+
+            // Condition sigma_3 = 0:
+            int cond_u3 = u0.coeff[3] == 0;
+            // combine the conditions:
+            cond_r &= (cond_u1 & cond_u3);
+            // mask generation:
+            mask = expand_mask_16bit(cond_r);
+            trgt_deg = 2 & mask;
+            fake_elem = 1 & mask;
+            }
+         else if(u0.get_degree() == 6)
+            {
+            uint32_t mask = 0;
+            int cond_r= r0.get_degree() == 0;
+            int cond_u1 = m_sp_field->gf_mul(u0.coeff[1], m_sp_field->gf_inv(r0.coeff[0])) == 1;
+            int cond_u3 = u0.coeff[3] == 0;
+
+            int cond_u5 = u0.coeff[5] == 0;
+
+            cond_r &= (cond_u1 & cond_u3 & cond_u5);
+            mask = expand_mask_16bit(cond_r);
+            trgt_deg = 4 & mask;
+            fake_elem = 1 & mask;
+            }
+         else if(u0.get_degree() == 8)
+            {
+            uint32_t mask = 0;
+            int cond_r= r0.get_degree() == 0;
+            int cond_u1 = m_sp_field->gf_mul(u0[1], m_sp_field->gf_inv(r0[0])) == 1;
+            int cond_u3 = u0.coeff[3] == 0;
+
+            int cond_u5 = u0.coeff[5] == 0;
+
+            int cond_u7 = u0.coeff[7] == 0;
+
+            cond_r &= (cond_u1 & cond_u3 & cond_u5 & cond_u7);
+            mask = expand_mask_16bit(cond_r);
+            trgt_deg = 6 & mask;
+            fake_elem = 1 & mask;
+            }
+         r0.patchup_deg_secure(trgt_deg, fake_elem);
+         }
+      // exchange
+      aux = r0; r0 = r1; r1 = aux;
+      aux = u0; u0 = u1; u1 = aux;
+
+      du = du + delta;
+      delta = 1;
+      while (r1[dr - delta] == 0)
+         {
+         delta++;
+         }
+
+
+      dr -= delta;
+      } /* end  while loop (dr >= break_deg) */
+
+
+   u1.set_degree( du);
+   r1.set_degree( dr);
+   //return u1 and r1;
+   return std::make_pair(u1,r1); // coefficients u,v
+   }
+
+polyn_gf2m::polyn_gf2m(size_t t, RandomNumberGenerator& rng, std::shared_ptr<GF2m_Field> sp_field)
+   :m_deg(static_cast<int>(t)),
+    coeff(t+1),
+    m_sp_field(sp_field)
+   {
+   this->set_coef(t, 1);
+   for(;;)
+      {
+      for(size_t i = 0; i < t; ++i)
+         {
+         this->set_coef(i, random_code_element(sp_field->get_cardinality(), rng));
+         }
+
+      const size_t degree = polyn_gf2m::degppf(*this);
+
+      if(degree >= t)
+         break;
+      }
+   }
+
+void polyn_gf2m::poly_shiftmod( const polyn_gf2m & g)
+   {
+   if(g.get_degree() <= 1)
+      {
+      throw Invalid_Argument("shiftmod cannot be called on polynomials of degree 1 or less");
+      }
+   std::shared_ptr<GF2m_Field> field = g.m_sp_field;
+
+   int t = g.get_degree();
+   gf2m a = field->gf_div(this->coeff[t-1], g.coeff[t]);
+   for (int i = t - 1; i > 0; --i)
+      {
+      this->coeff[i] = this->coeff[i - 1] ^ this->m_sp_field->gf_mul(a, g.coeff[i]);
+      }
+   this->coeff[0] = field->gf_mul(a, g.coeff[0]);
+   }
+
+std::vector<polyn_gf2m> polyn_gf2m::sqrt_mod_init(const polyn_gf2m & g)
+   {
+   uint32_t i, t;
+   uint32_t nb_polyn_sqrt_mat;
+   std::shared_ptr<GF2m_Field> m_sp_field = g.m_sp_field;
+   std::vector<polyn_gf2m> result;
+   t = g.get_degree();
+   nb_polyn_sqrt_mat = t/2;
+
+   std::vector<polyn_gf2m> sq_aux = polyn_gf2m::sqmod_init(g);
+
+
+   polyn_gf2m p( t - 1, g.get_sp_field());
+   p.set_degree( 1);
+
+   (*&p).set_coef( 1, 1);
+   // q(z) = 0, p(z) = z
+   for (i = 0; i < t * m_sp_field->get_extension_degree() - 1; ++i)
+      {
+      // q(z) <- p(z)^2 mod g(z)
+      polyn_gf2m q = p.sqmod(sq_aux, t);
+      // q(z) <-> p(z)
+      polyn_gf2m aux = q;
+      q = p;
+      p = aux;
+      }
+   // p(z) = z^(2^(tm-1)) mod g(z) = sqrt(z) mod g(z)
+
+   for (i = 0; i < nb_polyn_sqrt_mat; ++i)
+      {
+      result.push_back(polyn_gf2m(t - 1, g.get_sp_field()));
+      }
+
+   result[0] = p;
+   result[0].get_degree();
+   for(i = 1; i < nb_polyn_sqrt_mat; i++)
+      {
+      result[i] = result[i - 1];
+      result[i].poly_shiftmod(g),
+         result[i].get_degree();
+      }
+
+   return result;
+   }
+
+std::vector<polyn_gf2m> syndrome_init(polyn_gf2m const& generator, std::vector<gf2m> const& support, int n)
+   {
+   int i,j,t;
+   gf2m a;
+
+
+   std::shared_ptr<GF2m_Field> m_sp_field = generator.m_sp_field;
+
+   std::vector<polyn_gf2m> result;
+   t = generator.get_degree();
+
+   //g(z)=g_t+g_(t-1).z^(t-1)+......+g_1.z+g_0
+   //f(z)=f_(t-1).z^(t-1)+......+f_1.z+f_0
+
+   for(j=0;j<n;j++)
+      {
+      result.push_back(polyn_gf2m( t-1, m_sp_field));
+
+      (*&result[j]).set_coef(t-1,1);
+      for(i=t-2;i>=0;i--)
+         {
+         (*&result[j]).set_coef(i, (generator)[i+1]  ^
+                                m_sp_field->gf_mul(lex_to_gray(support[j]),result[j][i+1]));
+         }
+      a = ((generator)[0] ^ m_sp_field->gf_mul(lex_to_gray(support[j]),result[j][0]));
+      for(i=0;i<t;i++)
+         {
+         (*&result[j]).set_coef(i, m_sp_field->gf_div(result[j][i],a));
+         }
+      }
+   return result;
+   }
+
+polyn_gf2m::polyn_gf2m(const secure_vector<uint8_t>& encoded, std::shared_ptr<GF2m_Field> sp_field )
+   :m_sp_field(sp_field)
+   {
+   if(encoded.size() % 2)
+      {
+      throw Decoding_Error("encoded polynomial has odd length");
+      }
+   for(uint32_t i = 0; i < encoded.size(); i += 2)
+      {
+      gf2m el = (encoded[i] << 8) | encoded[i + 1];
+      coeff.push_back(el);
+      }
+   get_degree();
+
+   }
+secure_vector<uint8_t> polyn_gf2m::encode() const
+   {
+   secure_vector<uint8_t> result;
+
+   if(m_deg < 1)
+      {
+      result.push_back(0);
+      result.push_back(0);
+      return result;
+      }
+
+   uint32_t len = m_deg+1;
+   for(unsigned i = 0; i < len; i++)
+      {
+      // "big endian" encoding of the GF(2^m) elements
+      result.push_back(get_byte(0, coeff[i]));
+      result.push_back(get_byte(1, coeff[i]));
+      }
+   return result;
+   }
+
+void polyn_gf2m::swap(polyn_gf2m& other)
+   {
+   std::swap(this->m_deg, other.m_deg);
+   std::swap(this->m_sp_field, other.m_sp_field);
+   std::swap(this->coeff, other.coeff);
+   }
+
+bool polyn_gf2m::operator==(const polyn_gf2m & other) const
+   {
+   if(m_deg != other.m_deg || coeff != other.coeff)
+      {
+      return false;
+      }
+   return true;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h b/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h
@@ -0,0 +1,170 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and  Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_POLYN_GF2M_H_
+#define BOTAN_POLYN_GF2M_H_
+
+#include <botan/secmem.h>
+#include <botan/gf2m_small_m.h>
+#include <utility>
+#include <string>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(polyn_gf2m.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+class polyn_gf2m
+   {
+   public:
+      /**
+      * create a zero polynomial:
+      */
+      explicit polyn_gf2m(std::shared_ptr<GF2m_Field> sp_field);
+
+      polyn_gf2m() : m_deg(-1) {}
+
+      polyn_gf2m(const secure_vector<uint8_t>& encoded, std::shared_ptr<GF2m_Field> sp_field);
+
+      polyn_gf2m& operator=(const polyn_gf2m&) = default;
+
+      /**
+      * create zero polynomial with reservation of space for a degree d polynomial
+      */
+      polyn_gf2m(int d, std::shared_ptr<GF2m_Field> sp_field);
+
+      polyn_gf2m(polyn_gf2m const& other);
+
+      /**
+      * random irreducible polynomial of degree t
+      */
+      polyn_gf2m(size_t t, RandomNumberGenerator& rng, std::shared_ptr<GF2m_Field> sp_field);
+
+      /** decode a polynomial from memory: **/
+      polyn_gf2m(const uint8_t* mem, uint32_t mem_len, std::shared_ptr<GF2m_Field> sp_field);
+
+      /**
+      *  create a polynomial from memory area (encoded)
+      */
+      polyn_gf2m(int degree, const uint8_t* mem, size_t mem_byte_len, std::shared_ptr<GF2m_Field> sp_field);
+
+      bool operator==(const polyn_gf2m & other) const ;
+
+      bool operator!=(const polyn_gf2m & other) const { return !(*this == other); }
+
+      polyn_gf2m(polyn_gf2m&& other)
+         {
+         this->swap(other);
+         }
+
+      polyn_gf2m & operator=(polyn_gf2m&& other)
+         {
+         if(this != &other)
+            {
+            this->swap(other);
+            }
+         return *this;
+         }
+
+      void swap(polyn_gf2m& other);
+
+      secure_vector<uint8_t> encode() const;
+
+      std::shared_ptr<GF2m_Field> get_sp_field() const
+         { return m_sp_field; }
+
+      gf2m& operator[](size_t i) { return coeff[i]; }
+
+      gf2m operator[](size_t i) const { return coeff[i]; }
+
+      gf2m get_lead_coef() const { return coeff[m_deg]; }
+
+      gf2m get_coef(size_t i) const { return coeff[i]; }
+
+      inline void set_coef(size_t i, gf2m v)
+         {
+         coeff[i] = v;
+         }
+
+      inline void add_to_coef(size_t i, gf2m v)
+         {
+         coeff[i] ^= v;
+         }
+
+      std::string to_string() const;
+
+      void encode(uint32_t min_numo_coeffs, uint8_t* mem, uint32_t mem_len) const;
+
+      int get_degree() const;
+
+      /**
+      * determine the degree in a timing secure manner. the timing of this function
+      * only depends on the number of allocated coefficients, not on the actual
+      * degree
+      */
+      int calc_degree_secure() const;
+
+      size_t degppf(const polyn_gf2m& g);
+
+      static std::vector<polyn_gf2m> sqmod_init(const polyn_gf2m & g);
+
+      static std::vector<polyn_gf2m> sqrt_mod_init(const polyn_gf2m & g);
+
+
+      polyn_gf2m sqmod(const std::vector<polyn_gf2m> & sq, int d);
+      void set_to_zero();
+      gf2m eval(gf2m a);
+
+      static std::pair<polyn_gf2m, polyn_gf2m> eea_with_coefficients(const polyn_gf2m & p,
+                                                                     const polyn_gf2m & g,
+                                                                     int break_deg);
+
+      void patchup_deg_secure( uint32_t trgt_deg, volatile gf2m patch_elem);
+
+   private:
+
+      void set_degree(int d) { m_deg = d; }
+
+      void poly_shiftmod( const polyn_gf2m & g);
+      void realloc(uint32_t new_size);
+      static polyn_gf2m gcd(polyn_gf2m const& p1, polyn_gf2m const& p2);
+
+      /**
+      * destructive:
+      */
+      static void remainder(polyn_gf2m & p, const polyn_gf2m & g);
+
+      static polyn_gf2m gcd_aux(polyn_gf2m& p1, polyn_gf2m& p2);
+   public:
+      // public member variable:
+      int m_deg;
+
+      // public member variable:
+      secure_vector<gf2m> coeff;
+
+      // public member variable:
+      std::shared_ptr<GF2m_Field> m_sp_field;
+   };
+
+gf2m random_gf2m(RandomNumberGenerator& rng);
+gf2m random_code_element(uint16_t code_length, RandomNumberGenerator& rng);
+
+std::vector<polyn_gf2m> syndrome_init(polyn_gf2m const& generator, std::vector<gf2m> const& support, int n);
+
+/**
+* Find the roots of a polynomial over GF(2^m) using the method by Federenko et al.
+*/
+secure_vector<gf2m> find_roots_gf2m_decomp(const polyn_gf2m & polyn, size_t code_length);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/mceies/mceies.cpp b/third_party/botan/src/lib/pubkey/mceies/mceies.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mceies/mceies.cpp
@@ -0,0 +1,110 @@
+/*
+* McEliece Integrated Encryption System
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mceies.h>
+#include <botan/aead.h>
+#include <botan/rng.h>
+#include <botan/mceliece.h>
+#include <botan/pubkey.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> aead_key(const secure_vector<uint8_t>& mk,
+                             const AEAD_Mode& aead)
+   {
+   // Fold the key as required for the AEAD mode in use
+   if(aead.valid_keylength(mk.size()))
+      return mk;
+
+   secure_vector<uint8_t> r(aead.key_spec().maximum_keylength());
+   BOTAN_ASSERT_NOMSG(r.size() > 0);
+   for(size_t i = 0; i != mk.size(); ++i)
+      r[i % r.size()] ^= mk[i];
+   return r;
+   }
+
+}
+
+secure_vector<uint8_t>
+mceies_encrypt(const McEliece_PublicKey& pubkey,
+               const uint8_t pt[], size_t pt_len,
+               const uint8_t ad[], size_t ad_len,
+               RandomNumberGenerator& rng,
+               const std::string& algo)
+   {
+   PK_KEM_Encryptor kem_op(pubkey, rng, "KDF1(SHA-512)");
+
+   secure_vector<uint8_t> mce_ciphertext, mce_key;
+   kem_op.encrypt(mce_ciphertext, mce_key, 64, rng);
+
+   const size_t mce_code_bytes = (pubkey.get_code_length() + 7) / 8;
+
+   BOTAN_ASSERT(mce_ciphertext.size() == mce_code_bytes, "Unexpected size");
+
+   std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(algo, ENCRYPTION);
+
+   const size_t nonce_len = aead->default_nonce_length();
+
+   aead->set_key(aead_key(mce_key, *aead));
+   aead->set_associated_data(ad, ad_len);
+
+   const secure_vector<uint8_t> nonce = rng.random_vec(nonce_len);
+
+   secure_vector<uint8_t> msg(mce_ciphertext.size() + nonce.size() + pt_len);
+   copy_mem(msg.data(), mce_ciphertext.data(), mce_ciphertext.size());
+   copy_mem(msg.data() + mce_ciphertext.size(), nonce.data(), nonce.size());
+   copy_mem(msg.data() + mce_ciphertext.size() + nonce.size(), pt, pt_len);
+
+   aead->start(nonce);
+   aead->finish(msg, mce_ciphertext.size() + nonce.size());
+   return msg;
+   }
+
+secure_vector<uint8_t>
+mceies_decrypt(const McEliece_PrivateKey& privkey,
+               const uint8_t ct[], size_t ct_len,
+               const uint8_t ad[], size_t ad_len,
+               const std::string& algo)
+   {
+   try
+      {
+      Null_RNG null_rng;
+      PK_KEM_Decryptor kem_op(privkey, null_rng, "KDF1(SHA-512)");
+
+      const size_t mce_code_bytes = (privkey.get_code_length() + 7) / 8;
+
+      std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(algo, DECRYPTION);
+
+      const size_t nonce_len = aead->default_nonce_length();
+
+      if(ct_len < mce_code_bytes + nonce_len + aead->tag_size())
+         throw Decoding_Error("Input message too small to be valid");
+
+      const secure_vector<uint8_t> mce_key = kem_op.decrypt(ct, mce_code_bytes, 64);
+
+      aead->set_key(aead_key(mce_key, *aead));
+      aead->set_associated_data(ad, ad_len);
+
+      secure_vector<uint8_t> pt(ct + mce_code_bytes + nonce_len, ct + ct_len);
+
+      aead->start(&ct[mce_code_bytes], nonce_len);
+      aead->finish(pt, 0);
+      return pt;
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      throw;
+      }
+   catch(std::exception& e)
+      {
+      throw Decoding_Error("mce_decrypt failed: " + std::string(e.what()));
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/mceies/mceies.h b/third_party/botan/src/lib/pubkey/mceies/mceies.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/mceies/mceies.h
@@ -0,0 +1,46 @@
+/*
+* McEliece Integrated Encryption System
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MCEIES_H_
+#define BOTAN_MCEIES_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class McEliece_PublicKey;
+class McEliece_PrivateKey;
+
+/**
+* McEliece Integrated Encryption System
+* Derive a shared key using MCE KEM and encrypt/authenticate the
+* plaintext and AD using AES-256 in OCB mode.
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) mceies_encrypt(const McEliece_PublicKey& pubkey,
+                         const uint8_t pt[], size_t pt_len,
+                         const uint8_t ad[], size_t ad_len,
+                         RandomNumberGenerator& rng,
+                         const std::string& aead = "AES-256/OCB");
+
+/**
+* McEliece Integrated Encryption System
+* Derive a shared key using MCE KEM and decrypt/authenticate the
+* ciphertext and AD using AES-256 in OCB mode.
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) mceies_decrypt(const McEliece_PrivateKey& privkey,
+                         const uint8_t ct[], size_t ct_len,
+                         const uint8_t ad[], size_t ad_len,
+                         const std::string& aead = "AES-256/OCB");
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/newhope/newhope.cpp b/third_party/botan/src/lib/pubkey/newhope/newhope.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/newhope/newhope.cpp
@@ -0,0 +1,800 @@
+/*
+* NEWHOPE Ring-LWE scheme
+* Based on the public domain reference implementation by the
+* designers (https://github.com/tpoeppelmann/newhope)
+*
+* Further changes
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/newhope.h>
+#include <botan/hash.h>
+#include <botan/rng.h>
+#include <botan/stream_cipher.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+newhope_poly::~newhope_poly()
+   {
+   secure_scrub_memory(coeffs, sizeof(coeffs));
+   }
+
+typedef newhope_poly poly;
+
+namespace {
+
+static const uint16_t PARAM_Q = 12289;
+static const size_t PARAM_N = 1024;
+
+/* Incomplete-reduction routines; for details on allowed input ranges
+ * and produced output ranges, see the description in the paper:
+ * https://cryptojedi.org/papers/#newhope */
+
+inline uint16_t montgomery_reduce(uint32_t a)
+   {
+   const uint32_t qinv = 12287; // -inverse_mod(p,2^18)
+   const uint32_t rlog = 18;
+   const uint32_t rlog_mask = ((1 << rlog) - 1);
+
+   uint32_t u = (a * qinv);
+   u &= rlog_mask;
+   u *= PARAM_Q;
+   u += a;
+   return (u >> rlog);
+   }
+
+inline uint16_t barrett_reduce(uint16_t a)
+   {
+   uint32_t u = (static_cast<uint32_t>(a) * 5) >> 16;
+   u *= PARAM_Q;
+   a = static_cast<uint16_t>(a - u);
+   return a;
+   }
+
+inline void mul_coefficients(uint16_t* poly, const uint16_t* factors)
+   {
+   for(size_t i = 0; i < PARAM_N; i++)
+      {
+      poly[i] = montgomery_reduce(poly[i] * factors[i]);
+      }
+   }
+
+/* GS_bo_to_no; omegas need to be in Montgomery domain */
+inline void ntt(uint16_t* a, const uint16_t* omega)
+   {
+   for(size_t i = 0; i < 10; i+=2)
+      {
+      // Even level
+      size_t distance = (1ULL << i);
+      for(size_t start = 0; start < distance; start++)
+         {
+         size_t jTwiddle = 0;
+         for(size_t j = start; j < PARAM_N-1; j += 2*distance)
+            {
+            uint16_t W = omega[jTwiddle++];
+            uint16_t temp = a[j];
+            a[j] = (temp + a[j + distance]); // Omit reduction (be lazy)
+            a[j + distance] = montgomery_reduce((W * (static_cast<uint32_t>(temp) + 3*PARAM_Q - a[j + distance])));
+            }
+         }
+
+      // Odd level
+      distance <<= 1;
+      for(size_t start = 0; start < distance; start++)
+         {
+         size_t jTwiddle = 0;
+         for(size_t j = start; j < PARAM_N-1; j += 2*distance)
+            {
+            uint16_t W = omega[jTwiddle++];
+            uint16_t temp = a[j];
+            a[j] = barrett_reduce((temp + a[j + distance]));
+            a[j + distance] = montgomery_reduce((W * (static_cast<uint32_t>(temp) + 3*PARAM_Q - a[j + distance])));
+            }
+         }
+      }
+   }
+
+inline void poly_frombytes(poly* r, const uint8_t* a)
+   {
+   for(size_t i = 0; i < PARAM_N/4; i++)
+      {
+      r->coeffs[4*i+0] =  a[7*i+0]       | ((static_cast<uint16_t>(a[7*i+1]) & 0x3f) << 8);
+      r->coeffs[4*i+1] = (a[7*i+1] >> 6) | (static_cast<uint16_t>(a[7*i+2]) << 2) | (static_cast<uint16_t>
+                         (a[7*i+3] & 0x0f) << 10);
+      r->coeffs[4*i+2] = (a[7*i+3] >> 4) | (static_cast<uint16_t>(a[7*i+4]) << 4) | (static_cast<uint16_t>
+                         (a[7*i+5] & 0x03) << 12);
+      r->coeffs[4*i+3] = (a[7*i+5] >> 2) | (static_cast<uint16_t>(a[7*i+6]) << 6);
+      }
+   }
+
+inline void poly_tobytes(uint8_t* r, const poly* p)
+   {
+   for(size_t i = 0; i < PARAM_N/4; i++)
+      {
+      uint16_t t0 = barrett_reduce(p->coeffs[4*i+0]); //Make sure that coefficients have only 14 bits
+      uint16_t t1 = barrett_reduce(p->coeffs[4*i+1]);
+      uint16_t t2 = barrett_reduce(p->coeffs[4*i+2]);
+      uint16_t t3 = barrett_reduce(p->coeffs[4*i+3]);
+
+      uint16_t m;
+      int16_t c;
+
+      m = t0 - PARAM_Q;
+      c = m;
+      c >>= 15;
+      t0 = m ^ ((t0^m)&c); // <Make sure that coefficients are in [0,q]
+
+      m = t1 - PARAM_Q;
+      c = m;
+      c >>= 15;
+      t1 = m ^ ((t1^m)&c); // <Make sure that coefficients are in [0,q]
+
+      m = t2 - PARAM_Q;
+      c = m;
+      c >>= 15;
+      t2 = m ^ ((t2^m)&c); // <Make sure that coefficients are in [0,q]
+
+      m = t3 - PARAM_Q;
+      c = m;
+      c >>= 15;
+      t3 = m ^ ((t3^m)&c); // <Make sure that coefficients are in [0,q]
+
+      r[7*i+0] =  t0 & 0xff;
+      r[7*i+1] = static_cast<uint8_t>((t0 >> 8) | (t1 << 6));
+      r[7*i+2] = static_cast<uint8_t>((t1 >> 2));
+      r[7*i+3] = static_cast<uint8_t>((t1 >> 10) | (t2 << 4));
+      r[7*i+4] = static_cast<uint8_t>((t2 >> 4));
+      r[7*i+5] = static_cast<uint8_t>((t2 >> 12) | (t3 << 2));
+      r[7*i+6] = static_cast<uint8_t>((t3 >> 6));
+      }
+   }
+
+inline void poly_getnoise(Botan::RandomNumberGenerator& rng, poly* r)
+   {
+   uint8_t buf[4*PARAM_N];
+
+   rng.randomize(buf, 4*PARAM_N);
+
+   for(size_t i = 0; i < PARAM_N; i++)
+      {
+      const uint32_t t = load_le<uint32_t>(buf, i);
+      uint32_t d = 0;
+      for(size_t j = 0; j < 8; j++)
+         {
+         d += (t >> j) & 0x01010101;
+         }
+      const uint32_t a = ((d >> 8) & 0xff) + (d & 0xff);
+      const uint32_t b = (d >> 24) + ((d >> 16) & 0xff);
+      r->coeffs[i] = static_cast<uint16_t>(a + PARAM_Q - b);
+      }
+   }
+
+inline void poly_pointwise(poly* r, const poly* a, const poly* b)
+   {
+   for(size_t i = 0; i < PARAM_N; i++)
+      {
+      const uint16_t t = montgomery_reduce(3186*b->coeffs[i]); /* t is now in Montgomery domain */
+      r->coeffs[i] = montgomery_reduce(a->coeffs[i] * t); /* r->coeffs[i] is back in normal domain */
+      }
+   }
+
+inline void poly_add(poly* r, const poly* a, const poly* b)
+   {
+   for(size_t i = 0; i < PARAM_N; i++)
+      {
+      r->coeffs[i] = barrett_reduce(a->coeffs[i] + b->coeffs[i]);
+      }
+   }
+
+inline void poly_ntt(poly* r)
+   {
+   static const uint16_t omegas_montgomery[PARAM_N/2] =
+      {
+      4075, 6974, 7373, 7965, 3262, 5079, 522, 2169, 6364, 1018, 1041, 8775, 2344,
+      11011, 5574, 1973, 4536, 1050, 6844, 3860, 3818, 6118, 2683, 1190, 4789,
+      7822, 7540, 6752, 5456, 4449, 3789, 12142, 11973, 382, 3988, 468, 6843, 5339,
+      6196, 3710, 11316, 1254, 5435, 10930, 3998, 10256, 10367, 3879, 11889, 1728,
+      6137, 4948, 5862, 6136, 3643, 6874, 8724, 654, 10302, 1702, 7083, 6760, 56,
+      3199, 9987, 605, 11785, 8076, 5594, 9260, 6403, 4782, 6212, 4624, 9026, 8689,
+      4080, 11868, 6221, 3602, 975, 8077, 8851, 9445, 5681, 3477, 1105, 142, 241,
+      12231, 1003, 3532, 5009, 1956, 6008, 11404, 7377, 2049, 10968, 12097, 7591,
+      5057, 3445, 4780, 2920, 7048, 3127, 8120, 11279, 6821, 11502, 8807, 12138,
+      2127, 2839, 3957, 431, 1579, 6383, 9784, 5874, 677, 3336, 6234, 2766, 1323,
+      9115, 12237, 2031, 6956, 6413, 2281, 3969, 3991, 12133, 9522, 4737, 10996,
+      4774, 5429, 11871, 3772, 453, 5908, 2882, 1805, 2051, 1954, 11713, 3963,
+      2447, 6142, 8174, 3030, 1843, 2361, 12071, 2908, 3529, 3434, 3202, 7796,
+      2057, 5369, 11939, 1512, 6906, 10474, 11026, 49, 10806, 5915, 1489, 9789,
+      5942, 10706, 10431, 7535, 426, 8974, 3757, 10314, 9364, 347, 5868, 9551,
+      9634, 6554, 10596, 9280, 11566, 174, 2948, 2503, 6507, 10723, 11606, 2459,
+      64, 3656, 8455, 5257, 5919, 7856, 1747, 9166, 5486, 9235, 6065, 835, 3570,
+      4240, 11580, 4046, 10970, 9139, 1058, 8210, 11848, 922, 7967, 1958, 10211,
+      1112, 3728, 4049, 11130, 5990, 1404, 325, 948, 11143, 6190, 295, 11637, 5766,
+      8212, 8273, 2919, 8527, 6119, 6992, 8333, 1360, 2555, 6167, 1200, 7105, 7991,
+      3329, 9597, 12121, 5106, 5961, 10695, 10327, 3051, 9923, 4896, 9326, 81,
+      3091, 1000, 7969, 4611, 726, 1853, 12149, 4255, 11112, 2768, 10654, 1062,
+      2294, 3553, 4805, 2747, 4846, 8577, 9154, 1170, 2319, 790, 11334, 9275, 9088,
+      1326, 5086, 9094, 6429, 11077, 10643, 3504, 3542, 8668, 9744, 1479, 1, 8246,
+      7143, 11567, 10984, 4134, 5736, 4978, 10938, 5777, 8961, 4591, 5728, 6461,
+      5023, 9650, 7468, 949, 9664, 2975, 11726, 2744, 9283, 10092, 5067, 12171,
+      2476, 3748, 11336, 6522, 827, 9452, 5374, 12159, 7935, 3296, 3949, 9893,
+      4452, 10908, 2525, 3584, 8112, 8011, 10616, 4989, 6958, 11809, 9447, 12280,
+      1022, 11950, 9821, 11745, 5791, 5092, 2089, 9005, 2881, 3289, 2013, 9048,
+      729, 7901, 1260, 5755, 4632, 11955, 2426, 10593, 1428, 4890, 5911, 3932,
+      9558, 8830, 3637, 5542, 145, 5179, 8595, 3707, 10530, 355, 3382, 4231, 9741,
+      1207, 9041, 7012, 1168, 10146, 11224, 4645, 11885, 10911, 10377, 435, 7952,
+      4096, 493, 9908, 6845, 6039, 2422, 2187, 9723, 8643, 9852, 9302, 6022, 7278,
+      1002, 4284, 5088, 1607, 7313, 875, 8509, 9430, 1045, 2481, 5012, 7428, 354,
+      6591, 9377, 11847, 2401, 1067, 7188, 11516, 390, 8511, 8456, 7270, 545, 8585,
+      9611, 12047, 1537, 4143, 4714, 4885, 1017, 5084, 1632, 3066, 27, 1440, 8526,
+      9273, 12046, 11618, 9289, 3400, 9890, 3136, 7098, 8758, 11813, 7384, 3985,
+      11869, 6730, 10745, 10111, 2249, 4048, 2884, 11136, 2126, 1630, 9103, 5407,
+      2686, 9042, 2969, 8311, 9424, 9919, 8779, 5332, 10626, 1777, 4654, 10863,
+      7351, 3636, 9585, 5291, 8374, 2166, 4919, 12176, 9140, 12129, 7852, 12286,
+      4895, 10805, 2780, 5195, 2305, 7247, 9644, 4053, 10600, 3364, 3271, 4057,
+      4414, 9442, 7917, 2174
+      };
+
+   static const uint16_t psis_bitrev_montgomery[PARAM_N] =
+      {
+      4075, 6974, 7373, 7965, 3262, 5079, 522, 2169, 6364, 1018, 1041, 8775, 2344,
+      11011, 5574, 1973, 4536, 1050, 6844, 3860, 3818, 6118, 2683, 1190, 4789,
+      7822, 7540, 6752, 5456, 4449, 3789, 12142, 11973, 382, 3988, 468, 6843, 5339,
+      6196, 3710, 11316, 1254, 5435, 10930, 3998, 10256, 10367, 3879, 11889, 1728,
+      6137, 4948, 5862, 6136, 3643, 6874, 8724, 654, 10302, 1702, 7083, 6760, 56,
+      3199, 9987, 605, 11785, 8076, 5594, 9260, 6403, 4782, 6212, 4624, 9026, 8689,
+      4080, 11868, 6221, 3602, 975, 8077, 8851, 9445, 5681, 3477, 1105, 142, 241,
+      12231, 1003, 3532, 5009, 1956, 6008, 11404, 7377, 2049, 10968, 12097, 7591,
+      5057, 3445, 4780, 2920, 7048, 3127, 8120, 11279, 6821, 11502, 8807, 12138,
+      2127, 2839, 3957, 431, 1579, 6383, 9784, 5874, 677, 3336, 6234, 2766, 1323,
+      9115, 12237, 2031, 6956, 6413, 2281, 3969, 3991, 12133, 9522, 4737, 10996,
+      4774, 5429, 11871, 3772, 453, 5908, 2882, 1805, 2051, 1954, 11713, 3963,
+      2447, 6142, 8174, 3030, 1843, 2361, 12071, 2908, 3529, 3434, 3202, 7796,
+      2057, 5369, 11939, 1512, 6906, 10474, 11026, 49, 10806, 5915, 1489, 9789,
+      5942, 10706, 10431, 7535, 426, 8974, 3757, 10314, 9364, 347, 5868, 9551,
+      9634, 6554, 10596, 9280, 11566, 174, 2948, 2503, 6507, 10723, 11606, 2459,
+      64, 3656, 8455, 5257, 5919, 7856, 1747, 9166, 5486, 9235, 6065, 835, 3570,
+      4240, 11580, 4046, 10970, 9139, 1058, 8210, 11848, 922, 7967, 1958, 10211,
+      1112, 3728, 4049, 11130, 5990, 1404, 325, 948, 11143, 6190, 295, 11637, 5766,
+      8212, 8273, 2919, 8527, 6119, 6992, 8333, 1360, 2555, 6167, 1200, 7105, 7991,
+      3329, 9597, 12121, 5106, 5961, 10695, 10327, 3051, 9923, 4896, 9326, 81,
+      3091, 1000, 7969, 4611, 726, 1853, 12149, 4255, 11112, 2768, 10654, 1062,
+      2294, 3553, 4805, 2747, 4846, 8577, 9154, 1170, 2319, 790, 11334, 9275, 9088,
+      1326, 5086, 9094, 6429, 11077, 10643, 3504, 3542, 8668, 9744, 1479, 1, 8246,
+      7143, 11567, 10984, 4134, 5736, 4978, 10938, 5777, 8961, 4591, 5728, 6461,
+      5023, 9650, 7468, 949, 9664, 2975, 11726, 2744, 9283, 10092, 5067, 12171,
+      2476, 3748, 11336, 6522, 827, 9452, 5374, 12159, 7935, 3296, 3949, 9893,
+      4452, 10908, 2525, 3584, 8112, 8011, 10616, 4989, 6958, 11809, 9447, 12280,
+      1022, 11950, 9821, 11745, 5791, 5092, 2089, 9005, 2881, 3289, 2013, 9048,
+      729, 7901, 1260, 5755, 4632, 11955, 2426, 10593, 1428, 4890, 5911, 3932,
+      9558, 8830, 3637, 5542, 145, 5179, 8595, 3707, 10530, 355, 3382, 4231, 9741,
+      1207, 9041, 7012, 1168, 10146, 11224, 4645, 11885, 10911, 10377, 435, 7952,
+      4096, 493, 9908, 6845, 6039, 2422, 2187, 9723, 8643, 9852, 9302, 6022, 7278,
+      1002, 4284, 5088, 1607, 7313, 875, 8509, 9430, 1045, 2481, 5012, 7428, 354,
+      6591, 9377, 11847, 2401, 1067, 7188, 11516, 390, 8511, 8456, 7270, 545, 8585,
+      9611, 12047, 1537, 4143, 4714, 4885, 1017, 5084, 1632, 3066, 27, 1440, 8526,
+      9273, 12046, 11618, 9289, 3400, 9890, 3136, 7098, 8758, 11813, 7384, 3985,
+      11869, 6730, 10745, 10111, 2249, 4048, 2884, 11136, 2126, 1630, 9103, 5407,
+      2686, 9042, 2969, 8311, 9424, 9919, 8779, 5332, 10626, 1777, 4654, 10863,
+      7351, 3636, 9585, 5291, 8374, 2166, 4919, 12176, 9140, 12129, 7852, 12286,
+      4895, 10805, 2780, 5195, 2305, 7247, 9644, 4053, 10600, 3364, 3271, 4057,
+      4414, 9442, 7917, 2174, 3947, 11951, 2455, 6599, 10545, 10975, 3654, 2894,
+      7681, 7126, 7287, 12269, 4119, 3343, 2151, 1522, 7174, 7350, 11041, 2442,
+      2148, 5959, 6492, 8330, 8945, 5598, 3624, 10397, 1325, 6565, 1945, 11260,
+      10077, 2674, 3338, 3276, 11034, 506, 6505, 1392, 5478, 8778, 1178, 2776,
+      3408, 10347, 11124, 2575, 9489, 12096, 6092, 10058, 4167, 6085, 923, 11251,
+      11912, 4578, 10669, 11914, 425, 10453, 392, 10104, 8464, 4235, 8761, 7376,
+      2291, 3375, 7954, 8896, 6617, 7790, 1737, 11667, 3982, 9342, 6680, 636, 6825,
+      7383, 512, 4670, 2900, 12050, 7735, 994, 1687, 11883, 7021, 146, 10485, 1403,
+      5189, 6094, 2483, 2054, 3042, 10945, 3981, 10821, 11826, 8882, 8151, 180,
+      9600, 7684, 5219, 10880, 6780, 204, 11232, 2600, 7584, 3121, 3017, 11053,
+      7814, 7043, 4251, 4739, 11063, 6771, 7073, 9261, 2360, 11925, 1928, 11825,
+      8024, 3678, 3205, 3359, 11197, 5209, 8581, 3238, 8840, 1136, 9363, 1826,
+      3171, 4489, 7885, 346, 2068, 1389, 8257, 3163, 4840, 6127, 8062, 8921, 612,
+      4238, 10763, 8067, 125, 11749, 10125, 5416, 2110, 716, 9839, 10584, 11475,
+      11873, 3448, 343, 1908, 4538, 10423, 7078, 4727, 1208, 11572, 3589, 2982,
+      1373, 1721, 10753, 4103, 2429, 4209, 5412, 5993, 9011, 438, 3515, 7228, 1218,
+      8347, 5232, 8682, 1327, 7508, 4924, 448, 1014, 10029, 12221, 4566, 5836,
+      12229, 2717, 1535, 3200, 5588, 5845, 412, 5102, 7326, 3744, 3056, 2528, 7406,
+      8314, 9202, 6454, 6613, 1417, 10032, 7784, 1518, 3765, 4176, 5063, 9828,
+      2275, 6636, 4267, 6463, 2065, 7725, 3495, 8328, 8755, 8144, 10533, 5966,
+      12077, 9175, 9520, 5596, 6302, 8400, 579, 6781, 11014, 5734, 11113, 11164,
+      4860, 1131, 10844, 9068, 8016, 9694, 3837, 567, 9348, 7000, 6627, 7699, 5082,
+      682, 11309, 5207, 4050, 7087, 844, 7434, 3769, 293, 9057, 6940, 9344, 10883,
+      2633, 8190, 3944, 5530, 5604, 3480, 2171, 9282, 11024, 2213, 8136, 3805, 767,
+      12239, 216, 11520, 6763, 10353, 7, 8566, 845, 7235, 3154, 4360, 3285, 10268,
+      2832, 3572, 1282, 7559, 3229, 8360, 10583, 6105, 3120, 6643, 6203, 8536,
+      8348, 6919, 3536, 9199, 10891, 11463, 5043, 1658, 5618, 8787, 5789, 4719,
+      751, 11379, 6389, 10783, 3065, 7806, 6586, 2622, 5386, 510, 7628, 6921, 578,
+      10345, 11839, 8929, 4684, 12226, 7154, 9916, 7302, 8481, 3670, 11066, 2334,
+      1590, 7878, 10734, 1802, 1891, 5103, 6151, 8820, 3418, 7846, 9951, 4693, 417,
+      9996, 9652, 4510, 2946, 5461, 365, 881, 1927, 1015, 11675, 11009, 1371,
+      12265, 2485, 11385, 5039, 6742, 8449, 1842, 12217, 8176, 9577, 4834, 7937,
+      9461, 2643, 11194, 3045, 6508, 4094, 3451, 7911, 11048, 5406, 4665, 3020,
+      6616, 11345, 7519, 3669, 5287, 1790, 7014, 5410, 11038, 11249, 2035, 6125,
+      10407, 4565, 7315, 5078, 10506, 2840, 2478, 9270, 4194, 9195, 4518, 7469,
+      1160, 6878, 2730, 10421, 10036, 1734, 3815, 10939, 5832, 10595, 10759, 4423,
+      8420, 9617, 7119, 11010, 11424, 9173, 189, 10080, 10526, 3466, 10588, 7592,
+      3578, 11511, 7785, 9663, 530, 12150, 8957, 2532, 3317, 9349, 10243, 1481,
+      9332, 3454, 3758, 7899, 4218, 2593, 11410, 2276, 982, 6513, 1849, 8494, 9021,
+      4523, 7988, 8, 457, 648, 150, 8000, 2307, 2301, 874, 5650, 170, 9462, 2873,
+      9855, 11498, 2535, 11169, 5808, 12268, 9687, 1901, 7171, 11787, 3846, 1573,
+      6063, 3793, 466, 11259, 10608, 3821, 6320, 4649, 6263, 2929
+      };
+
+   mul_coefficients(r->coeffs, psis_bitrev_montgomery);
+   ntt(r->coeffs, omegas_montgomery);
+   }
+
+inline void bitrev_vector(uint16_t* poly)
+   {
+   static const uint16_t bitrev_table[1024] =
+      {
+      0, 512, 256, 768, 128, 640, 384, 896, 64, 576, 320, 832, 192, 704, 448, 960, 32, 544, 288, 800, 160, 672, 416, 928, 96, 608, 352, 864, 224, 736, 480, 992,
+      16, 528, 272, 784, 144, 656, 400, 912, 80, 592, 336, 848, 208, 720, 464, 976, 48, 560, 304, 816, 176, 688, 432, 944, 112, 624, 368, 880, 240, 752, 496, 1008,
+      8, 520, 264, 776, 136, 648, 392, 904, 72, 584, 328, 840, 200, 712, 456, 968, 40, 552, 296, 808, 168, 680, 424, 936, 104, 616, 360, 872, 232, 744, 488, 1000,
+      24, 536, 280, 792, 152, 664, 408, 920, 88, 600, 344, 856, 216, 728, 472, 984, 56, 568, 312, 824, 184, 696, 440, 952, 120, 632, 376, 888, 248, 760, 504, 1016,
+      4, 516, 260, 772, 132, 644, 388, 900, 68, 580, 324, 836, 196, 708, 452, 964, 36, 548, 292, 804, 164, 676, 420, 932, 100, 612, 356, 868, 228, 740, 484, 996,
+      20, 532, 276, 788, 148, 660, 404, 916, 84, 596, 340, 852, 212, 724, 468, 980, 52, 564, 308, 820, 180, 692, 436, 948, 116, 628, 372, 884, 244, 756, 500, 1012,
+      12, 524, 268, 780, 140, 652, 396, 908, 76, 588, 332, 844, 204, 716, 460, 972, 44, 556, 300, 812, 172, 684, 428, 940, 108, 620, 364, 876, 236, 748, 492, 1004,
+      28, 540, 284, 796, 156, 668, 412, 924, 92, 604, 348, 860, 220, 732, 476, 988, 60, 572, 316, 828, 188, 700, 444, 956, 124, 636, 380, 892, 252, 764, 508, 1020,
+      2, 514, 258, 770, 130, 642, 386, 898, 66, 578, 322, 834, 194, 706, 450, 962, 34, 546, 290, 802, 162, 674, 418, 930, 98, 610, 354, 866, 226, 738, 482, 994,
+      18, 530, 274, 786, 146, 658, 402, 914, 82, 594, 338, 850, 210, 722, 466, 978, 50, 562, 306, 818, 178, 690, 434, 946, 114, 626, 370, 882, 242, 754, 498, 1010,
+      10, 522, 266, 778, 138, 650, 394, 906, 74, 586, 330, 842, 202, 714, 458, 970, 42, 554, 298, 810, 170, 682, 426, 938, 106, 618, 362, 874, 234, 746, 490, 1002,
+      26, 538, 282, 794, 154, 666, 410, 922, 90, 602, 346, 858, 218, 730, 474, 986, 58, 570, 314, 826, 186, 698, 442, 954, 122, 634, 378, 890, 250, 762, 506, 1018,
+      6, 518, 262, 774, 134, 646, 390, 902, 70, 582, 326, 838, 198, 710, 454, 966, 38, 550, 294, 806, 166, 678, 422, 934, 102, 614, 358, 870, 230, 742, 486, 998,
+      22, 534, 278, 790, 150, 662, 406, 918, 86, 598, 342, 854, 214, 726, 470, 982, 54, 566, 310, 822, 182, 694, 438, 950, 118, 630, 374, 886, 246, 758, 502, 1014,
+      14, 526, 270, 782, 142, 654, 398, 910, 78, 590, 334, 846, 206, 718, 462, 974, 46, 558, 302, 814, 174, 686, 430, 942, 110, 622, 366, 878, 238, 750, 494, 1006,
+      30, 542, 286, 798, 158, 670, 414, 926, 94, 606, 350, 862, 222, 734, 478, 990, 62, 574, 318, 830, 190, 702, 446, 958, 126, 638, 382, 894, 254, 766, 510, 1022,
+      1, 513, 257, 769, 129, 641, 385, 897, 65, 577, 321, 833, 193, 705, 449, 961, 33, 545, 289, 801, 161, 673, 417, 929, 97, 609, 353, 865, 225, 737, 481, 993,
+      17, 529, 273, 785, 145, 657, 401, 913, 81, 593, 337, 849, 209, 721, 465, 977, 49, 561, 305, 817, 177, 689, 433, 945, 113, 625, 369, 881, 241, 753, 497, 1009,
+      9, 521, 265, 777, 137, 649, 393, 905, 73, 585, 329, 841, 201, 713, 457, 969, 41, 553, 297, 809, 169, 681, 425, 937, 105, 617, 361, 873, 233, 745, 489, 1001,
+      25, 537, 281, 793, 153, 665, 409, 921, 89, 601, 345, 857, 217, 729, 473, 985, 57, 569, 313, 825, 185, 697, 441, 953, 121, 633, 377, 889, 249, 761, 505, 1017,
+      5, 517, 261, 773, 133, 645, 389, 901, 69, 581, 325, 837, 197, 709, 453, 965, 37, 549, 293, 805, 165, 677, 421, 933, 101, 613, 357, 869, 229, 741, 485, 997,
+      21, 533, 277, 789, 149, 661, 405, 917, 85, 597, 341, 853, 213, 725, 469, 981, 53, 565, 309, 821, 181, 693, 437, 949, 117, 629, 373, 885, 245, 757, 501, 1013,
+      13, 525, 269, 781, 141, 653, 397, 909, 77, 589, 333, 845, 205, 717, 461, 973, 45, 557, 301, 813, 173, 685, 429, 941, 109, 621, 365, 877, 237, 749, 493, 1005,
+      29, 541, 285, 797, 157, 669, 413, 925, 93, 605, 349, 861, 221, 733, 477, 989, 61, 573, 317, 829, 189, 701, 445, 957, 125, 637, 381, 893, 253, 765, 509, 1021,
+      3, 515, 259, 771, 131, 643, 387, 899, 67, 579, 323, 835, 195, 707, 451, 963, 35, 547, 291, 803, 163, 675, 419, 931, 99, 611, 355, 867, 227, 739, 483, 995,
+      19, 531, 275, 787, 147, 659, 403, 915, 83, 595, 339, 851, 211, 723, 467, 979, 51, 563, 307, 819, 179, 691, 435, 947, 115, 627, 371, 883, 243, 755, 499, 1011,
+      11, 523, 267, 779, 139, 651, 395, 907, 75, 587, 331, 843, 203, 715, 459, 971, 43, 555, 299, 811, 171, 683, 427, 939, 107, 619, 363, 875, 235, 747, 491, 1003,
+      27, 539, 283, 795, 155, 667, 411, 923, 91, 603, 347, 859, 219, 731, 475, 987, 59, 571, 315, 827, 187, 699, 443, 955, 123, 635, 379, 891, 251, 763, 507, 1019,
+      7, 519, 263, 775, 135, 647, 391, 903, 71, 583, 327, 839, 199, 711, 455, 967, 39, 551, 295, 807, 167, 679, 423, 935, 103, 615, 359, 871, 231, 743, 487, 999,
+      23, 535, 279, 791, 151, 663, 407, 919, 87, 599, 343, 855, 215, 727, 471, 983, 55, 567, 311, 823, 183, 695, 439, 951, 119, 631, 375, 887, 247, 759, 503, 1015,
+      15, 527, 271, 783, 143, 655, 399, 911, 79, 591, 335, 847, 207, 719, 463, 975, 47, 559, 303, 815, 175, 687, 431, 943, 111, 623, 367, 879, 239, 751, 495, 1007,
+      31, 543, 287, 799, 159, 671, 415, 927, 95, 607, 351, 863, 223, 735, 479, 991, 63, 575, 319, 831, 191, 703, 447, 959, 127, 639, 383, 895, 255, 767, 511, 1023
+      };
+
+   for(size_t i = 0; i < PARAM_N; i++)
+      {
+      const uint16_t r = bitrev_table[i];
+      if(i < r)
+         {
+         const uint16_t tmp = poly[i];
+         poly[i] = poly[r];
+         poly[r] = tmp;
+         }
+      }
+   }
+
+inline void poly_invntt(poly* r)
+   {
+   static const uint16_t omegas_inv_montgomery[PARAM_N/2] =
+      {
+      4075, 5315, 4324, 4916, 10120, 11767, 7210, 9027, 10316, 6715, 1278, 9945,
+      3514, 11248, 11271, 5925, 147, 8500, 7840, 6833, 5537, 4749, 4467, 7500,
+      11099, 9606, 6171, 8471, 8429, 5445, 11239, 7753, 9090, 12233, 5529, 5206,
+      10587, 1987, 11635, 3565, 5415, 8646, 6153, 6427, 7341, 6152, 10561, 400,
+      8410, 1922, 2033, 8291, 1359, 6854, 11035, 973, 8579, 6093, 6950, 5446,
+      11821, 8301, 11907, 316, 52, 3174, 10966, 9523, 6055, 8953, 11612, 6415,
+      2505, 5906, 10710, 11858, 8332, 9450, 10162, 151, 3482, 787, 5468, 1010,
+      4169, 9162, 5241, 9369, 7509, 8844, 7232, 4698, 192, 1321, 10240, 4912, 885,
+      6281, 10333, 7280, 8757, 11286, 58, 12048, 12147, 11184, 8812, 6608, 2844,
+      3438, 4212, 11314, 8687, 6068, 421, 8209, 3600, 3263, 7665, 6077, 7507, 5886,
+      3029, 6695, 4213, 504, 11684, 2302, 1962, 1594, 6328, 7183, 168, 2692, 8960,
+      4298, 5184, 11089, 6122, 9734, 10929, 3956, 5297, 6170, 3762, 9370, 4016,
+      4077, 6523, 652, 11994, 6099, 1146, 11341, 11964, 10885, 6299, 1159, 8240,
+      8561, 11177, 2078, 10331, 4322, 11367, 441, 4079, 11231, 3150, 1319, 8243,
+      709, 8049, 8719, 11454, 6224, 3054, 6803, 3123, 10542, 4433, 6370, 7032,
+      3834, 8633, 12225, 9830, 683, 1566, 5782, 9786, 9341, 12115, 723, 3009, 1693,
+      5735, 2655, 2738, 6421, 11942, 2925, 1975, 8532, 3315, 11863, 4754, 1858,
+      1583, 6347, 2500, 10800, 6374, 1483, 12240, 1263, 1815, 5383, 10777, 350,
+      6920, 10232, 4493, 9087, 8855, 8760, 9381, 218, 9928, 10446, 9259, 4115,
+      6147, 9842, 8326, 576, 10335, 10238, 10484, 9407, 6381, 11836, 8517, 418,
+      6860, 7515, 1293, 7552, 2767, 156, 8298, 8320, 10008, 5876, 5333, 10258,
+      10115, 4372, 2847, 7875, 8232, 9018, 8925, 1689, 8236, 2645, 5042, 9984,
+      7094, 9509, 1484, 7394, 3, 4437, 160, 3149, 113, 7370, 10123, 3915, 6998,
+      2704, 8653, 4938, 1426, 7635, 10512, 1663, 6957, 3510, 2370, 2865, 3978,
+      9320, 3247, 9603, 6882, 3186, 10659, 10163, 1153, 9405, 8241, 10040, 2178,
+      1544, 5559, 420, 8304, 4905, 476, 3531, 5191, 9153, 2399, 8889, 3000, 671,
+      243, 3016, 3763, 10849, 12262, 9223, 10657, 7205, 11272, 7404, 7575, 8146,
+      10752, 242, 2678, 3704, 11744, 5019, 3833, 3778, 11899, 773, 5101, 11222,
+      9888, 442, 2912, 5698, 11935, 4861, 7277, 9808, 11244, 2859, 3780, 11414,
+      4976, 10682, 7201, 8005, 11287, 5011, 6267, 2987, 2437, 3646, 2566, 10102,
+      9867, 6250, 5444, 2381, 11796, 8193, 4337, 11854, 1912, 1378, 404, 7644,
+      1065, 2143, 11121, 5277, 3248, 11082, 2548, 8058, 8907, 11934, 1759, 8582,
+      3694, 7110, 12144, 6747, 8652, 3459, 2731, 8357, 6378, 7399, 10861, 1696,
+      9863, 334, 7657, 6534, 11029, 4388, 11560, 3241, 10276, 9000, 9408, 3284,
+      10200, 7197, 6498, 544, 2468, 339, 11267, 9, 2842, 480, 5331, 7300, 1673,
+      4278, 4177, 8705, 9764, 1381, 7837, 2396, 8340, 8993, 4354, 130, 6915, 2837,
+      11462, 5767, 953, 8541, 9813, 118, 7222, 2197, 3006, 9545, 563, 9314, 2625,
+      11340, 4821, 2639, 7266, 5828, 6561, 7698, 3328, 6512, 1351, 7311, 6553,
+      8155, 1305, 722, 5146, 4043, 12288, 10810, 2545, 3621, 8747, 8785, 1646,
+      1212, 5860, 3195, 7203, 10963, 3201, 3014, 955, 11499, 9970, 11119, 3135,
+      3712, 7443, 9542, 7484, 8736, 9995, 11227, 1635, 9521, 1177, 8034, 140,
+      10436, 11563, 7678, 4320, 11289, 9198, 12208, 2963, 7393, 2366, 9238
+      };
+
+   static const uint16_t psis_inv_montgomery[PARAM_N] =
+      {
+      256, 10570, 1510, 7238, 1034, 7170, 6291, 7921, 11665, 3422, 4000, 2327,
+      2088, 5565, 795, 10647, 1521, 5484, 2539, 7385, 1055, 7173, 8047, 11683,
+      1669, 1994, 3796, 5809, 4341, 9398, 11876, 12230, 10525, 12037, 12253, 3506,
+      4012, 9351, 4847, 2448, 7372, 9831, 3160, 2207, 5582, 2553, 7387, 6322, 9681,
+      1383, 10731, 1533, 219, 5298, 4268, 7632, 6357, 9686, 8406, 4712, 9451,
+      10128, 4958, 5975, 11387, 8649, 11769, 6948, 11526, 12180, 1740, 10782, 6807,
+      2728, 7412, 4570, 4164, 4106, 11120, 12122, 8754, 11784, 3439, 5758, 11356,
+      6889, 9762, 11928, 1704, 1999, 10819, 12079, 12259, 7018, 11536, 1648, 1991,
+      2040, 2047, 2048, 10826, 12080, 8748, 8272, 8204, 1172, 1923, 7297, 2798,
+      7422, 6327, 4415, 7653, 6360, 11442, 12168, 7005, 8023, 9924, 8440, 8228,
+      2931, 7441, 1063, 3663, 5790, 9605, 10150, 1450, 8985, 11817, 10466, 10273,
+      12001, 3470, 7518, 1074, 1909, 7295, 9820, 4914, 702, 5367, 7789, 8135, 9940,
+      1420, 3714, 11064, 12114, 12264, 1752, 5517, 9566, 11900, 1700, 3754, 5803,
+      829, 1874, 7290, 2797, 10933, 5073, 7747, 8129, 6428, 6185, 11417, 1631, 233,
+      5300, 9535, 10140, 11982, 8734, 8270, 2937, 10953, 8587, 8249, 2934, 9197,
+      4825, 5956, 4362, 9401, 1343, 3703, 529, 10609, 12049, 6988, 6265, 895, 3639,
+      4031, 4087, 4095, 585, 10617, 8539, 4731, 4187, 9376, 3095, 9220, 10095,
+      10220, 1460, 10742, 12068, 1724, 5513, 11321, 6884, 2739, 5658, 6075, 4379,
+      11159, 10372, 8504, 4726, 9453, 3106, 7466, 11600, 10435, 8513, 9994, 8450,
+      9985, 3182, 10988, 8592, 2983, 9204, 4826, 2445, 5616, 6069, 867, 3635, 5786,
+      11360, 5134, 2489, 10889, 12089, 1727, 7269, 2794, 9177, 1311, 5454, 9557,
+      6632, 2703, 9164, 10087, 1441, 3717, 531, 3587, 2268, 324, 5313, 759, 1864,
+      5533, 2546, 7386, 9833, 8427, 4715, 11207, 1601, 7251, 4547, 11183, 12131,
+      1733, 10781, 10318, 1474, 10744, 5046, 4232, 11138, 10369, 6748, 964, 7160,
+      4534, 7670, 8118, 8182, 4680, 11202, 6867, 981, 8918, 1274, 182, 26, 7026,
+      8026, 11680, 12202, 10521, 1503, 7237, 4545, 5916, 9623, 8397, 11733, 10454,
+      3249, 9242, 6587, 941, 1890, 270, 10572, 6777, 9746, 6659, 6218, 6155, 6146,
+      878, 1881, 7291, 11575, 12187, 1741, 7271, 8061, 11685, 6936, 4502, 9421,
+      4857, 4205, 7623, 1089, 10689, 1527, 8996, 10063, 11971, 10488, 6765, 2722,
+      3900, 9335, 11867, 6962, 11528, 5158, 4248, 4118, 5855, 2592, 5637, 6072,
+      2623, 7397, 8079, 9932, 4930, 5971, 853, 3633, 519, 8852, 11798, 3441, 11025,
+      1575, 225, 8810, 11792, 12218, 3501, 9278, 3081, 9218, 4828, 7712, 8124,
+      11694, 12204, 3499, 4011, 573, 3593, 5780, 7848, 9899, 10192, 1456, 208,
+      7052, 2763, 7417, 11593, 10434, 12024, 8740, 11782, 10461, 3250, 5731, 7841,
+      9898, 1414, 202, 3540, 7528, 2831, 2160, 10842, 5060, 4234, 4116, 588, 84,
+      12, 7024, 2759, 9172, 6577, 11473, 1639, 9012, 3043, 7457, 6332, 11438, 1634,
+      1989, 9062, 11828, 8712, 11778, 12216, 10523, 6770, 9745, 10170, 4964, 9487,
+      6622, 946, 8913, 6540, 6201, 4397, 9406, 8366, 9973, 8447, 8229, 11709, 8695,
+      10020, 3187, 5722, 2573, 10901, 6824, 4486, 4152, 9371, 8361, 2950, 2177,
+      311, 1800, 9035, 8313, 11721, 3430, 490, 70, 10, 1757, 251, 3547, 7529,
+      11609, 3414, 7510, 4584, 4166, 9373, 1339, 5458, 7802, 11648, 1664, 7260,
+      9815, 10180, 6721, 9738, 10169, 8475, 8233, 9954, 1422, 8981, 1283, 5450,
+      11312, 1616, 3742, 11068, 10359, 4991, 713, 3613, 9294, 8350, 4704, 672, 96,
+      7036, 9783, 11931, 3460, 5761, 823, 10651, 12055, 10500, 1500, 5481, 783,
+      3623, 11051, 8601, 8251, 8201, 11705, 10450, 5004, 4226, 7626, 2845, 2162,
+      3820, 7568, 9859, 3164, 452, 10598, 1514, 5483, 6050, 6131, 4387, 7649, 8115,
+      6426, 918, 8909, 8295, 1185, 5436, 11310, 8638, 1234, 5443, 11311, 5127,
+      2488, 2111, 10835, 5059, 7745, 2862, 3920, 560, 80, 1767, 2008, 3798, 11076,
+      6849, 2734, 10924, 12094, 8750, 1250, 10712, 6797, 971, 7161, 1023, 8924,
+      4786, 7706, 4612, 4170, 7618, 6355, 4419, 5898, 11376, 10403, 10264, 6733,
+      4473, 639, 5358, 2521, 9138, 3061, 5704, 4326, 618, 5355, 765, 5376, 768,
+      7132, 4530, 9425, 3102, 9221, 6584, 11474, 10417, 10266, 12000, 6981, 6264,
+      4406, 2385, 7363, 4563, 4163, 7617, 9866, 3165, 9230, 11852, 10471, 5007,
+      5982, 11388, 5138, 734, 3616, 11050, 12112, 6997, 11533, 12181, 10518, 12036,
+      3475, 2252, 7344, 9827, 4915, 9480, 6621, 4457, 7659, 9872, 6677, 4465, 4149,
+      7615, 4599, 657, 3605, 515, 10607, 6782, 4480, 640, 1847, 3775, 5806, 2585,
+      5636, 9583, 1369, 10729, 8555, 10000, 11962, 5220, 7768, 8132, 8184, 9947,
+      1421, 203, 29, 8782, 11788, 1684, 10774, 10317, 4985, 9490, 8378, 4708,
+      11206, 5112, 5997, 7879, 11659, 12199, 8765, 10030, 4944, 5973, 6120, 6141,
+      6144, 7900, 11662, 1666, 238, 34, 3516, 5769, 9602, 8394, 9977, 6692, 956,
+      10670, 6791, 9748, 11926, 8726, 11780, 5194, 742, 106, 8793, 10034, 3189,
+      10989, 5081, 4237, 5872, 4350, 2377, 10873, 6820, 6241, 11425, 10410, 10265,
+      3222, 5727, 9596, 4882, 2453, 2106, 3812, 11078, 12116, 5242, 4260, 11142,
+      8614, 11764, 12214, 5256, 4262, 4120, 11122, 5100, 11262, 5120, 2487, 5622,
+      9581, 8391, 8221, 2930, 10952, 12098, 6995, 6266, 9673, 4893, 699, 3611,
+      4027, 5842, 11368, 1624, 232, 8811, 8281, 1183, 169, 8802, 3013, 2186, 5579,
+      797, 3625, 4029, 11109, 1587, 7249, 11569, 8675, 6506, 2685, 10917, 12093,
+      12261, 12285, 1755, 7273, 1039, 1904, 272, 3550, 9285, 3082, 5707, 6082,
+      4380, 7648, 11626, 5172, 4250, 9385, 8363, 8217, 4685, 5936, 848, 8899, 6538,
+      934, 1889, 3781, 9318, 10109, 10222, 6727, 961, 5404, 772, 5377, 9546, 8386,
+      1198, 8949, 3034, 2189, 7335, 4559, 5918, 2601, 10905, 5069, 9502, 3113,
+      7467, 8089, 11689, 5181, 9518, 8382, 2953, 3933, 4073, 4093, 7607, 8109,
+      2914, 5683, 4323, 11151, 1593, 10761, 6804, 972, 3650, 2277, 5592, 4310,
+      7638, 9869, 4921, 703, 1856, 9043, 4803, 9464, 1352, 8971, 11815, 5199, 7765,
+      6376, 4422, 7654, 2849, 407, 8836, 6529, 7955, 2892, 9191, 1313, 10721,
+      12065, 12257, 1751, 9028, 8312, 2943, 2176, 3822, 546, 78, 8789, 11789,
+      10462, 12028, 6985, 4509, 9422, 1346, 5459, 4291, 613, 10621, 6784, 9747,
+      3148, 7472, 2823, 5670, 810, 7138, 8042, 4660, 7688, 6365, 6176, 6149, 2634,
+      5643, 9584, 10147, 11983, 5223, 9524, 11894, 10477, 8519, 1217, 3685, 2282,
+      326, 10580, 3267, 7489, 4581, 2410, 5611, 11335, 6886, 8006, 8166, 11700,
+      3427, 11023, 8597, 10006, 3185, 455, 65, 5276, 7776, 4622, 5927, 7869, 9902,
+      11948, 5218, 2501, 5624, 2559, 10899, 1557, 1978, 10816, 10323, 8497, 4725,
+      675, 1852, 10798, 12076, 10503, 3256, 9243, 3076, 2195, 10847, 12083, 10504,
+      12034, 10497
+      };
+
+   bitrev_vector(r->coeffs);
+   ntt(r->coeffs, omegas_inv_montgomery);
+   mul_coefficients(r->coeffs, psis_inv_montgomery);
+   }
+
+inline void encode_a(uint8_t* r, const poly* pk, const uint8_t* seed)
+   {
+   poly_tobytes(r, pk);
+   for(size_t i = 0; i < NEWHOPE_SEED_BYTES; i++)
+      {
+      r[NEWHOPE_POLY_BYTES+i] = seed[i];
+      }
+   }
+
+inline void decode_a(poly* pk, uint8_t* seed, const uint8_t* r)
+   {
+   poly_frombytes(pk, r);
+   for(size_t i = 0; i < NEWHOPE_SEED_BYTES; i++)
+      {
+      seed[i] = r[NEWHOPE_POLY_BYTES+i];
+      }
+   }
+
+inline void encode_b(uint8_t* r, const poly* b, const poly* c)
+   {
+   poly_tobytes(r, b);
+   for(size_t i = 0; i < PARAM_N/4; i++)
+      {
+      r[NEWHOPE_POLY_BYTES+i] = static_cast<uint8_t>(c->coeffs[4*i] |
+                                                     (c->coeffs[4*i+1] << 2) |
+                                                     (c->coeffs[4*i+2] << 4) |
+                                                     (c->coeffs[4*i+3] << 6));
+      }
+   }
+
+inline void decode_b(poly* b, poly* c, const uint8_t* r)
+   {
+   poly_frombytes(b, r);
+   for(size_t i = 0; i < PARAM_N/4; i++)
+      {
+      c->coeffs[4*i+0] =  r[NEWHOPE_POLY_BYTES+i]       & 0x03;
+      c->coeffs[4*i+1] = (r[NEWHOPE_POLY_BYTES+i] >> 2) & 0x03;
+      c->coeffs[4*i+2] = (r[NEWHOPE_POLY_BYTES+i] >> 4) & 0x03;
+      c->coeffs[4*i+3] = (r[NEWHOPE_POLY_BYTES+i] >> 6);
+      }
+   }
+
+inline int32_t ct_abs(int32_t v)
+   {
+   int32_t mask = v >> 31;
+   return (v ^ mask) - mask;
+   }
+
+inline int32_t f(int32_t* v0, int32_t* v1, int32_t x)
+   {
+   int32_t xit, t, r, b;
+
+   // Next 6 lines compute t = x/PARAM_Q;
+   b = x*2730;
+   t = b >> 25;
+   b = x - t*12289;
+   b = 12288 - b;
+   b >>= 31;
+   t -= b;
+
+   r = t & 1;
+   xit = (t>>1);
+   *v0 = xit+r; // v0 = round(x/(2*PARAM_Q))
+
+   t -= 1;
+   r = t & 1;
+   *v1 = (t>>1)+r;
+
+   return ct_abs(x-((*v0)*2*PARAM_Q));
+   }
+
+inline void helprec(poly* c, const poly* v, RandomNumberGenerator& rng)
+   {
+   uint8_t rand[32];
+
+   rng.randomize(rand, 32);
+
+   for(size_t i = 0; i < 256; i++)
+      {
+      int32_t v0[4], v1[4];
+      uint8_t rbit = (rand[i>>3] >> (i&7)) & 1;
+      int32_t k;
+
+      k  = f(v0+0, v1+0, 8*v->coeffs[  0+i] + 4*rbit);
+      k += f(v0+1, v1+1, 8*v->coeffs[256+i] + 4*rbit);
+      k += f(v0+2, v1+2, 8*v->coeffs[512+i] + 4*rbit);
+      k += f(v0+3, v1+3, 8*v->coeffs[768+i] + 4*rbit);
+
+      k = (2*PARAM_Q-1-k) >> 31;
+
+      int32_t v_tmp[4];
+      v_tmp[0] = ((~k) & v0[0]) ^ (k & v1[0]);
+      v_tmp[1] = ((~k) & v0[1]) ^ (k & v1[1]);
+      v_tmp[2] = ((~k) & v0[2]) ^ (k & v1[2]);
+      v_tmp[3] = ((~k) & v0[3]) ^ (k & v1[3]);
+
+      c->coeffs[  0+i] = (v_tmp[0] -   v_tmp[3]) & 3;
+      c->coeffs[256+i] = (v_tmp[1] -   v_tmp[3]) & 3;
+      c->coeffs[512+i] = (v_tmp[2] -   v_tmp[3]) & 3;
+      c->coeffs[768+i] = (-   k    + 2*v_tmp[3]) & 3;
+      }
+   }
+
+inline int32_t g(int32_t x)
+   {
+   int32_t t, c, b;
+
+   // Next 6 lines compute t = x/(4*PARAM_Q);
+   b = x*2730;
+   t = b >> 27;
+   b = x - t*49156;
+   b = 49155 - b;
+   b >>= 31;
+   t -= b;
+
+   c = t & 1;
+   t = (t >> 1) + c; // t = round(x/(8*PARAM_Q))
+
+   t *= 8*PARAM_Q;
+
+   return ct_abs(t - x);
+   }
+
+inline int16_t LDDecode(int32_t xi0, int32_t xi1, int32_t xi2, int32_t xi3)
+   {
+   int32_t t;
+
+   t  = g(xi0);
+   t += g(xi1);
+   t += g(xi2);
+   t += g(xi3);
+
+   t -= 8*PARAM_Q;
+   t >>= 31;
+   return t&1;
+   }
+
+inline void rec(uint8_t* key, const poly* v, const poly* c)
+   {
+   clear_mem(key, 32);
+
+   for(size_t i = 0; i < 256; i++)
+      {
+      const int32_t tmp0 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[  0+i]) - PARAM_Q * (2*c->coeffs[  0+i]+c->coeffs[768+i]);
+      const int32_t tmp1 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[256+i]) - PARAM_Q * (2*c->coeffs[256+i]+c->coeffs[768+i]);
+      const int32_t tmp2 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[512+i]) - PARAM_Q * (2*c->coeffs[512+i]+c->coeffs[768+i]);
+      const int32_t tmp3 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[768+i]) - PARAM_Q * (c->coeffs[768+i]);
+
+      key[i>>3] |= LDDecode(tmp0, tmp1, tmp2, tmp3) << (i & 7);
+      }
+   }
+
+void gen_a(poly* a, const uint8_t* seed, Newhope_Mode mode)
+   {
+   std::vector<uint8_t> buf(168*16);
+
+   std::unique_ptr<StreamCipher> xof;
+
+   if(mode == Newhope_Mode::BoringSSL)
+      {
+      xof = StreamCipher::create_or_throw("CTR-BE(AES-128)");
+      xof->set_key(seed, 16);
+      xof->set_iv(seed + 16, 16);
+      }
+   else
+      {
+      xof = StreamCipher::create_or_throw("SHAKE-128");
+      xof->set_key(seed, NEWHOPE_SEED_BYTES);
+      }
+
+   zeroise(buf);
+   xof->encrypt(buf);
+
+   size_t pos = 0, ctr = 0;
+
+   while(ctr < PARAM_N)
+      {
+      // Specialized for q = 12889
+      const uint16_t val = (buf[pos] | (static_cast<uint16_t>(buf[pos+1]) << 8)) & 0x3fff;
+
+      if(val < PARAM_Q)
+         {
+         a->coeffs[ctr++] = val;
+         }
+      pos += 2;
+      if(pos >= buf.size())
+         {
+         zeroise(buf);
+         xof->encrypt(buf);
+         pos = 0;
+         }
+      }
+   }
+
+}
+
+// API FUNCTIONS
+
+void newhope_keygen(uint8_t* send, poly* sk, RandomNumberGenerator& rng,
+                    Newhope_Mode mode)
+   {
+   poly a, e, r, pk;
+   uint8_t seed[NEWHOPE_SEED_BYTES];
+
+   rng.randomize(seed, NEWHOPE_SEED_BYTES);
+
+   gen_a(&a, seed, mode);
+
+   poly_getnoise(rng, sk);
+   poly_ntt(sk);
+
+   poly_getnoise(rng, &e);
+   poly_ntt(&e);
+
+   poly_pointwise(&r, sk, &a);
+   poly_add(&pk, &e, &r);
+
+   encode_a(send, &pk, seed);
+   }
+
+void newhope_sharedb(uint8_t* sharedkey, uint8_t* send, const uint8_t* received,
+                     RandomNumberGenerator& rng,
+                     Newhope_Mode mode)
+   {
+   poly sp, ep, v, a, pka, c, epp, bp;
+   uint8_t seed[NEWHOPE_SEED_BYTES];
+
+   decode_a(&pka, seed, received);
+   gen_a(&a, seed, mode);
+
+   poly_getnoise(rng, &sp);
+   poly_ntt(&sp);
+   poly_getnoise(rng, &ep);
+   poly_ntt(&ep);
+
+   poly_pointwise(&bp, &a, &sp);
+   poly_add(&bp, &bp, &ep);
+
+   poly_pointwise(&v, &pka, &sp);
+   poly_invntt(&v);
+
+   poly_getnoise(rng, &epp);
+   poly_add(&v, &v, &epp);
+
+   helprec(&c, &v, rng);
+
+   encode_b(send, &bp, &c);
+
+   rec(sharedkey, &v, &c);
+
+   const std::string kdf_hash = (mode == Newhope_Mode::SHA3) ? "SHA-3(256)" : "SHA-256";
+   std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(kdf_hash);
+
+   hash->update(sharedkey, 32);
+   hash->final(sharedkey);
+   }
+
+void newhope_shareda(uint8_t sharedkey[],
+                     const poly* sk,
+                     const uint8_t received[],
+                     Newhope_Mode mode)
+   {
+   poly v, bp, c;
+
+   decode_b(&bp, &c, received);
+
+   poly_pointwise(&v, sk, &bp);
+   poly_invntt(&v);
+
+   rec(sharedkey, &v, &c);
+
+   const std::string kdf_hash = (mode == Newhope_Mode::SHA3) ? "SHA-3(256)" : "SHA-256";
+   std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(kdf_hash);
+
+   hash->update(sharedkey, 32);
+   hash->final(sharedkey);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/newhope/newhope.h b/third_party/botan/src/lib/pubkey/newhope/newhope.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/newhope/newhope.h
@@ -0,0 +1,85 @@
+/*
+* NEWHOPE Ring-LWE scheme
+* Based on the public domain reference implementation by the
+* designers (https://github.com/tpoeppelmann/newhope)
+*
+* Further changes
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NEWHOPE_H_
+#define BOTAN_NEWHOPE_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/*
+* WARNING: This API is preliminary and will change
+* Currently pubkey.h does not support a 2-phase KEM scheme of
+* the sort NEWHOPE exports.
+*/
+
+// TODO: change to just a secure_vector
+class BOTAN_UNSTABLE_API newhope_poly final
+   {
+   public:
+      uint16_t coeffs[1024];
+      ~newhope_poly();
+   };
+
+enum Newhope_Params
+   {
+   NEWHOPE_SENDABYTES = 1824,
+   NEWHOPE_SENDBBYTES = 2048,
+
+   NEWHOPE_OFFER_BYTES  = 1824,
+   NEWHOPE_ACCEPT_BYTES = 2048,
+   NEWHOPE_SHARED_KEY_BYTES = 32,
+
+   NEWHOPE_SEED_BYTES = 32,
+   NEWHOPE_POLY_BYTES = 1792,
+
+   CECPQ1_OFFER_BYTES   = NEWHOPE_OFFER_BYTES + 32,
+   CECPQ1_ACCEPT_BYTES  = NEWHOPE_ACCEPT_BYTES + 32,
+   CECPQ1_SHARED_KEY_BYTES = NEWHOPE_SHARED_KEY_BYTES + 32
+   };
+
+/**
+* This chooses the XOF + hash for NewHope
+* The official NewHope specification and reference implementation use
+* SHA-3 and SHAKE-128. BoringSSL instead uses SHA-256 and AES-128 in
+* CTR mode. CECPQ1 (x25519+NewHope) always uses BoringSSL's mode
+*/
+enum class Newhope_Mode
+   {
+   SHA3,
+   BoringSSL
+   };
+
+// offer
+void BOTAN_PUBLIC_API(2,0) newhope_keygen(uint8_t send[NEWHOPE_SENDABYTES],
+                              newhope_poly* sk,
+                              RandomNumberGenerator& rng,
+                              Newhope_Mode = Newhope_Mode::SHA3);
+
+// accept
+void BOTAN_PUBLIC_API(2,0) newhope_sharedb(uint8_t sharedkey[NEWHOPE_SHARED_KEY_BYTES],
+                               uint8_t send[],
+                               const uint8_t* received,
+                               RandomNumberGenerator& rng,
+                               Newhope_Mode mode = Newhope_Mode::SHA3);
+
+// finish
+void BOTAN_PUBLIC_API(2,0) newhope_shareda(uint8_t sharedkey[NEWHOPE_SHARED_KEY_BYTES],
+                               const newhope_poly* ska,
+                               const uint8_t* received,
+                               Newhope_Mode mode = Newhope_Mode::SHA3);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp b/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp
@@ -0,0 +1,339 @@
+/*
+* PKCS #5 PBES2
+* (C) 1999-2008,2014 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbes2.h>
+#include <botan/cipher_mode.h>
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/alg_id.h>
+#include <botan/oids.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_SCRYPT)
+   #include <botan/scrypt.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+bool known_pbes_cipher_mode(const std::string& mode)
+   {
+   return (mode == "CBC" || mode == "GCM" || mode == "SIV");
+   }
+
+SymmetricKey derive_key(const std::string& passphrase,
+                        const AlgorithmIdentifier& kdf_algo,
+                        size_t default_key_size)
+   {
+   if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2"))
+      {
+      secure_vector<uint8_t> salt;
+      size_t iterations = 0, key_length = 0;
+
+      AlgorithmIdentifier prf_algo;
+      BER_Decoder(kdf_algo.get_parameters())
+         .start_cons(SEQUENCE)
+         .decode(salt, OCTET_STRING)
+         .decode(iterations)
+         .decode_optional(key_length, INTEGER, UNIVERSAL)
+         .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED,
+                          AlgorithmIdentifier("HMAC(SHA-160)",
+                                              AlgorithmIdentifier::USE_NULL_PARAM))
+         .end_cons();
+
+      if(salt.size() < 8)
+         throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small");
+
+      if(key_length == 0)
+         key_length = default_key_size;
+
+      const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid());
+      std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(" + prf + ")"));
+      return pbkdf->pbkdf_iterations(key_length, passphrase, salt.data(), salt.size(), iterations);
+      }
+#if defined(BOTAN_HAS_SCRYPT)
+   else if(kdf_algo.get_oid() == OID::from_string("Scrypt"))
+      {
+      secure_vector<uint8_t> salt;
+      size_t N = 0, r = 0, p = 0;
+      size_t key_length = 0;
+
+      AlgorithmIdentifier prf_algo;
+      BER_Decoder(kdf_algo.get_parameters())
+         .start_cons(SEQUENCE)
+         .decode(salt, OCTET_STRING)
+         .decode(N)
+         .decode(r)
+         .decode(p)
+         .decode_optional(key_length, INTEGER, UNIVERSAL)
+         .end_cons();
+
+      if(key_length == 0)
+         key_length = default_key_size;
+
+      secure_vector<uint8_t> output(key_length);
+      scrypt(output.data(), output.size(), passphrase,
+             salt.data(), salt.size(), N, r, p);
+
+      return SymmetricKey(output);
+      }
+#endif
+   else
+      throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " +
+                           kdf_algo.get_oid().to_string());
+   }
+
+secure_vector<uint8_t> derive_key(const std::string& passphrase,
+                                  const std::string& digest,
+                                  RandomNumberGenerator& rng,
+                                  size_t* msec_in_iterations_out,
+                                  size_t iterations_if_msec_null,
+                                  size_t key_length,
+                                  AlgorithmIdentifier& kdf_algo)
+   {
+   const secure_vector<uint8_t> salt = rng.random_vec(12);
+
+   if(digest == "Scrypt")
+      {
+#if defined(BOTAN_HAS_SCRYPT)
+
+      std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
+
+      std::unique_ptr<PasswordHash> pwhash;
+
+      if(msec_in_iterations_out)
+         {
+         const std::chrono::milliseconds msec(*msec_in_iterations_out);
+         pwhash = pwhash_fam->tune(key_length, msec);
+         }
+      else
+         {
+         pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
+         }
+
+      secure_vector<uint8_t> key(key_length);
+      pwhash->derive_key(key.data(), key.size(),
+                         passphrase.c_str(), passphrase.size(),
+                         salt.data(), salt.size());
+
+      const size_t N = pwhash->memory_param();
+      const size_t r = pwhash->iterations();
+      const size_t p = pwhash->parallelism();
+
+      if(msec_in_iterations_out)
+         *msec_in_iterations_out = 0;
+
+      std::vector<uint8_t> scrypt_params;
+      DER_Encoder(scrypt_params)
+         .start_cons(SEQUENCE)
+            .encode(salt, OCTET_STRING)
+            .encode(N)
+            .encode(r)
+            .encode(p)
+            .encode(key_length)
+         .end_cons();
+
+      kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params);
+      return key;
+#else
+      throw Not_Implemented("Scrypt is not available in this build");
+#endif
+      }
+   else
+      {
+      const std::string prf = "HMAC(" + digest + ")";
+      const std::string pbkdf_name = "PBKDF2(" + prf + ")";
+
+      std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name);
+      if(!pwhash_fam)
+         throw Invalid_Argument("Unknown password hash digest " + digest);
+
+      std::unique_ptr<PasswordHash> pwhash;
+
+      if(msec_in_iterations_out)
+         {
+         const std::chrono::milliseconds msec(*msec_in_iterations_out);
+         pwhash = pwhash_fam->tune(key_length, msec);
+         }
+      else
+         {
+         pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
+         }
+
+      secure_vector<uint8_t> key(key_length);
+      pwhash->derive_key(key.data(), key.size(),
+                         passphrase.c_str(), passphrase.size(),
+                         salt.data(), salt.size());
+
+      std::vector<uint8_t> pbkdf2_params;
+
+      const size_t iterations = pwhash->iterations();
+
+      if(msec_in_iterations_out)
+         *msec_in_iterations_out = iterations;
+
+      DER_Encoder(pbkdf2_params)
+         .start_cons(SEQUENCE)
+            .encode(salt, OCTET_STRING)
+            .encode(iterations)
+            .encode(key_length)
+            .encode_if(prf != "HMAC(SHA-160)",
+                       AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM))
+         .end_cons();
+
+      kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params);
+      return key;
+      }
+   }
+
+/*
+* PKCS#5 v2.0 PBE Encryption
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits,
+                     const std::string& passphrase,
+                     size_t* msec_in_iterations_out,
+                     size_t iterations_if_msec_null,
+                     const std::string& cipher,
+                     const std::string& prf,
+                     RandomNumberGenerator& rng)
+   {
+   const std::vector<std::string> cipher_spec = split_on(cipher, '/');
+   if(cipher_spec.size() != 2)
+      throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
+
+   if(!known_pbes_cipher_mode(cipher_spec[1]))
+      throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
+
+   const OID cipher_oid = OIDS::str2oid_or_empty(cipher);
+   if(cipher_oid.empty())
+      throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher);
+
+   std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION);
+
+   if(!enc)
+      throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher);
+
+   const size_t key_length = enc->key_spec().maximum_keylength();
+
+   const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
+
+   AlgorithmIdentifier kdf_algo;
+
+   const secure_vector<uint8_t> derived_key =
+      derive_key(passphrase, prf, rng,
+                 msec_in_iterations_out, iterations_if_msec_null,
+                 key_length, kdf_algo);
+
+   enc->set_key(derived_key);
+   enc->start(iv);
+   secure_vector<uint8_t> ctext = key_bits;
+   enc->finish(ctext);
+
+   std::vector<uint8_t> encoded_iv;
+   DER_Encoder(encoded_iv).encode(iv, OCTET_STRING);
+
+   std::vector<uint8_t> pbes2_params;
+   DER_Encoder(pbes2_params)
+      .start_cons(SEQUENCE)
+      .encode(kdf_algo)
+      .encode(AlgorithmIdentifier(cipher, encoded_iv))
+      .end_cons();
+
+   AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params);
+
+   return std::make_pair(id, unlock(ctext));
+   }
+
+}
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
+              const std::string& passphrase,
+              std::chrono::milliseconds msec,
+              const std::string& cipher,
+              const std::string& digest,
+              RandomNumberGenerator& rng)
+   {
+   size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
+   return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
+   // return value msec_in_iterations_out discarded
+   }
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits,
+                   const std::string& passphrase,
+                   std::chrono::milliseconds msec,
+                   size_t* out_iterations_if_nonnull,
+                   const std::string& cipher,
+                   const std::string& digest,
+                   RandomNumberGenerator& rng)
+   {
+   size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
+
+   auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
+
+   if(out_iterations_if_nonnull)
+      *out_iterations_if_nonnull = msec_in_iterations_out;
+
+   return ret;
+   }
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits,
+                   const std::string& passphrase,
+                   size_t pbkdf_iter,
+                   const std::string& cipher,
+                   const std::string& digest,
+                   RandomNumberGenerator& rng)
+   {
+   return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng);
+   }
+
+secure_vector<uint8_t>
+pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
+              const std::string& passphrase,
+              const std::vector<uint8_t>& params)
+   {
+   AlgorithmIdentifier kdf_algo, enc_algo;
+
+   BER_Decoder(params)
+      .start_cons(SEQUENCE)
+         .decode(kdf_algo)
+         .decode(enc_algo)
+      .end_cons();
+
+   const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid());
+   const std::vector<std::string> cipher_spec = split_on(cipher, '/');
+   if(cipher_spec.size() != 2)
+      throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
+   if(!known_pbes_cipher_mode(cipher_spec[1]))
+      throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
+
+   secure_vector<uint8_t> iv;
+   BER_Decoder(enc_algo.get_parameters()).decode(iv, OCTET_STRING).verify_end();
+
+   std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION);
+   if(!dec)
+      throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher);
+
+   dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength()));
+
+   dec->start(iv);
+
+   secure_vector<uint8_t> buf = key_bits;
+   dec->finish(buf);
+
+   return buf;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pbes2/pbes2.h b/third_party/botan/src/lib/pubkey/pbes2/pbes2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pbes2/pbes2.h
@@ -0,0 +1,87 @@
+/*
+* PKCS #5 v2.0 PBE
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBE_PKCS_v20_H_
+#define BOTAN_PBE_PKCS_v20_H_
+
+#include <botan/alg_id.h>
+#include <chrono>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbes2.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param msec how many milliseconds to run PBKDF2
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,0) pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
+                        const std::string& passphrase,
+                        std::chrono::milliseconds msec,
+                        const std::string& cipher,
+                        const std::string& digest,
+                        RandomNumberGenerator& rng);
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param msec how many milliseconds to run PBKDF2
+* @param out_iterations_if_nonnull if not null, set to the number
+* of PBKDF iterations used
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,1) pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits,
+                             const std::string& passphrase,
+                             std::chrono::milliseconds msec,
+                             size_t* out_iterations_if_nonnull,
+                             const std::string& cipher,
+                             const std::string& digest,
+                             RandomNumberGenerator& rng);
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param iterations how many iterations to run PBKDF2
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,1) pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits,
+                             const std::string& passphrase,
+                             size_t iterations,
+                             const std::string& cipher,
+                             const std::string& digest,
+                             RandomNumberGenerator& rng);
+
+/**
+* Decrypt a PKCS #5 v2.0 encrypted stream
+* @param key_bits the input
+* @param passphrase the passphrase to use for decryption
+* @param params the PBES2 parameters
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
+                        const std::string& passphrase,
+                        const std::vector<uint8_t>& params);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pem/pem.cpp b/third_party/botan/src/lib/pubkey/pem/pem.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pem/pem.cpp
@@ -0,0 +1,169 @@
+/*
+* PEM Encoding/Decoding
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pem.h>
+#include <botan/data_src.h>
+#include <botan/base64.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace PEM_Code {
+
+namespace {
+
+std::string linewrap(size_t width, const std::string& in)
+   {
+   std::string out;
+   for(size_t i = 0; i != in.size(); ++i)
+      {
+      if(i > 0 && i % width == 0)
+         {
+         out.push_back('\n');
+         }
+      out.push_back(in[i]);
+      }
+   if(out.size() > 0 && out[out.size()-1] != '\n')
+      {
+      out.push_back('\n');
+      }
+
+   return out;
+   }
+
+}
+
+/*
+* PEM encode BER/DER-encoded objects
+*/
+std::string encode(const uint8_t der[], size_t length, const std::string& label, size_t width)
+   {
+   const std::string PEM_HEADER = "-----BEGIN " + label + "-----\n";
+   const std::string PEM_TRAILER = "-----END " + label + "-----\n";
+
+   return (PEM_HEADER + linewrap(width, base64_encode(der, length)) + PEM_TRAILER);
+   }
+
+/*
+* Decode PEM down to raw BER/DER
+*/
+secure_vector<uint8_t> decode_check_label(DataSource& source,
+                                          const std::string& label_want)
+   {
+   std::string label_got;
+   secure_vector<uint8_t> ber = decode(source, label_got);
+   if(label_got != label_want)
+      throw Decoding_Error("PEM: Label mismatch, wanted " + label_want +
+                           ", got " + label_got);
+   return ber;
+   }
+
+/*
+* Decode PEM down to raw BER/DER
+*/
+secure_vector<uint8_t> decode(DataSource& source, std::string& label)
+   {
+   const size_t RANDOM_CHAR_LIMIT = 8;
+
+   label.clear();
+
+   const std::string PEM_HEADER1 = "-----BEGIN ";
+   const std::string PEM_HEADER2 = "-----";
+   size_t position = 0;
+
+   while(position != PEM_HEADER1.length())
+      {
+      uint8_t b;
+      if(!source.read_byte(b))
+         throw Decoding_Error("PEM: No PEM header found");
+      if(b == PEM_HEADER1[position])
+         ++position;
+      else if(position >= RANDOM_CHAR_LIMIT)
+         throw Decoding_Error("PEM: Malformed PEM header");
+      else
+         position = 0;
+      }
+   position = 0;
+   while(position != PEM_HEADER2.length())
+      {
+      uint8_t b;
+      if(!source.read_byte(b))
+         throw Decoding_Error("PEM: No PEM header found");
+      if(b == PEM_HEADER2[position])
+         ++position;
+      else if(position)
+         throw Decoding_Error("PEM: Malformed PEM header");
+
+      if(position == 0)
+         label += static_cast<char>(b);
+      }
+
+   std::vector<char> b64;
+
+   const std::string PEM_TRAILER = "-----END " + label + "-----";
+   position = 0;
+   while(position != PEM_TRAILER.length())
+      {
+      uint8_t b;
+      if(!source.read_byte(b))
+         throw Decoding_Error("PEM: No PEM trailer found");
+      if(b == PEM_TRAILER[position])
+         ++position;
+      else if(position)
+         throw Decoding_Error("PEM: Malformed PEM trailer");
+
+      if(position == 0)
+         b64.push_back(b);
+      }
+
+   return base64_decode(b64.data(), b64.size());
+   }
+
+secure_vector<uint8_t> decode_check_label(const std::string& pem,
+                                          const std::string& label_want)
+   {
+   DataSource_Memory src(pem);
+   return decode_check_label(src, label_want);
+   }
+
+secure_vector<uint8_t> decode(const std::string& pem, std::string& label)
+   {
+   DataSource_Memory src(pem);
+   return decode(src, label);
+   }
+
+/*
+* Search for a PEM signature
+*/
+bool matches(DataSource& source, const std::string& extra,
+             size_t search_range)
+   {
+   const std::string PEM_HEADER = "-----BEGIN " + extra;
+
+   secure_vector<uint8_t> search_buf(search_range);
+   size_t got = source.peek(search_buf.data(), search_buf.size(), 0);
+
+   if(got < PEM_HEADER.length())
+      return false;
+
+   size_t index = 0;
+
+   for(size_t j = 0; j != got; ++j)
+      {
+      if(search_buf[j] == PEM_HEADER[index])
+         ++index;
+      else
+         index = 0;
+      if(index == PEM_HEADER.size())
+         return true;
+      }
+   return false;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pem/pem.h b/third_party/botan/src/lib/pubkey/pem/pem.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pem/pem.h
@@ -0,0 +1,91 @@
+/*
+* PEM Encoding/Decoding
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PEM_H_
+#define BOTAN_PEM_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+class DataSource;
+
+namespace PEM_Code {
+
+/**
+* Encode some binary data in PEM format
+* @param data binary data to encode
+* @param data_len length of binary data in bytes
+* @param label PEM label put after BEGIN and END
+* @param line_width after this many characters, a new line is inserted
+*/
+BOTAN_PUBLIC_API(2,0) std::string encode(const uint8_t data[],
+                                         size_t data_len,
+                                         const std::string& label,
+                                         size_t line_width = 64);
+
+/**
+* Encode some binary data in PEM format
+* @param data binary data to encode
+* @param label PEM label
+* @param line_width after this many characters, a new line is inserted
+*/
+template<typename Alloc>
+std::string encode(const std::vector<uint8_t, Alloc>& data,
+                   const std::string& label,
+                   size_t line_width = 64)
+   {
+   return encode(data.data(), data.size(), label, line_width);
+   }
+
+/**
+* Decode PEM data
+* @param pem a datasource containing PEM encoded data
+* @param label is set to the PEM label found for later inspection
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> decode(DataSource& pem,
+                                                    std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a string containing PEM encoded data
+* @param label is set to the PEM label found for later inspection
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> decode(const std::string& pem,
+                                                    std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a datasource containing PEM encoded data
+* @param label is what we expect the label to be
+*/
+BOTAN_PUBLIC_API(2,0)
+secure_vector<uint8_t> decode_check_label(DataSource& pem,
+                                          const std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a string containing PEM encoded data
+* @param label is what we expect the label to be
+*/
+BOTAN_PUBLIC_API(2,0)
+secure_vector<uint8_t> decode_check_label(const std::string& pem,
+                                          const std::string& label);
+
+/**
+* Heuristic test for PEM data.
+*/
+BOTAN_PUBLIC_API(2,0) bool matches(DataSource& source,
+                                   const std::string& extra = "",
+                                   size_t search_range = 4096);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pk_algs.cpp b/third_party/botan/src/lib/pubkey/pk_algs.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_algs.cpp
@@ -0,0 +1,426 @@
+/*
+* PK Key
+* (C) 1999-2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pk_algs.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_RSA)
+  #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+  #include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+  #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+  #include <botan/dh.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+  #include <botan/ecc_key.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+  #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+  #include <botan/ecgdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+  #include <botan/eckcdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+  #include <botan/ed25519.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+  #include <botan/gost_3410.h>
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+  #include <botan/elgamal.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+  #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+  #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+  #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+  #include <botan/xmss.h>
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+  #include <botan/sm2.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<Public_Key>
+load_public_key(const AlgorithmIdentifier& alg_id,
+                const std::vector<uint8_t>& key_bits)
+   {
+   const std::string oid_str = alg_id.get_oid().to_formatted_string();
+   const std::vector<std::string> alg_info = split_on(oid_str, '/');
+   const std::string alg_name = alg_info[0];
+
+#if defined(BOTAN_HAS_RSA)
+   if(alg_name == "RSA")
+      return std::unique_ptr<Public_Key>(new RSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+   if(alg_name == "Curve25519")
+      return std::unique_ptr<Public_Key>(new Curve25519_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+   if(alg_name == "McEliece")
+      return std::unique_ptr<Public_Key>(new McEliece_PublicKey(key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   if(alg_name == "ECDSA")
+      return std::unique_ptr<Public_Key>(new ECDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+   if(alg_name == "ECDH")
+      return std::unique_ptr<Public_Key>(new ECDH_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   if(alg_name == "DH")
+      return std::unique_ptr<Public_Key>(new DH_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+   if(alg_name == "DSA")
+      return std::unique_ptr<Public_Key>(new DSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+   if(alg_name == "ElGamal")
+      return std::unique_ptr<Public_Key>(new ElGamal_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+   if(alg_name == "ECGDSA")
+      return std::unique_ptr<Public_Key>(new ECGDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+   if(alg_name == "ECKCDSA")
+      return std::unique_ptr<Public_Key>(new ECKCDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+   if(alg_name == "Ed25519")
+      return std::unique_ptr<Public_Key>(new Ed25519_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+      return std::unique_ptr<Public_Key>(new GOST_3410_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+   if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+      return std::unique_ptr<Public_Key>(new SM2_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+   if(alg_name == "XMSS")
+      return std::unique_ptr<Public_Key>(new XMSS_PublicKey(key_bits));
+#endif
+
+   throw Decoding_Error("Unknown or unavailable public key algorithm " + alg_name);
+   }
+
+std::unique_ptr<Private_Key>
+load_private_key(const AlgorithmIdentifier& alg_id,
+                 const secure_vector<uint8_t>& key_bits)
+   {
+   const std::string alg_name = alg_id.get_oid().to_formatted_string();
+
+#if defined(BOTAN_HAS_RSA)
+   if(alg_name == "RSA")
+      return std::unique_ptr<Private_Key>(new RSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+   if(alg_name == "Curve25519")
+      return std::unique_ptr<Private_Key>(new Curve25519_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   if(alg_name == "ECDSA")
+      return std::unique_ptr<Private_Key>(new ECDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+   if(alg_name == "ECDH")
+      return std::unique_ptr<Private_Key>(new ECDH_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   if(alg_name == "DH")
+      return std::unique_ptr<Private_Key>(new DH_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+   if(alg_name == "DSA")
+      return std::unique_ptr<Private_Key>(new DSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+   if(alg_name == "McEliece")
+      return std::unique_ptr<Private_Key>(new McEliece_PrivateKey(key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+   if(alg_name == "ECGDSA")
+      return std::unique_ptr<Private_Key>(new ECGDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+   if(alg_name == "ECKCDSA")
+      return std::unique_ptr<Private_Key>(new ECKCDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+   if(alg_name == "Ed25519")
+      return std::unique_ptr<Private_Key>(new Ed25519_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+      return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+   if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+      return std::unique_ptr<Private_Key>(new SM2_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+   if(alg_name == "ElGamal")
+      return std::unique_ptr<Private_Key>(new ElGamal_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+   if(alg_name == "XMSS")
+      return std::unique_ptr<Private_Key>(new XMSS_PrivateKey(key_bits));
+#endif
+
+   throw Decoding_Error("Unknown or unavailable public key algorithm " + alg_name);
+   }
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+namespace {
+
+std::string default_ec_group_for(const std::string& alg_name)
+   {
+   if(alg_name == "SM2" || alg_name == "SM2_Enc" || alg_name == "SM2_Sig")
+      return "sm2p256v1";
+   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256")
+      return "gost_256A";
+   if(alg_name == "GOST-34.10-2012-512")
+      return "gost_512A";
+   if(alg_name == "ECGDSA")
+      return "brainpool256r1";
+   return "secp256r1";
+
+   }
+
+}
+
+#endif
+
+std::unique_ptr<Private_Key>
+create_private_key(const std::string& alg_name,
+                   RandomNumberGenerator& rng,
+                   const std::string& params,
+                   const std::string& provider)
+   {
+   /*
+   * Default paramaters are chosen for work factor > 2**128 where possible
+   */
+
+#if defined(BOTAN_HAS_CURVE_25519)
+   if(alg_name == "Curve25519")
+      return std::unique_ptr<Private_Key>(new Curve25519_PrivateKey(rng));
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+   if(alg_name == "RSA")
+      {
+      const size_t rsa_bits = (params.empty() ? 3072 : to_u32bit(params));
+#if defined(BOTAN_HAS_OPENSSL)
+      if(provider.empty() || provider == "openssl")
+         {
+         std::unique_ptr<Botan::Private_Key> pk;
+         if((pk = make_openssl_rsa_private_key(rng, rsa_bits)))
+            return pk;
+
+         if(!provider.empty())
+            return nullptr;
+         }
+#endif
+      return std::unique_ptr<Private_Key>(new RSA_PrivateKey(rng, rsa_bits));
+      }
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+   if(alg_name == "McEliece")
+      {
+      std::vector<std::string> mce_param =
+         Botan::split_on(params.empty() ? "2960,57" : params, ',');
+
+      if(mce_param.size() != 2)
+         throw Invalid_Argument("create_private_key bad McEliece parameters " + params);
+
+      size_t mce_n = Botan::to_u32bit(mce_param[0]);
+      size_t mce_t = Botan::to_u32bit(mce_param[1]);
+
+      return std::unique_ptr<Botan::Private_Key>(new Botan::McEliece_PrivateKey(rng, mce_n, mce_t));
+      }
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+   if(alg_name == "XMSS")
+      {
+      return std::unique_ptr<Private_Key>(
+         new XMSS_PrivateKey(XMSS_Parameters(params.empty() ? "XMSS-SHA2_10_512" : params).oid(), rng));
+      }
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+   if(alg_name == "Ed25519")
+      {
+      return std::unique_ptr<Private_Key>(new Ed25519_PrivateKey(rng));
+      }
+#endif
+
+   // ECC crypto
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+   if(alg_name == "ECDSA" ||
+      alg_name == "ECDH" ||
+      alg_name == "ECKCDSA" ||
+      alg_name == "ECGDSA" ||
+      alg_name == "SM2" ||
+      alg_name == "SM2_Sig" ||
+      alg_name == "SM2_Enc" ||
+      alg_name == "GOST-34.10" ||
+      alg_name == "GOST-34.10-2012-256" ||
+      alg_name == "GOST-34.10-2012-512")
+      {
+      const EC_Group ec_group(params.empty() ? default_ec_group_for(alg_name) : params);
+
+#if defined(BOTAN_HAS_ECDSA)
+      if(alg_name == "ECDSA")
+         return std::unique_ptr<Private_Key>(new ECDSA_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+      if(alg_name == "ECDH")
+         return std::unique_ptr<Private_Key>(new ECDH_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+      if(alg_name == "ECKCDSA")
+         return std::unique_ptr<Private_Key>(new ECKCDSA_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+      if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+         return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+      if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+         return std::unique_ptr<Private_Key>(new SM2_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+      if(alg_name == "ECGDSA")
+         return std::unique_ptr<Private_Key>(new ECGDSA_PrivateKey(rng, ec_group));
+#endif
+      }
+#endif
+
+   // DL crypto
+#if defined(BOTAN_HAS_DL_GROUP)
+   if(alg_name == "DH" || alg_name == "DSA" || alg_name == "ElGamal")
+      {
+      std::string default_group = (alg_name == "DSA") ? "dsa/botan/2048" : "modp/ietf/2048";
+      DL_Group modp_group(params.empty() ? default_group : params);
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+      if(alg_name == "DH")
+         return std::unique_ptr<Private_Key>(new DH_PrivateKey(rng, modp_group));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+      if(alg_name == "DSA")
+         return std::unique_ptr<Private_Key>(new DSA_PrivateKey(rng, modp_group));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+      if(alg_name == "ElGamal")
+         return std::unique_ptr<Private_Key>(new ElGamal_PrivateKey(rng, modp_group));
+#endif
+      }
+#endif
+
+   BOTAN_UNUSED(alg_name, rng, params, provider);
+
+   return std::unique_ptr<Private_Key>();
+   }
+
+std::vector<std::string>
+probe_provider_private_key(const std::string& alg_name,
+                           const std::vector<std::string> possible)
+   {
+   std::vector<std::string> providers;
+   for(auto&& prov : possible)
+      {
+      if(prov == "base" ||
+#if defined(BOTAN_HAS_OPENSSL)
+         (prov == "openssl" && alg_name == "RSA") ||
+#endif
+         0)
+         {
+         providers.push_back(prov); // available
+         }
+      }
+
+   BOTAN_UNUSED(alg_name);
+
+   return providers;
+   }
+}
diff --git a/third_party/botan/src/lib/pubkey/pk_algs.h b/third_party/botan/src/lib/pubkey/pk_algs.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_algs.h
@@ -0,0 +1,46 @@
+/*
+* PK Key Factory
+* (C) 1999-2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_KEY_FACTORY_H_
+#define BOTAN_PK_KEY_FACTORY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/alg_id.h>
+#include <memory>
+
+namespace Botan {
+
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Public_Key>
+load_public_key(const AlgorithmIdentifier& alg_id,
+                const std::vector<uint8_t>& key_bits);
+
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Private_Key>
+load_private_key(const AlgorithmIdentifier& alg_id,
+                 const secure_vector<uint8_t>& key_bits);
+
+/**
+* Create a new key
+* For ECC keys, algo_params specifies EC group (eg, "secp256r1")
+* For DH/DSA/ElGamal keys, algo_params is DL group (eg, "modp/ietf/2048")
+* For RSA, algo_params is integer keylength
+* For McEliece, algo_params is n,t
+* If algo_params is left empty, suitable default parameters are chosen.
+*/
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Private_Key>
+create_private_key(const std::string& algo_name,
+                   RandomNumberGenerator& rng,
+                   const std::string& algo_params = "",
+                   const std::string& provider = "");
+
+BOTAN_PUBLIC_API(2,2)
+std::vector<std::string>
+probe_provider_private_key(const std::string& algo_name,
+                           const std::vector<std::string> possible);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pk_keys.cpp b/third_party/botan/src/lib/pubkey/pk_keys.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_keys.cpp
@@ -0,0 +1,145 @@
+/*
+* PK Key Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pk_keys.h>
+#include <botan/pk_ops.h>
+#include <botan/der_enc.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+std::string create_hex_fingerprint(const uint8_t bits[],
+                                   size_t bits_len,
+                                   const std::string& hash_name)
+   {
+   std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_name));
+   const std::string hex_hash = hex_encode(hash_fn->process(bits, bits_len));
+
+   std::string fprint;
+
+   for(size_t i = 0; i != hex_hash.size(); i += 2)
+      {
+      if(i != 0)
+         fprint.push_back(':');
+
+      fprint.push_back(hex_hash[i]);
+      fprint.push_back(hex_hash[i+1]);
+      }
+
+   return fprint;
+   }
+
+std::vector<uint8_t> Public_Key::subject_public_key() const
+   {
+   std::vector<uint8_t> output;
+
+   DER_Encoder(output).start_cons(SEQUENCE)
+         .encode(algorithm_identifier())
+         .encode(public_key_bits(), BIT_STRING)
+      .end_cons();
+
+   return output;
+   }
+
+/*
+* Default OID access
+*/
+OID Public_Key::get_oid() const
+   {
+   const OID o = OIDS::str2oid_or_empty(algo_name());
+   if(o.empty())
+      throw Lookup_Error("PK algo " + algo_name() + " has no defined OIDs");
+   return o;
+   }
+
+secure_vector<uint8_t> Private_Key::private_key_info() const
+   {
+   const size_t PKCS8_VERSION = 0;
+
+   return DER_Encoder()
+         .start_cons(SEQUENCE)
+            .encode(PKCS8_VERSION)
+            .encode(pkcs8_algorithm_identifier())
+            .encode(private_key_bits(), OCTET_STRING)
+         .end_cons()
+      .get_contents();
+   }
+
+/*
+* Hash of the X.509 subjectPublicKey encoding
+*/
+std::string Public_Key::fingerprint_public(const std::string& hash_algo) const
+   {
+   return create_hex_fingerprint(subject_public_key(), hash_algo);
+   }
+
+/*
+* Hash of the PKCS #8 encoding for this key object
+*/
+std::string Private_Key::fingerprint_private(const std::string& hash_algo) const
+   {
+   return create_hex_fingerprint(private_key_bits(), hash_algo);
+   }
+
+std::unique_ptr<PK_Ops::Encryption>
+Public_Key::create_encryption_op(RandomNumberGenerator& /*rng*/,
+                                 const std::string& /*params*/,
+                                 const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support encryption");
+   }
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+Public_Key::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+                                     const std::string& /*params*/,
+                                     const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support KEM encryption");
+   }
+
+std::unique_ptr<PK_Ops::Verification>
+Public_Key::create_verification_op(const std::string& /*params*/,
+                                   const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support verification");
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+Private_Key::create_decryption_op(RandomNumberGenerator& /*rng*/,
+                                  const std::string& /*params*/,
+                                  const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support decryption");
+   }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+Private_Key::create_kem_decryption_op(RandomNumberGenerator& /*rng*/,
+                                      const std::string& /*params*/,
+                                      const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support KEM decryption");
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+Private_Key::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                 const std::string& /*params*/,
+                                 const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support signatures");
+   }
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+Private_Key::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
+                                     const std::string& /*params*/,
+                                     const std::string& /*provider*/) const
+   {
+   throw Lookup_Error(algo_name() + " does not support key agreement");
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pk_keys.h b/third_party/botan/src/lib/pubkey/pk_keys.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_keys.h
@@ -0,0 +1,330 @@
+/*
+* PK Key Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_KEYS_H_
+#define BOTAN_PK_KEYS_H_
+
+#include <botan/secmem.h>
+#include <botan/asn1_oid.h>
+#include <botan/alg_id.h>
+#include <botan/pk_ops_fwd.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* The two types of signature format supported by Botan.
+*/
+enum Signature_Format { IEEE_1363, DER_SEQUENCE };
+
+/**
+* Public Key Base Class.
+*/
+class BOTAN_PUBLIC_API(2,0) Public_Key
+   {
+   public:
+      Public_Key() =default;
+      Public_Key(const Public_Key& other) = default;
+      Public_Key& operator=(const Public_Key& other) = default;
+      virtual ~Public_Key() = default;
+
+      /**
+      * Get the name of the underlying public key scheme.
+      * @return name of the public key scheme
+      */
+      virtual std::string algo_name() const = 0;
+
+      /**
+      * Return the estimated strength of the underlying key against
+      * the best currently known attack. Note that this ignores anything
+      * but pure attacks against the key itself and do not take into
+      * account padding schemes, usage mistakes, etc which might reduce
+      * the strength. However it does suffice to provide an upper bound.
+      *
+      * @return estimated strength in bits
+      */
+      virtual size_t estimated_strength() const = 0;
+
+      /**
+      * Return an integer value best approximating the length of the
+      * primary security parameter. For example for RSA this will be
+      * the size of the modulus, for ECDSA the size of the ECC group,
+      * and for McEliece the size of the code will be returned.
+      */
+      virtual size_t key_length() const = 0;
+
+      /**
+      * Get the OID of the underlying public key scheme.
+      * @return OID of the public key scheme
+      */
+      virtual OID get_oid() const;
+
+      /**
+      * Test the key values for consistency.
+      * @param rng rng to use
+      * @param strong whether to perform strong and lengthy version
+      * of the test
+      * @return true if the test is passed
+      */
+      virtual bool check_key(RandomNumberGenerator& rng,
+                             bool strong) const = 0;
+
+
+      /**
+      * @return X.509 AlgorithmIdentifier for this key
+      */
+      virtual AlgorithmIdentifier algorithm_identifier() const = 0;
+
+      /**
+      * @return BER encoded public key bits
+      */
+      virtual std::vector<uint8_t> public_key_bits() const = 0;
+
+      /**
+      * @return X.509 subject key encoding for this key object
+      */
+      std::vector<uint8_t> subject_public_key() const;
+
+      /**
+       * @return Hash of the subject public key
+       */
+      std::string fingerprint_public(const std::string& alg = "SHA-256") const;
+
+      // Internal or non-public declarations follow
+
+      /**
+      * Returns more than 1 if the output of this algorithm
+      * (ciphertext, signature) should be treated as more than one
+      * value. This is used for algorithms like DSA and ECDSA, where
+      * the (r,s) output pair can be encoded as either a plain binary
+      * list or a TLV tagged DER encoding depending on the protocol.
+      *
+      * This function is public but applications should have few
+      * reasons to ever call this.
+      *
+      * @return number of message parts
+      */
+      virtual size_t message_parts() const { return 1; }
+
+      /**
+      * Returns how large each of the message parts refered to
+      * by message_parts() is
+      *
+      * This function is public but applications should have few
+      * reasons to ever call this.
+      *
+      * @return size of the message parts in bits
+      */
+      virtual size_t message_part_size() const { return 0; }
+
+      virtual Signature_Format default_x509_signature_format() const
+         {
+         return (this->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+         }
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return an encryption operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::Encryption>
+         create_encryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const;
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return a KEM encryption operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::KEM_Encryption>
+         create_kem_encryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const;
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return a verification operation for this key/params or throw
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const;
+   };
+
+/**
+* Private Key Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) Private_Key : public virtual Public_Key
+   {
+   public:
+      Private_Key() = default;
+      Private_Key(const Private_Key& other) = default;
+      Private_Key& operator=(const Private_Key& other) = default;
+      virtual ~Private_Key() = default;
+
+      virtual bool stateful_operation() const { return false; }
+
+      /**
+      * @return BER encoded private key bits
+      */
+      virtual secure_vector<uint8_t> private_key_bits() const = 0;
+
+      /**
+      * @return PKCS #8 private key encoding for this key object
+      */
+      secure_vector<uint8_t> private_key_info() const;
+
+      /**
+      * @return PKCS #8 AlgorithmIdentifier for this key
+      * Might be different from the X.509 identifier, but normally is not
+      */
+      virtual AlgorithmIdentifier pkcs8_algorithm_identifier() const
+         { return algorithm_identifier(); }
+
+      // Internal or non-public declarations follow
+
+      /**
+       * @return Hash of the PKCS #8 encoding for this key object
+       */
+      std::string fingerprint_private(const std::string& alg) const;
+
+      BOTAN_DEPRECATED("Use fingerprint_private or fingerprint_public")
+         inline std::string fingerprint(const std::string& alg) const
+         {
+         return fingerprint_private(alg); // match behavior in previous versions
+         }
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return an decryption operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      *
+      */
+      virtual std::unique_ptr<PK_Ops::Decryption>
+         create_decryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const;
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return a KEM decryption operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::KEM_Decryption>
+         create_kem_decryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const;
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return a signature operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const;
+
+      /**
+      * This is an internal library function exposed on key types.
+      * In almost all cases applications should use wrappers in pubkey.h
+      *
+      * Return a key agreement operation for this key/params or throw
+      *
+      * @param rng a random number generator. The PK_Op may maintain a
+      * reference to the RNG and use it many times. The rng must outlive
+      * any operations which reference it.
+      * @param params additional parameters
+      * @param provider the provider to use
+      */
+      virtual std::unique_ptr<PK_Ops::Key_Agreement>
+         create_key_agreement_op(RandomNumberGenerator& rng,
+                                 const std::string& params,
+                                 const std::string& provider) const;
+   };
+
+/**
+* PK Secret Value Derivation Key
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement_Key : public virtual Private_Key
+   {
+   public:
+      /*
+      * @return public component of this key
+      */
+      virtual std::vector<uint8_t> public_value() const = 0;
+
+      PK_Key_Agreement_Key() = default;
+      PK_Key_Agreement_Key(const PK_Key_Agreement_Key&) = default;
+      PK_Key_Agreement_Key& operator=(const PK_Key_Agreement_Key&) = default;
+      virtual ~PK_Key_Agreement_Key() = default;
+   };
+
+/*
+* Old compat typedefs
+* TODO: remove these?
+*/
+typedef PK_Key_Agreement_Key PK_KA_Key;
+typedef Public_Key X509_PublicKey;
+typedef Private_Key PKCS8_PrivateKey;
+
+std::string BOTAN_PUBLIC_API(2,4)
+   create_hex_fingerprint(const uint8_t bits[], size_t len,
+                          const std::string& hash_name);
+
+template<typename Alloc>
+std::string create_hex_fingerprint(const std::vector<uint8_t, Alloc>& vec,
+                                   const std::string& hash_name)
+   {
+   return create_hex_fingerprint(vec.data(), vec.size(), hash_name);
+   }
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pk_ops.cpp b/third_party/botan/src/lib/pubkey/pk_ops.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_ops.cpp
@@ -0,0 +1,173 @@
+/*
+* PK Operation Types
+* (C) 2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+PK_Ops::Encryption_with_EME::Encryption_with_EME(const std::string& eme)
+   {
+   m_eme.reset(get_eme(eme));
+   if(!m_eme.get())
+      throw Algorithm_Not_Found(eme);
+   }
+
+size_t PK_Ops::Encryption_with_EME::max_input_bits() const
+   {
+   return 8 * m_eme->maximum_input_size(max_raw_input_bits());
+   }
+
+secure_vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(const uint8_t msg[], size_t msg_len,
+                                                         RandomNumberGenerator& rng)
+   {
+   const size_t max_raw = max_raw_input_bits();
+   const std::vector<uint8_t> encoded = unlock(m_eme->encode(msg, msg_len, max_raw, rng));
+   return raw_encrypt(encoded.data(), encoded.size(), rng);
+   }
+
+PK_Ops::Decryption_with_EME::Decryption_with_EME(const std::string& eme)
+   {
+   m_eme.reset(get_eme(eme));
+   if(!m_eme.get())
+      throw Algorithm_Not_Found(eme);
+   }
+
+secure_vector<uint8_t>
+PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask,
+                                     const uint8_t ciphertext[],
+                                     size_t ciphertext_len)
+   {
+   const secure_vector<uint8_t> raw = raw_decrypt(ciphertext, ciphertext_len);
+   return m_eme->unpad(valid_mask, raw.data(), raw.size());
+   }
+
+PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(const std::string& kdf)
+   {
+   if(kdf != "Raw")
+      m_kdf.reset(get_kdf(kdf));
+   }
+
+secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
+                                                          const uint8_t w[], size_t w_len,
+                                                          const uint8_t salt[], size_t salt_len)
+   {
+   secure_vector<uint8_t> z = raw_agree(w, w_len);
+   if(m_kdf)
+      return m_kdf->derive_key(key_len, z, salt, salt_len);
+   return z;
+  }
+
+PK_Ops::Signature_with_EMSA::Signature_with_EMSA(const std::string& emsa) :
+   Signature(),
+   m_emsa(get_emsa(emsa)),
+   m_hash(hash_for_emsa(emsa)),
+   m_prefix_used(false)
+   {
+   if(!m_emsa)
+      throw Algorithm_Not_Found(emsa);
+   }
+
+void PK_Ops::Signature_with_EMSA::update(const uint8_t msg[], size_t msg_len)
+   {
+   if(has_prefix() && !m_prefix_used)
+      {
+      m_prefix_used = true;
+      secure_vector<uint8_t> prefix = message_prefix();
+      m_emsa->update(prefix.data(), prefix.size());
+      }
+   m_emsa->update(msg, msg_len);
+   }
+
+secure_vector<uint8_t> PK_Ops::Signature_with_EMSA::sign(RandomNumberGenerator& rng)
+   {
+   m_prefix_used = false;
+   const secure_vector<uint8_t> msg = m_emsa->raw_data();
+   const auto padded = m_emsa->encoding_of(msg, this->max_input_bits(), rng);
+   return raw_sign(padded.data(), padded.size(), rng);
+   }
+
+PK_Ops::Verification_with_EMSA::Verification_with_EMSA(const std::string& emsa) :
+   Verification(),
+   m_emsa(get_emsa(emsa)),
+   m_hash(hash_for_emsa(emsa)),
+   m_prefix_used(false)
+   {
+   if(!m_emsa)
+      throw Algorithm_Not_Found(emsa);
+   }
+
+void PK_Ops::Verification_with_EMSA::update(const uint8_t msg[], size_t msg_len)
+   {
+   if(has_prefix() && !m_prefix_used)
+      {
+      m_prefix_used = true;
+      secure_vector<uint8_t> prefix = message_prefix();
+      m_emsa->update(prefix.data(), prefix.size());
+      }
+   m_emsa->update(msg, msg_len);
+   }
+
+bool PK_Ops::Verification_with_EMSA::is_valid_signature(const uint8_t sig[], size_t sig_len)
+   {
+   m_prefix_used = false;
+   const secure_vector<uint8_t> msg = m_emsa->raw_data();
+
+   if(with_recovery())
+      {
+      secure_vector<uint8_t> output_of_key = verify_mr(sig, sig_len);
+      return m_emsa->verify(output_of_key, msg, max_input_bits());
+      }
+   else
+      {
+      Null_RNG rng;
+      secure_vector<uint8_t> encoded = m_emsa->encoding_of(msg, max_input_bits(), rng);
+      return verify(encoded.data(), encoded.size(), sig, sig_len);
+      }
+   }
+
+void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                                                  secure_vector<uint8_t>& out_shared_key,
+                                                  size_t desired_shared_key_len,
+                                                  Botan::RandomNumberGenerator& rng,
+                                                  const uint8_t salt[],
+                                                  size_t salt_len)
+   {
+   secure_vector<uint8_t> raw_shared;
+   this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
+
+   out_shared_key = m_kdf->derive_key(desired_shared_key_len,
+                                      raw_shared.data(), raw_shared.size(),
+                                      salt, salt_len);
+   }
+
+PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(const std::string& kdf)
+   {
+   m_kdf.reset(get_kdf(kdf));
+   }
+
+secure_vector<uint8_t>
+PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(const uint8_t encap_key[],
+                                             size_t len,
+                                             size_t desired_shared_key_len,
+                                             const uint8_t salt[],
+                                             size_t salt_len)
+   {
+   secure_vector<uint8_t> raw_shared = this->raw_kem_decrypt(encap_key, len);
+
+   return m_kdf->derive_key(desired_shared_key_len,
+                            raw_shared.data(), raw_shared.size(),
+                            salt, salt_len);
+   }
+
+PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(const std::string& kdf)
+   {
+   m_kdf.reset(get_kdf(kdf));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pk_ops.h b/third_party/botan/src/lib/pubkey/pk_ops.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_ops.h
@@ -0,0 +1,161 @@
+/*
+* (C) 2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATIONS_H_
+#define BOTAN_PK_OPERATIONS_H_
+
+/**
+* Ordinary applications should never need to include or use this
+* header. It is exposed only for specialized applications which want
+* to implement new versions of public key crypto without merging them
+* as changes to the library. One actual example of such usage is an
+* application which creates RSA signatures using a custom TPM library.
+* Unless you're doing something like that, you don't need anything
+* here. Instead use pubkey.h which wraps these types safely and
+* provides a stable application-oriented API.
+*/
+
+#include <botan/pk_keys.h>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class EME;
+class KDF;
+class EMSA;
+
+namespace PK_Ops {
+
+/**
+* Public key encryption interface
+*/
+class BOTAN_PUBLIC_API(2,0) Encryption
+   {
+   public:
+      virtual secure_vector<uint8_t> encrypt(const uint8_t msg[],
+                                          size_t msg_len,
+                                          RandomNumberGenerator& rng) = 0;
+
+      virtual size_t max_input_bits() const = 0;
+
+      virtual size_t ciphertext_length(size_t ptext_len) const = 0;
+
+      virtual ~Encryption() = default;
+   };
+
+/**
+* Public key decryption interface
+*/
+class BOTAN_PUBLIC_API(2,0) Decryption
+   {
+   public:
+      virtual secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+                                          const uint8_t ciphertext[],
+                                          size_t ciphertext_len) = 0;
+
+      virtual size_t plaintext_length(size_t ctext_len) const = 0;
+
+      virtual ~Decryption() = default;
+   };
+
+/**
+* Public key signature verification interface
+*/
+class BOTAN_PUBLIC_API(2,0) Verification
+   {
+   public:
+      /*
+      * Add more data to the message currently being signed
+      * @param msg the message
+      * @param msg_len the length of msg in bytes
+      */
+      virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+
+      /*
+      * Perform a verification operation
+      * @param rng a random number generator
+      */
+      virtual bool is_valid_signature(const uint8_t sig[], size_t sig_len) = 0;
+
+      virtual ~Verification() = default;
+   };
+
+/**
+* Public key signature creation interface
+*/
+class BOTAN_PUBLIC_API(2,0) Signature
+   {
+   public:
+      /*
+      * Add more data to the message currently being signed
+      * @param msg the message
+      * @param msg_len the length of msg in bytes
+      */
+      virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+
+      /*
+      * Perform a signature operation
+      * @param rng a random number generator
+      */
+      virtual secure_vector<uint8_t> sign(RandomNumberGenerator& rng) = 0;
+
+      /*
+      * Return an upper bound on the length of the output signature
+      */
+      virtual size_t signature_length() const = 0;
+
+      virtual ~Signature() = default;
+   };
+
+/**
+* A generic key agreement operation (eg DH or ECDH)
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Agreement
+   {
+   public:
+      virtual secure_vector<uint8_t> agree(size_t key_len,
+                                           const uint8_t other_key[], size_t other_key_len,
+                                           const uint8_t salt[], size_t salt_len) = 0;
+
+      virtual size_t agreed_value_size() const = 0;
+
+      virtual ~Key_Agreement() = default;
+   };
+
+/**
+* KEM (key encapsulation)
+*/
+class BOTAN_PUBLIC_API(2,0) KEM_Encryption
+   {
+   public:
+      virtual void kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                               secure_vector<uint8_t>& out_shared_key,
+                               size_t desired_shared_key_len,
+                               Botan::RandomNumberGenerator& rng,
+                               const uint8_t salt[],
+                               size_t salt_len) = 0;
+
+      virtual ~KEM_Encryption() = default;
+   };
+
+class BOTAN_PUBLIC_API(2,0) KEM_Decryption
+   {
+   public:
+      virtual secure_vector<uint8_t> kem_decrypt(const uint8_t encap_key[],
+                                              size_t len,
+                                              size_t desired_shared_key_len,
+                                              const uint8_t salt[],
+                                              size_t salt_len) = 0;
+
+      virtual ~KEM_Decryption() = default;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pk_ops_fwd.h b/third_party/botan/src/lib/pubkey/pk_ops_fwd.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_ops_fwd.h
@@ -0,0 +1,27 @@
+/*
+* PK Operation Types Forward Decls
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATIONS_FWD_H_
+#define BOTAN_PK_OPERATIONS_FWD_H_
+
+namespace Botan {
+
+namespace PK_Ops {
+
+class Encryption;
+class Decryption;
+class Verification;
+class Signature;
+class Key_Agreement;
+class KEM_Encryption;
+class KEM_Decryption;
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pk_ops_impl.h b/third_party/botan/src/lib/pubkey/pk_ops_impl.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pk_ops_impl.h
@@ -0,0 +1,231 @@
+
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATION_IMPL_H_
+#define BOTAN_PK_OPERATION_IMPL_H_
+
+#include <botan/pk_ops.h>
+#include <botan/eme.h>
+#include <botan/kdf.h>
+#include <botan/emsa.h>
+
+namespace Botan {
+
+namespace PK_Ops {
+
+class Encryption_with_EME : public Encryption
+   {
+   public:
+      size_t max_input_bits() const override;
+
+      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len,
+                                  RandomNumberGenerator& rng) override;
+
+      ~Encryption_with_EME() = default;
+   protected:
+      explicit Encryption_with_EME(const std::string& eme);
+   private:
+      virtual size_t max_raw_input_bits() const = 0;
+
+      virtual secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t len,
+                                              RandomNumberGenerator& rng) = 0;
+      std::unique_ptr<EME> m_eme;
+   };
+
+class Decryption_with_EME : public Decryption
+   {
+   public:
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+                                  const uint8_t msg[], size_t msg_len) override;
+
+      ~Decryption_with_EME() = default;
+   protected:
+      explicit Decryption_with_EME(const std::string& eme);
+   private:
+      virtual secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t len) = 0;
+      std::unique_ptr<EME> m_eme;
+   };
+
+class Verification_with_EMSA : public Verification
+   {
+   public:
+      ~Verification_with_EMSA() = default;
+
+      void update(const uint8_t msg[], size_t msg_len) override;
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+
+      bool do_check(const secure_vector<uint8_t>& msg,
+                    const uint8_t sig[], size_t sig_len);
+
+      std::string hash_for_signature() { return m_hash; }
+
+   protected:
+      explicit Verification_with_EMSA(const std::string& emsa);
+
+      /**
+      * Get the maximum message size in bits supported by this public key.
+      * @return maximum message in bits
+      */
+      virtual size_t max_input_bits() const = 0;
+
+      /**
+      * @return boolean specifying if this signature scheme uses
+      * a message prefix returned by message_prefix()
+      */
+      virtual bool has_prefix() { return false; }
+
+      /**
+      * @return the message prefix if this signature scheme uses
+      * a message prefix, signaled via has_prefix()
+      */
+      virtual secure_vector<uint8_t> message_prefix() const { throw Invalid_State("No prefix"); }
+
+      /**
+      * @return boolean specifying if this key type supports message
+      * recovery and thus if you need to call verify() or verify_mr()
+      */
+      virtual bool with_recovery() const = 0;
+
+      /*
+      * Perform a signature check operation
+      * @param msg the message
+      * @param msg_len the length of msg in bytes
+      * @param sig the signature
+      * @param sig_len the length of sig in bytes
+      * @returns if signature is a valid one for message
+      */
+      virtual bool verify(const uint8_t[], size_t,
+                          const uint8_t[], size_t)
+         {
+         throw Invalid_State("Message recovery required");
+         }
+
+      /*
+      * Perform a signature operation (with message recovery)
+      * Only call this if with_recovery() returns true
+      * @param msg the message
+      * @param msg_len the length of msg in bytes
+      * @returns recovered message
+      */
+      virtual secure_vector<uint8_t> verify_mr(const uint8_t[], size_t)
+         {
+         throw Invalid_State("Message recovery not supported");
+         }
+
+      std::unique_ptr<EMSA> clone_emsa() const { return std::unique_ptr<EMSA>(m_emsa->clone()); }
+
+   private:
+      std::unique_ptr<EMSA> m_emsa;
+      const std::string m_hash;
+      bool m_prefix_used;
+   };
+
+class Signature_with_EMSA : public Signature
+   {
+   public:
+      void update(const uint8_t msg[], size_t msg_len) override;
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+   protected:
+      explicit Signature_with_EMSA(const std::string& emsa);
+      ~Signature_with_EMSA() = default;
+
+      std::string hash_for_signature() { return m_hash; }
+
+      /**
+      * @return boolean specifying if this signature scheme uses
+      * a message prefix returned by message_prefix()
+      */
+      virtual bool has_prefix() { return false; }
+
+      /**
+      * @return the message prefix if this signature scheme uses
+      * a message prefix, signaled via has_prefix()
+      */
+      virtual secure_vector<uint8_t> message_prefix() const { throw Invalid_State("No prefix"); }
+
+      std::unique_ptr<EMSA> clone_emsa() const { return std::unique_ptr<EMSA>(m_emsa->clone()); }
+
+   private:
+
+      /**
+      * Get the maximum message size in bits supported by this public key.
+      * @return maximum message in bits
+      */
+      virtual size_t max_input_bits() const = 0;
+
+      bool self_test_signature(const std::vector<uint8_t>& msg,
+                               const std::vector<uint8_t>& sig) const;
+
+      virtual secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+                                           RandomNumberGenerator& rng) = 0;
+
+      std::unique_ptr<EMSA> m_emsa;
+      const std::string m_hash;
+      bool m_prefix_used;
+   };
+
+class Key_Agreement_with_KDF : public Key_Agreement
+   {
+   public:
+      secure_vector<uint8_t> agree(size_t key_len,
+                                const uint8_t other_key[], size_t other_key_len,
+                                const uint8_t salt[], size_t salt_len) override;
+
+   protected:
+      explicit Key_Agreement_with_KDF(const std::string& kdf);
+      ~Key_Agreement_with_KDF() = default;
+   private:
+      virtual secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) = 0;
+      std::unique_ptr<KDF> m_kdf;
+   };
+
+class KEM_Encryption_with_KDF : public KEM_Encryption
+   {
+   public:
+      void kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                       secure_vector<uint8_t>& out_shared_key,
+                       size_t desired_shared_key_len,
+                       Botan::RandomNumberGenerator& rng,
+                       const uint8_t salt[],
+                       size_t salt_len) override;
+
+   protected:
+      virtual void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                                   secure_vector<uint8_t>& raw_shared_key,
+                                   Botan::RandomNumberGenerator& rng) = 0;
+
+      explicit KEM_Encryption_with_KDF(const std::string& kdf);
+      ~KEM_Encryption_with_KDF() = default;
+   private:
+      std::unique_ptr<KDF> m_kdf;
+   };
+
+class KEM_Decryption_with_KDF : public KEM_Decryption
+   {
+   public:
+      secure_vector<uint8_t> kem_decrypt(const uint8_t encap_key[],
+                                      size_t len,
+                                      size_t desired_shared_key_len,
+                                      const uint8_t salt[],
+                                      size_t salt_len) override;
+
+   protected:
+      virtual secure_vector<uint8_t>
+      raw_kem_decrypt(const uint8_t encap_key[], size_t len) = 0;
+
+      explicit KEM_Decryption_with_KDF(const std::string& kdf);
+      ~KEM_Decryption_with_KDF() = default;
+   private:
+      std::unique_ptr<KDF> m_kdf;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pkcs8.cpp b/third_party/botan/src/lib/pubkey/pkcs8.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pkcs8.cpp
@@ -0,0 +1,490 @@
+/*
+* PKCS #8
+* (C) 1999-2010,2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs8.h>
+#include <botan/rng.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/alg_id.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <botan/scan_name.h>
+#include <botan/pk_algs.h>
+
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+   #include <botan/pbes2.h>
+#endif
+
+namespace Botan {
+
+namespace PKCS8 {
+
+namespace {
+
+/*
+* Get info from an EncryptedPrivateKeyInfo
+*/
+secure_vector<uint8_t> PKCS8_extract(DataSource& source,
+                                  AlgorithmIdentifier& pbe_alg_id)
+   {
+   secure_vector<uint8_t> key_data;
+
+   BER_Decoder(source)
+      .start_cons(SEQUENCE)
+         .decode(pbe_alg_id)
+         .decode(key_data, OCTET_STRING)
+      .verify_end();
+
+   return key_data;
+   }
+
+/*
+* PEM decode and/or decrypt a private key
+*/
+secure_vector<uint8_t> PKCS8_decode(
+   DataSource& source,
+   std::function<std::string ()> get_passphrase,
+   AlgorithmIdentifier& pk_alg_id,
+   bool is_encrypted)
+   {
+   AlgorithmIdentifier pbe_alg_id;
+   secure_vector<uint8_t> key_data, key;
+
+   try {
+      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
+         {
+         if(is_encrypted)
+            {
+            key_data = PKCS8_extract(source, pbe_alg_id);
+            }
+         else
+            {
+            // todo read more efficiently
+            while(!source.end_of_data())
+               {
+               uint8_t b;
+               size_t read = source.read_byte(b);
+               if(read)
+                  {
+                  key_data.push_back(b);
+                  }
+               }
+            }
+         }
+      else
+         {
+         std::string label;
+         key_data = PEM_Code::decode(source, label);
+
+         // todo remove autodetect for pem as well?
+         if(label == "PRIVATE KEY")
+            is_encrypted = false;
+         else if(label == "ENCRYPTED PRIVATE KEY")
+            {
+            DataSource_Memory key_source(key_data);
+            key_data = PKCS8_extract(key_source, pbe_alg_id);
+            }
+         else
+            throw PKCS8_Exception("Unknown PEM label " + label);
+         }
+
+      if(key_data.empty())
+         throw PKCS8_Exception("No key data found");
+      }
+   catch(Decoding_Error& e)
+      {
+      throw Decoding_Error("PKCS #8 private key decoding", e);
+      }
+
+   try
+      {
+      if(is_encrypted)
+         {
+         if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
+            throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
+#else
+         BOTAN_UNUSED(get_passphrase);
+         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
+#endif
+         }
+      else
+         key = key_data;
+
+      BER_Decoder(key)
+         .start_cons(SEQUENCE)
+            .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
+            .decode(pk_alg_id)
+            .decode(key, OCTET_STRING)
+            .discard_remaining()
+         .end_cons();
+      }
+   catch(std::exception& e)
+      {
+      throw Decoding_Error("PKCS #8 private key decoding", e);
+      }
+   return key;
+   }
+
+}
+
+/*
+* BER encode a PKCS #8 private key, unencrypted
+*/
+secure_vector<uint8_t> BER_encode(const Private_Key& key)
+   {
+   // keeping around for compat
+   return key.private_key_info();
+   }
+
+/*
+* PEM encode a PKCS #8 private key, unencrypted
+*/
+std::string PEM_encode(const Private_Key& key)
+   {
+   return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
+   }
+
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+
+namespace {
+
+std::pair<std::string, std::string>
+choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
+   {
+   if(pbe_algo.empty())
+      {
+      /*
+      * For algorithms where we are using a non-RFC format anyway, default to
+      * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
+      * compatible.
+      */
+      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
+
+      if(nonstandard_pk)
+         {
+#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
+         return std::make_pair("AES-256/SIV", "SHA-512");
+#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
+         return std::make_pair("AES-256/GCM", "SHA-512");
+#endif
+         }
+
+      // Default is something compatible with everyone else
+      return std::make_pair("AES-256/CBC", "SHA-256");
+      }
+
+   SCAN_Name request(pbe_algo);
+
+   if(request.arg_count() != 2 ||
+      (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
+      {
+      throw Invalid_Argument("Unsupported PBE " + pbe_algo);
+      }
+
+   return std::make_pair(request.arg(0), request.arg(1));
+   }
+
+}
+
+#endif
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode(const Private_Key& key,
+                             RandomNumberGenerator& rng,
+                             const std::string& pass,
+                             std::chrono::milliseconds msec,
+                             const std::string& pbe_algo)
+   {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
+
+   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
+                         pbe_params.first, pbe_params.second, rng);
+
+   std::vector<uint8_t> output;
+   DER_Encoder der(output);
+   der.start_cons(SEQUENCE)
+         .encode(pbe_info.first)
+         .encode(pbe_info.second, OCTET_STRING)
+      .end_cons();
+
+   return output;
+#else
+   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
+   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
+#endif
+   }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode(const Private_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& pass,
+                       std::chrono::milliseconds msec,
+                       const std::string& pbe_algo)
+   {
+   if(pass.empty())
+      return PEM_encode(key);
+
+   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
+                           "ENCRYPTED PRIVATE KEY");
+   }
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+                                                     RandomNumberGenerator& rng,
+                                                     const std::string& pass,
+                                                     size_t pbkdf_iterations,
+                                                     const std::string& cipher,
+                                                     const std::string& pbkdf_hash)
+   {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+      pbes2_encrypt_iter(key.private_key_info(),
+                         pass, pbkdf_iterations,
+                         cipher.empty() ? "AES-256/CBC" : cipher,
+                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+                         rng);
+
+   std::vector<uint8_t> output;
+   DER_Encoder der(output);
+   der.start_cons(SEQUENCE)
+         .encode(pbe_info.first)
+         .encode(pbe_info.second, OCTET_STRING)
+      .end_cons();
+
+   return output;
+
+#else
+   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
+   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
+#endif
+   }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+                                            RandomNumberGenerator& rng,
+                                            const std::string& pass,
+                                            size_t pbkdf_iterations,
+                                            const std::string& cipher,
+                                            const std::string& pbkdf_hash)
+   {
+   return PEM_Code::encode(
+      PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
+      "ENCRYPTED PRIVATE KEY");
+   }
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+                                                     RandomNumberGenerator& rng,
+                                                     const std::string& pass,
+                                                     std::chrono::milliseconds pbkdf_msec,
+                                                     size_t* pbkdf_iterations,
+                                                     const std::string& cipher,
+                                                     const std::string& pbkdf_hash)
+   {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+      pbes2_encrypt_msec(key.private_key_info(), pass,
+                         pbkdf_msec, pbkdf_iterations,
+                         cipher.empty() ? "AES-256/CBC" : cipher,
+                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+                         rng);
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .encode(pbe_info.first)
+         .encode(pbe_info.second, OCTET_STRING)
+      .end_cons();
+
+   return output;
+#else
+   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
+   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
+#endif
+   }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+                                            RandomNumberGenerator& rng,
+                                            const std::string& pass,
+                                            std::chrono::milliseconds pbkdf_msec,
+                                            size_t* pbkdf_iterations,
+                                            const std::string& cipher,
+                                            const std::string& pbkdf_hash)
+   {
+   return PEM_Code::encode(
+      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
+      "ENCRYPTED PRIVATE KEY");
+   }
+
+namespace {
+
+/*
+* Extract a private key (encrypted/unencrypted) and return it
+*/
+std::unique_ptr<Private_Key>
+load_key(DataSource& source,
+         std::function<std::string ()> get_pass,
+         bool is_encrypted)
+   {
+   AlgorithmIdentifier alg_id;
+   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
+
+   const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
+   if(alg_name.empty())
+      throw PKCS8_Exception("Unknown algorithm OID: " +
+                            alg_id.get_oid().to_string());
+
+   return load_private_key(alg_id, pkcs8_key);
+   }
+
+}
+
+/*
+* Extract an encrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+                      std::function<std::string ()> get_pass)
+   {
+   return load_key(source, get_pass, true);
+   }
+
+/*
+* Extract an encrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+                      const std::string& pass)
+   {
+   // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
+   // See https://github.com/randombit/botan/issues/2255.
+   return load_key(source, std::bind([](const std::string p) { return p; }, pass), true);
+   }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source)
+   {
+   auto fail_fn = []() -> std::string {
+      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
+   };
+
+   return load_key(source, fail_fn, false);
+   }
+
+/*
+* Make a copy of this private key
+*/
+std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
+   {
+   DataSource_Memory source(PEM_encode(key));
+   return PKCS8::load_key(source);
+   }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+                      RandomNumberGenerator& rng,
+                      std::function<std::string ()> get_pass)
+   {
+   BOTAN_UNUSED(rng);
+   return PKCS8::load_key(source, get_pass).release();
+   }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+                      RandomNumberGenerator& rng,
+                      const std::string& pass)
+   {
+   BOTAN_UNUSED(rng);
+   return PKCS8::load_key(source, pass).release();
+   }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+                      RandomNumberGenerator& rng)
+   {
+   BOTAN_UNUSED(rng);
+   return PKCS8::load_key(source).release();
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+                      RandomNumberGenerator& rng,
+                      std::function<std::string ()> get_pass)
+   {
+   BOTAN_UNUSED(rng);
+   DataSource_Stream in(fsname);
+   return PKCS8::load_key(in, get_pass).release();
+   }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+                      RandomNumberGenerator& rng,
+                      const std::string& pass)
+   {
+   BOTAN_UNUSED(rng);
+   DataSource_Stream in(fsname);
+   // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
+   // See https://github.com/randombit/botan/issues/2255.
+   return PKCS8::load_key(in, std::bind([](const std::string p) { return p; }, pass)).release();
+   }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+                      RandomNumberGenerator& rng)
+   {
+   BOTAN_UNUSED(rng);
+   DataSource_Stream in(fsname);
+   return PKCS8::load_key(in).release();
+   }
+#endif
+
+/*
+* Make a copy of this private key
+*/
+Private_Key* copy_key(const Private_Key& key,
+                      RandomNumberGenerator& rng)
+   {
+   BOTAN_UNUSED(rng);
+   return PKCS8::copy_key(key).release();
+   }
+
+
+
+}
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pkcs8.h b/third_party/botan/src/lib/pubkey/pkcs8.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pkcs8.h
@@ -0,0 +1,288 @@
+/*
+* PKCS #8
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS8_H_
+#define BOTAN_PKCS8_H_
+
+#include <botan/pk_keys.h>
+#include <botan/exceptn.h>
+#include <botan/secmem.h>
+#include <functional>
+#include <chrono>
+#include <memory>
+
+namespace Botan {
+
+class DataSource;
+class RandomNumberGenerator;
+
+/**
+* PKCS #8 General Exception
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS8_Exception final : public Decoding_Error
+   {
+   public:
+      explicit PKCS8_Exception(const std::string& error) :
+         Decoding_Error("PKCS #8: " + error) {}
+   };
+
+/**
+* This namespace contains functions for handling PKCS #8 private keys
+*/
+namespace PKCS8 {
+
+/**
+* BER encode a private key
+* @param key the private key to encode
+* @return BER encoded key
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> BER_encode(const Private_Key& key);
+
+/**
+* Get a string containing a PEM encoded private key.
+* @param key the key to encode
+* @return encoded key
+*/
+BOTAN_PUBLIC_API(2,0) std::string PEM_encode(const Private_Key& key);
+
+/**
+* Encrypt a key using PKCS #8 encryption
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param msec number of milliseconds to run the password derivation
+* @param pbe_algo the name of the desired password-based encryption
+*        algorithm; if empty ("") a reasonable (portable/secure)
+*        default will be chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t>
+BER_encode(const Private_Key& key,
+           RandomNumberGenerator& rng,
+           const std::string& pass,
+           std::chrono::milliseconds msec = std::chrono::milliseconds(300),
+           const std::string& pbe_algo = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param msec number of milliseconds to run the password derivation
+* @param pbe_algo the name of the desired password-based encryption
+*        algorithm; if empty ("") a reasonable (portable/secure)
+*        default will be chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,0) std::string
+PEM_encode(const Private_Key& key,
+           RandomNumberGenerator& rng,
+           const std::string& pass,
+           std::chrono::milliseconds msec = std::chrono::milliseconds(300),
+           const std::string& pbe_algo = "");
+
+/**
+* Encrypt a key using PKCS #8 encryption and a fixed iteration count
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_iter number of interations to run PBKDF2
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+*   are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+*   If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+*   For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,1) std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+                                RandomNumberGenerator& rng,
+                                const std::string& pass,
+                                size_t pbkdf_iter,
+                                const std::string& cipher = "",
+                                const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_iter number of iterations to run PBKDF
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+*   are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+*   If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+*   For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,1) std::string
+PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+                                RandomNumberGenerator& rng,
+                                const std::string& pass,
+                                size_t pbkdf_iter,
+                                const std::string& cipher = "",
+                                const std::string& pbkdf_hash = "");
+
+/**
+* Encrypt a key using PKCS #8 encryption and a variable iteration count
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_msec how long to run PBKDF2
+* @param pbkdf_iterations if non-null, set to the number of iterations used
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+*   are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+*   If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+*   For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,1) std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+                                RandomNumberGenerator& rng,
+                                const std::string& pass,
+                                std::chrono::milliseconds pbkdf_msec,
+                                size_t* pbkdf_iterations,
+                                const std::string& cipher = "",
+                                const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_msec how long in milliseconds to run PBKDF2
+* @param pbkdf_iterations (output argument) number of iterations of PBKDF
+*  that ended up being used
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+*   are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+*   If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+*   For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,1) std::string
+PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+                                RandomNumberGenerator& rng,
+                                const std::string& pass,
+                                std::chrono::milliseconds pbkdf_msec,
+                                size_t* pbkdf_iterations,
+                                const std::string& cipher = "",
+                                const std::string& pbkdf_hash = "");
+
+/**
+* Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+                                            RandomNumberGenerator& rng,
+                                            std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+                                            RandomNumberGenerator& rng,
+                                            const std::string& pass);
+
+/** Load an unencrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+                                            RandomNumberGenerator& rng);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/**
+* Load an encrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+                                            RandomNumberGenerator& rng,
+                                            std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+                                            RandomNumberGenerator& rng,
+                                            const std::string& pass);
+
+/** Load an unencrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+                                            RandomNumberGenerator& rng);
+#endif
+
+/**
+* Copy an existing encoded key object.
+* @param key the key to copy
+* @param rng ignored for compatibility
+* @return new copy of the key
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* copy_key(const Private_Key& key,
+                                            RandomNumberGenerator& rng);
+
+
+/**
+* Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+                                      std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+                                      const std::string& pass);
+
+/** Load an unencrypted key from a data source.
+* @param source the data source providing the encoded key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source);
+
+/**
+* Copy an existing encoded key object.
+* @param key the key to copy
+* @return new copy of the key
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> copy_key(const Private_Key& key);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/pubkey.cpp b/third_party/botan/src/lib/pubkey/pubkey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pubkey.cpp
@@ -0,0 +1,388 @@
+/*
+* (C) 1999-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/pk_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> PK_Decryptor::decrypt(const uint8_t in[], size_t length) const
+   {
+   uint8_t valid_mask = 0;
+
+   secure_vector<uint8_t> decoded = do_decrypt(valid_mask, in, length);
+
+   if(valid_mask == 0)
+      throw Decoding_Error("Invalid public key ciphertext, cannot decrypt");
+
+   return decoded;
+   }
+
+secure_vector<uint8_t>
+PK_Decryptor::decrypt_or_random(const uint8_t in[],
+                                size_t length,
+                                size_t expected_pt_len,
+                                RandomNumberGenerator& rng,
+                                const uint8_t required_content_bytes[],
+                                const uint8_t required_content_offsets[],
+                                size_t required_contents_length) const
+   {
+   const secure_vector<uint8_t> fake_pms = rng.random_vec(expected_pt_len);
+
+   uint8_t decrypt_valid = 0;
+   secure_vector<uint8_t> decoded = do_decrypt(decrypt_valid, in, length);
+
+   auto valid_mask = CT::Mask<uint8_t>::is_equal(decrypt_valid, 0xFF);
+   valid_mask &= CT::Mask<uint8_t>(CT::Mask<size_t>::is_zero(decoded.size() ^ expected_pt_len));
+
+   decoded.resize(expected_pt_len);
+
+   for(size_t i = 0; i != required_contents_length; ++i)
+      {
+      /*
+      These values are chosen by the application and for TLS are constants,
+      so this early failure via assert is fine since we know 0,1 < 48
+
+      If there is a protocol that has content checks on the key where
+      the expected offsets are controllable by the attacker this could
+      still leak.
+
+      Alternately could always reduce the offset modulo the length?
+      */
+
+      const uint8_t exp = required_content_bytes[i];
+      const uint8_t off = required_content_offsets[i];
+
+      BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext");
+
+      auto eq = CT::Mask<uint8_t>::is_equal(decoded[off], exp);
+
+      valid_mask &= eq;
+      }
+
+   // If valid_mask is false, assign fake pre master instead
+   valid_mask.select_n(decoded.data(), decoded.data(), fake_pms.data(), expected_pt_len);
+
+   return decoded;
+   }
+
+secure_vector<uint8_t>
+PK_Decryptor::decrypt_or_random(const uint8_t in[],
+                                size_t length,
+                                size_t expected_pt_len,
+                                RandomNumberGenerator& rng) const
+   {
+   return decrypt_or_random(in, length, expected_pt_len, rng,
+                            nullptr, nullptr, 0);
+   }
+
+PK_Encryptor_EME::PK_Encryptor_EME(const Public_Key& key,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& padding,
+                                   const std::string& provider)
+   {
+   m_op = key.create_encryption_op(rng, padding, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support encryption");
+   }
+
+PK_Encryptor_EME::~PK_Encryptor_EME() { /* for unique_ptr */ }
+
+size_t PK_Encryptor_EME::ciphertext_length(size_t ptext_len) const
+   {
+   return m_op->ciphertext_length(ptext_len);
+   }
+
+std::vector<uint8_t>
+PK_Encryptor_EME::enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const
+   {
+   return unlock(m_op->encrypt(in, length, rng));
+   }
+
+size_t PK_Encryptor_EME::maximum_input_size() const
+   {
+   return m_op->max_input_bits() / 8;
+   }
+
+PK_Decryptor_EME::PK_Decryptor_EME(const Private_Key& key,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& padding,
+                                   const std::string& provider)
+   {
+   m_op = key.create_decryption_op(rng, padding, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support decryption");
+   }
+
+PK_Decryptor_EME::~PK_Decryptor_EME() { /* for unique_ptr */ }
+
+size_t PK_Decryptor_EME::plaintext_length(size_t ctext_len) const
+   {
+   return m_op->plaintext_length(ctext_len);
+   }
+
+secure_vector<uint8_t> PK_Decryptor_EME::do_decrypt(uint8_t& valid_mask,
+                                                 const uint8_t in[], size_t in_len) const
+   {
+   return m_op->decrypt(valid_mask, in, in_len);
+   }
+
+PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& param,
+                                   const std::string& provider)
+   {
+   m_op = key.create_kem_encryption_op(rng, param, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support KEM encryption");
+   }
+
+PK_KEM_Encryptor::~PK_KEM_Encryptor() { /* for unique_ptr */ }
+
+void PK_KEM_Encryptor::encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                               secure_vector<uint8_t>& out_shared_key,
+                               size_t desired_shared_key_len,
+                               Botan::RandomNumberGenerator& rng,
+                               const uint8_t salt[],
+                               size_t salt_len)
+   {
+   m_op->kem_encrypt(out_encapsulated_key,
+                     out_shared_key,
+                     desired_shared_key_len,
+                     rng,
+                     salt,
+                     salt_len);
+   }
+
+PK_KEM_Decryptor::PK_KEM_Decryptor(const Private_Key& key,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& param,
+                                   const std::string& provider)
+   {
+   m_op = key.create_kem_decryption_op(rng, param, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support KEM decryption");
+   }
+
+PK_KEM_Decryptor::~PK_KEM_Decryptor() { /* for unique_ptr */ }
+
+secure_vector<uint8_t> PK_KEM_Decryptor::decrypt(const uint8_t encap_key[],
+                                              size_t encap_key_len,
+                                              size_t desired_shared_key_len,
+                                              const uint8_t salt[],
+                                              size_t salt_len)
+   {
+   return m_op->kem_decrypt(encap_key, encap_key_len,
+                            desired_shared_key_len,
+                            salt, salt_len);
+   }
+
+PK_Key_Agreement::PK_Key_Agreement(const Private_Key& key,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& kdf,
+                                   const std::string& provider)
+   {
+   m_op = key.create_key_agreement_op(rng, kdf, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support key agreement");
+   }
+
+PK_Key_Agreement::~PK_Key_Agreement() { /* for unique_ptr */ }
+
+PK_Key_Agreement& PK_Key_Agreement::operator=(PK_Key_Agreement&& other)
+   {
+   if(this != &other)
+      {
+      m_op = std::move(other.m_op);
+      }
+   return (*this);
+   }
+
+PK_Key_Agreement::PK_Key_Agreement(PK_Key_Agreement&& other) :
+   m_op(std::move(other.m_op))
+   {}
+
+size_t PK_Key_Agreement::agreed_value_size() const
+   {
+   return m_op->agreed_value_size();
+   }
+
+SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
+                                          const uint8_t in[], size_t in_len,
+                                          const uint8_t salt[],
+                                          size_t salt_len) const
+   {
+   return m_op->agree(key_len, in, in_len, salt, salt_len);
+   }
+
+static void check_der_format_supported(Signature_Format format, size_t parts)
+   {
+      if(format != IEEE_1363 && parts == 1)
+         throw Invalid_Argument("PK: This algorithm does not support DER encoding");
+   }
+
+PK_Signer::PK_Signer(const Private_Key& key,
+                     RandomNumberGenerator& rng,
+                     const std::string& emsa,
+                     Signature_Format format,
+                     const std::string& provider)
+   {
+   m_op = key.create_signature_op(rng, emsa, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support signature generation");
+   m_sig_format = format;
+   m_parts = key.message_parts();
+   m_part_size = key.message_part_size();
+   check_der_format_supported(format, m_parts);
+   }
+
+PK_Signer::~PK_Signer() { /* for unique_ptr */ }
+
+void PK_Signer::update(const uint8_t in[], size_t length)
+   {
+   m_op->update(in, length);
+   }
+
+namespace {
+
+std::vector<uint8_t> der_encode_signature(const std::vector<uint8_t>& sig,
+                                          size_t parts,
+                                          size_t part_size)
+   {
+   if(sig.size() % parts != 0 || sig.size() != parts * part_size)
+      throw Encoding_Error("Unexpected size for DER signature");
+
+   std::vector<BigInt> sig_parts(parts);
+   for(size_t i = 0; i != sig_parts.size(); ++i)
+      sig_parts[i].binary_decode(&sig[part_size*i], part_size);
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+      .encode_list(sig_parts)
+      .end_cons();
+   return output;
+   }
+
+}
+
+size_t PK_Signer::signature_length() const
+   {
+   if(m_sig_format == IEEE_1363)
+      {
+      return m_op->signature_length();
+      }
+   else if(m_sig_format == DER_SEQUENCE)
+      {
+      // This is a large over-estimate but its easier than computing
+      // the exact value
+      return m_op->signature_length() + (8 + 4*m_parts);
+      }
+   else
+      throw Internal_Error("PK_Signer: Invalid signature format enum");
+   }
+
+std::vector<uint8_t> PK_Signer::signature(RandomNumberGenerator& rng)
+   {
+   const std::vector<uint8_t> sig = unlock(m_op->sign(rng));
+
+   if(m_sig_format == IEEE_1363)
+      {
+      return sig;
+      }
+   else if(m_sig_format == DER_SEQUENCE)
+      {
+      return der_encode_signature(sig, m_parts, m_part_size);
+      }
+   else
+      throw Internal_Error("PK_Signer: Invalid signature format enum");
+   }
+
+PK_Verifier::PK_Verifier(const Public_Key& key,
+                         const std::string& emsa,
+                         Signature_Format format,
+                         const std::string& provider)
+   {
+   m_op = key.create_verification_op(emsa, provider);
+   if(!m_op)
+      throw Invalid_Argument("Key type " + key.algo_name() + " does not support signature verification");
+   m_sig_format = format;
+   m_parts = key.message_parts();
+   m_part_size = key.message_part_size();
+   check_der_format_supported(format, m_parts);
+   }
+
+PK_Verifier::~PK_Verifier() { /* for unique_ptr */ }
+
+void PK_Verifier::set_input_format(Signature_Format format)
+   {
+   check_der_format_supported(format, m_parts);
+   m_sig_format = format;
+   }
+
+bool PK_Verifier::verify_message(const uint8_t msg[], size_t msg_length,
+                                 const uint8_t sig[], size_t sig_length)
+   {
+   update(msg, msg_length);
+   return check_signature(sig, sig_length);
+   }
+
+void PK_Verifier::update(const uint8_t in[], size_t length)
+   {
+   m_op->update(in, length);
+   }
+
+bool PK_Verifier::check_signature(const uint8_t sig[], size_t length)
+   {
+   try {
+      if(m_sig_format == IEEE_1363)
+         {
+         return m_op->is_valid_signature(sig, length);
+         }
+      else if(m_sig_format == DER_SEQUENCE)
+         {
+         std::vector<uint8_t> real_sig;
+         BER_Decoder decoder(sig, length);
+         BER_Decoder ber_sig = decoder.start_cons(SEQUENCE);
+
+         BOTAN_ASSERT_NOMSG(m_parts != 0 && m_part_size != 0);
+
+         size_t count = 0;
+
+         while(ber_sig.more_items())
+            {
+            BigInt sig_part;
+            ber_sig.decode(sig_part);
+            real_sig += BigInt::encode_1363(sig_part, m_part_size);
+            ++count;
+            }
+
+         if(count != m_parts)
+            throw Decoding_Error("PK_Verifier: signature size invalid");
+
+         const std::vector<uint8_t> reencoded =
+            der_encode_signature(real_sig, m_parts, m_part_size);
+
+         if(reencoded.size() != length ||
+            same_mem(reencoded.data(), sig, reencoded.size()) == false)
+            {
+            throw Decoding_Error("PK_Verifier: signature is not the canonical DER encoding");
+            }
+
+         return m_op->is_valid_signature(real_sig.data(), real_sig.size());
+         }
+      else
+         throw Internal_Error("PK_Verifier: Invalid signature format enum");
+      }
+   catch(Invalid_Argument&) { return false; }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/pubkey.h b/third_party/botan/src/lib/pubkey/pubkey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/pubkey.h
@@ -0,0 +1,800 @@
+/*
+* Public Key Interface
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PUBKEY_H_
+#define BOTAN_PUBKEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/pk_ops_fwd.h>
+#include <botan/symkey.h>
+#include <string>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+  #include <botan/system_rng.h>
+  #define BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS
+#endif
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Public Key Encryptor
+* This is the primary interface for public key encryption
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Encryptor
+   {
+   public:
+
+      /**
+      * Encrypt a message.
+      * @param in the message as a byte array
+      * @param length the length of the above byte array
+      * @param rng the random number source to use
+      * @return encrypted message
+      */
+      std::vector<uint8_t> encrypt(const uint8_t in[], size_t length,
+                                 RandomNumberGenerator& rng) const
+         {
+         return enc(in, length, rng);
+         }
+
+      /**
+      * Encrypt a message.
+      * @param in the message
+      * @param rng the random number source to use
+      * @return encrypted message
+      */
+      template<typename Alloc>
+      std::vector<uint8_t> encrypt(const std::vector<uint8_t, Alloc>& in,
+                                RandomNumberGenerator& rng) const
+         {
+         return enc(in.data(), in.size(), rng);
+         }
+
+      /**
+      * Return the maximum allowed message size in bytes.
+      * @return maximum message size in bytes
+      */
+      virtual size_t maximum_input_size() const = 0;
+
+      /**
+      * Return an upper bound on the ciphertext length
+      */
+      virtual size_t ciphertext_length(size_t ctext_len) const = 0;
+
+      PK_Encryptor() = default;
+      virtual ~PK_Encryptor() = default;
+
+      PK_Encryptor(const PK_Encryptor&) = delete;
+      PK_Encryptor& operator=(const PK_Encryptor&) = delete;
+
+   private:
+      virtual std::vector<uint8_t> enc(const uint8_t[], size_t,
+                                    RandomNumberGenerator&) const = 0;
+   };
+
+/**
+* Public Key Decryptor
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Decryptor
+   {
+   public:
+      /**
+      * Decrypt a ciphertext, throwing an exception if the input
+      * seems to be invalid (eg due to an accidental or malicious
+      * error in the ciphertext).
+      *
+      * @param in the ciphertext as a byte array
+      * @param length the length of the above byte array
+      * @return decrypted message
+      */
+      secure_vector<uint8_t> decrypt(const uint8_t in[], size_t length) const;
+
+      /**
+      * Same as above, but taking a vector
+      * @param in the ciphertext
+      * @return decrypted message
+      */
+      template<typename Alloc>
+      secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc>& in) const
+         {
+         return decrypt(in.data(), in.size());
+         }
+
+      /**
+      * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+      * invalid padding) or is not the expected length, instead
+      * returns a random string of the expected length. Use to avoid
+      * oracle attacks, especially against PKCS #1 v1.5 decryption.
+      */
+      secure_vector<uint8_t>
+      decrypt_or_random(const uint8_t in[],
+                        size_t length,
+                        size_t expected_pt_len,
+                        RandomNumberGenerator& rng) const;
+
+      /**
+      * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+      * invalid padding) or is not the expected length, instead
+      * returns a random string of the expected length. Use to avoid
+      * oracle attacks, especially against PKCS #1 v1.5 decryption.
+      *
+      * Additionally checks (also in const time) that:
+      *    contents[required_content_offsets[i]] == required_content_bytes[i]
+      * for 0 <= i < required_contents
+      *
+      * Used for example in TLS, which encodes the client version in
+      * the content bytes: if there is any timing variation the version
+      * check can be used as an oracle to recover the key.
+      */
+      secure_vector<uint8_t>
+      decrypt_or_random(const uint8_t in[],
+                        size_t length,
+                        size_t expected_pt_len,
+                        RandomNumberGenerator& rng,
+                        const uint8_t required_content_bytes[],
+                        const uint8_t required_content_offsets[],
+                        size_t required_contents) const;
+
+      /**
+      * Return an upper bound on the plaintext length for a particular
+      * ciphertext input length
+      */
+      virtual size_t plaintext_length(size_t ctext_len) const = 0;
+
+      PK_Decryptor() = default;
+      virtual ~PK_Decryptor() = default;
+
+      PK_Decryptor(const PK_Decryptor&) = delete;
+      PK_Decryptor& operator=(const PK_Decryptor&) = delete;
+
+   private:
+      virtual secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+                                             const uint8_t in[], size_t in_len) const = 0;
+   };
+
+/**
+* Public Key Signer. Use the sign_message() functions for small
+* messages. Use multiple calls update() to process large messages and
+* generate the signature by finally calling signature().
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Signer final
+   {
+   public:
+
+      /**
+      * Construct a PK Signer.
+      * @param key the key to use inside this signer
+      * @param rng the random generator to use
+      * @param emsa the EMSA to use
+      * An example would be "EMSA1(SHA-224)".
+      * @param format the signature format to use
+      * @param provider the provider to use
+      */
+      PK_Signer(const Private_Key& key,
+                RandomNumberGenerator& rng,
+                const std::string& emsa,
+                Signature_Format format = IEEE_1363,
+                const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      /**
+      * Construct a PK Signer.
+      * @param key the key to use inside this signer
+      * @param emsa the EMSA to use
+      * An example would be "EMSA1(SHA-224)".
+      * @param format the signature format to use
+      */
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_Signer(const Private_Key& key,
+                const std::string& emsa,
+                Signature_Format format = IEEE_1363,
+                const std::string& provider = "") :
+         PK_Signer(key, system_rng(), emsa, format, provider)
+         {}
+#endif
+
+      ~PK_Signer();
+
+      PK_Signer(const PK_Signer&) = delete;
+      PK_Signer& operator=(const PK_Signer&) = delete;
+
+      /**
+      * Sign a message all in one go
+      * @param in the message to sign as a byte array
+      * @param length the length of the above byte array
+      * @param rng the rng to use
+      * @return signature
+      */
+      std::vector<uint8_t> sign_message(const uint8_t in[], size_t length,
+                                     RandomNumberGenerator& rng)
+         {
+         this->update(in, length);
+         return this->signature(rng);
+         }
+
+      /**
+      * Sign a message.
+      * @param in the message to sign
+      * @param rng the rng to use
+      * @return signature
+      */
+      template<typename Alloc>
+         std::vector<uint8_t> sign_message(const std::vector<uint8_t, Alloc>& in,
+                                           RandomNumberGenerator& rng)
+         {
+         return sign_message(in.data(), in.size(), rng);
+         }
+
+      /**
+      * Add a message part (single byte).
+      * @param in the byte to add
+      */
+      void update(uint8_t in) { update(&in, 1); }
+
+      /**
+      * Add a message part.
+      * @param in the message part to add as a byte array
+      * @param length the length of the above byte array
+      */
+      void update(const uint8_t in[], size_t length);
+
+      /**
+      * Add a message part.
+      * @param in the message part to add
+      */
+      template<typename Alloc>
+      void update(const std::vector<uint8_t, Alloc>& in)
+         {
+         update(in.data(), in.size());
+         }
+
+      /**
+      * Add a message part.
+      * @param in the message part to add
+      */
+      void update(const std::string& in)
+         {
+         update(cast_char_ptr_to_uint8(in.data()), in.size());
+         }
+
+      /**
+      * Get the signature of the so far processed message (provided by the
+      * calls to update()).
+      * @param rng the rng to use
+      * @return signature of the total message
+      */
+      std::vector<uint8_t> signature(RandomNumberGenerator& rng);
+
+
+      /**
+      * Set the output format of the signature.
+      * @param format the signature format to use
+      */
+      void set_output_format(Signature_Format format) { m_sig_format = format; }
+
+      /**
+      * Return an upper bound on the length of the signatures this
+      * PK_Signer will produce
+      */
+      size_t signature_length() const;
+
+   private:
+      std::unique_ptr<PK_Ops::Signature> m_op;
+      Signature_Format m_sig_format;
+      size_t m_parts, m_part_size;
+   };
+
+/**
+* Public Key Verifier. Use the verify_message() functions for small
+* messages. Use multiple calls update() to process large messages and
+* verify the signature by finally calling check_signature().
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Verifier final
+   {
+   public:
+      /**
+      * Construct a PK Verifier.
+      * @param pub_key the public key to verify against
+      * @param emsa the EMSA to use (eg "EMSA3(SHA-1)")
+      * @param format the signature format to use
+      * @param provider the provider to use
+      */
+      PK_Verifier(const Public_Key& pub_key,
+                  const std::string& emsa,
+                  Signature_Format format = IEEE_1363,
+                  const std::string& provider = "");
+
+      ~PK_Verifier();
+
+      PK_Verifier& operator=(const PK_Verifier&) = delete;
+      PK_Verifier(const PK_Verifier&) = delete;
+
+      /**
+      * Verify a signature.
+      * @param msg the message that the signature belongs to, as a byte array
+      * @param msg_length the length of the above byte array msg
+      * @param sig the signature as a byte array
+      * @param sig_length the length of the above byte array sig
+      * @return true if the signature is valid
+      */
+      bool verify_message(const uint8_t msg[], size_t msg_length,
+                          const uint8_t sig[], size_t sig_length);
+      /**
+      * Verify a signature.
+      * @param msg the message that the signature belongs to
+      * @param sig the signature
+      * @return true if the signature is valid
+      */
+      template<typename Alloc, typename Alloc2>
+      bool verify_message(const std::vector<uint8_t, Alloc>& msg,
+                          const std::vector<uint8_t, Alloc2>& sig)
+         {
+         return verify_message(msg.data(), msg.size(),
+                               sig.data(), sig.size());
+         }
+
+      /**
+      * Add a message part (single byte) of the message corresponding to the
+      * signature to be verified.
+      * @param in the byte to add
+      */
+      void update(uint8_t in) { update(&in, 1); }
+
+      /**
+      * Add a message part of the message corresponding to the
+      * signature to be verified.
+      * @param msg_part the new message part as a byte array
+      * @param length the length of the above byte array
+      */
+      void update(const uint8_t msg_part[], size_t length);
+
+      /**
+      * Add a message part of the message corresponding to the
+      * signature to be verified.
+      * @param in the new message part
+      */
+      template<typename Alloc>
+         void update(const std::vector<uint8_t, Alloc>& in)
+         {
+         update(in.data(), in.size());
+         }
+
+      /**
+      * Add a message part of the message corresponding to the
+      * signature to be verified.
+      */
+      void update(const std::string& in)
+         {
+         update(cast_char_ptr_to_uint8(in.data()), in.size());
+         }
+
+      /**
+      * Check the signature of the buffered message, i.e. the one build
+      * by successive calls to update.
+      * @param sig the signature to be verified as a byte array
+      * @param length the length of the above byte array
+      * @return true if the signature is valid, false otherwise
+      */
+      bool check_signature(const uint8_t sig[], size_t length);
+
+      /**
+      * Check the signature of the buffered message, i.e. the one build
+      * by successive calls to update.
+      * @param sig the signature to be verified
+      * @return true if the signature is valid, false otherwise
+      */
+      template<typename Alloc>
+      bool check_signature(const std::vector<uint8_t, Alloc>& sig)
+         {
+         return check_signature(sig.data(), sig.size());
+         }
+
+      /**
+      * Set the format of the signatures fed to this verifier.
+      * @param format the signature format to use
+      */
+      void set_input_format(Signature_Format format);
+
+   private:
+      std::unique_ptr<PK_Ops::Verification> m_op;
+      Signature_Format m_sig_format;
+      size_t m_parts, m_part_size;
+   };
+
+/**
+* Object used for key agreement
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final
+   {
+   public:
+
+      /**
+      * Construct a PK Key Agreement.
+      * @param key the key to use
+      * @param rng the random generator to use
+      * @param kdf name of the KDF to use (or 'Raw' for no KDF)
+      * @param provider the algo provider to use (or empty for default)
+      */
+      PK_Key_Agreement(const Private_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& kdf,
+                       const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      /**
+      * Construct a PK Key Agreement.
+      * @param key the key to use
+      * @param kdf name of the KDF to use (or 'Raw' for no KDF)
+      * @param provider the algo provider to use (or empty for default)
+      */
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_Key_Agreement(const Private_Key& key,
+                       const std::string& kdf,
+                       const std::string& provider = "") :
+         PK_Key_Agreement(key, system_rng(), kdf, provider)
+         {}
+#endif
+
+      ~PK_Key_Agreement();
+
+      // For ECIES
+      PK_Key_Agreement& operator=(PK_Key_Agreement&&);
+      PK_Key_Agreement(PK_Key_Agreement&&);
+
+      PK_Key_Agreement& operator=(const PK_Key_Agreement&) = delete;
+      PK_Key_Agreement(const PK_Key_Agreement&) = delete;
+
+      /**
+      * Perform Key Agreement Operation
+      * @param key_len the desired key output size
+      * @param in the other parties key
+      * @param in_len the length of in in bytes
+      * @param params extra derivation params
+      * @param params_len the length of params in bytes
+      */
+      SymmetricKey derive_key(size_t key_len,
+                              const uint8_t in[],
+                              size_t in_len,
+                              const uint8_t params[],
+                              size_t params_len) const;
+
+      /**
+      * Perform Key Agreement Operation
+      * @param key_len the desired key output size
+      * @param in the other parties key
+      * @param params extra derivation params
+      * @param params_len the length of params in bytes
+      */
+      SymmetricKey derive_key(size_t key_len,
+                              const std::vector<uint8_t>& in,
+                              const uint8_t params[],
+                              size_t params_len) const
+         {
+         return derive_key(key_len, in.data(), in.size(),
+                           params, params_len);
+         }
+
+      /**
+      * Perform Key Agreement Operation
+      * @param key_len the desired key output size
+      * @param in the other parties key
+      * @param in_len the length of in in bytes
+      * @param params extra derivation params
+      */
+      SymmetricKey derive_key(size_t key_len,
+                              const uint8_t in[], size_t in_len,
+                              const std::string& params = "") const
+         {
+         return derive_key(key_len, in, in_len,
+                           cast_char_ptr_to_uint8(params.data()),
+                           params.length());
+         }
+
+      /**
+      * Perform Key Agreement Operation
+      * @param key_len the desired key output size
+      * @param in the other parties key
+      * @param params extra derivation params
+      */
+      SymmetricKey derive_key(size_t key_len,
+                              const std::vector<uint8_t>& in,
+                              const std::string& params = "") const
+         {
+         return derive_key(key_len, in.data(), in.size(),
+                           cast_char_ptr_to_uint8(params.data()),
+                           params.length());
+         }
+
+      /**
+      * Return the underlying size of the value that is agreed.
+      * If derive_key is called with a length of 0 with a "Raw"
+      * KDF, it will return a value of this size.
+      */
+      size_t agreed_value_size() const;
+
+   private:
+      std::unique_ptr<PK_Ops::Key_Agreement> m_op;
+   };
+
+/**
+* Encryption using a standard message recovery algorithm like RSA or
+* ElGamal, paired with an encoding scheme like OAEP.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Encryptor_EME final : public PK_Encryptor
+   {
+   public:
+      size_t maximum_input_size() const override;
+
+      /**
+      * Construct an instance.
+      * @param key the key to use inside the encryptor
+      * @param rng the RNG to use
+      * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
+      * @param provider the provider to use
+      */
+      PK_Encryptor_EME(const Public_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& padding,
+                       const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      /**
+      * Construct an instance.
+      * @param key the key to use inside the encryptor
+      * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
+      */
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_Encryptor_EME(const Public_Key& key,
+                       const std::string& padding,
+                       const std::string& provider = "") :
+         PK_Encryptor_EME(key, system_rng(), padding, provider) {}
+#endif
+
+      ~PK_Encryptor_EME();
+
+      PK_Encryptor_EME& operator=(const PK_Encryptor_EME&) = delete;
+      PK_Encryptor_EME(const PK_Encryptor_EME&) = delete;
+
+      /**
+      * Return an upper bound on the ciphertext length for a particular
+      * plaintext input length
+      */
+      size_t ciphertext_length(size_t ptext_len) const override;
+   private:
+      std::vector<uint8_t> enc(const uint8_t[], size_t,
+                             RandomNumberGenerator& rng) const override;
+
+      std::unique_ptr<PK_Ops::Encryption> m_op;
+   };
+
+/**
+* Decryption with an MR algorithm and an EME.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Decryptor_EME final : public PK_Decryptor
+   {
+   public:
+     /**
+      * Construct an instance.
+      * @param key the key to use inside the decryptor
+      * @param rng the random generator to use
+      * @param eme the EME to use
+      * @param provider the provider to use
+      */
+      PK_Decryptor_EME(const Private_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& eme,
+                       const std::string& provider = "");
+
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      /**
+      * Construct an instance.
+      * @param key the key to use inside the decryptor
+      * @param eme the message encoding scheme to use (eg "OAEP(SHA-256)")
+      */
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_Decryptor_EME(const Private_Key& key,
+                       const std::string& eme,
+                       const std::string& provider = "") :
+         PK_Decryptor_EME(key, system_rng(), eme, provider) {}
+#endif
+
+      size_t plaintext_length(size_t ptext_len) const override;
+
+      ~PK_Decryptor_EME();
+      PK_Decryptor_EME& operator=(const PK_Decryptor_EME&) = delete;
+      PK_Decryptor_EME(const PK_Decryptor_EME&) = delete;
+   private:
+      secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+                                     const uint8_t in[],
+                                     size_t in_len) const override;
+
+      std::unique_ptr<PK_Ops::Decryption> m_op;
+   };
+
+/**
+* Public Key Key Encapsulation Mechanism Encryption.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_KEM_Encryptor final
+   {
+   public:
+      /**
+      * Construct an instance.
+      * @param key the key to use inside the encryptor
+      * @param rng the RNG to use
+      * @param kem_param additional KEM parameters
+      * @param provider the provider to use
+      */
+      PK_KEM_Encryptor(const Public_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& kem_param = "",
+                       const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_KEM_Encryptor(const Public_Key& key,
+                       const std::string& kem_param = "",
+                       const std::string& provider = "") :
+         PK_KEM_Encryptor(key, system_rng(), kem_param, provider) {}
+#endif
+
+      ~PK_KEM_Encryptor();
+
+      PK_KEM_Encryptor& operator=(const PK_KEM_Encryptor&) = delete;
+      PK_KEM_Encryptor(const PK_KEM_Encryptor&) = delete;
+
+      /**
+      * Generate a shared key for data encryption.
+      * @param out_encapsulated_key the generated encapsulated key
+      * @param out_shared_key the generated shared key
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @param rng the RNG to use
+      * @param salt a salt value used in the KDF
+      * @param salt_len size of the salt value in bytes
+      */
+      void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                   secure_vector<uint8_t>& out_shared_key,
+                   size_t desired_shared_key_len,
+                   Botan::RandomNumberGenerator& rng,
+                   const uint8_t salt[],
+                   size_t salt_len);
+
+      /**
+      * Generate a shared key for data encryption.
+      * @param out_encapsulated_key the generated encapsulated key
+      * @param out_shared_key the generated shared key
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @param rng the RNG to use
+      * @param salt a salt value used in the KDF
+      */
+      template<typename Alloc>
+         void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                      secure_vector<uint8_t>& out_shared_key,
+                      size_t desired_shared_key_len,
+                      Botan::RandomNumberGenerator& rng,
+                      const std::vector<uint8_t, Alloc>& salt)
+         {
+         this->encrypt(out_encapsulated_key,
+                       out_shared_key,
+                       desired_shared_key_len,
+                       rng,
+                       salt.data(), salt.size());
+         }
+
+
+      /**
+      * Generate a shared key for data encryption.
+      * @param out_encapsulated_key the generated encapsulated key
+      * @param out_shared_key the generated shared key
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @param rng the RNG to use
+      */
+      void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                   secure_vector<uint8_t>& out_shared_key,
+                   size_t desired_shared_key_len,
+                   Botan::RandomNumberGenerator& rng)
+         {
+         this->encrypt(out_encapsulated_key,
+                       out_shared_key,
+                       desired_shared_key_len,
+                       rng,
+                       nullptr,
+                       0);
+         }
+
+   private:
+      std::unique_ptr<PK_Ops::KEM_Encryption> m_op;
+   };
+
+/**
+* Public Key Key Encapsulation Mechanism Decryption.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_KEM_Decryptor final
+   {
+   public:
+      /**
+      * Construct an instance.
+      * @param key the key to use inside the decryptor
+      * @param rng the RNG to use
+      * @param kem_param additional KEM parameters
+      * @param provider the provider to use
+      */
+      PK_KEM_Decryptor(const Private_Key& key,
+                       RandomNumberGenerator& rng,
+                       const std::string& kem_param = "",
+                       const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+      BOTAN_DEPRECATED("Use constructor taking a RNG object")
+      PK_KEM_Decryptor(const Private_Key& key,
+                       const std::string& kem_param = "",
+                       const std::string& provider = "") :
+         PK_KEM_Decryptor(key, system_rng(), kem_param, provider)
+         {}
+#endif
+
+      ~PK_KEM_Decryptor();
+      PK_KEM_Decryptor& operator=(const PK_KEM_Decryptor&) = delete;
+      PK_KEM_Decryptor(const PK_KEM_Decryptor&) = delete;
+
+      /**
+      * Decrypts the shared key for data encryption.
+      * @param encap_key the encapsulated key
+      * @param encap_key_len size of the encapsulated key in bytes
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @param salt a salt value used in the KDF
+      * @param salt_len size of the salt value in bytes
+      * @return the shared data encryption key
+      */
+      secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
+                                  size_t encap_key_len,
+                                  size_t desired_shared_key_len,
+                                  const uint8_t salt[],
+                                  size_t salt_len);
+
+      /**
+      * Decrypts the shared key for data encryption.
+      * @param encap_key the encapsulated key
+      * @param encap_key_len size of the encapsulated key in bytes
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @return the shared data encryption key
+      */
+      secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
+                                  size_t encap_key_len,
+                                  size_t desired_shared_key_len)
+         {
+         return this->decrypt(encap_key, encap_key_len,
+                              desired_shared_key_len,
+                              nullptr, 0);
+         }
+
+      /**
+      * Decrypts the shared key for data encryption.
+      * @param encap_key the encapsulated key
+      * @param desired_shared_key_len desired size of the shared key in bytes
+      * @param salt a salt value used in the KDF
+      * @return the shared data encryption key
+      */
+      template<typename Alloc1, typename Alloc2>
+         secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc1>& encap_key,
+                                     size_t desired_shared_key_len,
+                                     const std::vector<uint8_t, Alloc2>& salt)
+         {
+         return this->decrypt(encap_key.data(), encap_key.size(),
+                              desired_shared_key_len,
+                              salt.data(), salt.size());
+         }
+
+   private:
+      std::unique_ptr<PK_Ops::KEM_Decryption> m_op;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp b/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp
@@ -0,0 +1,59 @@
+/*
+* RFC 6979 Deterministic Nonce Generator
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rfc6979.h>
+#include <botan/hmac_drbg.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(const std::string& hash,
+                                                 const BigInt& order,
+                                                 const BigInt& x) :
+   m_order(order),
+   m_qlen(m_order.bits()),
+   m_rlen(m_qlen / 8 + (m_qlen % 8 ? 1 : 0)),
+   m_rng_in(m_rlen * 2),
+   m_rng_out(m_rlen)
+   {
+   m_hmac_drbg.reset(new HMAC_DRBG(MessageAuthenticationCode::create("HMAC(" + hash + ")")));
+   BigInt::encode_1363(m_rng_in.data(), m_rlen, x);
+   }
+
+RFC6979_Nonce_Generator::~RFC6979_Nonce_Generator()
+   {
+   // for ~unique_ptr
+   }
+
+const BigInt& RFC6979_Nonce_Generator::nonce_for(const BigInt& m)
+   {
+   BigInt::encode_1363(&m_rng_in[m_rlen], m_rlen, m);
+   m_hmac_drbg->clear();
+   m_hmac_drbg->initialize_with(m_rng_in.data(), m_rng_in.size());
+
+   do
+      {
+      m_hmac_drbg->randomize(m_rng_out.data(), m_rng_out.size());
+      m_k.binary_decode(m_rng_out.data(), m_rng_out.size());
+      m_k >>= (8*m_rlen - m_qlen);
+      }
+   while(m_k == 0 || m_k >= m_order);
+
+   return m_k;
+   }
+
+BigInt generate_rfc6979_nonce(const BigInt& x,
+                              const BigInt& q,
+                              const BigInt& h,
+                              const std::string& hash)
+   {
+   RFC6979_Nonce_Generator gen(hash, q, x);
+   BigInt k = gen.nonce_for(h);
+   return k;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h b/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h
@@ -0,0 +1,55 @@
+/*
+* RFC 6979 Deterministic Nonce Generator
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC6979_GENERATOR_H_
+#define BOTAN_RFC6979_GENERATOR_H_
+
+#include <botan/bigint.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rfc6979.h)
+
+namespace Botan {
+
+class HMAC_DRBG;
+
+class BOTAN_PUBLIC_API(2,0) RFC6979_Nonce_Generator final
+   {
+   public:
+      /**
+      * Note: keeps persistent reference to order
+      */
+      RFC6979_Nonce_Generator(const std::string& hash,
+                              const BigInt& order,
+                              const BigInt& x);
+
+      ~RFC6979_Nonce_Generator();
+
+      const BigInt& nonce_for(const BigInt& m);
+   private:
+      const BigInt& m_order;
+      BigInt m_k;
+      size_t m_qlen, m_rlen;
+      std::unique_ptr<HMAC_DRBG> m_hmac_drbg;
+      secure_vector<uint8_t> m_rng_in, m_rng_out;
+   };
+
+/**
+* @param x the secret (EC)DSA key
+* @param q the group order
+* @param h the message hash already reduced mod q
+* @param hash the hash function used to generate h
+*/
+BigInt BOTAN_PUBLIC_API(2,0) generate_rfc6979_nonce(const BigInt& x,
+                                        const BigInt& q,
+                                        const BigInt& h,
+                                        const std::string& hash);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/rsa/rsa.cpp b/third_party/botan/src/lib/pubkey/rsa/rsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/rsa/rsa.cpp
@@ -0,0 +1,746 @@
+/*
+* RSA
+* (C) 1999-2010,2015,2016,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/keypair.h>
+#include <botan/blinding.h>
+#include <botan/reducer.h>
+#include <botan/workfactor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/monty.h>
+#include <botan/divide.h>
+#include <botan/internal/monty_exp.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+  #include <botan/internal/thread_pool.h>
+#endif
+
+namespace Botan {
+
+class RSA_Public_Data final
+   {
+   public:
+      RSA_Public_Data(BigInt&& n, BigInt&& e) :
+         m_n(n),
+         m_e(e),
+         m_monty_n(std::make_shared<Montgomery_Params>(m_n)),
+         m_public_modulus_bits(m_n.bits()),
+         m_public_modulus_bytes(m_n.bytes())
+         {}
+
+      BigInt public_op(const BigInt& m) const
+         {
+         const size_t powm_window = 1;
+         auto powm_m_n = monty_precompute(m_monty_n, m, powm_window, false);
+         return monty_execute_vartime(*powm_m_n, m_e);
+         }
+
+      const BigInt& get_n() const { return m_n; }
+      const BigInt& get_e() const { return m_e; }
+      size_t public_modulus_bits() const { return m_public_modulus_bits; }
+      size_t public_modulus_bytes() const { return m_public_modulus_bytes; }
+
+   private:
+      BigInt m_n;
+      BigInt m_e;
+      std::shared_ptr<const Montgomery_Params> m_monty_n;
+      size_t m_public_modulus_bits;
+      size_t m_public_modulus_bytes;
+   };
+
+class RSA_Private_Data final
+   {
+   public:
+      RSA_Private_Data(BigInt&& d, BigInt&& p, BigInt&& q,
+                       BigInt&& d1, BigInt&& d2, BigInt&& c) :
+         m_d(d),
+         m_p(p),
+         m_q(q),
+         m_d1(d1),
+         m_d2(d2),
+         m_c(c),
+         m_mod_p(m_p),
+         m_mod_q(m_q),
+         m_monty_p(std::make_shared<Montgomery_Params>(m_p, m_mod_p)),
+         m_monty_q(std::make_shared<Montgomery_Params>(m_q, m_mod_q)),
+         m_p_bits(m_p.bits()),
+         m_q_bits(m_q.bits())
+         {}
+
+      const BigInt& get_d() const { return m_d; }
+      const BigInt& get_p() const { return m_p; }
+      const BigInt& get_q() const { return m_q; }
+      const BigInt& get_d1() const { return m_d1; }
+      const BigInt& get_d2() const { return m_d2; }
+      const BigInt& get_c() const { return m_c; }
+
+   //private:
+      BigInt m_d;
+      BigInt m_p;
+      BigInt m_q;
+      BigInt m_d1;
+      BigInt m_d2;
+      BigInt m_c;
+
+      Modular_Reducer m_mod_p;
+      Modular_Reducer m_mod_q;
+      std::shared_ptr<const Montgomery_Params> m_monty_p;
+      std::shared_ptr<const Montgomery_Params> m_monty_q;
+      size_t m_p_bits;
+      size_t m_q_bits;
+   };
+
+std::shared_ptr<const RSA_Public_Data> RSA_PublicKey::public_data() const
+   {
+   return m_public;
+   }
+
+const BigInt& RSA_PublicKey::get_n() const { return m_public->get_n(); }
+const BigInt& RSA_PublicKey::get_e() const { return m_public->get_e(); }
+
+void RSA_PublicKey::init(BigInt&& n, BigInt&& e)
+   {
+   if(n.is_negative() || n.is_even() || e.is_negative() || e.is_even())
+      throw Decoding_Error("Invalid RSA public key parameters");
+   m_public = std::make_shared<RSA_Public_Data>(std::move(n), std::move(e));
+   }
+
+RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier&,
+                             const std::vector<uint8_t>& key_bits)
+   {
+   BigInt n, e;
+   BER_Decoder(key_bits)
+      .start_cons(SEQUENCE)
+      .decode(n)
+      .decode(e)
+      .end_cons();
+
+   init(std::move(n), std::move(e));
+   }
+
+RSA_PublicKey::RSA_PublicKey(const BigInt& modulus, const BigInt& exponent)
+   {
+   BigInt n = modulus;
+   BigInt e = exponent;
+   init(std::move(n), std::move(e));
+   }
+
+size_t RSA_PublicKey::key_length() const
+   {
+   return m_public->public_modulus_bits();
+   }
+
+size_t RSA_PublicKey::estimated_strength() const
+   {
+   return if_work_factor(key_length());
+   }
+
+AlgorithmIdentifier RSA_PublicKey::algorithm_identifier() const
+   {
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_NULL_PARAM);
+   }
+
+std::vector<uint8_t> RSA_PublicKey::public_key_bits() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder der(output);
+   der.start_cons(SEQUENCE)
+         .encode(get_n())
+         .encode(get_e())
+      .end_cons();
+
+   return output;
+   }
+
+/*
+* Check RSA Public Parameters
+*/
+bool RSA_PublicKey::check_key(RandomNumberGenerator&, bool) const
+   {
+   if(get_n() < 35 || get_n().is_even() || get_e() < 3 || get_e().is_even())
+      return false;
+   return true;
+   }
+
+std::shared_ptr<const RSA_Private_Data> RSA_PrivateKey::private_data() const
+   {
+   return m_private;
+   }
+
+secure_vector<uint8_t> RSA_PrivateKey::private_key_bits() const
+   {
+   return DER_Encoder()
+      .start_cons(SEQUENCE)
+         .encode(static_cast<size_t>(0))
+         .encode(get_n())
+         .encode(get_e())
+         .encode(get_d())
+         .encode(get_p())
+         .encode(get_q())
+         .encode(get_d1())
+         .encode(get_d2())
+         .encode(get_c())
+      .end_cons()
+   .get_contents();
+   }
+
+const BigInt& RSA_PrivateKey::get_p() const { return m_private->get_p(); }
+const BigInt& RSA_PrivateKey::get_q() const { return m_private->get_q(); }
+const BigInt& RSA_PrivateKey::get_d() const { return m_private->get_d(); }
+const BigInt& RSA_PrivateKey::get_c() const { return m_private->get_c(); }
+const BigInt& RSA_PrivateKey::get_d1() const { return m_private->get_d1(); }
+const BigInt& RSA_PrivateKey::get_d2() const { return m_private->get_d2(); }
+
+void RSA_PrivateKey::init(BigInt&& d, BigInt&& p, BigInt&& q,
+                          BigInt&& d1, BigInt&& d2, BigInt&& c)
+   {
+   m_private = std::make_shared<RSA_Private_Data>(
+      std::move(d), std::move(p), std::move(q), std::move(d1), std::move(d2), std::move(c));
+   }
+
+RSA_PrivateKey::RSA_PrivateKey(const AlgorithmIdentifier&,
+                               const secure_vector<uint8_t>& key_bits)
+   {
+   BigInt n, e, d, p, q, d1, d2, c;
+
+   BER_Decoder(key_bits)
+      .start_cons(SEQUENCE)
+         .decode_and_check<size_t>(0, "Unknown PKCS #1 key format version")
+         .decode(n)
+         .decode(e)
+         .decode(d)
+         .decode(p)
+         .decode(q)
+         .decode(d1)
+         .decode(d2)
+         .decode(c)
+      .end_cons();
+
+   RSA_PublicKey::init(std::move(n), std::move(e));
+
+   RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+                        std::move(d1), std::move(d2), std::move(c));
+   }
+
+RSA_PrivateKey::RSA_PrivateKey(const BigInt& prime1,
+                               const BigInt& prime2,
+                               const BigInt& exp,
+                               const BigInt& d_exp,
+                               const BigInt& mod)
+   {
+   BigInt p = prime1;
+   BigInt q = prime2;
+   BigInt n = mod;
+   if(n.is_zero())
+      n = p * q;
+
+   BigInt e = exp;
+
+   BigInt d = d_exp;
+
+   const BigInt p_minus_1 = p - 1;
+   const BigInt q_minus_1 = q - 1;
+
+   if(d.is_zero())
+      {
+      const BigInt phi_n = lcm(p_minus_1, q_minus_1);
+      d = inverse_mod(e, phi_n);
+      }
+
+   BigInt d1 = ct_modulo(d, p_minus_1);
+   BigInt d2 = ct_modulo(d, q_minus_1);
+   BigInt c = inverse_mod(q, p);
+
+   RSA_PublicKey::init(std::move(n), std::move(e));
+
+   RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+                        std::move(d1), std::move(d2), std::move(c));
+   }
+
+/*
+* Create a RSA private key
+*/
+RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng,
+                               size_t bits, size_t exp)
+   {
+   if(bits < 1024)
+      throw Invalid_Argument(algo_name() + ": Can't make a key that is only " +
+                             std::to_string(bits) + " bits long");
+   if(exp < 3 || exp % 2 == 0)
+      throw Invalid_Argument(algo_name() + ": Invalid encryption exponent");
+
+   BigInt n, e, d, p, q, d1, d2, c;
+
+   e = exp;
+
+   const size_t p_bits = (bits + 1) / 2;
+   const size_t q_bits = bits - p_bits;
+
+   do
+      {
+      // TODO could generate primes in thread pool
+      p = generate_rsa_prime(rng, rng, p_bits, e);
+      q = generate_rsa_prime(rng, rng, q_bits, e);
+      n = p * q;
+      } while(n.bits() != bits);
+
+   const BigInt p_minus_1 = p - 1;
+   const BigInt q_minus_1 = q - 1;
+
+   const BigInt phi_n = lcm(p_minus_1, q_minus_1);
+   d = inverse_mod(e, phi_n);
+   d1 = ct_modulo(d, p_minus_1);
+   d2 = ct_modulo(d, q_minus_1);
+   c = inverse_mod(q, p);
+
+   RSA_PublicKey::init(std::move(n), std::move(e));
+
+   RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+                        std::move(d1), std::move(d2), std::move(c));
+   }
+
+/*
+* Check Private RSA Parameters
+*/
+bool RSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+   {
+   if(get_n() < 35 || get_n().is_even() || get_e() < 3 || get_e().is_even())
+      return false;
+
+   if(get_d() < 2 || get_p() < 3 || get_q() < 3)
+      return false;
+
+   if(get_p() * get_q() != get_n())
+      return false;
+
+   if(get_d1() != ct_modulo(get_d(), get_p() - 1))
+      return false;
+   if(get_d2() != ct_modulo(get_d(), get_q() - 1))
+      return false;
+   if(get_c() != inverse_mod(get_q(), get_p()))
+      return false;
+
+   const size_t prob = (strong) ? 128 : 12;
+
+   if(!is_prime(get_p(), rng, prob))
+      return false;
+   if(!is_prime(get_q(), rng, prob))
+      return false;
+
+   if(strong)
+      {
+      if(ct_modulo(get_e() * get_d(), lcm(get_p() - 1, get_q() - 1)) != 1)
+         return false;
+
+      return KeyPair::signature_consistency_check(rng, *this, "EMSA4(SHA-256)");
+      }
+
+   return true;
+   }
+
+namespace {
+
+/**
+* RSA private (decrypt/sign) operation
+*/
+class RSA_Private_Operation
+   {
+   protected:
+      size_t public_modulus_bits() const { return m_public->public_modulus_bits(); }
+      size_t public_modulus_bytes() const { return m_public->public_modulus_bytes(); }
+
+      explicit RSA_Private_Operation(const RSA_PrivateKey& rsa, RandomNumberGenerator& rng) :
+         m_public(rsa.public_data()),
+         m_private(rsa.private_data()),
+         m_blinder(m_public->get_n(), rng,
+                   [this](const BigInt& k) { return m_public->public_op(k); },
+                   [this](const BigInt& k) { return inverse_mod(k, m_public->get_n()); }),
+         m_blinding_bits(64),
+         m_max_d1_bits(m_private->m_p_bits + m_blinding_bits),
+         m_max_d2_bits(m_private->m_q_bits + m_blinding_bits)
+         {
+         }
+
+      secure_vector<uint8_t> raw_op(const uint8_t input[], size_t input_len)
+         {
+         const BigInt input_bn(input, input_len);
+         if(input_bn >= m_public->get_n())
+            throw Invalid_Argument("RSA private op - input is too large");
+
+         // TODO: This should be a function on blinder
+         // BigInt Blinder::run_blinded_function(std::function<BigInt, BigInt> fn, const BigInt& input);
+
+         const BigInt recovered = m_blinder.unblind(rsa_private_op(m_blinder.blind(input_bn)));
+         BOTAN_ASSERT(input_bn == m_public->public_op(recovered), "RSA consistency check");
+         return BigInt::encode_1363(recovered, m_public->public_modulus_bytes());
+         }
+
+   private:
+
+      BigInt rsa_private_op(const BigInt& m) const
+         {
+         /*
+         TODO
+         Consider using Montgomery reduction instead of Barrett, using
+         the "Smooth RSA-CRT" method. https://eprint.iacr.org/2007/039.pdf
+         */
+
+         static constexpr size_t powm_window = 4;
+
+         // Compute this in main thread to avoid racing on the rng
+         const BigInt d1_mask(m_blinder.rng(), m_blinding_bits);
+
+#if defined(BOTAN_HAS_THREAD_UTILS) && !defined(BOTAN_HAS_VALGRIND)
+   #define BOTAN_RSA_USE_ASYNC
+#endif
+
+#if defined(BOTAN_RSA_USE_ASYNC)
+         /*
+         * Precompute m.sig_words in the main thread before calling async. Otherwise
+         * the two threads race (during Modular_Reducer::reduce) and while the output
+         * is correct in both threads, helgrind warns.
+         */
+         m.sig_words();
+
+         auto future_j1 = Thread_Pool::global_instance().run([this, &m, &d1_mask]() {
+#endif
+            const BigInt masked_d1 = m_private->get_d1() + (d1_mask * (m_private->get_p() - 1));
+            auto powm_d1_p = monty_precompute(m_private->m_monty_p, m_private->m_mod_p.reduce(m), powm_window);
+            BigInt j1 = monty_execute(*powm_d1_p, masked_d1, m_max_d1_bits);
+
+#if defined(BOTAN_RSA_USE_ASYNC)
+         return j1;
+         });
+#endif
+
+         const BigInt d2_mask(m_blinder.rng(), m_blinding_bits);
+         const BigInt masked_d2 = m_private->get_d2() + (d2_mask * (m_private->get_q() - 1));
+         auto powm_d2_q = monty_precompute(m_private->m_monty_q, m_private->m_mod_q.reduce(m), powm_window);
+         const BigInt j2 = monty_execute(*powm_d2_q, masked_d2, m_max_d2_bits);
+
+#if defined(BOTAN_RSA_USE_ASYNC)
+         BigInt j1 = future_j1.get();
+#endif
+
+         /*
+         * To recover the final value from the CRT representation (j1,j2)
+         * we use Garner's algorithm:
+         * c = q^-1 mod p (this is precomputed)
+         * h = c*(j1-j2) mod p
+         * m = j2 + h*q
+         *
+         * We must avoid leaking if j1 >= j2 or not, as doing so allows deriving
+         * information about the secret prime. Do this by first adding p to j1,
+         * which should ensure the subtraction of j2 does not underflow. But
+         * this may still underflow if p and q are imbalanced in size.
+         */
+
+         j1 = m_private->m_mod_p.multiply(m_private->m_mod_p.reduce((m_private->get_p() + j1) - j2), m_private->get_c());
+         return mul_add(j1, m_private->get_q(), j2);
+         }
+
+      std::shared_ptr<const RSA_Public_Data> m_public;
+      std::shared_ptr<const RSA_Private_Data> m_private;
+
+      // XXX could the blinder starting pair be shared?
+      Blinder m_blinder;
+      const size_t m_blinding_bits;
+      const size_t m_max_d1_bits;
+      const size_t m_max_d2_bits;
+   };
+
+class RSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA,
+                                      private RSA_Private_Operation
+   {
+   public:
+      size_t max_input_bits() const override { return public_modulus_bits() - 1; }
+
+      size_t signature_length() const override { return public_modulus_bytes(); }
+
+      RSA_Signature_Operation(const RSA_PrivateKey& rsa, const std::string& emsa, RandomNumberGenerator& rng) :
+         PK_Ops::Signature_with_EMSA(emsa),
+         RSA_Private_Operation(rsa, rng)
+         {
+         }
+
+      secure_vector<uint8_t> raw_sign(const uint8_t input[], size_t input_len,
+                                      RandomNumberGenerator&) override
+         {
+         return raw_op(input, input_len);
+         }
+   };
+
+class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_EME,
+                                       private RSA_Private_Operation
+   {
+   public:
+
+      RSA_Decryption_Operation(const RSA_PrivateKey& rsa, const std::string& eme, RandomNumberGenerator& rng) :
+         PK_Ops::Decryption_with_EME(eme),
+         RSA_Private_Operation(rsa, rng)
+         {
+         }
+
+      size_t plaintext_length(size_t) const override { return public_modulus_bytes(); }
+
+      secure_vector<uint8_t> raw_decrypt(const uint8_t input[], size_t input_len) override
+         {
+         return raw_op(input, input_len);
+         }
+   };
+
+class RSA_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF,
+                                           private RSA_Private_Operation
+   {
+   public:
+
+      RSA_KEM_Decryption_Operation(const RSA_PrivateKey& key,
+                                   const std::string& kdf,
+                                   RandomNumberGenerator& rng) :
+         PK_Ops::KEM_Decryption_with_KDF(kdf),
+         RSA_Private_Operation(key, rng)
+         {}
+
+      secure_vector<uint8_t>
+      raw_kem_decrypt(const uint8_t encap_key[], size_t len) override
+         {
+         return raw_op(encap_key, len);
+         }
+   };
+
+/**
+* RSA public (encrypt/verify) operation
+*/
+class RSA_Public_Operation
+   {
+   public:
+      explicit RSA_Public_Operation(const RSA_PublicKey& rsa) :
+         m_public(rsa.public_data())
+         {}
+
+         size_t get_max_input_bits() const
+         {
+         const size_t n_bits = m_public->public_modulus_bits();
+
+         /*
+         Make Coverity happy that n_bits - 1 won't underflow
+
+         5 bit minimum: smallest possible RSA key is 3*5
+         */
+         BOTAN_ASSERT_NOMSG(n_bits >= 5);
+         return n_bits - 1;
+         }
+
+   protected:
+      BigInt public_op(const BigInt& m) const
+         {
+         if(m >= m_public->get_n())
+            throw Invalid_Argument("RSA public op - input is too large");
+
+         return m_public->public_op(m);
+         }
+
+      size_t public_modulus_bytes() const { return m_public->public_modulus_bytes(); }
+
+      const BigInt& get_n() const { return m_public->get_n(); }
+
+      std::shared_ptr<const RSA_Public_Data> m_public;
+   };
+
+class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_EME,
+                                       private RSA_Public_Operation
+   {
+   public:
+
+      RSA_Encryption_Operation(const RSA_PublicKey& rsa, const std::string& eme) :
+         PK_Ops::Encryption_with_EME(eme),
+         RSA_Public_Operation(rsa)
+         {
+         }
+
+      size_t ciphertext_length(size_t) const override { return public_modulus_bytes(); }
+
+      size_t max_raw_input_bits() const override { return get_max_input_bits(); }
+
+      secure_vector<uint8_t> raw_encrypt(const uint8_t input[], size_t input_len,
+                                         RandomNumberGenerator&) override
+         {
+         BigInt input_bn(input, input_len);
+         return BigInt::encode_1363(public_op(input_bn), public_modulus_bytes());
+         }
+   };
+
+class RSA_Verify_Operation final : public PK_Ops::Verification_with_EMSA,
+                                   private RSA_Public_Operation
+   {
+   public:
+
+      size_t max_input_bits() const override { return get_max_input_bits(); }
+
+      RSA_Verify_Operation(const RSA_PublicKey& rsa, const std::string& emsa) :
+         PK_Ops::Verification_with_EMSA(emsa),
+         RSA_Public_Operation(rsa)
+         {
+         }
+
+      bool with_recovery() const override { return true; }
+
+      secure_vector<uint8_t> verify_mr(const uint8_t input[], size_t input_len) override
+         {
+         BigInt input_bn(input, input_len);
+         return BigInt::encode_locked(public_op(input_bn));
+         }
+   };
+
+class RSA_KEM_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF,
+                                           private RSA_Public_Operation
+   {
+   public:
+
+      RSA_KEM_Encryption_Operation(const RSA_PublicKey& key,
+                                   const std::string& kdf) :
+         PK_Ops::KEM_Encryption_with_KDF(kdf),
+         RSA_Public_Operation(key) {}
+
+   private:
+      void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                           secure_vector<uint8_t>& raw_shared_key,
+                           Botan::RandomNumberGenerator& rng) override
+         {
+         const BigInt r = BigInt::random_integer(rng, 1, get_n());
+         const BigInt c = public_op(r);
+
+         out_encapsulated_key = BigInt::encode_locked(c);
+         raw_shared_key = BigInt::encode_locked(r);
+         }
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+RSA_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+                                    const std::string& params,
+                                    const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      try
+         {
+         return make_openssl_rsa_enc_op(*this, params);
+         }
+      catch(Exception& e)
+         {
+         /*
+         * If OpenSSL for some reason could not handle this (eg due to OAEP params),
+         * throw if openssl was specifically requested but otherwise just fall back
+         * to the normal version.
+         */
+         if(provider == "openssl")
+            throw Lookup_Error("OpenSSL RSA provider rejected key:" + std::string(e.what()));
+         }
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Encryption>(new RSA_Encryption_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+RSA_PublicKey::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+                                        const std::string& params,
+                                        const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::KEM_Encryption>(new RSA_KEM_Encryption_Operation(*this, params));
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Verification>
+RSA_PublicKey::create_verification_op(const std::string& params,
+                                      const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      std::unique_ptr<PK_Ops::Verification> res = make_openssl_rsa_ver_op(*this, params);
+      if(res)
+         return res;
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Verification>(new RSA_Verify_Operation(*this, params));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+                                     const std::string& params,
+                                     const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      try
+         {
+         return make_openssl_rsa_dec_op(*this, params);
+         }
+      catch(Exception& e)
+         {
+         if(provider == "openssl")
+            throw Lookup_Error("OpenSSL RSA provider rejected key:" + std::string(e.what()));
+         }
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Decryption>(new RSA_Decryption_Operation(*this, params, rng));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+RSA_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& rng,
+                                         const std::string& params,
+                                         const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::KEM_Decryption>(new RSA_KEM_Decryption_Operation(*this, params, rng));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+RSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+                                    const std::string& params,
+                                    const std::string& provider) const
+   {
+#if defined(BOTAN_HAS_OPENSSL)
+   if(provider == "openssl" || provider.empty())
+      {
+      std::unique_ptr<PK_Ops::Signature> res = make_openssl_rsa_sig_op(*this, params);
+      if(res)
+         return res;
+      }
+#endif
+
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(new RSA_Signature_Operation(*this, params, rng));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/rsa/rsa.h b/third_party/botan/src/lib/pubkey/rsa/rsa.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/rsa/rsa.h
@@ -0,0 +1,180 @@
+/*
+* RSA
+* (C) 1999-2008,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RSA_H_
+#define BOTAN_RSA_H_
+
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+class RSA_Public_Data;
+class RSA_Private_Data;
+
+/**
+* RSA Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKey : public virtual Public_Key
+   {
+   public:
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      RSA_PublicKey(const AlgorithmIdentifier& alg_id,
+                    const std::vector<uint8_t>& key_bits);
+
+      /**
+      * Create a public key.
+      * @arg n the modulus
+      * @arg e the exponent
+      */
+      RSA_PublicKey(const BigInt& n, const BigInt& e);
+
+      std::string algo_name() const override { return "RSA"; }
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      AlgorithmIdentifier algorithm_identifier() const override;
+
+      std::vector<uint8_t> public_key_bits() const override;
+
+      /**
+      * @return public modulus
+      */
+      const BigInt& get_n() const;
+
+      /**
+      * @return public exponent
+      */
+      const BigInt& get_e() const;
+
+      size_t key_length() const override;
+      size_t estimated_strength() const override;
+
+      // internal functions:
+      std::shared_ptr<const RSA_Public_Data> public_data() const;
+
+      std::unique_ptr<PK_Ops::Encryption>
+         create_encryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::KEM_Encryption>
+         create_kem_encryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+
+   protected:
+      RSA_PublicKey() = default;
+
+      void init(BigInt&& n, BigInt&& e);
+
+      std::shared_ptr<const RSA_Public_Data> m_public;
+   };
+
+/**
+* RSA Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKey final : public Private_Key, public RSA_PublicKey
+   {
+   public:
+      /**
+      * Load a private key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits PKCS#1 RSAPrivateKey bits
+      */
+      RSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+                     const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Construct a private key from the specified parameters.
+      * @param p the first prime
+      * @param q the second prime
+      * @param e the exponent
+      * @param d if specified, this has to be d with
+      * exp * d = 1 mod (p - 1, q - 1). Leave it as 0 if you wish to
+      * the constructor to calculate it.
+      * @param n if specified, this must be n = p * q. Leave it as 0
+      * if you wish to the constructor to calculate it.
+      */
+      RSA_PrivateKey(const BigInt& p, const BigInt& q,
+                     const BigInt& e, const BigInt& d = 0,
+                     const BigInt& n = 0);
+
+      /**
+      * Create a new private key with the specified bit length
+      * @param rng the random number generator to use
+      * @param bits the desired bit length of the private key
+      * @param exp the public exponent to be used
+      */
+      RSA_PrivateKey(RandomNumberGenerator& rng,
+                     size_t bits, size_t exp = 65537);
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      /**
+      * Get the first prime p.
+      * @return prime p
+      */
+      const BigInt& get_p() const;
+
+      /**
+      * Get the second prime q.
+      * @return prime q
+      */
+      const BigInt& get_q() const;
+
+      /**
+      * Get d with exp * d = 1 mod (p - 1, q - 1).
+      * @return d
+      */
+      const BigInt& get_d() const;
+
+      const BigInt& get_c() const;
+      const BigInt& get_d1() const;
+      const BigInt& get_d2() const;
+
+      secure_vector<uint8_t> private_key_bits() const override;
+
+      // internal functions:
+      std::shared_ptr<const RSA_Private_Data> private_data() const;
+
+      std::unique_ptr<PK_Ops::Decryption>
+         create_decryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::KEM_Decryption>
+         create_kem_decryption_op(RandomNumberGenerator& rng,
+                                  const std::string& params,
+                                  const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+
+   private:
+
+      void init(BigInt&& d, BigInt&& p, BigInt&& q, BigInt&& d1, BigInt&& d2, BigInt&& c);
+
+      std::shared_ptr<const RSA_Private_Data> m_private;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/sm2/sm2.cpp b/third_party/botan/src/lib/pubkey/sm2/sm2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/sm2/sm2.cpp
@@ -0,0 +1,306 @@
+/*
+* SM2 Signatures
+* (C) 2017,2018 Ribose Inc
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm2.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/loadstor.h>
+#include <botan/numthry.h>
+#include <botan/keypair.h>
+#include <botan/hash.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+std::string SM2_PublicKey::algo_name() const
+   {
+   return "SM2";
+   }
+
+bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng,
+                               bool strong) const
+   {
+   if(!public_point().on_the_curve())
+      return false;
+
+   if(!strong)
+      return true;
+
+   return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
+   }
+
+SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id,
+                               const secure_vector<uint8_t>& key_bits) :
+   EC_PrivateKey(alg_id, key_bits)
+   {
+   m_da_inv = domain().inverse_mod_order(m_private_key + 1);
+   }
+
+SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng,
+                               const EC_Group& domain,
+                               const BigInt& x) :
+   EC_PrivateKey(rng, domain, x)
+   {
+   m_da_inv = domain.inverse_mod_order(m_private_key + 1);
+   }
+
+std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
+                                    const std::string& user_id,
+                                    const EC_Group& domain,
+                                    const PointGFp& pubkey)
+   {
+   if(user_id.size() >= 8192)
+      throw Invalid_Argument("SM2 user id too long to represent");
+
+   const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
+
+   hash.update(get_byte(0, uid_len));
+   hash.update(get_byte(1, uid_len));
+   hash.update(user_id);
+
+   const size_t p_bytes = domain.get_p_bytes();
+
+   hash.update(BigInt::encode_1363(domain.get_a(), p_bytes));
+   hash.update(BigInt::encode_1363(domain.get_b(), p_bytes));
+   hash.update(BigInt::encode_1363(domain.get_g_x(), p_bytes));
+   hash.update(BigInt::encode_1363(domain.get_g_y(), p_bytes));
+   hash.update(BigInt::encode_1363(pubkey.get_affine_x(), p_bytes));
+   hash.update(BigInt::encode_1363(pubkey.get_affine_y(), p_bytes));
+
+   std::vector<uint8_t> za(hash.output_length());
+   hash.final(za.data());
+
+   return za;
+   }
+
+namespace {
+
+/**
+* SM2 signature operation
+*/
+class SM2_Signature_Operation final : public PK_Ops::Signature
+   {
+   public:
+
+      SM2_Signature_Operation(const SM2_PrivateKey& sm2,
+                              const std::string& ident,
+                              const std::string& hash) :
+         m_group(sm2.domain()),
+         m_x(sm2.private_value()),
+         m_da_inv(sm2.get_da_inv())
+         {
+         if(hash == "Raw")
+            {
+            // m_hash is null, m_za is empty
+            }
+         else
+            {
+            m_hash = HashFunction::create_or_throw(hash);
+            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
+            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2.public_point());
+            m_hash->update(m_za);
+            }
+         }
+
+      size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(m_hash)
+            {
+            m_hash->update(msg, msg_len);
+            }
+         else
+            {
+            m_digest.insert(m_digest.end(), msg, msg + msg_len);
+            }
+         }
+
+      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+
+   private:
+      const EC_Group m_group;
+      const BigInt& m_x;
+      const BigInt& m_da_inv;
+
+      std::vector<uint8_t> m_za;
+      secure_vector<uint8_t> m_digest;
+      std::unique_ptr<HashFunction> m_hash;
+      std::vector<BigInt> m_ws;
+   };
+
+secure_vector<uint8_t>
+SM2_Signature_Operation::sign(RandomNumberGenerator& rng)
+   {
+   BigInt e;
+   if(m_hash)
+      {
+      e = BigInt::decode(m_hash->final());
+      // prepend ZA for next signature if any
+      m_hash->update(m_za);
+      }
+   else
+      {
+      e = BigInt::decode(m_digest);
+      m_digest.clear();
+      }
+
+   const BigInt k = m_group.random_scalar(rng);
+
+   const BigInt r = m_group.mod_order(
+      m_group.blinded_base_point_multiply_x(k, rng, m_ws) + e);
+   const BigInt s = m_group.multiply_mod_order(m_da_inv, m_group.mod_order(k - r*m_x));
+
+   return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order().bytes());
+   }
+
+/**
+* SM2 verification operation
+*/
+class SM2_Verification_Operation final : public PK_Ops::Verification
+   {
+   public:
+      SM2_Verification_Operation(const SM2_PublicKey& sm2,
+                                 const std::string& ident,
+                                 const std::string& hash) :
+         m_group(sm2.domain()),
+         m_gy_mul(m_group.get_base_point(), sm2.public_point())
+         {
+         if(hash == "Raw")
+            {
+            // m_hash is null, m_za is empty
+            }
+         else
+            {
+            m_hash = HashFunction::create_or_throw(hash);
+            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
+            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2.public_point());
+            m_hash->update(m_za);
+            }
+         }
+
+      void update(const uint8_t msg[], size_t msg_len) override
+         {
+         if(m_hash)
+            {
+            m_hash->update(msg, msg_len);
+            }
+         else
+            {
+            m_digest.insert(m_digest.end(), msg, msg + msg_len);
+            }
+         }
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+   private:
+      const EC_Group m_group;
+      const PointGFp_Multi_Point_Precompute m_gy_mul;
+      secure_vector<uint8_t> m_digest;
+      std::vector<uint8_t> m_za;
+      std::unique_ptr<HashFunction> m_hash;
+   };
+
+bool SM2_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len)
+   {
+   BigInt e;
+   if(m_hash)
+      {
+      e = BigInt::decode(m_hash->final());
+      // prepend ZA for next signature if any
+      m_hash->update(m_za);
+      }
+   else
+      {
+      e = BigInt::decode(m_digest);
+      m_digest.clear();
+      }
+
+   if(sig_len != m_group.get_order().bytes()*2)
+      return false;
+
+   const BigInt r(sig, sig_len / 2);
+   const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+   if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+      return false;
+
+   const BigInt t = m_group.mod_order(r + s);
+
+   if(t == 0)
+      return false;
+
+   const PointGFp R = m_gy_mul.multi_exp(s, t);
+
+   // ???
+   if(R.is_zero())
+      return false;
+
+   return (m_group.mod_order(R.get_affine_x() + e) == r);
+   }
+
+void parse_sm2_param_string(const std::string& params,
+                            std::string& userid,
+                            std::string& hash)
+   {
+   // GM/T 0009-2012 specifies this as the default userid
+   const std::string default_userid = "1234567812345678";
+
+   // defaults:
+   userid = default_userid;
+   hash = "SM3";
+
+   /*
+   * SM2 parameters have the following possible formats:
+   * Ident [since 2.2.0]
+   * Ident,Hash [since 2.3.0]
+   */
+
+   auto comma = params.find(',');
+   if(comma == std::string::npos)
+      {
+      userid = params;
+      }
+   else
+      {
+      userid = params.substr(0, comma);
+      hash = params.substr(comma+1, std::string::npos);
+      }
+   }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+SM2_PublicKey::create_verification_op(const std::string& params,
+                                      const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      std::string userid, hash;
+      parse_sm2_param_string(params, userid, hash);
+      return std::unique_ptr<PK_Ops::Verification>(new SM2_Verification_Operation(*this, userid, hash));
+      }
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+                                    const std::string& params,
+                                    const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      std::string userid, hash;
+      parse_sm2_param_string(params, userid, hash);
+      return std::unique_ptr<PK_Ops::Signature>(new SM2_Signature_Operation(*this, userid, hash));
+      }
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/sm2/sm2.h b/third_party/botan/src/lib/pubkey/sm2/sm2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/sm2/sm2.h
@@ -0,0 +1,124 @@
+/*
+* SM2
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM2_KEY_H_
+#define BOTAN_SM2_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents SM2 public keys
+*/
+class BOTAN_PUBLIC_API(2,2) SM2_PublicKey : public virtual EC_PublicKey
+   {
+   public:
+
+      /**
+      * Create a public key from a given public point.
+      * @param dom_par the domain parameters associated with this key
+      * @param public_point the public point defining this key
+      */
+      SM2_PublicKey(const EC_Group& dom_par,
+                    const PointGFp& public_point) :
+         EC_PublicKey(dom_par, public_point) {}
+
+      /**
+      * Load a public key.
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits DER encoded public key bits
+      */
+      SM2_PublicKey(const AlgorithmIdentifier& alg_id,
+                    const std::vector<uint8_t>& key_bits) :
+         EC_PublicKey(alg_id, key_bits) {}
+
+      /**
+      * Get this keys algorithm name.
+      * @result this keys algorithm name
+      */
+      std::string algo_name() const override;
+
+      size_t message_parts() const override { return 2; }
+
+      size_t message_part_size() const override
+         { return domain().get_order().bytes(); }
+
+      std::unique_ptr<PK_Ops::Verification>
+         create_verification_op(const std::string& params,
+                                const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Encryption>
+         create_encryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+   protected:
+      SM2_PublicKey() = default;
+   };
+
+/**
+* This class represents SM2 private keys
+*/
+class BOTAN_PUBLIC_API(2,2) SM2_PrivateKey final :
+   public SM2_PublicKey, public EC_PrivateKey
+   {
+   public:
+
+      /**
+      * Load a private key
+      * @param alg_id the X.509 algorithm identifier
+      * @param key_bits ECPrivateKey bits
+      */
+      SM2_PrivateKey(const AlgorithmIdentifier& alg_id,
+                     const secure_vector<uint8_t>& key_bits);
+
+      /**
+      * Create a private key.
+      * @param rng a random number generator
+      * @param domain parameters to used for this key
+      * @param x the private key (if zero, generate a new random key)
+      */
+      SM2_PrivateKey(RandomNumberGenerator& rng,
+                     const EC_Group& domain,
+                     const BigInt& x = 0);
+
+      bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+      std::unique_ptr<PK_Ops::Signature>
+         create_signature_op(RandomNumberGenerator& rng,
+                             const std::string& params,
+                             const std::string& provider) const override;
+
+      std::unique_ptr<PK_Ops::Decryption>
+         create_decryption_op(RandomNumberGenerator& rng,
+                              const std::string& params,
+                              const std::string& provider) const override;
+
+      const BigInt& get_da_inv() const { return m_da_inv; }
+   private:
+      BigInt m_da_inv;
+   };
+
+class HashFunction;
+
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,5) sm2_compute_za(HashFunction& hash,
+                                     const std::string& user_id,
+                                     const EC_Group& domain,
+                                     const PointGFp& pubkey);
+
+// For compat with versions 2.2 - 2.7
+typedef SM2_PublicKey SM2_Signature_PublicKey;
+typedef SM2_PublicKey SM2_Encryption_PublicKey;
+
+typedef SM2_PrivateKey SM2_Signature_PrivateKey;
+typedef SM2_PrivateKey SM2_Encryption_PrivateKey;
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp b/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -0,0 +1,267 @@
+/*
+* SM2 Encryption
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm2.h>
+#include <botan/internal/point_mul.h>
+#include <botan/pk_ops.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+class SM2_Encryption_Operation final : public PK_Ops::Encryption
+   {
+   public:
+      SM2_Encryption_Operation(const SM2_Encryption_PublicKey& key,
+                               RandomNumberGenerator& rng,
+                               const std::string& kdf_hash) :
+         m_group(key.domain()),
+         m_kdf_hash(kdf_hash),
+         m_ws(PointGFp::WORKSPACE_SIZE),
+         m_mul_public_point(key.public_point(), rng, m_ws)
+         {
+         std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+         m_hash_size = hash->output_length();
+         }
+
+      size_t max_input_bits() const override
+         {
+         // This is arbitrary, but assumes SM2 is used for key encapsulation
+         return 512;
+         }
+
+      size_t ciphertext_length(size_t ptext_len) const override
+         {
+         const size_t elem_size = m_group.get_order_bytes();
+         const size_t der_overhead = 16;
+
+         return der_overhead + 2*elem_size + m_hash_size + ptext_len;
+         }
+
+      secure_vector<uint8_t> encrypt(const uint8_t msg[],
+                                     size_t msg_len,
+                                     RandomNumberGenerator& rng) override
+         {
+         std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+         std::unique_ptr<KDF> kdf = KDF::create_or_throw("KDF2(" + m_kdf_hash + ")");
+
+         const size_t p_bytes = m_group.get_p_bytes();
+
+         const BigInt k = m_group.random_scalar(rng);
+
+         const PointGFp C1 = m_group.blinded_base_point_multiply(k, rng, m_ws);
+         const BigInt x1 = C1.get_affine_x();
+         const BigInt y1 = C1.get_affine_y();
+         std::vector<uint8_t> x1_bytes(p_bytes);
+         std::vector<uint8_t> y1_bytes(p_bytes);
+         BigInt::encode_1363(x1_bytes.data(), x1_bytes.size(), x1);
+         BigInt::encode_1363(y1_bytes.data(), y1_bytes.size(), y1);
+
+         const PointGFp kPB = m_mul_public_point.mul(k, rng, m_group.get_order(), m_ws);
+
+         const BigInt x2 = kPB.get_affine_x();
+         const BigInt y2 = kPB.get_affine_y();
+         std::vector<uint8_t> x2_bytes(p_bytes);
+         std::vector<uint8_t> y2_bytes(p_bytes);
+         BigInt::encode_1363(x2_bytes.data(), x2_bytes.size(), x2);
+         BigInt::encode_1363(y2_bytes.data(), y2_bytes.size(), y2);
+
+         secure_vector<uint8_t> kdf_input;
+         kdf_input += x2_bytes;
+         kdf_input += y2_bytes;
+
+         const secure_vector<uint8_t> kdf_output =
+            kdf->derive_key(msg_len, kdf_input.data(), kdf_input.size());
+
+         secure_vector<uint8_t> masked_msg(msg_len);
+         xor_buf(masked_msg.data(), msg, kdf_output.data(), msg_len);
+
+         hash->update(x2_bytes);
+         hash->update(msg, msg_len);
+         hash->update(y2_bytes);
+         std::vector<uint8_t> C3(hash->output_length());
+         hash->final(C3.data());
+
+         return DER_Encoder()
+            .start_cons(SEQUENCE)
+            .encode(x1)
+            .encode(y1)
+            .encode(C3, OCTET_STRING)
+            .encode(masked_msg, OCTET_STRING)
+            .end_cons()
+            .get_contents();
+         }
+
+   private:
+      const EC_Group m_group;
+      const std::string m_kdf_hash;
+
+      std::vector<BigInt> m_ws;
+      PointGFp_Var_Point_Precompute m_mul_public_point;
+      size_t m_hash_size;
+   };
+
+class SM2_Decryption_Operation final : public PK_Ops::Decryption
+   {
+   public:
+      SM2_Decryption_Operation(const SM2_Encryption_PrivateKey& key,
+                               RandomNumberGenerator& rng,
+                               const std::string& kdf_hash) :
+         m_key(key),
+         m_rng(rng),
+         m_kdf_hash(kdf_hash)
+         {
+         std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+         m_hash_size = hash->output_length();
+         }
+
+      size_t plaintext_length(size_t ptext_len) const override
+         {
+         /*
+         * This ignores the DER encoding and so overestimates the
+         * plaintext length by 12 bytes or so
+         */
+         const size_t elem_size = m_key.domain().get_order_bytes();
+
+         if(ptext_len < 2*elem_size + m_hash_size)
+            return 0;
+
+         return ptext_len - (2*elem_size + m_hash_size);
+         }
+
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+                                     const uint8_t ciphertext[],
+                                     size_t ciphertext_len) override
+         {
+         const EC_Group& group = m_key.domain();
+         const BigInt& cofactor = group.get_cofactor();
+         const size_t p_bytes = group.get_p_bytes();
+
+         valid_mask = 0x00;
+
+         std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+         std::unique_ptr<KDF> kdf = KDF::create_or_throw("KDF2(" + m_kdf_hash + ")");
+
+         // Too short to be valid - no timing problem from early return
+         if(ciphertext_len < 1 + p_bytes*2 + hash->output_length())
+            {
+            return secure_vector<uint8_t>();
+            }
+
+         BigInt x1, y1;
+         secure_vector<uint8_t> C3, masked_msg;
+
+         BER_Decoder(ciphertext, ciphertext_len)
+            .start_cons(SEQUENCE)
+            .decode(x1)
+            .decode(y1)
+            .decode(C3, OCTET_STRING)
+            .decode(masked_msg, OCTET_STRING)
+            .end_cons()
+            .verify_end();
+
+         std::vector<uint8_t> recode_ctext;
+         DER_Encoder(recode_ctext)
+            .start_cons(SEQUENCE)
+            .encode(x1)
+            .encode(y1)
+            .encode(C3, OCTET_STRING)
+            .encode(masked_msg, OCTET_STRING)
+            .end_cons();
+
+         if(recode_ctext.size() != ciphertext_len)
+            return secure_vector<uint8_t>();
+
+         if(same_mem(recode_ctext.data(), ciphertext, ciphertext_len) == false)
+            return secure_vector<uint8_t>();
+
+         PointGFp C1 = group.point(x1, y1);
+         C1.randomize_repr(m_rng);
+
+         // Here C1 is publically invalid, so no problem with early return:
+         if(!C1.on_the_curve())
+            return secure_vector<uint8_t>();
+
+         if(cofactor > 1 && (C1 * cofactor).is_zero())
+            {
+            return secure_vector<uint8_t>();
+            }
+
+         const PointGFp dbC1 = group.blinded_var_point_multiply(
+            C1, m_key.private_value(), m_rng, m_ws);
+
+         const BigInt x2 = dbC1.get_affine_x();
+         const BigInt y2 = dbC1.get_affine_y();
+
+         secure_vector<uint8_t> x2_bytes(p_bytes);
+         secure_vector<uint8_t> y2_bytes(p_bytes);
+         BigInt::encode_1363(x2_bytes.data(), x2_bytes.size(), x2);
+         BigInt::encode_1363(y2_bytes.data(), y2_bytes.size(), y2);
+
+         secure_vector<uint8_t> kdf_input;
+         kdf_input += x2_bytes;
+         kdf_input += y2_bytes;
+
+         const secure_vector<uint8_t> kdf_output =
+            kdf->derive_key(masked_msg.size(), kdf_input.data(), kdf_input.size());
+
+         xor_buf(masked_msg.data(), kdf_output.data(), kdf_output.size());
+
+         hash->update(x2_bytes);
+         hash->update(masked_msg);
+         hash->update(y2_bytes);
+         secure_vector<uint8_t> u = hash->final();
+
+         if(constant_time_compare(u.data(), C3.data(), hash->output_length()) == false)
+            return secure_vector<uint8_t>();
+
+         valid_mask = 0xFF;
+         return masked_msg;
+         }
+   private:
+      const SM2_Encryption_PrivateKey& m_key;
+      RandomNumberGenerator& m_rng;
+      const std::string m_kdf_hash;
+      std::vector<BigInt> m_ws;
+      size_t m_hash_size;
+   };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+SM2_PublicKey::create_encryption_op(RandomNumberGenerator& rng,
+                                    const std::string& params,
+                                    const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      const std::string kdf_hash = (params.empty() ? "SM3" : params);
+      return std::unique_ptr<PK_Ops::Encryption>(new SM2_Encryption_Operation(*this, rng, kdf_hash));
+      }
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::unique_ptr<PK_Ops::Decryption>
+SM2_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+                                     const std::string& params,
+                                     const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      const std::string kdf_hash = (params.empty() ? "SM3" : params);
+      return std::unique_ptr<PK_Ops::Decryption>(new SM2_Decryption_Operation(*this, rng, kdf_hash));
+      }
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h b/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h
@@ -0,0 +1,15 @@
+/*
+* SM2 Encryption
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM2_ENC_KEY_H_
+#define BOTAN_SM2_ENC_KEY_H_
+
+#include <botan/sm2.h>
+
+BOTAN_DEPRECATED_HEADER(sm2_enc.h)
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/workfactor.cpp b/third_party/botan/src/lib/pubkey/workfactor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/workfactor.cpp
@@ -0,0 +1,66 @@
+/*
+* Public Key Work Factor Functions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/workfactor.h>
+#include <algorithm>
+#include <cmath>
+
+namespace Botan {
+
+size_t ecp_work_factor(size_t bits)
+   {
+   return bits / 2;
+   }
+
+namespace {
+
+size_t nfs_workfactor(size_t bits, double log2_k)
+   {
+   // approximates natural logarithm of an integer of given bitsize
+   const double log2_e = 1.44269504088896340736;
+   const double log_p = bits / log2_e;
+
+   const double log_log_p = std::log(log_p);
+
+   // RFC 3766: k * e^((1.92 + o(1)) * cubrt(ln(n) * (ln(ln(n)))^2))
+   const double est = 1.92 * std::pow(log_p * log_log_p * log_log_p, 1.0/3.0);
+
+   // return log2 of the workfactor
+   return static_cast<size_t>(log2_k + log2_e * est);
+   }
+
+}
+
+size_t if_work_factor(size_t bits)
+   {
+   // RFC 3766 estimates k at .02 and o(1) to be effectively zero for sizes of interest
+
+   const double log2_k = -5.6438; // log2(.02)
+   return nfs_workfactor(bits, log2_k);
+   }
+
+size_t dl_work_factor(size_t bits)
+   {
+   // Lacking better estimates...
+   return if_work_factor(bits);
+   }
+
+size_t dl_exponent_size(size_t bits)
+   {
+   /*
+   This uses a slightly tweaked version of the standard work factor
+   function above. It assumes k is 1 (thus overestimating the strength
+   of the prime group by 5-6 bits), and always returns at least 128 bits
+   (this only matters for very small primes).
+   */
+   const size_t min_workfactor = 64;
+   const double log2_k = 0;
+
+   return 2 * std::max<size_t>(min_workfactor, nfs_workfactor(bits, log2_k));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/workfactor.h b/third_party/botan/src/lib/pubkey/workfactor.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/workfactor.h
@@ -0,0 +1,50 @@
+/*
+* Public Key Work Factor Functions
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WORKFACTOR_H_
+#define BOTAN_WORKFACTOR_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Estimate work factor for discrete logarithm
+* @param prime_group_size size of the group in bits
+* @return estimated security level for this group
+*/
+BOTAN_PUBLIC_API(2,0) size_t dl_work_factor(size_t prime_group_size);
+
+/**
+* Return the appropriate exponent size to use for a particular prime
+* group. This is twice the size of the estimated cost of breaking the
+* key using an index calculus attack; the assumption is that if an
+* arbitrary discrete log on a group of size bits would take about 2^n
+* effort, and thus using an exponent of size 2^(2*n) implies that all
+* available attacks are about as easy (as e.g Pollard's kangaroo
+* algorithm can compute the DL in sqrt(x) operations) while minimizing
+* the exponent size for performance reasons.
+*/
+BOTAN_PUBLIC_API(2,0) size_t dl_exponent_size(size_t prime_group_size);
+
+/**
+* Estimate work factor for integer factorization
+* @param n_bits size of modulus in bits
+* @return estimated security level for this modulus
+*/
+BOTAN_PUBLIC_API(2,0) size_t if_work_factor(size_t n_bits);
+
+/**
+* Estimate work factor for EC discrete logarithm
+* @param prime_group_size size of the group in bits
+* @return estimated security level for this group
+*/
+BOTAN_PUBLIC_API(2,0) size_t ecp_work_factor(size_t prime_group_size);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/x509_key.cpp b/third_party/botan/src/lib/pubkey/x509_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/x509_key.cpp
@@ -0,0 +1,106 @@
+/*
+* X.509 Public Key
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_key.h>
+#include <botan/data_src.h>
+#include <botan/ber_dec.h>
+#include <botan/pem.h>
+#include <botan/alg_id.h>
+#include <botan/pk_algs.h>
+
+namespace Botan {
+
+namespace X509 {
+
+std::vector<uint8_t> BER_encode(const Public_Key& key)
+   {
+   // keeping it around for compat
+   return key.subject_public_key();
+   }
+
+/*
+* PEM encode a X.509 public key
+*/
+std::string PEM_encode(const Public_Key& key)
+   {
+   return PEM_Code::encode(key.subject_public_key(),
+                           "PUBLIC KEY");
+   }
+
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(DataSource& source)
+   {
+   try {
+      AlgorithmIdentifier alg_id;
+      std::vector<uint8_t> key_bits;
+
+      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
+         {
+         BER_Decoder(source)
+            .start_cons(SEQUENCE)
+            .decode(alg_id)
+            .decode(key_bits, BIT_STRING)
+         .end_cons();
+         }
+      else
+         {
+         DataSource_Memory ber(
+            PEM_Code::decode_check_label(source, "PUBLIC KEY")
+            );
+
+         BER_Decoder(ber)
+            .start_cons(SEQUENCE)
+            .decode(alg_id)
+            .decode(key_bits, BIT_STRING)
+         .end_cons();
+         }
+
+      if(key_bits.empty())
+         throw Decoding_Error("X.509 public key decoding");
+
+      return load_public_key(alg_id, key_bits).release();
+      }
+   catch(Decoding_Error& e)
+      {
+      throw Decoding_Error("X.509 public key decoding", e);
+      }
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(const std::string& fsname)
+   {
+   DataSource_Stream source(fsname, true);
+   return X509::load_key(source);
+   }
+#endif
+
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(const std::vector<uint8_t>& mem)
+   {
+   DataSource_Memory source(mem);
+   return X509::load_key(source);
+   }
+
+/*
+* Make a copy of this public key
+*/
+Public_Key* copy_key(const Public_Key& key)
+   {
+   DataSource_Memory source(PEM_encode(key));
+   return X509::load_key(source);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/pubkey/x509_key.h b/third_party/botan/src/lib/pubkey/x509_key.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/x509_key.h
@@ -0,0 +1,80 @@
+/*
+* X.509 Public Key
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PUBLIC_KEY_H_
+#define BOTAN_X509_PUBLIC_KEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class DataSource;
+
+/**
+* The two types of X509 encoding supported by Botan.
+* This enum is not used anymore, and will be removed in a future major release.
+*/
+enum X509_Encoding { RAW_BER, PEM };
+
+/**
+* This namespace contains functions for handling X.509 public keys
+*/
+namespace X509 {
+
+/**
+* BER encode a key
+* @param key the public key to encode
+* @return BER encoding of this key
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t> BER_encode(const Public_Key& key);
+
+/**
+* PEM encode a public key into a string.
+* @param key the key to encode
+* @return PEM encoded key
+*/
+BOTAN_PUBLIC_API(2,0) std::string PEM_encode(const Public_Key& key);
+
+/**
+* Create a public key from a data source.
+* @param source the source providing the DER or PEM encoded key
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/**
+* Create a public key from a file
+* @param filename pathname to the file to load
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(const std::string& filename);
+#endif
+
+/**
+* Create a public key from a memory region.
+* @param enc the memory region containing the DER or PEM encoded key
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(const std::vector<uint8_t>& enc);
+
+/**
+* Copy a key.
+* @param key the public key to copy
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* copy_key(const Public_Key& key);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/atomic.h b/third_party/botan/src/lib/pubkey/xmss/atomic.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/atomic.h
@@ -0,0 +1,55 @@
+/*
+ * Atomic
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_ATOMIC_H_
+#define BOTAN_ATOMIC_H_
+
+#include <botan/types.h>
+#include <atomic>
+#include <memory>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(atomic.h)
+
+namespace Botan {
+
+template <typename T>
+/**
+ * Simple helper class to expand std::atomic with copy constructor and copy
+ * assignment operator, i.e. for use as element in a container like
+ * std::vector. The construction of instances of this wrapper is NOT atomic
+ * and needs to be properly guarded.
+ **/
+class Atomic final
+   {
+   public:
+      Atomic() = default;
+      Atomic(const Atomic& data) : m_data(data.m_data.load()) {}
+      Atomic(const std::atomic<T>& data) : m_data(data.load()) {}
+      ~Atomic() = default;
+
+      Atomic& operator=(const Atomic& a)
+         {
+         m_data.store(a.m_data.load());
+         return *this;
+         }
+
+      Atomic& operator=(const std::atomic<T>& a)
+         {
+         m_data.store(a.load());
+         return *this;
+         }
+
+      operator std::atomic<T>& () { return m_data; }
+      operator T() { return m_data.load(); }
+
+   private:
+      std::atomic<T> m_data;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss.h b/third_party/botan/src/lib/pubkey/xmss/xmss.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss.h
@@ -0,0 +1,26 @@
+/**
+ * @file xmss.h
+ * Includes headers required for Extended Hash-Based Signatures (XMSS)
+ * as described in [1]. @see Botan::XMSS_PublicKey and @see
+ * Botan::XMSS_PrivateKey for further
+ * details.
+ *
+ * <pre>
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ * </pre>
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_H_
+#define BOTAN_XMSS_H_
+
+#include <botan/xmss_publickey.h>
+#include <botan/xmss_privatekey.h>
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_address.h b/third_party/botan/src/lib/pubkey/xmss/xmss_address.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_address.h
@@ -0,0 +1,405 @@
+/*
+ * XMSS Address
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_ADDRESS_H_
+#define BOTAN_XMSS_ADDRESS_H_
+
+#include <botan/xmss_tools.h>
+
+namespace Botan {
+
+/**
+ * Generic XMSS Address type holding 256 Bits of data. Properties
+ * of all three address formats L-Tree-Address, Hash-Tree-Address,
+ * OTS-Hash-Address can be called depending on the type currently
+ * assigned to the XMSS address using set_type().
+ **/
+class XMSS_Address final
+   {
+   public:
+      /**
+       * Distinct types an XMSS_Address can represent. The available types
+       * are specified in [1] - 2.5 Hash Function Address Scheme.
+       **/
+      enum class Type : uint8_t
+         {
+         None = 255,
+         OTS_Hash_Address = 0,
+         LTree_Address = 1,
+         Hash_Tree_Address = 2
+         };
+
+      /**
+       * The available modes for an XMSS Address:
+       *   - Key_Mode: Used to generate the key.
+       *   - Mask_Mode: Sets the n-byte bitmask (OTS-Hash-Address)
+       *   - Mask_MSB_Mode: Used to generate the b most significant bytes of
+       *     the 2n-byte bitmask (LTree Address and Hash Tree Address).
+       *   - Mask_LSB_Mode: Used to generated the b least significant bytes
+       *     of the 2n-byte bitmask. (LTree Address and Hash Tree Address).
+       **/
+      enum class Key_Mask : uint8_t
+         {
+         Key_Mode = 0,
+         Mask_Mode = 1,
+         Mask_MSB_Mode = 1,
+         Mask_LSB_Mode = 2
+         };
+
+      /**
+       * Layer Address for XMSS is constantly zero and can not be changed this
+       * property is only of relevance to XMSS_MT.
+       *
+       * @return Layer address, which is constant 0 for XMSS.
+       **/
+      uint8_t get_layer_addr() const { return 0; }
+
+      /**
+       * Layer Address for XMSS is constantly zero and can not be changed this
+       * property is only of relevance to XMSS_MT. Calling this method for
+       * XMSS will result in an error.
+       **/
+      void set_layer_addr()
+         {
+         BOTAN_ASSERT(false, "Only available in XMSS_MT.");
+         }
+
+      /**
+       * Tree Address for XMSS is constantly zero and can not be changed this
+       * property is only of relevance to XMSS_MT.
+       *
+       * @return Tree address, which is constant 0 for XMSS.
+       **/
+      uint64_t get_tree_addr() const { return 0; }
+
+      /**
+       * Tree Address for XMSS is constantly zero and can not be changed this
+       * property is only of relevance to XMSS_MT. Calling this method for
+       * XMSS will result in an error.
+       **/
+      void set_tree_addr()
+         {
+         BOTAN_ASSERT(false, "Only available in XMSS_MT.");
+         }
+
+      /**
+       * retrieves the logical type currently assigned to the XMSS Address
+       * instance.
+       *
+       * @return Type of the address (OTS_Hash_Address, LTree_Address or
+       *         Hash_Tree_Address)
+       **/
+      Type get_type() const
+         {
+         return static_cast<Type>(m_data[15]);
+         }
+
+      /**
+       * Changes the logical type currently assigned to the XMSS Address
+       * instance. Please note that changing the type will automatically
+       * reset the 128 LSBs of the Address to zero. This affects the
+       * key_mask_mode property as well as all properties identified by
+       * XMSS_Address::Property.
+       *
+       * @param type Type that shall be assigned to the address
+       *        (OTS_Hash_Address, LTree_Address or Hash_Tree_Address)
+       **/
+      void set_type(Type type)
+         {
+         m_data[15] = static_cast<uint8_t>(type);
+         std::fill(m_data.begin() + 16, m_data.end(), static_cast<uint8_t>(0));
+         }
+
+      /**
+       * Retrieves the mode the address os currently set to. (See
+       * XMSS_Address::Key_Mask for details.)
+       *
+       * @return currently active mode
+       **/
+      Key_Mask get_key_mask_mode() const
+         {
+         return Key_Mask(m_data[31]);
+         }
+
+      /**
+       * Changes the mode the address currently used address mode.
+       * (XMSS_Address::Key_Mask for details.)
+       *
+       * @param value Target mode.
+       **/
+      void set_key_mask_mode(Key_Mask value)
+         {
+         BOTAN_ASSERT(value != Key_Mask::Mask_LSB_Mode ||
+                      get_type() != Type::OTS_Hash_Address,
+                      "Invalid Key_Mask for current XMSS_Address::Type.");
+         m_data[31] = static_cast<uint8_t>(value);
+         }
+
+      /**
+       * Retrieve the index of the OTS key pair within the tree. A call to
+       * this method is only valid, if the address type is set to
+       * Type::OTS_Hash_Address.
+       *
+       * @return index of OTS key pair.
+       **/
+      uint32_t get_ots_address() const
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "get_ots_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         return get_hi32(2);
+         }
+
+      /**
+       * Sets the index of the OTS key pair within the tree. A call to this
+       * method is only valid, if the address type is set to
+       * Type::OTS_Hash_Address.
+       *
+       * @param value index of OTS key pair.
+       **/
+      void set_ots_address(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "set_ots_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         set_hi32(2, value);
+         }
+
+      /**
+       * Retrieves the index of the leaf computed with this LTree. A call to
+       * this method is only valid, if the address type is set to
+       * Type::LTree_Address.
+       *
+       * @return index of the leaf.
+       **/
+      uint32_t get_ltree_address() const
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address,
+                      "set_ltree_address() requires XMSS_Address::Type::"
+                      "LTree_Address.");
+         return get_hi32(2);
+         }
+
+      /**
+       * Sets the index of the leaf computed with this LTree. A call to this
+       * method is only valid, if the address type is set to
+       * Type::LTree_Address.
+       *
+       * @param value index of the leaf.
+       **/
+      void set_ltree_address(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address,
+                      "set_ltree_address() requires XMSS_Address::Type::"
+                      "LTree_Address.");
+         set_hi32(2, value);
+         }
+
+      /**
+       * Retrieve the chain address. A call to this method is only valid, if
+       * the address type is set to Type::OTS_Hash_Address.
+       *
+       * @return chain address.
+       **/
+      uint32_t get_chain_address() const
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "get_chain_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         return get_lo32(2);
+         }
+
+      /**
+       * Set the chain address. A call to this method is only valid, if
+       * the address type is set to Type::OTS_Hash_Address.
+       **/
+      void set_chain_address(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "set_chain_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         set_lo32(2, value);
+         }
+
+      /**
+       * Retrieves the height of the tree node to be computed within the
+       * tree. A call to this method is only valid, if the address type is
+       * set to Type::LTree_Address or Type::Hash_Tree_Address.
+       *
+       * @return height of the tree node.
+       **/
+      uint32_t get_tree_height() const
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+                      get_type() == Type::Hash_Tree_Address,
+                      "get_tree_height() requires XMSS_Address::Type::"
+                      "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+         return get_lo32(2);
+         }
+
+      /**
+       * Sets the height of the tree node to be computed within the
+       * tree. A call to this method is only valid, if the address type is
+       * set to Type::LTree_Address or Type::Hash_Tree_Address.
+       *
+       * @param value height of the tree node.
+       **/
+      void set_tree_height(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+                      get_type() == Type::Hash_Tree_Address,
+                      "set_tree_height() requires XMSS_Address::Type::"
+                      "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+         set_lo32(2, value);
+         }
+
+      /**
+       * Retrieves the address of the hash function call within the chain.
+       * A call to this method is only valid, if the address type is
+       * set to Type::OTS_Hash_Address.
+       *
+       * @return address of the hash function call within chain.
+       **/
+      uint32_t get_hash_address() const
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "get_hash_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         return get_hi32(3);
+         }
+
+      /**
+       * Sets the address of the hash function call within the chain.
+       * A call to this method is only valid, if the address type is
+       * set to Type::OTS_Hash_Address.
+       *
+       * @param value address of the hash function call within chain.
+       **/
+      void set_hash_address(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+                      "set_hash_address() requires XMSS_Address::Type::"
+                      "OTS_Hash_Address.");
+         set_hi32(3, value);
+         }
+
+      /**
+       * Retrieves the index of the tree node at current tree height in the
+       * tree. A call to this method is only valid, if the address type is
+       * set to Type::LTree_Address or Type::Hash_Tree_Address.
+       *
+       * @return index of the tree node at current height.
+       **/
+      uint32_t get_tree_index() const
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+                      get_type() == Type::Hash_Tree_Address,
+                      "get_tree_index() requires XMSS_Address::Type::"
+                      "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+         return get_hi32(3);
+         }
+
+      /**
+       * Sets the index of the tree node at current tree height in the
+       * tree. A call to this method is only valid, if the address type is
+       * set to Type::LTree_Address or Type::Hash_Tree_Address.
+       *
+       * @param value index of the tree node at current height.
+       **/
+      void set_tree_index(uint32_t value)
+         {
+         BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+                      get_type() == Type::Hash_Tree_Address,
+                      "set_tree_index() requires XMSS_Address::Type::"
+                      "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+         set_hi32(3, value);
+         }
+
+      const secure_vector<uint8_t>& bytes() const
+         {
+         return m_data;
+         }
+
+      secure_vector<uint8_t>& bytes()
+         {
+         return m_data;
+         }
+
+      /**
+       * @return the size of an XMSS_Address
+       **/
+      size_t size() const
+         {
+         return m_data.size();
+         }
+
+      XMSS_Address()
+         : m_data(m_address_size)
+         {
+         set_type(Type::None);
+         }
+
+      XMSS_Address(Type type)
+         : m_data(m_address_size)
+         {
+         set_type(type);
+         }
+
+      XMSS_Address(const secure_vector<uint8_t>& data) : m_data(data)
+         {
+         BOTAN_ASSERT(m_data.size() == m_address_size,
+                      "XMSS_Address must be of 256 bits size.");
+         }
+
+      XMSS_Address(secure_vector<uint8_t>&& data) : m_data(std::move(data))
+         {
+         BOTAN_ASSERT(m_data.size() == m_address_size,
+                      "XMSS_Address must be of 256 bits size.");
+         }
+
+   protected:
+      secure_vector<uint8_t> m_data;
+
+   private:
+      static const size_t m_address_size = 32;
+
+      inline uint32_t get_hi32(size_t offset) const
+         {
+         return ((0x000000FF & m_data[8 * offset + 3])       |
+                 (0x000000FF & m_data[8 * offset + 2]) <<  8 |
+                 (0x000000FF & m_data[8 * offset + 1]) << 16 |
+                 (0x000000FF & m_data[8 * offset    ]) << 24);
+         }
+
+      inline void set_hi32(size_t offset, uint32_t value)
+         {
+         m_data[offset * 8    ] = ((value >> 24) & 0xFF);
+         m_data[offset * 8 + 1] = ((value >> 16) & 0xFF);
+         m_data[offset * 8 + 2] = ((value >>  8) & 0xFF);
+         m_data[offset * 8 + 3] = ((value      ) & 0xFF);
+         }
+
+      inline uint32_t get_lo32(size_t offset) const
+         {
+         return ((0x000000FF & m_data[8 * offset + 7])       |
+                 (0x000000FF & m_data[8 * offset + 6]) <<  8 |
+                 (0x000000FF & m_data[8 * offset + 5]) << 16 |
+                 (0x000000FF & m_data[8 * offset + 4]) << 24);
+         }
+
+      inline void set_lo32(size_t offset, uint32_t value)
+         {
+         m_data[offset * 8 + 4] = ((value >> 24) & 0xFF);
+         m_data[offset * 8 + 5] = ((value >> 16) & 0xFF);
+         m_data[offset * 8 + 6] = ((value >>  8) & 0xFF);
+         m_data[offset * 8 + 7] = ((value      ) & 0xFF);
+         }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp
@@ -0,0 +1,74 @@
+/*
+ * XMSS Common Ops
+ * Operations shared by XMSS signature generation and verification operations.
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_common_ops.h>
+
+namespace Botan {
+
+void
+XMSS_Common_Ops::randomize_tree_hash(secure_vector<uint8_t>& result,
+                                     const secure_vector<uint8_t>& left,
+                                     const secure_vector<uint8_t>& right,
+                                     XMSS_Address& adrs,
+                                     const secure_vector<uint8_t>& seed,
+                                     XMSS_Hash& hash,
+                                     const XMSS_Parameters& params)
+   {
+   adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Key_Mode);
+   secure_vector<uint8_t> key { hash.prf(seed, adrs.bytes()) };
+
+   adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_MSB_Mode);
+   secure_vector<uint8_t> bitmask_l { hash.prf(seed, adrs.bytes()) };
+
+   adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_LSB_Mode);
+   secure_vector<uint8_t> bitmask_r { hash.prf(seed, adrs.bytes()) };
+
+   BOTAN_ASSERT(bitmask_l.size() == left.size() &&
+                bitmask_r.size() == right.size(),
+                "Bitmask size doesn't match node size.");
+
+   secure_vector<uint8_t> concat_xor(params.element_size() * 2);
+   for(size_t i = 0; i < left.size(); i++)
+      {
+      concat_xor[i] = left[i] ^ bitmask_l[i];
+      concat_xor[i + left.size()] = right[i] ^ bitmask_r[i];
+      }
+
+   hash.h(result, key, concat_xor);
+   }
+
+
+void
+XMSS_Common_Ops::create_l_tree(secure_vector<uint8_t>& result,
+                               wots_keysig_t pk,
+                               XMSS_Address& adrs,
+                               const secure_vector<uint8_t>& seed,
+                               XMSS_Hash& hash,
+                               const XMSS_Parameters& params)
+   {
+   size_t l = params.len();
+   adrs.set_tree_height(0);
+
+   while(l > 1)
+      {
+      for(size_t i = 0; i < l >> 1; i++)
+         {
+         adrs.set_tree_index(static_cast<uint32_t>(i));
+         randomize_tree_hash(pk[i], pk[2 * i], pk[2 * i + 1], adrs, seed, hash, params);
+         }
+      if(l & 0x01)
+         {
+         pk[l >> 1] = pk[l - 1];
+         }
+      l = (l >> 1) + (l & 0x01);
+      adrs.set_tree_height(adrs.get_tree_height() + 1);
+      }
+   result = pk[0];
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h b/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h
@@ -0,0 +1,81 @@
+/*
+ * XMSS Common Ops
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_COMMON_OPS_H_
+#define BOTAN_XMSS_COMMON_OPS_H_
+
+#include <vector>
+#include <botan/secmem.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_address.h>
+#include <botan/xmss_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xmss_common_ops.h)
+
+namespace Botan {
+
+typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
+
+/**
+ * Operations shared by XMSS signature generation and verification operations.
+ **/
+class XMSS_Common_Ops
+   {
+   public:
+      /**
+        * Algorithm 7: "RAND_HASH"
+        *
+        * Generates a randomized hash.
+        *
+        * This overload is used in multithreaded scenarios, where it is
+        * required to provide seperate instances of XMSS_Hash to each
+        * thread.
+        *
+        * @param[out] result The resulting randomized hash.
+        * @param[in] left Left half of the hash function input.
+        * @param[in] right Right half of the hash function input.
+        * @param[in] adrs Adress of the hash function call.
+        * @param[in] seed The seed for G.
+        * @param[in] hash Instance of XMSS_Hash, that may only by the thead
+        *            executing generate_public_key.
+        **/
+      static void randomize_tree_hash(
+         secure_vector<uint8_t>& result,
+         const secure_vector<uint8_t>& left,
+         const secure_vector<uint8_t>& right,
+         XMSS_Address& adrs,
+         const secure_vector<uint8_t>& seed,
+         XMSS_Hash& hash,
+         const XMSS_Parameters& params);
+
+      /**
+       * Algorithm 8: "ltree"
+       * Create an L-tree used to compute the leaves of the binary hash tree.
+       * Takes a WOTS+ public key and compresses it to a single n-byte value.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each thread.
+       *
+       * @param[out] result Public key compressed to a single n-byte value
+       *             pk[0].
+       * @param[in] pk Winternitz One Time Signatures+ public key.
+       * @param[in] adrs Address encoding the address of the L-Tree
+       * @param[in] seed The seed generated during the public key generation.
+       * @param[in] hash Instance of XMSS_Hash, that may only be used by the
+       *            thead executing create_l_tree.
+      **/
+      static void create_l_tree(secure_vector<uint8_t>& result,
+                                wots_keysig_t pk,
+                                XMSS_Address& adrs,
+                                const secure_vector<uint8_t>& seed,
+                                XMSS_Hash& hash,
+                                const XMSS_Parameters& params);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp
@@ -0,0 +1,80 @@
+/*
+ * XMSS Hash
+ * A collection of pseudorandom hash functions required for XMSS and WOTS
+ * computations.
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_hash.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+XMSS_Hash::XMSS_Hash(const XMSS_Hash& hash)
+   : XMSS_Hash(hash.m_hash_func_name)
+   {
+   }
+
+XMSS_Hash::XMSS_Hash(const std::string& h_func_name) :
+   m_hash(HashFunction::create(h_func_name)),
+   m_hash_func_name(h_func_name)
+   {
+   if(!m_hash)
+      throw Lookup_Error("XMSS cannot use hash " + h_func_name +
+                         " because it is unavailable");
+
+   m_output_length = m_hash->output_length();
+   BOTAN_ASSERT(m_output_length > 0, "Hash output length of zero is invalid.");
+
+   m_zero_padding.resize(m_output_length - 1);
+   m_msg_hash.reset(m_hash->clone());
+   }
+
+void
+XMSS_Hash::h(secure_vector<uint8_t>& result,
+             const secure_vector<uint8_t>& key,
+             const secure_vector<uint8_t>& data)
+   {
+   m_hash->update(m_zero_padding);
+   m_hash->update(m_id_h);
+   m_hash->update(key);
+   m_hash->update(data);
+   m_hash->final(result);
+   }
+
+void XMSS_Hash::h_msg_init(const secure_vector<uint8_t>& randomness,
+                           const secure_vector<uint8_t>& root,
+                           const secure_vector<uint8_t>& index_bytes)
+   {
+   m_msg_hash->clear();
+   m_msg_hash->update(m_zero_padding);
+   m_msg_hash->update(m_id_hmsg);
+   m_msg_hash->update(randomness);
+   m_msg_hash->update(root);
+   m_msg_hash->update(index_bytes);
+   }
+
+void XMSS_Hash::h_msg_update(const uint8_t data[], size_t size)
+   {
+   m_msg_hash->update(data, size);
+   }
+
+secure_vector<uint8_t> XMSS_Hash::h_msg_final()
+   {
+   return m_msg_hash->final();
+   }
+
+secure_vector<uint8_t>
+XMSS_Hash::h_msg(const secure_vector<uint8_t>& randomness,
+                 const secure_vector<uint8_t>& root,
+                 const secure_vector<uint8_t>& index_bytes,
+                 const secure_vector<uint8_t>& data)
+   {
+   h_msg_init(randomness, root, index_bytes);
+   m_msg_hash->update(data);
+   return m_msg_hash->final();
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h b/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h
@@ -0,0 +1,156 @@
+/*
+ * XMSS Hash
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_HASH_H_
+#define BOTAN_XMSS_HASH_H_
+
+#include <botan/hash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_hash.h)
+
+namespace Botan {
+
+/**
+ * A collection of pseudorandom hash functions required for XMSS and WOTS
+ * computations.
+ **/
+class XMSS_Hash final
+   {
+   public:
+      XMSS_Hash(const std::string& h_func_name);
+      XMSS_Hash(const XMSS_Hash& hash);
+
+      /**
+       * Pseudoranom function creating a hash out of a key and data using
+       * a cryptographic hash function.
+       *
+       * @param[out] result The hash calculated using key and data.
+       * @param[in] key An n-byte key value.
+       * @param[in] data A 32-byte XMSS_Address data value
+       **/
+      inline void prf(secure_vector<uint8_t>& result,
+                      const secure_vector<uint8_t>& key,
+                      const secure_vector<uint8_t>& data)
+         {
+         m_hash->update(m_zero_padding);
+         m_hash->update(m_id_prf);
+         m_hash->update(key);
+         m_hash->update(data);
+         m_hash->final(result);
+         }
+
+      /**
+       * Pseudoranom function creating a hash out of a key and data using
+       * a cryptographic hash function.
+       *
+       * @param[in] key An n-byte key value.
+       * @param[in] data A 32-byte XMSS_Address data value
+       * @return result The hash calculated using key and data.
+       **/
+      inline secure_vector<uint8_t> prf(const secure_vector<uint8_t>& key,
+                                        const secure_vector<uint8_t>& data)
+         {
+         m_hash->update(m_zero_padding);
+         m_hash->update(m_id_prf);
+         m_hash->update(key);
+         m_hash->update(data);
+         return m_hash->final();
+         }
+
+      /**
+       * F is a keyed cryptographic hash function used by the WOTS+ algorithm.
+       *
+       * @param[out] result The hash calculated using key and data.
+       * @param[in] key key of length n bytes.
+       * @param[in] data string of arbitrary length.
+       **/
+      void f(secure_vector<uint8_t>& result,
+             const secure_vector<uint8_t>& key,
+             const secure_vector<uint8_t>& data)
+         {
+         m_hash->update(m_zero_padding);
+         m_hash->update(m_id_f);
+         m_hash->update(key);
+         m_hash->update(data);
+         m_hash->final(result);
+         }
+
+      /**
+       * Cryptographic hash function h accepting n byte keys and 2n byte
+       * strings of data.
+       *
+       * @param[out] result The hash calculated using key and data.
+       * @param[in] key key of length n bytes.
+       * @param[in] data string of 2n bytes length.
+       **/
+      void h(secure_vector<uint8_t>& result,
+             const secure_vector<uint8_t>& key,
+             const secure_vector<uint8_t>& data);
+
+      /**
+       * Cryptographic hash function h accepting 3n byte keys and data
+       * strings of arbitrary length.
+       *
+       * @param randomness n-byte value.
+       * @param root n-byte root node.
+       * @param index_bytes Index value padded with leading zeros.
+       * @param data string of arbitrary length.
+       *
+       * @return hash value of n-bytes length.
+       **/
+      secure_vector<uint8_t> h_msg(const secure_vector<uint8_t>& randomness,
+                                   const secure_vector<uint8_t>& root,
+                                   const secure_vector<uint8_t>& index_bytes,
+                                   const secure_vector<uint8_t>& data);
+
+      /**
+       * Initializes buffered h_msg computation with prefix data.
+       *
+       * @param randomness random n-byte value.
+       * @param root n-byte root node.
+       * @param index_bytes Index value padded with leading zeros.
+       **/
+      void h_msg_init(const secure_vector<uint8_t>& randomness,
+                      const secure_vector<uint8_t>& root,
+                      const secure_vector<uint8_t>& index_bytes);
+
+      /**
+       * Adds a message block to buffered h_msg computation.
+       *
+       * @param data A message block
+       * @param size Length of the message block in bytes.
+       **/
+      void h_msg_update(const uint8_t data[], size_t size);
+
+      /**
+       * Finalizes buffered h_msg computation and retrieves the result.
+       *
+       * @return Hash calculated using the prefix set by h_msg_init() and
+       *         message blocks provided through calls to h_msg_update().
+       **/
+      secure_vector<uint8_t> h_msg_final();
+
+      size_t output_length() const { return m_output_length; }
+
+   private:
+      static const uint8_t m_id_f = 0x00;
+      static const uint8_t m_id_h = 0x01;
+      static const uint8_t m_id_hmsg = 0x02;
+      static const uint8_t m_id_prf = 0x03;
+
+      std::unique_ptr<HashFunction> m_hash;
+      std::unique_ptr<HashFunction> m_msg_hash;
+      //32 byte id prefixes prepended to the hash input.
+      std::vector<uint8_t> m_zero_padding;
+      size_t m_output_length;
+      const std::string m_hash_func_name;
+
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp
@@ -0,0 +1,84 @@
+/*
+ * XMSS Index Registry
+ * A registry for XMSS private keys, keeps track of the leaf index for
+ * independend copies of the same key.
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_index_registry.h>
+#include <botan/hash.h>
+#include <limits>
+
+namespace Botan {
+
+const std::string XMSS_Index_Registry::m_index_hash_function = "SHA-256";
+
+uint64_t XMSS_Index_Registry::make_key_id(
+   const secure_vector<uint8_t>& private_seed,
+   const secure_vector<uint8_t>& prf) const
+   {
+   std::unique_ptr<HashFunction> hash =
+      HashFunction::create(m_index_hash_function);
+   BOTAN_ASSERT(hash != nullptr, "XMSS_Index_Registry requires SHA-256");
+   hash->update(private_seed);
+   hash->update(prf);
+   secure_vector<uint8_t> result = hash->final();
+   uint64_t key_id = 0;
+   for(size_t i = 0; i < sizeof(key_id); i++)
+      {
+      key_id = ((key_id << 8) | result[i]);
+      }
+
+   return key_id;
+   }
+
+std::shared_ptr<Atomic<size_t>>
+XMSS_Index_Registry::get(const secure_vector<uint8_t>& private_seed,
+                         const secure_vector<uint8_t>& prf)
+   {
+   size_t pos = get(make_key_id(private_seed, prf));
+
+   if(pos < std::numeric_limits<size_t>::max())
+      {
+      return m_leaf_indices[pos];
+      }
+   else
+      {
+      return m_leaf_indices[add(make_key_id(private_seed, prf))];
+      }
+   }
+
+size_t XMSS_Index_Registry::get(uint64_t id) const
+   {
+   for(size_t i = 0; i < m_key_ids.size(); i++)
+      {
+      if(m_key_ids[i] == id)
+         {
+         return i;
+         }
+      }
+
+   return std::numeric_limits<size_t>::max();
+   }
+
+size_t XMSS_Index_Registry::add(uint64_t id, size_t last_unused)
+   {
+   lock_guard_type<mutex_type> lock(m_mutex);
+   size_t pos = get(id);
+   if(pos < m_key_ids.size())
+      {
+      if(last_unused > *(m_leaf_indices[pos]))
+         {
+         m_leaf_indices[pos] = std::make_shared<Atomic<size_t>>(last_unused);
+         }
+      return pos;
+      }
+
+   m_key_ids.push_back(id);
+   m_leaf_indices.push_back(std::make_shared<Atomic<size_t>>(last_unused));
+   return m_key_ids.size() - 1;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h b/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h
@@ -0,0 +1,105 @@
+/*
+ * XMSS Index Registry
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_INDEX_REGISTRY_H_
+#define BOTAN_XMSS_INDEX_REGISTRY_H_
+
+#include <string>
+
+#include <botan/secmem.h>
+#include <botan/atomic.h>
+#include <botan/mutex.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_index_registry.h)
+
+namespace Botan {
+
+/**
+ * A registry for XMSS private keys, keeps track of the leaf index for
+ * independend copies of the same key.
+ **/
+class XMSS_Index_Registry final
+   {
+   public:
+      XMSS_Index_Registry(const XMSS_Index_Registry&) = delete;
+      XMSS_Index_Registry& operator=(const XMSS_Index_Registry&) = delete;
+
+      /**
+       * Retrieves a handle to the process-wide unique XMSS index registry.
+       *
+       * @return Reference to unique XMSS index registry.
+       **/
+      static XMSS_Index_Registry& get_instance()
+         {
+         static XMSS_Index_Registry self;
+         return self;
+         }
+
+      /**
+       * Retrieves the last unused leaf index for the private key identified
+       * by private_seed and prf. The leaf index will be updated properly
+       * across independent copies of private_key.
+       *
+       * @param private_seed Part of the unique identifier for an
+       *                     XMSS_PrivateKey.
+       * @param prf Part of the unique identifier for an XMSS_PrivateKey.
+       *
+       * @return last unused leaf index for private_key.
+       **/
+      std::shared_ptr<Atomic<size_t>>
+                                   get(const secure_vector<uint8_t>& private_seed,
+                                       const secure_vector<uint8_t>& prf);
+
+   private:
+      XMSS_Index_Registry() = default;
+
+      static const std::string m_index_hash_function;
+
+      /**
+       * Creates a unique 64-bit id for an XMSS_Private key, by interpreting
+       * the first 64-bit of HASH(PRIVATE_SEED || PRF) as 64 bit integer
+       * value.
+       *
+       * @return unique integral identifier for an XMSS private key.
+       **/
+      uint64_t make_key_id(const secure_vector<uint8_t>& private_seed,
+                           const secure_vector<uint8_t>& prf) const;
+
+      /**
+       * Retrieves the index position of a key within the registry or
+       * max(size_t) if key has not been found.
+       *
+       * @param id unique id of the XMSS private key (see make_key_id()).
+       *
+       * @return index position of key or max(size_t) if key not found.
+       **/
+      size_t get(uint64_t id) const;
+
+      /**
+       * If XMSS_PrivateKey identified by id is already registered, the
+       * position of the according registry entry is returned. If last_unused
+       * is bigger than the last unused index stored for the key identified by
+       * id the unused leaf index for this key is set to last_unused. If no key
+       * matching id is registed yet, an entry of id is added, with the last
+       * unused leaf index initialized to the value of last_unused.
+       *
+       * @last_unused Initial value for the last unused leaf index of the
+       *              registered key.
+       *
+       * @return positon of leaf index registry entry for key identified
+       *         by id.
+       **/
+      size_t add(uint64_t id, size_t last_unused = 0);
+
+      std::vector<uint64_t> m_key_ids;
+      std::vector<std::shared_ptr<Atomic<size_t>>> m_leaf_indices;
+      mutex_type m_mutex;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h b/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h
@@ -0,0 +1,52 @@
+/*
+ * XMSS Key Pair
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_KEY_PAIR_H_
+#define BOTAN_XMSS_KEY_PAIR_H_
+
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_wots_parameters.h>
+#include <botan/xmss_publickey.h>
+#include <botan/xmss_privatekey.h>
+
+BOTAN_DEPRECATED_HEADER(xmss_key_pair.h)
+
+namespace Botan {
+
+/**
+ * A pair of XMSS public and private key.
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_Key_Pair
+   {
+   public:
+      XMSS_Key_Pair(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+                    RandomNumberGenerator& rng)
+         : m_priv_key(xmss_oid, rng), m_pub_key(m_priv_key) {}
+
+      XMSS_Key_Pair(const XMSS_PublicKey& pub_key,
+                    const XMSS_PrivateKey& priv_key)
+         : m_priv_key(priv_key), m_pub_key(pub_key)
+         {}
+
+      XMSS_Key_Pair(XMSS_PublicKey&& pub_key,
+                    XMSS_PrivateKey&& priv_key)
+         : m_priv_key(std::move(priv_key)), m_pub_key(std::move(pub_key)) {}
+
+      const XMSS_PublicKey& public_key() const { return m_pub_key; }
+      XMSS_PublicKey& public_key() { return m_pub_key; }
+
+      const XMSS_PrivateKey& private_key() const { return m_priv_key; }
+      XMSS_PrivateKey& private_key() { return m_priv_key; }
+
+   private:
+      XMSS_PrivateKey m_priv_key;
+      XMSS_PublicKey m_pub_key;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp
@@ -0,0 +1,184 @@
+/*
+ * XMSS Parameters
+ * Descibes a signature method for XMSS, as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_parameters.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+XMSS_Parameters::xmss_algorithm_t XMSS_Parameters::xmss_id_from_string(const std::string& param_set)
+   {
+   if(param_set == "XMSS-SHA2_10_256")
+      { return XMSS_SHA2_10_256; }
+   if(param_set == "XMSS-SHA2_16_256")
+      { return XMSS_SHA2_16_256; }
+   if(param_set == "XMSS-SHA2_20_256")
+      { return XMSS_SHA2_20_256; }
+   if(param_set == "XMSS-SHA2_10_512")
+      { return XMSS_SHA2_10_512; }
+   if(param_set == "XMSS-SHA2_16_512")
+      { return XMSS_SHA2_16_512; }
+   if(param_set == "XMSS-SHA2_20_512")
+      { return XMSS_SHA2_20_512; }
+   if(param_set == "XMSS-SHAKE_10_256")
+      { return XMSS_SHAKE_10_256; }
+   if(param_set == "XMSS-SHAKE_16_256")
+      { return XMSS_SHAKE_16_256; }
+   if(param_set == "XMSS-SHAKE_20_256")
+      { return XMSS_SHAKE_20_256; }
+   if(param_set == "XMSS-SHAKE_10_512")
+      { return XMSS_SHAKE_10_512; }
+   if(param_set == "XMSS-SHAKE_16_512")
+      { return XMSS_SHAKE_16_512; }
+   if(param_set == "XMSS-SHAKE_20_512")
+      { return XMSS_SHAKE_20_512; }
+   throw Lookup_Error("Unknown XMSS algorithm param '" + param_set + "'");
+   }
+
+XMSS_Parameters::XMSS_Parameters(const std::string& param_set)
+   : XMSS_Parameters(XMSS_Parameters::xmss_id_from_string(param_set))
+   {
+   }
+
+XMSS_Parameters::XMSS_Parameters(xmss_algorithm_t oid)
+   : m_oid(oid)
+   {
+   switch(oid)
+      {
+      case XMSS_SHA2_10_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 10;
+         m_name = "XMSS-SHA2_10_256";
+         m_hash_name = "SHA-256";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256;
+         break;
+      case XMSS_SHA2_16_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 16;
+         m_name = "XMSS-SHA2_16_256";
+         m_hash_name = "SHA-256";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256;
+         break;
+      case XMSS_SHA2_20_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 20;
+         m_name = "XMSS-SHA2_20_256";
+         m_hash_name = "SHA-256";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256;
+         break;
+      case XMSS_SHA2_10_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 10;
+         m_name = "XMSS-SHA2_10_512";
+         m_hash_name = "SHA-512";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512;
+         break;
+      case XMSS_SHA2_16_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 16;
+         m_name = "XMSS-SHA2_16_512";
+         m_hash_name = "SHA-512";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512;
+         break;
+      case XMSS_SHA2_20_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 20;
+         m_name = "XMSS-SHA2_20_512";
+         m_hash_name = "SHA-512";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512;
+         break;
+      case XMSS_SHAKE_10_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 10;
+         m_name = "XMSS-SHAKE_10_256";
+         m_hash_name = "SHAKE-128(256)";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256;
+         break;
+      case XMSS_SHAKE_16_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 16;
+         m_name = "XMSS-SHAKE_16_256";
+         m_hash_name = "SHAKE-128(256)";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256;
+         break;
+      case XMSS_SHAKE_20_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_tree_height = 20;
+         m_name = "XMSS-SHAKE_20_256";
+         m_hash_name = "SHAKE-128(256)";
+         m_strength = 256;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256;
+         break;
+      case XMSS_SHAKE_10_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 10;
+         m_name = "XMSS-SHAKE_10_512";
+         m_hash_name = "SHAKE-256(512)";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512;
+         break;
+      case XMSS_SHAKE_16_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 16;
+         m_name = "XMSS-SHAKE_16_512";
+         m_hash_name = "SHAKE-256(512)";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512;
+         break;
+      case XMSS_SHAKE_20_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_tree_height = 20;
+         m_name = "XMSS-SHAKE_20_512";
+         m_hash_name = "SHAKE-256(512)";
+         m_strength = 512;
+         m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512;
+         break;
+      default:
+         throw Not_Implemented("Algorithm id does not match any known XMSS algorithm id:" + std::to_string(oid));
+         break;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h b/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h
@@ -0,0 +1,119 @@
+/*
+ * XMSS Parameters
+ * (C) 2016,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PARAMETERS_H_
+#define BOTAN_XMSS_PARAMETERS_H_
+
+#include <botan/xmss_wots_parameters.h>
+#include <string>
+
+namespace Botan {
+
+/**
+ * Descibes a signature method for XMSS, as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_Parameters
+   {
+   public:
+      enum xmss_algorithm_t
+         {
+         XMSS_SHA2_10_256 = 0x00000001,
+         XMSS_SHA2_16_256 = 0x00000002,
+         XMSS_SHA2_20_256 = 0x00000003,
+         XMSS_SHA2_10_512 = 0x00000004,
+         XMSS_SHA2_16_512 = 0x00000005,
+         XMSS_SHA2_20_512 = 0x00000006,
+         XMSS_SHAKE_10_256 = 0x00000007,
+         XMSS_SHAKE_16_256 = 0x00000008,
+         XMSS_SHAKE_20_256 = 0x00000009,
+         XMSS_SHAKE_10_512 = 0x0000000a,
+         XMSS_SHAKE_16_512 = 0x0000000b,
+         XMSS_SHAKE_20_512 = 0x0000000c
+         };
+
+      static xmss_algorithm_t xmss_id_from_string(const std::string& algo_name);
+
+      XMSS_Parameters(const std::string& algo_name);
+      XMSS_Parameters(xmss_algorithm_t oid);
+
+      /**
+       * @return XMSS registry name for the chosen parameter set.
+       **/
+      const std::string& name() const
+         {
+         return m_name;
+         }
+
+      const std::string& hash_function_name() const
+         {
+         return m_hash_name;
+         }
+
+      /**
+       * Retrieves the uniform length of a message, and the size of
+       * each node. This correlates to XMSS parameter "n" defined
+       * in [1].
+       *
+       * @return element length in bytes.
+       **/
+      size_t element_size() const { return m_element_size; }
+
+      /**
+       * @returns The height (number of levels - 1) of the tree
+       **/
+      size_t tree_height() const { return m_tree_height; }
+
+      /**
+       * The Winternitz parameter.
+       *
+       * @return numeric base used for internal representation of
+       *         data.
+       **/
+      size_t wots_parameter() const { return m_w; }
+
+      size_t len() const { return m_len; }
+
+      xmss_algorithm_t oid() const { return m_oid; }
+
+      XMSS_WOTS_Parameters::ots_algorithm_t ots_oid() const
+         {
+         return m_wots_oid;
+         }
+
+      /**
+       * Returns the estimated pre-quantum security level of
+       * the chosen algorithm.
+       **/
+      size_t estimated_strength() const
+         {
+         return m_strength;
+         }
+
+      bool operator==(const XMSS_Parameters& p) const
+         {
+         return m_oid == p.m_oid;
+         }
+
+   private:
+      xmss_algorithm_t m_oid;
+      XMSS_WOTS_Parameters::ots_algorithm_t m_wots_oid;
+      std::string m_name;
+      std::string m_hash_name;
+      size_t m_element_size;
+      size_t m_tree_height;
+      size_t m_w;
+      size_t m_len;
+      size_t m_strength;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -0,0 +1,343 @@
+/*
+ * XMSS Private Key
+ * An XMSS: Extended Hash-Based Siganture private key.
+ * The XMSS private key does not support the X509 and PKCS7 standard. Instead
+ * the raw format described in [1] is used.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ * (C) 2019 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_privatekey.h>
+#include <botan/internal/xmss_signature_operation.h>
+#include <botan/xmss_common_ops.h>
+#include <botan/ber_dec.h>
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+   #include <botan/internal/thread_pool.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+secure_vector<uint8_t> extract_raw_key(const secure_vector<uint8_t>& key_bits)
+   {
+   secure_vector<uint8_t> raw_key;
+   try
+      {
+      BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+      }
+   catch(Decoding_Error&)
+      {
+      raw_key = key_bits;
+      }
+   return raw_key;
+   }
+
+}
+
+XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& key_bits)
+   : XMSS_PublicKey(unlock(key_bits)),
+     m_wots_priv_key(m_wots_params.oid(), m_public_seed),
+     m_hash(xmss_hash_function()),
+     m_index_reg(XMSS_Index_Registry::get_instance())
+   {
+   /*
+   The code requires sizeof(size_t) >= ceil(tree_height / 8)
+
+   Maximum supported tree height is 20, ceil(20/8) == 3, so 4 byte
+   size_t is sufficient for all defined parameters, or even a
+   (hypothetical) tree height 32, which would be extremely slow to
+   compute.
+   */
+   static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index");
+
+   secure_vector<uint8_t> raw_key = extract_raw_key(key_bits);
+
+   if(raw_key.size() != XMSS_PrivateKey::size())
+      {
+      throw Decoding_Error("Invalid XMSS private key size");
+      }
+
+   // extract & copy unused leaf index from raw_key
+   uint64_t unused_leaf = 0;
+   auto begin = (raw_key.begin() + XMSS_PublicKey::size());
+   auto end = raw_key.begin() + XMSS_PublicKey::size() + sizeof(uint32_t);
+
+   for(auto& i = begin; i != end; i++)
+      {
+      unused_leaf = ((unused_leaf << 8) | *i);
+      }
+
+   if(unused_leaf >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+      {
+      throw Decoding_Error("XMSS private key leaf index out of bounds");
+      }
+
+   begin = end;
+   end = begin + XMSS_PublicKey::m_xmss_params.element_size();
+   m_prf.clear();
+   m_prf.reserve(XMSS_PublicKey::m_xmss_params.element_size());
+   std::copy(begin, end, std::back_inserter(m_prf));
+
+   begin = end;
+   end = begin + m_wots_params.element_size();
+   m_wots_priv_key.set_private_seed(secure_vector<uint8_t>(begin, end));
+   set_unused_leaf_index(static_cast<size_t>(unused_leaf));
+   }
+
+XMSS_PrivateKey::XMSS_PrivateKey(
+   XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+   RandomNumberGenerator& rng)
+   : XMSS_PublicKey(xmss_algo_id, rng),
+     m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
+                     public_seed(),
+                     rng),
+     m_hash(xmss_hash_function()),
+     m_prf(rng.random_vec(XMSS_PublicKey::m_xmss_params.element_size())),
+     m_index_reg(XMSS_Index_Registry::get_instance())
+   {
+   XMSS_Address adrs;
+   set_root(tree_hash(0,
+                      XMSS_PublicKey::m_xmss_params.tree_height(),
+                      adrs));
+   }
+
+secure_vector<uint8_t>
+XMSS_PrivateKey::tree_hash(size_t start_idx,
+                           size_t target_node_height,
+                           XMSS_Address& adrs)
+   {
+   BOTAN_ASSERT_NOMSG(target_node_height <= 30);
+   BOTAN_ASSERT((start_idx % (static_cast<size_t>(1) << target_node_height)) == 0,
+                "Start index must be divisible by 2^{target node height}.");
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+   // dertermine number of parallel tasks to split the tree_hashing into.
+
+   Thread_Pool& thread_pool = Thread_Pool::global_instance();
+
+   const size_t split_level = std::min(target_node_height, thread_pool.worker_count());
+
+   // skip parallelization overhead for leaf nodes.
+   if(split_level == 0)
+      {
+      secure_vector<uint8_t> result;
+      tree_hash_subtree(result, start_idx, target_node_height, adrs);
+      return result;
+      }
+
+   const size_t subtrees = static_cast<size_t>(1) << split_level;
+   const size_t last_idx = (static_cast<size_t>(1) << (target_node_height)) + start_idx;
+   const size_t offs = (last_idx - start_idx) / subtrees;
+   // this cast cannot overflow because target_node_height is limited
+   uint8_t level = static_cast<uint8_t>(split_level); // current level in the tree
+
+   BOTAN_ASSERT((last_idx - start_idx) % subtrees == 0,
+                "Number of worker threads in tree_hash need to divide range "
+                "of calculated nodes.");
+
+   std::vector<secure_vector<uint8_t>> nodes(
+       subtrees,
+       secure_vector<uint8_t>(XMSS_PublicKey::m_xmss_params.element_size()));
+   std::vector<XMSS_Address> node_addresses(subtrees, adrs);
+   std::vector<XMSS_Hash> xmss_hash(subtrees, m_hash);
+   std::vector<std::future<void>> work;
+
+   // Calculate multiple subtrees in parallel.
+   for(size_t i = 0; i < subtrees; i++)
+      {
+      using tree_hash_subtree_fn_t =
+         void (XMSS_PrivateKey::*)(secure_vector<uint8_t>&,
+                                   size_t,
+                                   size_t,
+                                   XMSS_Address&,
+                                   XMSS_Hash&);
+
+      tree_hash_subtree_fn_t work_fn = &XMSS_PrivateKey::tree_hash_subtree;
+
+      work.push_back(thread_pool.run(
+                        work_fn,
+                        this,
+                        std::ref(nodes[i]),
+                        start_idx + i * offs,
+                        target_node_height - split_level,
+                        std::ref(node_addresses[i]),
+                        std::ref(xmss_hash[i])));
+      }
+
+   for(auto& w : work)
+      {
+      w.get();
+      }
+   work.clear();
+
+   // Parallelize the top tree levels horizontally
+   while(level-- > 1)
+      {
+      std::vector<secure_vector<uint8_t>> ro_nodes(
+         nodes.begin(), nodes.begin() + (static_cast<size_t>(1) << (level+1)));
+
+      for(size_t i = 0; i < (static_cast<size_t>(1) << level); i++)
+         {
+         BOTAN_ASSERT_NOMSG(xmss_hash.size() > i);
+
+         node_addresses[i].set_tree_height(static_cast<uint32_t>(target_node_height - (level + 1)));
+         node_addresses[i].set_tree_index(
+            (node_addresses[2 * i + 1].get_tree_index() - 1) >> 1);
+
+         work.push_back(thread_pool.run(
+               &XMSS_Common_Ops::randomize_tree_hash,
+               std::ref(nodes[i]),
+               std::cref(ro_nodes[2 * i]),
+               std::cref(ro_nodes[2 * i + 1]),
+               std::ref(node_addresses[i]),
+               std::cref(this->public_seed()),
+               std::ref(xmss_hash[i]),
+               std::cref(m_xmss_params)));
+         }
+
+      for(auto &w : work)
+         {
+         w.get();
+         }
+      work.clear();
+      }
+
+   // Avoid creation an extra thread to calculate root node.
+   node_addresses[0].set_tree_height(static_cast<uint32_t>(target_node_height - 1));
+   node_addresses[0].set_tree_index(
+      (node_addresses[1].get_tree_index() - 1) >> 1);
+   XMSS_Common_Ops::randomize_tree_hash(nodes[0],
+                                        nodes[0],
+                                        nodes[1],
+                                        node_addresses[0],
+                                        this->public_seed(),
+                                        m_hash,
+                                        m_xmss_params);
+   return nodes[0];
+#else
+   secure_vector<uint8_t> result;
+   tree_hash_subtree(result, start_idx, target_node_height, adrs, m_hash);
+   return result;
+#endif
+   }
+
+void
+XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
+                                   size_t start_idx,
+                                   size_t target_node_height,
+                                   XMSS_Address& adrs,
+                                   XMSS_Hash& hash)
+   {
+   const secure_vector<uint8_t>& seed = this->public_seed();
+
+   std::vector<secure_vector<uint8_t>> nodes(
+      target_node_height + 1,
+      secure_vector<uint8_t>(XMSS_PublicKey::m_xmss_params.element_size()));
+
+   // node stack, holds all nodes on stack and one extra "pending" node. This
+   // temporary node referred to as "node" in the XMSS standard document stays
+   // a pending element, meaning it is not regarded as element on the stack
+   // until level is increased.
+   std::vector<uint8_t> node_levels(target_node_height + 1);
+
+   uint8_t level = 0; // current level on the node stack.
+   XMSS_WOTS_PublicKey pk(m_wots_priv_key.wots_parameters().oid(), seed);
+   const size_t last_idx = (static_cast<size_t>(1) << target_node_height) + start_idx;
+
+   for(size_t i = start_idx; i < last_idx; i++)
+      {
+      adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+      adrs.set_ots_address(static_cast<uint32_t>(i));
+      this->wots_private_key().generate_public_key(
+         pk,
+         // getWOTS_SK(SK, s + i), reference implementation uses adrs
+         // instead of zero padded index s + i.
+         this->wots_private_key().at(adrs, hash),
+         adrs,
+         hash);
+      adrs.set_type(XMSS_Address::Type::LTree_Address);
+      adrs.set_ltree_address(static_cast<uint32_t>(i));
+      XMSS_Common_Ops::create_l_tree(nodes[level], pk, adrs, seed, hash, m_xmss_params);
+      node_levels[level] = 0;
+
+      adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+      adrs.set_tree_height(0);
+      adrs.set_tree_index(static_cast<uint32_t>(i));
+
+      while(level > 0 && node_levels[level] ==
+            node_levels[level - 1])
+         {
+         adrs.set_tree_index(((adrs.get_tree_index() - 1) >> 1));
+         XMSS_Common_Ops::randomize_tree_hash(nodes[level - 1],
+                                              nodes[level - 1],
+                                              nodes[level],
+                                              adrs,
+                                              seed,
+                                              hash,
+                                              m_xmss_params);
+         node_levels[level - 1]++;
+         level--; //Pop stack top element
+         adrs.set_tree_height(adrs.get_tree_height() + 1);
+         }
+      level++; //push temporary node to stack
+      }
+   result = nodes[level - 1];
+   }
+
+std::shared_ptr<Atomic<size_t>>
+XMSS_PrivateKey::recover_global_leaf_index() const
+   {
+   BOTAN_ASSERT(m_wots_priv_key.private_seed().size() ==
+                XMSS_PublicKey::m_xmss_params.element_size() &&
+                m_prf.size() == XMSS_PublicKey::m_xmss_params.element_size(),
+                "Trying to retrieve index for partially initialized "
+                "key.");
+   return m_index_reg.get(m_wots_priv_key.private_seed(),
+                          m_prf);
+   }
+
+secure_vector<uint8_t> XMSS_PrivateKey::raw_private_key() const
+   {
+   std::vector<uint8_t> pk { raw_public_key() };
+   secure_vector<uint8_t> result(pk.begin(), pk.end());
+   result.reserve(size());
+
+   for(int i = 3; i >= 0; i--)
+      {
+      result.push_back(
+         static_cast<uint8_t>(
+            static_cast<uint64_t>(unused_leaf_index()) >> 8 * i));
+      }
+
+   std::copy(m_prf.begin(), m_prf.end(), std::back_inserter(result));
+   std::copy(m_wots_priv_key.private_seed().begin(),
+             m_wots_priv_key.private_seed().end(),
+             std::back_inserter(result));
+
+   return result;
+   }
+
+std::unique_ptr<PK_Ops::Signature>
+XMSS_PrivateKey::create_signature_op(RandomNumberGenerator&,
+                                     const std::string&,
+                                     const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      return std::unique_ptr<PK_Ops::Signature>(
+         new XMSS_Signature_Operation(*this));
+
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h
@@ -0,0 +1,272 @@
+/*
+ * XMSS_PrivateKey.h
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PRIVATEKEY_H_
+#define BOTAN_XMSS_PRIVATEKEY_H_
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <botan/alg_id.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/types.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_publickey.h>
+#include <botan/atomic.h>
+#include <botan/xmss_wots_privatekey.h>
+#include <botan/xmss_index_registry.h>
+
+namespace Botan {
+
+/**
+ * An XMSS: Extended Hash-Based Signature private key.
+ * The XMSS private key does not support the X509 and PKCS7 standard. Instead
+ * the raw format described in [1] is used.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKey,
+   public virtual Private_Key
+   {
+   public:
+      /**
+      * Creates a new XMSS private key for the chosen XMSS signature method.
+      * New seeds for public/private key and pseudo random function input are
+      * generated using the provided RNG. The appropriate WOTS signature method
+      * will be automatically set based on the chosen XMSS signature method.
+      *
+      * @param xmss_algo_id Identifier for the selected XMSS signature method.
+      * @param rng A random number generator to use for key generation.
+      **/
+      XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+                      RandomNumberGenerator& rng);
+
+      /**
+       * Creates an XMSS_PrivateKey from a byte sequence produced by
+       * raw_private_key().
+       *
+       * @param raw_key An XMSS private key serialized using raw_private_key().
+       **/
+      XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key);
+
+      /**
+       * Creates a new XMSS private key for the chosen XMSS signature method
+       * using precomputed seeds for public/private keys and pseudo random
+       * function input. The appropriate WOTS signature method will be
+       * automatically set, based on the chosen XMSS signature method.
+       *
+       * @param xmss_algo_id Identifier for the selected XMSS signature method.
+       * @param idx_leaf Index of the next unused leaf.
+       * @param wots_priv_seed A seed to generate a Winternitz-One-Time-
+       *                      Signature private key from.
+       * @param prf a secret n-byte key sourced from a secure source
+       *        of uniformly random data.
+       * @param root Root node of the binary hash tree.
+       * @param public_seed The public seed.
+       **/
+      XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+                      size_t idx_leaf,
+                      const secure_vector<uint8_t>& wots_priv_seed,
+                      const secure_vector<uint8_t>& prf,
+                      const secure_vector<uint8_t>& root,
+                      const secure_vector<uint8_t>& public_seed)
+         : XMSS_PublicKey(xmss_algo_id, root, public_seed),
+           m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
+                           public_seed,
+                           wots_priv_seed),
+           m_hash(XMSS_PublicKey::m_xmss_params.hash_function_name()),
+           m_prf(prf),
+           m_index_reg(XMSS_Index_Registry::get_instance())
+         {
+         set_unused_leaf_index(idx_leaf);
+         }
+
+      bool stateful_operation() const override { return true; }
+
+      /**
+       * Retrieves the last unused leaf index of the private key. Reusing a leaf
+       * by utilizing leaf indices lower than the last unused leaf index will
+       * compromise security.
+       *
+       * @return Index of the last unused leaf.
+       **/
+      size_t unused_leaf_index() const
+         {
+         return *recover_global_leaf_index();
+         }
+
+      /**
+       * Sets the last unused leaf index of the private key. The leaf index
+       * will be updated automatically during every signing operation, and
+       * should not be set manually.
+       *
+       * @param idx Index of the last unused leaf.
+       **/
+      void set_unused_leaf_index(size_t idx)
+         {
+         if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+            {
+            throw Decoding_Error("XMSS private key leaf index out of bounds");
+            }
+         else
+            {
+            std::atomic<size_t>& index =
+               static_cast<std::atomic<size_t>&>(*recover_global_leaf_index());
+            size_t current = 0;
+
+            do
+               {
+               current = index.load();
+               if(current > idx)
+                  { return; }
+               }
+            while(!index.compare_exchange_strong(current, idx));
+            }
+         }
+
+      size_t reserve_unused_leaf_index()
+         {
+         size_t idx = (static_cast<std::atomic<size_t>&>(
+                          *recover_global_leaf_index())).fetch_add(1);
+         if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+            {
+            throw Decoding_Error("XMSS private key, one time signatures exhaused");
+            }
+         return idx;
+         }
+
+      /**
+       * Winternitz One Time Signature Scheme key utilized for signing
+       * operations.
+       *
+       * @return WOTS+ private key.
+       **/
+      const XMSS_WOTS_PrivateKey& wots_private_key() const
+         {
+         return m_wots_priv_key;
+         }
+
+      /**
+       * Winternitz One Time Signature Scheme key utilized for signing
+       * operations.
+       *
+       * @return WOTS+ private key.
+       **/
+      XMSS_WOTS_PrivateKey& wots_private_key()
+         {
+         return m_wots_priv_key;
+         }
+
+      const secure_vector<uint8_t>& prf() const
+         {
+         return m_prf;
+         }
+
+      secure_vector<uint8_t>& prf()
+         {
+         return m_prf;
+         }
+
+      void set_public_seed(
+         const secure_vector<uint8_t>& public_seed) override
+         {
+         m_public_seed = public_seed;
+         m_wots_priv_key.set_public_seed(public_seed);
+         }
+
+      void set_public_seed(secure_vector<uint8_t>&& public_seed) override
+         {
+         m_public_seed = std::move(public_seed);
+         m_wots_priv_key.set_public_seed(m_public_seed);
+         }
+
+      const secure_vector<uint8_t>& public_seed() const override
+         {
+         return m_public_seed;
+         }
+
+      std::unique_ptr<PK_Ops::Signature>
+      create_signature_op(RandomNumberGenerator&,
+                          const std::string&,
+                          const std::string& provider) const override;
+
+      secure_vector<uint8_t> private_key_bits() const override
+         {
+         return DER_Encoder().encode(raw_private_key(), OCTET_STRING).get_contents();
+         }
+
+      size_t size() const override
+         {
+         return XMSS_PublicKey::size() +
+                sizeof(uint32_t) +
+                2 * XMSS_PublicKey::m_xmss_params.element_size();
+         }
+
+      /**
+       * Generates a non standartized byte sequence representing the XMSS
+       * private key.
+       *
+       * @return byte sequence consisting of the following elements in order:
+       *         4-byte OID, n-byte root node, n-byte public seed,
+       *         8-byte unused leaf index, n-byte prf seed, n-byte private seed.
+       **/
+      secure_vector<uint8_t> raw_private_key() const;
+      /**
+       * Algorithm 9: "treeHash"
+       * Computes the internal n-byte nodes of a Merkle tree.
+       *
+       * @param start_idx The start index.
+       * @param target_node_height Height of the target node.
+       * @param adrs Address of the tree containing the target node.
+       *
+       * @return The root node of a tree of height target_node height with the
+       *         leftmost leaf being the hash of the WOTS+ pk with index
+       *         start_idx.
+       **/
+      secure_vector<uint8_t> tree_hash(
+         size_t start_idx,
+         size_t target_node_height,
+         XMSS_Address& adrs);
+
+   private:
+      /**
+       * Fetches shared unused leaf index from the index registry
+       **/
+      std::shared_ptr<Atomic<size_t>> recover_global_leaf_index() const;
+
+      inline void tree_hash_subtree(secure_vector<uint8_t>& result,
+                                    size_t start_idx,
+                                    size_t target_node_height,
+                                    XMSS_Address& adrs)
+         {
+         return tree_hash_subtree(result, start_idx, target_node_height, adrs, m_hash);
+         }
+
+
+      /**
+       * Helper for multithreaded tree hashing.
+       */
+      void tree_hash_subtree(secure_vector<uint8_t>& result,
+                             size_t start_idx,
+                             size_t target_node_height,
+                             XMSS_Address& adrs,
+                             XMSS_Hash& hash);
+
+      XMSS_WOTS_PrivateKey m_wots_priv_key;
+      XMSS_Hash m_hash;
+      secure_vector<uint8_t> m_prf;
+      XMSS_Index_Registry& m_index_reg;
+   };
+
+}
+
+#endif
+
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -0,0 +1,114 @@
+/*
+ * XMSS Public Key
+ * An XMSS: Extended Hash-Based Siganture public key.
+ * The XMSS public key does not support the X509 standard. Instead the
+ * raw format described in [1] is used.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_verification_operation.h>
+#include <botan/xmss_publickey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+std::vector<uint8_t> extract_raw_key(const std::vector<uint8_t>& key_bits)
+   {
+   std::vector<uint8_t> raw_key;
+   try
+      {
+      BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+      }
+   catch(Decoding_Error&)
+      {
+      raw_key = key_bits;
+      }
+   return raw_key;
+   }
+
+}
+
+XMSS_PublicKey::XMSS_PublicKey(const std::vector<uint8_t>& key_bits)
+   : m_raw_key(extract_raw_key(key_bits)),
+     m_xmss_params(XMSS_PublicKey::deserialize_xmss_oid(m_raw_key)),
+     m_wots_params(m_xmss_params.ots_oid())
+   {
+   if(m_raw_key.size() < XMSS_PublicKey::size())
+      {
+      throw Decoding_Error("Invalid XMSS public key size detected");
+      }
+
+   // extract & copy root from raw key
+   m_root.clear();
+   m_root.reserve(m_xmss_params.element_size());
+   auto begin = m_raw_key.begin() + sizeof(uint32_t);
+   auto end = begin + m_xmss_params.element_size();
+   std::copy(begin, end, std::back_inserter(m_root));
+
+   // extract & copy public seed from raw key
+   begin = end;
+   end = begin + m_xmss_params.element_size();
+   m_public_seed.clear();
+   m_public_seed.reserve(m_xmss_params.element_size());
+   std::copy(begin, end, std::back_inserter(m_public_seed));
+   }
+
+XMSS_Parameters::xmss_algorithm_t
+XMSS_PublicKey::deserialize_xmss_oid(const std::vector<uint8_t>& raw_key)
+   {
+   if(raw_key.size() < 4)
+      {
+      throw Decoding_Error("XMSS signature OID missing.");
+      }
+
+   // extract and convert algorithm id to enum type
+   uint32_t raw_id = 0;
+   for(size_t i = 0; i < 4; i++)
+      { raw_id = ((raw_id << 8) | raw_key[i]); }
+
+   return static_cast<XMSS_Parameters::xmss_algorithm_t>(raw_id);
+   }
+
+std::unique_ptr<PK_Ops::Verification>
+XMSS_PublicKey::create_verification_op(const std::string&,
+                                       const std::string& provider) const
+   {
+   if(provider == "base" || provider.empty())
+      {
+      return std::unique_ptr<PK_Ops::Verification>(
+                new XMSS_Verification_Operation(*this));
+      }
+   throw Provider_Not_Found(algo_name(), provider);
+   }
+
+std::vector<uint8_t> XMSS_PublicKey::raw_public_key() const
+   {
+   std::vector<uint8_t> result
+      {
+      static_cast<uint8_t>(m_xmss_params.oid() >> 24),
+      static_cast<uint8_t>(m_xmss_params.oid() >> 16),
+      static_cast<uint8_t>(m_xmss_params.oid() >>  8),
+      static_cast<uint8_t>(m_xmss_params.oid())
+      };
+
+   std::copy(m_root.begin(), m_root.end(), std::back_inserter(result));
+   std::copy(m_public_seed.begin(),
+             m_public_seed.end(),
+             std::back_inserter(result));
+
+   return result;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h
@@ -0,0 +1,274 @@
+/*
+ * XMSS Public Key
+ * (C) 2016,2017 Matthias Gierlings
+ * (C) 2019 René Korthaus, Rohde & Schwarz Cybersecurity
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PUBLICKEY_H_
+#define BOTAN_XMSS_PUBLICKEY_H_
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <botan/alg_id.h>
+#include <botan/asn1_oid.h>
+#include <botan/der_enc.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/types.h>
+#include <botan/pk_keys.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_wots_parameters.h>
+#include <botan/pk_ops.h>
+
+namespace Botan {
+
+class XMSS_Verification_Operation;
+
+/**
+ * An XMSS: Extended Hash-Based Signature public key.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
+   {
+   public:
+      /**
+       * Creates a new XMSS public key for the chosen XMSS signature method.
+       * New public and prf seeds are generated using rng. The appropriate WOTS
+       * signature method will be automatically set based on the chosen XMSS
+       * signature method.
+       *
+       * @param xmss_oid Identifier for the selected XMSS signature method.
+       * @param rng A random number generator to use for key generation.
+       **/
+      XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+                     RandomNumberGenerator& rng)
+         : m_xmss_params(xmss_oid), m_wots_params(m_xmss_params.ots_oid()),
+           m_root(m_xmss_params.element_size()),
+           m_public_seed(rng.random_vec(m_xmss_params.element_size())) {}
+
+      /**
+       * Loads a public key.
+       *
+       * Public key must be encoded as in RFC
+       * draft-vangeest-x509-hash-sigs-03.
+       *
+       * @param key_bits DER encoded public key bits
+       */
+      XMSS_PublicKey(const std::vector<uint8_t>& key_bits);
+
+      /**
+       * Creates a new XMSS public key for a chosen XMSS signature method as
+       * well as pre-computed root node and public_seed values.
+       *
+       * @param xmss_oid Identifier for the selected XMSS signature method.
+       * @param root Root node value.
+       * @param public_seed Public seed value.
+       **/
+      XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+                     const secure_vector<uint8_t>& root,
+                     const secure_vector<uint8_t>& public_seed)
+         : m_xmss_params(xmss_oid), m_wots_params(m_xmss_params.ots_oid()),
+           m_root(root), m_public_seed(public_seed) {}
+
+      /**
+       * Creates a new XMSS public key for a chosen XMSS signature method as
+       * well as pre-computed root node and public_seed values.
+       *
+       * @param xmss_oid Identifier for the selected XMSS signature method.
+       * @param root Root node value.
+       * @param public_seed Public seed value.
+       **/
+      XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+                     secure_vector<uint8_t>&& root,
+                     secure_vector<uint8_t>&& public_seed)
+         : m_xmss_params(xmss_oid), m_wots_params(m_xmss_params.ots_oid()),
+           m_root(std::move(root)), m_public_seed(std::move(public_seed)) {}
+
+      /**
+       * Retrieves the chosen XMSS signature method.
+       *
+       * @return XMSS signature method identifier.
+       **/
+      XMSS_Parameters::xmss_algorithm_t xmss_oid() const
+         {
+         return m_xmss_params.oid();
+         }
+
+      /**
+       * Sets the chosen XMSS signature method
+       **/
+      void set_xmss_oid(XMSS_Parameters::xmss_algorithm_t xmss_oid)
+         {
+         m_xmss_params = XMSS_Parameters(xmss_oid);
+         m_wots_params = XMSS_WOTS_Parameters(m_xmss_params.ots_oid());
+         }
+
+      /**
+       * Retrieves the XMSS parameters determined by the chosen XMSS Signature
+       * method.
+       *
+       * @return XMSS parameters.
+       **/
+      const XMSS_Parameters& xmss_parameters() const
+         {
+         return m_xmss_params;
+         }
+
+      /**
+       * Retrieves the XMSS parameters determined by the chosen XMSS Signature
+       * method.
+       *
+       * @return XMSS parameters.
+       **/
+      std::string xmss_hash_function() const
+         {
+         return m_xmss_params.hash_function_name();
+         }
+
+      /**
+       * Retrieves the Winternitz One Time Signature (WOTS) method,
+       * corresponding to the chosen XMSS signature method.
+       *
+       * @return XMSS WOTS signature method identifier.
+       **/
+      XMSS_WOTS_Parameters::ots_algorithm_t wots_oid() const
+         {
+         return m_wots_params.oid();
+         }
+
+      /**
+       * Retrieves the Winternitz One Time Signature (WOTS) parameters
+       * corresponding to the chosen XMSS signature method.
+       *
+       * @return XMSS WOTS signature method parameters.
+       **/
+      const XMSS_WOTS_Parameters& wots_parameters() const
+         {
+         return m_wots_params;
+         }
+
+      secure_vector<uint8_t>& root()
+         {
+         return m_root;
+         }
+
+      void set_root(const secure_vector<uint8_t>& root)
+         {
+         m_root = root;
+         }
+
+      void set_root(secure_vector<uint8_t>&& root)
+         {
+         m_root = std::move(root);
+         }
+
+      const secure_vector<uint8_t>& root() const
+         {
+         return m_root;
+         }
+
+      virtual secure_vector<uint8_t>& public_seed()
+         {
+         return m_public_seed;
+         }
+
+      virtual void set_public_seed(const secure_vector<uint8_t>& public_seed)
+         {
+         m_public_seed = public_seed;
+         }
+
+      virtual void set_public_seed(secure_vector<uint8_t>&& public_seed)
+         {
+         m_public_seed = std::move(public_seed);
+         }
+
+      virtual const secure_vector<uint8_t>& public_seed() const
+         {
+         return m_public_seed;
+         }
+
+      std::string algo_name() const override
+         {
+         return "XMSS";
+         }
+
+      AlgorithmIdentifier algorithm_identifier() const override
+         {
+         return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+         }
+
+      bool check_key(RandomNumberGenerator&, bool) const override
+         {
+         return true;
+         }
+
+      std::unique_ptr<PK_Ops::Verification>
+      create_verification_op(const std::string&,
+                             const std::string& provider) const override;
+
+      size_t estimated_strength() const override
+         {
+         return m_xmss_params.estimated_strength();
+         }
+
+      size_t key_length() const override
+         {
+         return m_xmss_params.estimated_strength();
+         }
+
+      /**
+       * Returns the encoded public key as defined in RFC
+       * draft-vangeest-x509-hash-sigs-03.
+       *
+       * @return encoded public key bits
+       **/
+      std::vector<uint8_t> public_key_bits() const override
+         {
+         std::vector<uint8_t> output;
+         DER_Encoder(output).encode(raw_public_key(), OCTET_STRING);
+         return output;
+         }
+
+      /**
+       * Size in bytes of the serialized XMSS public key produced by
+       * raw_public_key().
+       *
+       * @return size in bytes of serialized Public Key.
+       **/
+      virtual size_t size() const
+         {
+         return sizeof(uint32_t) + 2 * m_xmss_params.element_size();
+         }
+
+      /**
+       * Generates a byte sequence representing the XMSS
+       * public key, as defined in [1] (p. 23, "XMSS Public Key")
+       *
+       * @return 4-byte OID, followed by n-byte root node, followed by
+       *         public seed.
+       **/
+      virtual std::vector<uint8_t> raw_public_key() const;
+
+   protected:
+      std::vector<uint8_t> m_raw_key;
+      XMSS_Parameters m_xmss_params;
+      XMSS_WOTS_Parameters m_wots_params;
+      secure_vector<uint8_t> m_root;
+      secure_vector<uint8_t> m_public_seed;
+
+   private:
+      XMSS_Parameters::xmss_algorithm_t deserialize_xmss_oid(
+         const std::vector<uint8_t>& raw_key);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp
@@ -0,0 +1,91 @@
+/*
+ * XMSS Signature
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_signature.h>
+
+namespace Botan {
+
+XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid,
+                               const secure_vector<uint8_t>& raw_sig)
+   : m_leaf_idx(0), m_randomness(0, 0x00), m_tree_sig()
+   {
+   XMSS_Parameters xmss_params(oid);
+
+   if(raw_sig.size() != (xmss_params.len() + xmss_params.tree_height() + 1)
+                        * xmss_params.element_size() + sizeof(uint32_t))
+   {
+      throw Decoding_Error("XMSS signature size invalid.");
+   }
+
+   for(size_t i = 0; i < 4; i++)
+      { m_leaf_idx = ((m_leaf_idx << 8) | raw_sig[i]); }
+
+   if(m_leaf_idx >= (1ull << xmss_params.tree_height()))
+      {
+      throw Decoding_Error("XMSS signature leaf index out of bounds.");
+      }
+
+   auto begin = raw_sig.begin() + sizeof(uint32_t);
+   auto end = begin + xmss_params.element_size();
+   std::copy(begin, end, std::back_inserter(m_randomness));
+
+   for(size_t i = 0; i < xmss_params.len(); i++)
+      {
+      begin = end;
+      end = begin + xmss_params.element_size();
+      m_tree_sig.ots_signature().push_back(secure_vector<uint8_t>(0));
+      m_tree_sig.ots_signature().back().reserve(
+         xmss_params.element_size());
+      std::copy(begin,
+                end,
+                std::back_inserter(m_tree_sig.ots_signature().back()));
+      }
+
+   for(size_t i = 0; i < xmss_params.tree_height(); i++)
+      {
+      begin = end;
+      end = begin + xmss_params.element_size();
+      m_tree_sig.authentication_path().push_back(secure_vector<uint8_t>(0));
+      m_tree_sig.authentication_path().back().reserve(
+         xmss_params.element_size());
+      std::copy(begin,
+                end,
+                std::back_inserter(m_tree_sig.authentication_path().back()));
+      }
+   }
+
+secure_vector<uint8_t> XMSS_Signature::bytes() const
+   {
+   secure_vector<uint8_t> result
+      {
+      static_cast<uint8_t>(m_leaf_idx >> 24U),
+      static_cast<uint8_t>(m_leaf_idx >> 16U),
+      static_cast<uint8_t>(m_leaf_idx >>  8U),
+      static_cast<uint8_t>(m_leaf_idx)
+      };
+
+   std::copy(m_randomness.begin(),
+             m_randomness.end(),
+             std::back_inserter(result));
+
+   for(const auto& sig : tree().ots_signature())
+      {
+      std::copy(sig.begin(),
+                sig.end(),
+                std::back_inserter(result));
+      }
+
+   for(const auto& auth : tree().authentication_path())
+      {
+      std::copy(auth.begin(),
+                auth.end(),
+                std::back_inserter(result));
+      }
+   return result;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h b/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h
@@ -0,0 +1,127 @@
+/*
+ * XMSS Signature
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_SIGNATURE_H_
+#define BOTAN_XMSS_SIGNATURE_H_
+
+#include <cstddef>
+#include <botan/exceptn.h>
+#include <botan/types.h>
+#include <botan/secmem.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_wots_publickey.h>
+
+namespace Botan {
+
+class XMSS_Signature final
+   {
+   public:
+      /**
+       * Creates a signature from an XMSS signature method and a uint8_t sequence
+       * representing a raw signature.
+       *
+       * @param oid XMSS signature method
+       * @param raw_sig An XMSS signature serialized using
+       *                XMSS_Signature::bytes().
+       **/
+      XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid,
+                     const secure_vector<uint8_t>& raw_sig);
+
+      /**
+       * Creates an XMSS Signature from a leaf index used for signature
+       * generation, a random value and a tree signature.
+       *
+       * @param leaf_idx Leaf index used to generate the signature.
+       * @param randomness A random value.
+       * @param tree_sig A tree signature.
+       **/
+      XMSS_Signature(size_t leaf_idx,
+                     const secure_vector<uint8_t>& randomness,
+                     const XMSS_WOTS_PublicKey::TreeSignature& tree_sig)
+         : m_leaf_idx(leaf_idx), m_randomness(randomness),
+           m_tree_sig(tree_sig) {}
+
+      /**
+       * Creates an XMSS Signature from a leaf index used for signature
+       * generation, a random value and a tree signature.
+       *
+       * @param leaf_idx Leaf index used to generate the signature.
+       * @param randomness A random value.
+       * @param tree_sig A tree signature.
+       **/
+      XMSS_Signature(size_t leaf_idx,
+                     secure_vector<uint8_t>&& randomness,
+                     XMSS_WOTS_PublicKey::TreeSignature&& tree_sig)
+         : m_leaf_idx(leaf_idx), m_randomness(std::move(randomness)),
+           m_tree_sig(std::move(tree_sig)) {}
+
+      size_t unused_leaf_index() const { return m_leaf_idx; }
+      void set_unused_leaf_idx(size_t idx) { m_leaf_idx = idx; }
+
+      const secure_vector<uint8_t> randomness() const
+         {
+         return m_randomness;
+         }
+
+      secure_vector<uint8_t>& randomness()
+         {
+         return m_randomness;
+         }
+
+      void set_randomness(const secure_vector<uint8_t>& randomness)
+         {
+         m_randomness = randomness;
+         }
+
+      void set_randomness(secure_vector<uint8_t>&& randomness)
+         {
+         m_randomness = std::move(randomness);
+         }
+
+      const XMSS_WOTS_PublicKey::TreeSignature& tree() const
+         {
+         return m_tree_sig;
+         }
+
+      XMSS_WOTS_PublicKey::TreeSignature& tree()
+         {
+         return m_tree_sig;
+         }
+
+      void set_tree(const XMSS_WOTS_PublicKey::TreeSignature& tree_sig)
+         {
+         m_tree_sig = tree_sig;
+         }
+
+      void set_tree(XMSS_WOTS_PublicKey::TreeSignature&& tree_sig)
+         {
+         m_tree_sig = std::move(tree_sig);
+         }
+
+      /**
+       * Generates a serialized representation of XMSS Signature by
+       * concatenating the following elements in order:
+       * 4-byte leaf index, n-bytes randomness, ots_signature,
+       * authentication path.
+       *
+       * n is the element_size(), len equal to len(), h the tree height
+       * defined by the chosen XMSS signature method.
+       *
+       * @return serialized signature, a sequence of
+       *         4+(len + h + 1)n bytes.
+       **/
+      secure_vector<uint8_t> bytes() const;
+
+   private:
+      size_t m_leaf_idx;
+      secure_vector<uint8_t> m_randomness;
+      XMSS_WOTS_PublicKey::TreeSignature m_tree_sig;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp
@@ -0,0 +1,119 @@
+/*
+ * XMSS Signature Operation
+ * Signature generation operation for Extended Hash-Based Signatures (XMSS) as
+ * defined in:
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_signature_operation.h>
+
+namespace Botan {
+
+XMSS_Signature_Operation::XMSS_Signature_Operation(
+   const XMSS_PrivateKey& private_key) :
+   m_priv_key(private_key),
+   m_xmss_params(private_key.xmss_oid()),
+   m_hash(private_key.xmss_hash_function()),
+   m_randomness(0),
+   m_leaf_idx(0),
+   m_is_initialized(false)
+   {}
+
+XMSS_WOTS_PublicKey::TreeSignature
+XMSS_Signature_Operation::generate_tree_signature(const secure_vector<uint8_t>& msg,
+      XMSS_PrivateKey& xmss_priv_key,
+      XMSS_Address& adrs)
+   {
+
+   wots_keysig_t auth_path = build_auth_path(xmss_priv_key, adrs);
+   adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+   adrs.set_ots_address(m_leaf_idx);
+
+   wots_keysig_t sig_ots = xmss_priv_key.wots_private_key().sign(msg, adrs);
+   return XMSS_WOTS_PublicKey::TreeSignature(sig_ots, auth_path);
+   }
+
+XMSS_Signature
+XMSS_Signature_Operation::sign(const secure_vector<uint8_t>& msg_hash,
+                               XMSS_PrivateKey& xmss_priv_key)
+   {
+   XMSS_Address adrs;
+   XMSS_Signature sig(m_leaf_idx,
+                      m_randomness,
+                      generate_tree_signature(msg_hash, xmss_priv_key,adrs));
+   return sig;
+   }
+
+size_t XMSS_Signature_Operation::signature_length() const
+   {
+   return sizeof(uint64_t) + // size of leaf index
+          m_xmss_params.element_size() +
+          m_xmss_params.len() * m_xmss_params.element_size() +
+          m_xmss_params.tree_height() * m_xmss_params.element_size();
+   }
+
+wots_keysig_t
+XMSS_Signature_Operation::build_auth_path(XMSS_PrivateKey& priv_key,
+      XMSS_Address& adrs)
+   {
+   wots_keysig_t auth_path(m_xmss_params.tree_height());
+   adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+
+   for(size_t j = 0; j < m_xmss_params.tree_height(); j++)
+      {
+      size_t k = (m_leaf_idx / (1ULL << j)) ^ 0x01;
+      auth_path[j] = priv_key.tree_hash(k * (1ULL << j), j, adrs);
+      }
+
+   return auth_path;
+   }
+
+void XMSS_Signature_Operation::update(const uint8_t msg[], size_t msg_len)
+   {
+   initialize();
+   m_hash.h_msg_update(msg, msg_len);
+   }
+
+secure_vector<uint8_t>
+XMSS_Signature_Operation::sign(RandomNumberGenerator&)
+   {
+   initialize();
+   secure_vector<uint8_t> signature(sign(m_hash.h_msg_final(),
+                                         m_priv_key).bytes());
+   m_is_initialized = false;
+   return signature;
+   }
+
+void XMSS_Signature_Operation::initialize()
+   {
+   // return if we already initialized and reserved a leaf index for signing.
+   if(m_is_initialized)
+      { return; }
+
+   secure_vector<uint8_t> index_bytes;
+   // reserve leaf index so it can not be reused in by another signature
+   // operation using the same private key.
+   m_leaf_idx = static_cast<uint32_t>(m_priv_key.reserve_unused_leaf_index());
+
+   // write prefix for message hashing into buffer.
+   XMSS_Tools::concat(index_bytes, m_leaf_idx, 32);
+   m_randomness = m_hash.prf(m_priv_key.prf(), index_bytes);
+   index_bytes.clear();
+   XMSS_Tools::concat(index_bytes, m_leaf_idx,
+                      m_priv_key.xmss_parameters().element_size());
+   m_hash.h_msg_init(m_randomness,
+                     m_priv_key.root(),
+                     index_bytes);
+   m_is_initialized = true;
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h b/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h
@@ -0,0 +1,95 @@
+/*
+ * XMSS Signature Operation
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_SIGNATURE_OPERATION_H_
+#define BOTAN_XMSS_SIGNATURE_OPERATION_H_
+
+#include <cstddef>
+#include <string>
+#include <botan/secmem.h>
+#include <botan/types.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_privatekey.h>
+#include <botan/xmss_address.h>
+#include <botan/pk_ops.h>
+#include <botan/internal/xmss_signature.h>
+#include <botan/xmss_wots_publickey.h>
+#include <botan/xmss_common_ops.h>
+
+namespace Botan {
+
+/**
+ * Signature generation operation for Extended Hash-Based Signatures (XMSS) as
+ * defined in:
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class XMSS_Signature_Operation final : public virtual PK_Ops::Signature
+   {
+   public:
+      XMSS_Signature_Operation(const XMSS_PrivateKey& private_key);
+
+      /**
+       * Creates an XMSS signature for the message provided through call to
+       * update().
+       *
+       * @return serialized XMSS signature.
+       **/
+      secure_vector<uint8_t> sign(RandomNumberGenerator&) override;
+
+      void update(const uint8_t msg[], size_t msg_len) override;
+
+      size_t signature_length() const override;
+
+   private:
+      /**
+       * Algorithm 11: "treeSig"
+       * Generate a WOTS+ signature on a message with corresponding auth path.
+       *
+       * @param msg A message.
+       * @param xmss_priv_key A XMSS private key.
+       * @param adrs A XMSS Address.
+       **/
+      XMSS_WOTS_PublicKey::TreeSignature generate_tree_signature(
+         const secure_vector<uint8_t>& msg,
+         XMSS_PrivateKey& xmss_priv_key,
+         XMSS_Address& adrs);
+
+      /**
+       * Algorithm 12: "XMSS_sign"
+       * Generate an XMSS signature and update the XMSS secret key
+       *
+       * @param msg A message to sign of arbitrary length.
+       * @param [out] xmss_priv_key A XMSS private key. The private key will be
+       *              updated during the signing process.
+       *
+       * @return The signature of msg signed using xmss_priv_key.
+       **/
+      XMSS_Signature sign(
+         const secure_vector<uint8_t>& msg,
+         XMSS_PrivateKey& xmss_priv_key);
+
+      wots_keysig_t build_auth_path(XMSS_PrivateKey& priv_key,
+                                    XMSS_Address& adrs);
+
+      void initialize();
+
+      XMSS_PrivateKey m_priv_key;
+      const XMSS_Parameters m_xmss_params;
+      XMSS_Hash m_hash;
+      secure_vector<uint8_t> m_randomness;
+      uint32_t m_leaf_idx;
+      bool m_is_initialized;
+   };
+
+}
+
+#endif
+
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h b/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h
@@ -0,0 +1,108 @@
+/*
+ * XMSS Tools
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_TOOLS_H_
+#define BOTAN_XMSS_TOOLS_H_
+
+#include <botan/cpuid.h>
+#include <botan/secmem.h>
+#include <iterator>
+#include <type_traits>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_tools.h)
+
+namespace Botan {
+
+/**
+ * Helper tools for low level byte operations required
+ * for the XMSS implementation.
+ **/
+class XMSS_Tools final
+   {
+   public:
+      XMSS_Tools(const XMSS_Tools&) = delete;
+      void operator=(const XMSS_Tools&) = delete;
+
+      /**
+       * Concatenates the byte representation in big-endian order of any
+       * integral value to a secure_vector.
+       *
+       * @param target Vector to concatenate the byte representation of the
+       *               integral value to.
+       * @param src integral value to concatenate.
+       **/
+      template<typename T,
+               typename U = typename std::enable_if<std::is_integral<T>::value,
+                     void>::type>
+      static void concat(secure_vector<uint8_t>& target, const T& src);
+
+      /**
+       * Concatenates the last n bytes of the byte representation in big-endian
+       * order of any integral value to a to a secure_vector.
+       *
+       * @param target Vector to concatenate the byte representation of the
+       *               integral value to.
+       * @param src Integral value to concatenate.
+       * @param len number of bytes to concatenate. This value must be smaller
+       *            or equal to the size of type T.
+       **/
+      template <typename T,
+                typename U = typename std::enable_if<std::is_integral<T>::value,
+                void>::type>
+      static void concat(secure_vector<uint8_t>& target, const T& src, size_t len);
+
+   private:
+      XMSS_Tools();
+   };
+
+template <typename T, typename U>
+void XMSS_Tools::concat(secure_vector<uint8_t>& target, const T& src)
+   {
+   const uint8_t* src_bytes = reinterpret_cast<const uint8_t*>(&src);
+   if(CPUID::is_little_endian())
+      {
+      std::reverse_copy(src_bytes,
+                        src_bytes + sizeof(src),
+                        std::back_inserter(target));
+      }
+   else
+      {
+      std::copy(src_bytes,
+                src_bytes + sizeof(src),
+                std::back_inserter(target));
+      }
+   }
+
+
+template <typename T, typename U>
+void XMSS_Tools::concat(secure_vector<uint8_t>& target,
+                        const T& src,
+                        size_t len)
+   {
+   size_t c = static_cast<size_t>(std::min(len, sizeof(src)));
+   if(len > sizeof(src))
+      {
+      target.resize(target.size() + len - sizeof(src), 0);
+      }
+
+   const uint8_t* src_bytes = reinterpret_cast<const uint8_t*>(&src);
+   if(CPUID::is_little_endian())
+      {
+      std::reverse_copy(src_bytes,
+                        src_bytes + c,
+                        std::back_inserter(target));
+      }
+   else
+      {
+      std::copy(src_bytes + sizeof(src) - c,
+                src_bytes + sizeof(src),
+                std::back_inserter(target));
+      }
+   }
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp
@@ -0,0 +1,136 @@
+/*
+ * XMSS Verification Operation
+ * Provides signature verification capabilities for Extended Hash-Based
+ * Signatures (XMSS).
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_verification_operation.h>
+#include <botan/xmss_common_ops.h>
+
+namespace Botan {
+
+XMSS_Verification_Operation::XMSS_Verification_Operation(
+   const XMSS_PublicKey& public_key) :
+   m_pub_key(public_key),
+   m_hash(public_key.xmss_hash_function()),
+   m_msg_buf(0)
+   {
+   }
+
+secure_vector<uint8_t>
+XMSS_Verification_Operation::root_from_signature(const XMSS_Signature& sig,
+      const secure_vector<uint8_t>& msg,
+      XMSS_Address& adrs,
+      const secure_vector<uint8_t>& seed)
+   {
+   const auto params = m_pub_key.xmss_parameters();
+
+   const uint32_t next_index = static_cast<uint32_t>(sig.unused_leaf_index());
+   adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+   adrs.set_ots_address(next_index);
+
+   XMSS_WOTS_PublicKey pub_key_ots(m_pub_key.wots_parameters().oid(),
+                                   msg,
+                                   sig.tree().ots_signature(),
+                                   adrs,
+                                   seed);
+
+   adrs.set_type(XMSS_Address::Type::LTree_Address);
+   adrs.set_ltree_address(next_index);
+
+   std::array<secure_vector<uint8_t>, 2> node;
+   XMSS_Common_Ops::create_l_tree(node[0], pub_key_ots, adrs, seed, m_hash, params);
+
+   adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+   adrs.set_tree_index(next_index);
+
+   for(size_t k = 0; k < params.tree_height(); k++)
+      {
+      adrs.set_tree_height(static_cast<uint32_t>(k));
+      if(((next_index / (static_cast<size_t>(1) << k)) & 0x01) == 0)
+         {
+         adrs.set_tree_index(adrs.get_tree_index() >> 1);
+         XMSS_Common_Ops::randomize_tree_hash(node[1],
+                                              node[0],
+                                              sig.tree().authentication_path()[k],
+                                              adrs,
+                                              seed,
+                                              m_hash,
+                                              params);
+         }
+      else
+         {
+         adrs.set_tree_index((adrs.get_tree_index() - 1) >> 1);
+         XMSS_Common_Ops::randomize_tree_hash(node[1],
+                                              sig.tree().authentication_path()[k],
+                                              node[0],
+                                              adrs,
+                                              seed,
+                                              m_hash,
+                                              params);
+         }
+      node[0] = node[1];
+      }
+   return node[0];
+   }
+
+bool
+XMSS_Verification_Operation::verify(const XMSS_Signature& sig,
+                                    const secure_vector<uint8_t>& msg,
+                                    const XMSS_PublicKey& public_key)
+   {
+   XMSS_Address adrs;
+   secure_vector<uint8_t> index_bytes;
+   XMSS_Tools::concat(index_bytes,
+                      sig.unused_leaf_index(),
+                      m_pub_key.xmss_parameters().element_size());
+   secure_vector<uint8_t> msg_digest =
+      m_hash.h_msg(sig.randomness(),
+                   public_key.root(),
+                   index_bytes,
+                   msg);
+
+   secure_vector<uint8_t> node = root_from_signature(sig,
+                                 msg_digest,
+                                 adrs,
+                                 public_key.public_seed());
+
+   return (node == public_key.root());
+   }
+
+// FIXME: XMSS signature verification requires the "randomness" parameter out
+// of the XMSS signature, which is part of the prefix that is hashed before
+// msg. Since the signature is unknown till sign() is called all message
+// content has to be buffered. For large messages this can be inconvenient or
+// impossible.
+// Possible solution: Change PK_Ops::Verification interface to take the
+// signature as constructor argument, make sign a parameterless member call.
+void XMSS_Verification_Operation::update(const uint8_t msg[], size_t msg_len)
+   {
+   std::copy(msg, msg + msg_len, std::back_inserter(m_msg_buf));
+   }
+
+bool XMSS_Verification_Operation::is_valid_signature(const uint8_t sig[],
+      size_t sig_len)
+   {
+   try
+      {
+      XMSS_Signature signature(m_pub_key.xmss_parameters().oid(),
+                               secure_vector<uint8_t>(sig, sig + sig_len));
+      bool result = verify(signature, m_msg_buf, m_pub_key);
+      m_msg_buf.clear();
+      return result;
+      }
+   catch(...)
+      {
+      m_msg_buf.clear();
+      return false;
+      }
+   }
+
+}
+
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h b/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h
@@ -0,0 +1,76 @@
+/*
+ * XMSS Verification Operation
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_VERIFICATION_OPERATION_H_
+#define BOTAN_XMSS_VERIFICATION_OPERATION_H_
+
+#include <array>
+#include <cstddef>
+#include <iterator>
+#include <string>
+#include <botan/types.h>
+#include <botan/xmss_publickey.h>
+#include <botan/pk_ops.h>
+#include <botan/internal/xmss_signature.h>
+
+namespace Botan {
+
+/**
+ * Provides signature verification capabilities for Extended Hash-Based
+ * Signatures (XMSS).
+ **/
+ class XMSS_Verification_Operation final : public virtual PK_Ops::Verification
+   {
+   public:
+      XMSS_Verification_Operation(
+         const XMSS_PublicKey& public_key);
+
+      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+
+      void update(const uint8_t msg[], size_t msg_len) override;
+
+   private:
+      /**
+       * Algorithm 13: "XMSS_rootFromSig"
+       * Computes a root node using an XMSS signature, a message and a seed.
+       *
+       * @param msg A message.
+       * @param sig The XMSS signature for msg.
+       * @param ards A XMSS tree address.
+       * @param seed A seed.
+       *
+       * @return An n-byte string holding the value of the root of a tree
+       *         defined by the input parameters.
+       **/
+      secure_vector<uint8_t> root_from_signature(
+         const XMSS_Signature& sig,
+         const secure_vector<uint8_t>& msg,
+         XMSS_Address& ards,
+         const secure_vector<uint8_t>& seed);
+
+      /**
+       * Algorithm 14: "XMSS_verify"
+       * Verifies a XMSS signature using the corresponding XMSS public key.
+       *
+       * @param sig A XMSS signature.
+       * @param msg The message signed with sig.
+       * @param pub_key the public key
+       *
+       * @return true if signature sig is valid for msg, false otherwise.
+       **/
+      bool verify(const XMSS_Signature& sig,
+                  const secure_vector<uint8_t>& msg,
+                  const XMSS_PublicKey& pub_key);
+
+      const XMSS_PublicKey& m_pub_key;
+      XMSS_Hash m_hash;
+      secure_vector<uint8_t> m_msg_buf;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h
@@ -0,0 +1,68 @@
+/**
+ * XMSS WOTS Addressed Private Key
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_ADDRESSED_PRIVATEKEY_H_
+#define BOTAN_XMSS_WOTS_ADDRESSED_PRIVATEKEY_H_
+
+#include <botan/xmss_address.h>
+#include <botan/internal/xmss_wots_addressed_publickey.h>
+#include <botan/xmss_wots_privatekey.h>
+
+namespace Botan {
+
+/**
+ * Wrapper class to pair an XMSS_WOTS_PrivateKey with an XMSS Address. Since
+ * the PK_Ops::Signature interface does not allow an extra address
+ * parameter to be passed to the sign(RandomNumberGenerator&), the address
+ * needs to be stored together with the key and passed to the
+ * XMSS_WOTS_Signature_Operation() on creation.
+ **/
+class XMSS_WOTS_Addressed_PrivateKey final :
+      public virtual XMSS_WOTS_Addressed_PublicKey,
+      public virtual Private_Key
+   {
+   public:
+      XMSS_WOTS_Addressed_PrivateKey(const XMSS_WOTS_PrivateKey& private_key)
+         : XMSS_WOTS_Addressed_PublicKey(private_key),
+           m_priv_key(private_key) {}
+
+      XMSS_WOTS_Addressed_PrivateKey(const XMSS_WOTS_PrivateKey& private_key,
+                                     const XMSS_Address& adrs)
+         : XMSS_WOTS_Addressed_PublicKey(private_key, adrs),
+           m_priv_key(private_key) {}
+
+      XMSS_WOTS_Addressed_PrivateKey(XMSS_WOTS_PrivateKey&& private_key)
+         : XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey(private_key)),
+           m_priv_key(std::move(private_key)) {}
+
+      XMSS_WOTS_Addressed_PrivateKey(XMSS_WOTS_PrivateKey&& private_key,
+                                     XMSS_Address&& adrs)
+         : XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey(private_key),
+                                         std::move(adrs)),
+           m_priv_key(std::move(private_key)) {}
+
+      const XMSS_WOTS_PrivateKey& private_key() const { return m_priv_key; }
+      XMSS_WOTS_PrivateKey& private_key() { return m_priv_key; }
+
+      AlgorithmIdentifier
+      pkcs8_algorithm_identifier() const override
+         {
+         return m_priv_key.pkcs8_algorithm_identifier();
+         }
+
+      secure_vector<uint8_t> private_key_bits() const override
+         {
+         return m_priv_key.private_key_bits();
+         }
+
+   private:
+      XMSS_WOTS_PrivateKey m_priv_key;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h
@@ -0,0 +1,96 @@
+/**
+ * XMSS WOTS Addressed Public Key
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+
+#ifndef BOTAN_XMSS_WOTS_ADDRESSED_PUBLICKEY_H_
+#define BOTAN_XMSS_WOTS_ADDRESSED_PUBLICKEY_H_
+
+#include <botan/xmss_address.h>
+#include <botan/xmss_wots_publickey.h>
+
+namespace Botan {
+
+/**
+ * Wrapper class to pair a XMSS_WOTS_PublicKey with an XMSS Address. Since
+ * the PK_Ops::Verification interface does not allow an extra address
+ * parameter to be passed to the sign(RandomNumberGenerator&), the address
+ * needs to be stored together with the key and passed to the
+ * XMSS_WOTS_Verification_Operation() on creation.
+ **/
+class XMSS_WOTS_Addressed_PublicKey : public virtual Public_Key
+   {
+   public:
+      XMSS_WOTS_Addressed_PublicKey(const XMSS_WOTS_PublicKey& public_key)
+         : m_pub_key(public_key), m_adrs() {}
+
+      XMSS_WOTS_Addressed_PublicKey(const XMSS_WOTS_PublicKey& public_key,
+                                    const XMSS_Address& adrs)
+         : m_pub_key(public_key), m_adrs(adrs) {}
+
+      XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey&& public_key)
+         : m_pub_key(std::move(public_key)), m_adrs() {}
+
+      XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey&& public_key,
+                                    XMSS_Address&& adrs)
+         : m_pub_key(std::move(public_key)), m_adrs(std::move(adrs)) {}
+
+      const XMSS_WOTS_PublicKey& public_key() const { return m_pub_key; }
+      XMSS_WOTS_PublicKey& public_key()  { return m_pub_key; }
+
+      const XMSS_Address& address() const { return m_adrs; }
+      XMSS_Address& address() { return m_adrs; }
+
+      std::string algo_name() const override
+         {
+         return m_pub_key.algo_name();
+         }
+
+      AlgorithmIdentifier algorithm_identifier() const override
+         {
+         return m_pub_key.algorithm_identifier();
+         }
+
+      bool check_key(RandomNumberGenerator& rng, bool strong) const override
+         {
+         return m_pub_key.check_key(rng, strong);
+         }
+
+      std::unique_ptr<PK_Ops::Verification>
+      create_verification_op(const std::string& params,
+                             const std::string& provider) const override
+         {
+         return m_pub_key.create_verification_op(params, provider);
+         }
+
+      OID get_oid() const override
+         {
+         return m_pub_key.get_oid();
+         }
+
+      size_t estimated_strength() const override
+         {
+         return m_pub_key.estimated_strength();
+         }
+
+      size_t key_length() const override
+         {
+         return m_pub_key.estimated_strength();
+         }
+
+      std::vector<uint8_t> public_key_bits() const override
+         {
+         return m_pub_key.public_key_bits();
+         }
+
+   protected:
+      XMSS_WOTS_PublicKey m_pub_key;
+      XMSS_Address m_adrs;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
@@ -0,0 +1,136 @@
+/*
+ * XMSS WOTS Parameters
+ * Descibes a signature method for XMSS Winternitz One Time Signatures,
+ * as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots_parameters.h>
+#include <botan/exceptn.h>
+#include <cmath>
+
+namespace Botan {
+
+XMSS_WOTS_Parameters::ots_algorithm_t
+XMSS_WOTS_Parameters::xmss_wots_id_from_string(const std::string& param_set)
+   {
+   if(param_set == "WOTSP-SHA2_256")
+      { return WOTSP_SHA2_256; }
+   if(param_set == "WOTSP-SHA2_512")
+      { return WOTSP_SHA2_512; }
+   if(param_set == "WOTSP-SHAKE_256")
+      { return WOTSP_SHAKE_256; }
+   if(param_set == "WOTSP-SHAKE_512")
+      { return WOTSP_SHAKE_512; }
+   throw Invalid_Argument("Unknown XMSS-WOTS algorithm param '" + param_set + "'");
+   }
+
+XMSS_WOTS_Parameters::XMSS_WOTS_Parameters(const std::string& param_set)
+   : XMSS_WOTS_Parameters(xmss_wots_id_from_string(param_set))
+   {}
+
+XMSS_WOTS_Parameters::XMSS_WOTS_Parameters(ots_algorithm_t oid)
+   : m_oid(oid)
+   {
+   switch(oid)
+      {
+      case WOTSP_SHA2_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_name = "WOTSP-SHA2_256";
+         m_hash_name = "SHA-256";
+         m_strength = 256;
+         break;
+      case WOTSP_SHA2_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_name = "WOTSP-SHA2_512";
+         m_hash_name = "SHA-512";
+         m_strength = 512;
+         break;
+      case WOTSP_SHAKE_256:
+         m_element_size = 32;
+         m_w = 16;
+         m_len = 67;
+         m_name = "WOTSP-SHAKE_256";
+         m_hash_name = "SHAKE-128(256)";
+         m_strength = 256;
+         break;
+      case WOTSP_SHAKE_512:
+         m_element_size = 64;
+         m_w = 16;
+         m_len = 131;
+         m_name = "WOTSP-SHAKE_512";
+         m_hash_name = "SHAKE-256(512)";
+         m_strength = 512;
+         break;
+      default:
+         throw Not_Implemented("Algorithm id does not match any known XMSS WOTS algorithm id.");
+         break;
+      }
+
+   m_lg_w = (m_w == 16) ? 4 : 2;
+   m_len_1 = static_cast<size_t>(std::ceil((8 * element_size()) / m_lg_w));
+   m_len_2 = static_cast<size_t>(
+                floor(log2(m_len_1 * (wots_parameter() - 1)) / m_lg_w) + 1);
+   BOTAN_ASSERT(m_len == m_len_1 + m_len_2, "Invalid XMSS WOTS parameter "
+                "\"len\" detedted.");
+   }
+
+secure_vector<uint8_t>
+XMSS_WOTS_Parameters::base_w(const secure_vector<uint8_t>& msg, size_t out_size) const
+   {
+   secure_vector<uint8_t> result;
+   size_t in = 0;
+   size_t total = 0;
+   size_t bits = 0;
+
+   for(size_t i = 0; i < out_size; i++)
+      {
+      if(bits == 0)
+         {
+         total = msg[in];
+         in++;
+         bits += 8;
+         }
+      bits -= m_lg_w;
+      result.push_back(static_cast<uint8_t>((total >> bits) & (m_w - 1)));
+      }
+   return result;
+   }
+
+secure_vector<uint8_t>
+XMSS_WOTS_Parameters::base_w(size_t value) const
+   {
+   value <<= (8 - ((m_len_2 * m_lg_w) % 8));
+   size_t len_2_bytes = static_cast<size_t>(
+                           std::ceil(static_cast<float>(m_len_2 * m_lg_w) / 8.f));
+   secure_vector<uint8_t> result;
+   XMSS_Tools::concat(result, value, len_2_bytes);
+   return base_w(result, m_len_2);
+   }
+
+void
+XMSS_WOTS_Parameters::append_checksum(secure_vector<uint8_t>& data)
+   {
+   size_t csum = 0;
+
+   for(size_t i = 0; i < data.size(); i++)
+      {
+      csum += wots_parameter() - 1 - data[i];
+      }
+
+   secure_vector<uint8_t> csum_bytes = base_w(csum);
+   std::move(csum_bytes.begin(), csum_bytes.end(), std::back_inserter(data));
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h
@@ -0,0 +1,122 @@
+/*
+ * XMSS WOTS Parameters
+ * (C) 2016,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PARAMETERS_H_
+#define BOTAN_XMSS_WOTS_PARAMETERS_H_
+
+#include <botan/xmss_tools.h>
+#include <botan/secmem.h>
+#include <map>
+#include <string>
+
+namespace Botan {
+
+/**
+ * Descibes a signature method for XMSS Winternitz One Time Signatures,
+ * as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class XMSS_WOTS_Parameters final
+   {
+   public:
+      enum ots_algorithm_t
+         {
+         WOTSP_SHA2_256 = 0x00000001,
+         WOTSP_SHA2_512 = 0x00000002,
+         WOTSP_SHAKE_256 = 0x00000003,
+         WOTSP_SHAKE_512 = 0x00000004
+         };
+
+      XMSS_WOTS_Parameters(const std::string& algo_name);
+      XMSS_WOTS_Parameters(ots_algorithm_t ots_spec);
+
+      static ots_algorithm_t xmss_wots_id_from_string(const std::string& param_set);
+
+      /**
+       * Algorithm 1: convert input string to base.
+       *
+       * @param msg Input string (referred to as X in [1]).
+       * @param out_size size of message in base w.
+       *
+       * @return Input string converted to the given base.
+       **/
+      secure_vector<uint8_t> base_w(const secure_vector<uint8_t>& msg, size_t out_size) const;
+
+      secure_vector<uint8_t> base_w(size_t value) const;
+
+      void append_checksum(secure_vector<uint8_t>& data);
+
+      /**
+       * @return XMSS WOTS registry name for the chosen parameter set.
+       **/
+      const std::string& name() const
+         {
+         return m_name;
+         }
+
+      /**
+       * @return Botan name for the hash function used.
+       **/
+      const std::string& hash_function_name() const
+         {
+         return m_hash_name;
+         }
+
+      /**
+       * Retrieves the uniform length of a message, and the size of
+       * each node. This correlates to XMSS parameter "n" defined
+       * in [1].
+       *
+       * @return element length in bytes.
+       **/
+      size_t element_size() const { return m_element_size; }
+
+      /**
+       * The Winternitz parameter.
+       *
+       * @return numeric base used for internal representation of
+       *         data.
+       **/
+      size_t wots_parameter() const { return m_w; }
+
+      size_t len() const { return m_len; }
+
+      size_t len_1() const { return m_len_1; }
+
+      size_t len_2() const { return m_len_2; }
+
+      size_t lg_w() const { return m_lg_w; }
+
+      ots_algorithm_t oid() const { return m_oid; }
+
+      size_t estimated_strength() const { return m_strength; }
+
+      bool operator==(const XMSS_WOTS_Parameters& p) const
+         {
+         return m_oid == p.m_oid;
+         }
+
+   private:
+      static const std::map<std::string, ots_algorithm_t> m_oid_name_lut;
+      ots_algorithm_t m_oid;
+      std::string m_name;
+      std::string m_hash_name;
+      size_t m_element_size;
+      size_t m_w;
+      size_t m_len_1;
+      size_t m_len_2;
+      size_t m_len;
+      size_t m_strength;
+      uint8_t m_lg_w;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
@@ -0,0 +1,82 @@
+/*
+ * XMSS WOTS Private Key
+ * A Winternitz One Time Signature private key for use with Extended Hash-Based
+ * Signatures.
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots_privatekey.h>
+
+namespace Botan {
+
+wots_keysig_t
+XMSS_WOTS_PrivateKey::generate(const secure_vector<uint8_t>& priv_seed,
+                               XMSS_Hash& hash)
+   {
+   wots_keysig_t priv_key(m_wots_params.len(),
+                          secure_vector<uint8_t>(0));
+
+   for(size_t i = 0; i < m_wots_params.len(); i++)
+      {
+      XMSS_Tools::concat<size_t>(priv_key[i], i, 32);
+      hash.prf(priv_key[i], priv_seed, priv_key[i]);
+      }
+   return priv_key;
+   }
+
+
+XMSS_WOTS_PublicKey
+XMSS_WOTS_PrivateKey::generate_public_key(XMSS_Address& adrs)
+   {
+   XMSS_WOTS_PublicKey pub_key(m_wots_params.oid(),
+                               public_seed());
+   generate_public_key(pub_key, wots_keysig_t((*this)[adrs]), adrs);
+   return pub_key;
+   }
+
+void
+XMSS_WOTS_PrivateKey::generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+                                          wots_keysig_t&& in_key_data,
+                                          XMSS_Address& adrs,
+                                          XMSS_Hash& hash)
+   {
+   BOTAN_ASSERT(wots_parameters() == pub_key.wots_parameters() &&
+                public_seed() == pub_key.public_seed(),
+                "Conflicting public key data.");
+
+   pub_key.set_key_data(std::move(in_key_data));
+   for(size_t i = 0; i < m_wots_params.len(); i++)
+      {
+      adrs.set_chain_address(static_cast<uint32_t>(i));
+      chain(pub_key[i], 0, m_wots_params.wots_parameter() - 1, adrs,
+            public_seed(), hash);
+      }
+   }
+
+wots_keysig_t
+XMSS_WOTS_PrivateKey::sign(const secure_vector<uint8_t>& msg,
+                           XMSS_Address& adrs,
+                           XMSS_Hash& hash)
+
+   {
+   secure_vector<uint8_t> msg_digest
+      {
+      m_wots_params.base_w(msg, m_wots_params.len_1())
+      };
+
+   m_wots_params.append_checksum(msg_digest);
+   wots_keysig_t sig(this->at(adrs, hash));
+
+   for(size_t i = 0; i < m_wots_params.len(); i++)
+      {
+      adrs.set_chain_address(static_cast<uint32_t>(i));
+      chain(sig[i], 0 , msg_digest[i], adrs, m_public_seed, hash);
+      }
+
+   return sig;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h
@@ -0,0 +1,335 @@
+/*
+ * XMSS WOTS Private Key
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PRIVATEKEY_H_
+#define BOTAN_XMSS_WOTS_PRIVATEKEY_H_
+
+#include <cstddef>
+#include <memory>
+#include <botan/alg_id.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/rng.h>
+#include <botan/xmss_wots_parameters.h>
+#include <botan/xmss_address.h>
+#include <botan/xmss_wots_publickey.h>
+
+namespace Botan {
+
+/** A Winternitz One Time Signature private key for use with Extended Hash-Based
+ * Signatures.
+ **/
+class XMSS_WOTS_PrivateKey final : public virtual XMSS_WOTS_PublicKey,
+   public virtual Private_Key
+   {
+   public:
+      /**
+       * Creates a WOTS private key for the chosen XMSS WOTS signature method.
+       * Members need to be initialized manually.
+       *
+       * @param oid Identifier for the selected signature method.
+       **/
+      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid)
+         : XMSS_WOTS_PublicKey(oid)
+         {}
+
+      /**
+       * Creates a WOTS private key for the chosen XMSS WOTS signature method.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param rng A random number generator to use for key generation.
+       **/
+      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                           RandomNumberGenerator& rng)
+         : XMSS_WOTS_PublicKey(oid, rng),
+           m_private_seed(rng.random_vec(m_wots_params.element_size()))
+         {
+         set_key_data(generate(m_private_seed));
+         }
+
+      /**
+       * Constructs a WOTS private key. Chains will be generated on demand
+       * applying a hash function to a unique value generated from a secret
+       * seed and a counter. The secret seed of length n, will be
+       * automatically generated using AutoSeeded_RNG(). "n" equals
+       * the element size of the chosen WOTS security parameter set.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param public_seed A public seed used for the pseudo random generation
+       *        of public keys derived from this private key.
+       * @param rng A random number generator to use for key generation.
+       **/
+      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                           const secure_vector<uint8_t>& public_seed,
+                           RandomNumberGenerator& rng)
+         : XMSS_WOTS_PublicKey(oid, public_seed),
+           m_private_seed(rng.random_vec(m_wots_params.element_size()))
+         {
+         set_key_data(generate(m_private_seed));
+         }
+
+      /**
+       * Constructs a WOTS private key. Chains will be generated on demand
+       * applying a hash function to a unique value generated from a secret
+       * seed and a counter. The secret seed of length n, will be
+       * automatically generated using AutoSeeded_RNG(). "n" equals
+       * the element size of the chosen WOTS security parameter set.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param public_seed A public seed used for the pseudo random generation
+       *        of public keys derived from this private key.
+       **/
+      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                           const secure_vector<uint8_t>& public_seed)
+         : XMSS_WOTS_PublicKey(oid, public_seed)
+         {}
+
+      /**
+       * Constructs a WOTS private key. Chains will be generated on demand
+       * applying a hash function to a unique value generated from the
+       * secret seed and a counter.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param public_seed A public seed used for the pseudo random generation
+       *        of public keys derived from this private key.
+       * @param private_seed A secret uniformly random n-byte value.
+       **/
+      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                           const secure_vector<uint8_t>& public_seed,
+                           const secure_vector<uint8_t>& private_seed)
+         : XMSS_WOTS_PublicKey(oid, public_seed),
+           m_private_seed(private_seed)
+         {
+         set_key_data(generate(private_seed));
+         }
+
+      /**
+       * Retrieves the i-th WOTS private key using pseudo random key
+       * (re-)generation.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each
+       * thread.
+       *
+       * @param i Index of the key to retrieve.
+       * @param hash Instance of XMSS_Hash, that may only be used by the
+       *        thead executing at.
+       *
+       * @return WOTS secret key.
+       **/
+      wots_keysig_t at(size_t i, XMSS_Hash& hash)
+         {
+         secure_vector<uint8_t> idx_bytes;
+         XMSS_Tools::concat(idx_bytes, i, m_wots_params.element_size());
+         hash.h(idx_bytes, m_private_seed, idx_bytes);
+         return generate(idx_bytes, hash);
+         }
+
+      /**
+       * Retrieves the i-th WOTS private key using pseudo random key
+       * (re-)generation.
+       *
+       * @param i Index of the key to retrieve.
+       *
+       * @return WOTS secret key.
+       **/
+      inline wots_keysig_t operator[](size_t i)
+         {
+         return this->at(i, m_hash);
+         }
+
+      /**
+       * Retrieves the i-th WOTS private key using pseudo random key
+       * (re-)generation.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each
+       * thread.
+       *
+       * @param adrs The address of the key to retrieve.
+       * @param hash Instance of XMSS_Hash, that may only be used by the
+       *        thead executing at.
+       *
+       * @return WOTS secret key.
+       **/
+      wots_keysig_t at(const XMSS_Address& adrs, XMSS_Hash& hash)
+         {
+         secure_vector<uint8_t> result;
+         hash.prf(result, m_private_seed, adrs.bytes());
+         return generate(result, hash);
+         }
+
+      inline wots_keysig_t operator[](const XMSS_Address& adrs)
+         {
+         return this->at(adrs, m_hash);
+         }
+
+      wots_keysig_t generate_private_key(const secure_vector<uint8_t>& priv_seed);
+
+      /**
+       * Algorithm 4: "WOTS_genPK"
+       * Generates a Winternitz One Time Signature+ (WOTS+) Public Key from a
+       * given private key.
+       *
+       * @param adrs Hash function address encoding the address of the WOTS+
+       *             key pair within a greater structure.
+       *
+       * @return A XMSS_WOTS_PublicKey.
+       **/
+      XMSS_WOTS_PublicKey generate_public_key(XMSS_Address& adrs);
+
+      /**
+       * Algorithm 4: "WOTS_genPK"
+       * Initializes a Winternitz One Time Signature+ (WOTS+) Public Key's
+       * key_data() member, with data derived from in_key_data using the
+       * WOTS chaining function.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each
+       * thread.
+       *
+       * @param[out] pub_key Public key to initialize key_data() member on.
+       * @param in_key_data Input key material from private key used for
+       *        public key generation.
+       * @param adrs Hash function address encoding the address of
+       *        the WOTS+ key pair within a greater structure.
+       * @param hash Instance of XMSS_Hash, that may only by the thead
+       *        executing generate_public_key.
+       **/
+      void generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+                               wots_keysig_t&& in_key_data,
+                               XMSS_Address& adrs,
+                               XMSS_Hash& hash);
+      /**
+       * Algorithm 4: "WOTS_genPK"
+       * Initializes a Winternitz One Time Signature+ (WOTS+) Public Key's
+       * key_data() member, with data derived from in_key_data using the
+       * WOTS chaining function.
+       *
+       * @param[out] pub_key Public key to initialize key_data() member on.
+       * @param in_key_data Input key material from private key used for
+       *        public key generation.
+       * @param adrs Hash function address encoding the address of
+       *        the WOTS+ key pair within a greater structure.
+       **/
+      inline void generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+                                      wots_keysig_t&& in_key_data,
+                                      XMSS_Address& adrs)
+         {
+         generate_public_key(pub_key, std::forward<wots_keysig_t>(in_key_data), adrs, m_hash);
+         }
+
+      /**
+       * Algorithm 5: "WOTS_sign"
+       * Generates a signature from a private key and a message.
+       *
+       * @param msg A message to sign.
+       * @param adrs An OTS hash address identifying the WOTS+ key pair
+       *        used for signing.
+       *
+       * @return signature for msg.
+       **/
+      inline wots_keysig_t sign(const secure_vector<uint8_t>& msg,
+                                XMSS_Address& adrs)
+         {
+         return sign(msg, adrs, m_hash);
+         }
+
+      /**
+       * Algorithm 5: "WOTS_sign"
+       * Generates a signature from a private key and a message.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each
+       * thread.
+       *
+       * @param msg A message to sign.
+       * @param adrs An OTS hash address identifying the WOTS+ key pair
+       *        used for signing.
+       * @param hash Instance of XMSS_Hash, that may only be used by the
+       *        thead executing sign.
+       *
+       * @return signature for msg.
+       **/
+      wots_keysig_t sign(const secure_vector<uint8_t>& msg,
+                         XMSS_Address& adrs,
+                         XMSS_Hash& hash);
+
+      /**
+       * Retrieves the secret seed used to generate WOTS+ chains. The seed
+       * should be a uniformly random n-byte value.
+       *
+       * @return secret seed.
+       **/
+      const secure_vector<uint8_t>& private_seed() const
+         {
+         return m_private_seed;
+         }
+
+      /**
+       * Sets the secret seed used to generate WOTS+ chains. The seed
+       * should be a uniformly random n-byte value.
+       *
+       * @param private_seed Uniformly random n-byte value.
+       **/
+      void set_private_seed(const secure_vector<uint8_t>& private_seed)
+         {
+         m_private_seed = private_seed;
+         }
+
+      /**
+       * Sets the secret seed used to generate WOTS+ chains. The seed
+       * should be a uniformly random n-byte value.
+       *
+       * @param private_seed Uniformly random n-byte value.
+       **/
+      void set_private_seed(secure_vector<uint8_t>&& private_seed)
+         {
+         m_private_seed = std::move(private_seed);
+         }
+
+      AlgorithmIdentifier
+      pkcs8_algorithm_identifier() const override
+         {
+         throw Not_Implemented("No AlgorithmIdentifier available for XMSS-WOTS.");
+         }
+
+      secure_vector<uint8_t> private_key_bits() const override
+         {
+         throw Not_Implemented("No PKCS8 key format defined for XMSS-WOTS.");
+         }
+
+   private:
+      /**
+       * Algorithm 3: "Generating a WOTS+ Private Key".
+       * Generates a private key.
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each thread.
+       *
+       * @param private_seed Uniformly random n-byte value.
+       * @param[in] hash Instance of XMSS_Hash, that may only be used by the
+       *            thead executing generate.
+       *
+       * @returns a vector of length key_size() of vectors of n bytes length
+       *          containing uniformly random data.
+       **/
+      wots_keysig_t generate(const secure_vector<uint8_t>& private_seed,
+                             XMSS_Hash& hash);
+
+      inline wots_keysig_t generate(const secure_vector<uint8_t>& private_seed)
+         {
+         return generate(private_seed, m_hash);
+         }
+
+      secure_vector<uint8_t> m_private_seed;
+   };
+
+}
+
+#endif
+
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
@@ -0,0 +1,71 @@
+/*
+ * XMSS WOTS Public Key
+ * A Winternitz One Time Signature public key for use with Extended Hash-Based
+ * Signatures.
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots_publickey.h>
+
+namespace Botan {
+
+void
+XMSS_WOTS_PublicKey::chain(secure_vector<uint8_t>& result,
+                           size_t start_idx,
+                           size_t steps,
+                           XMSS_Address& adrs,
+                           const secure_vector<uint8_t>& seed,
+                           XMSS_Hash& hash)
+   {
+   secure_vector<uint8_t> prf_output(hash.output_length());
+
+   for(size_t i = start_idx;
+         i < (start_idx + steps) && i < m_wots_params.wots_parameter();
+         i++)
+      {
+      adrs.set_hash_address(static_cast<uint32_t>(i));
+
+      //Calculate tmp XOR bitmask
+      adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_Mode);
+      hash.prf(prf_output, seed, adrs.bytes());
+      xor_buf(result, prf_output, result.size());
+
+      // Calculate key
+      adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Key_Mode);
+
+      //Calculate f(key, tmp XOR bitmask)
+      hash.prf(prf_output, seed, adrs.bytes());
+      hash.f(result, prf_output, result);
+      }
+   }
+
+wots_keysig_t
+XMSS_WOTS_PublicKey::pub_key_from_signature(const secure_vector<uint8_t>& msg,
+                                            const wots_keysig_t& sig,
+                                            XMSS_Address& adrs,
+                                            const secure_vector<uint8_t>& seed)
+   {
+   secure_vector<uint8_t> msg_digest
+      {
+      m_wots_params.base_w(msg, m_wots_params.len_1())
+      };
+
+   m_wots_params.append_checksum(msg_digest);
+   wots_keysig_t result(sig);
+
+   for(size_t i = 0; i < m_wots_params.len(); i++)
+      {
+      adrs.set_chain_address(static_cast<uint32_t>(i));
+      chain(result[i],
+            msg_digest[i],
+            m_wots_params.wots_parameter() - 1 - msg_digest[i],
+            adrs,
+            seed);
+      }
+   return result;
+   }
+
+}
diff --git a/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h
@@ -0,0 +1,352 @@
+/*
+ * XMSS WOTS Public Key
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PUBLICKEY_H_
+#define BOTAN_XMSS_WOTS_PUBLICKEY_H_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+#include <botan/alg_id.h>
+#include <botan/rng.h>
+#include <botan/asn1_oid.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/types.h>
+#include <botan/xmss_wots_parameters.h>
+#include <botan/xmss_address.h>
+#include <botan/xmss_hash.h>
+
+namespace Botan {
+
+typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
+
+/**
+ * A Winternitz One Time Signature public key for use with Extended Hash-Based
+ * Signatures.
+ **/
+class XMSS_WOTS_PublicKey : virtual public Public_Key
+   {
+   public:
+      class TreeSignature final
+         {
+         public:
+            TreeSignature() = default;
+
+            TreeSignature(const wots_keysig_t& ots_sig,
+                          const wots_keysig_t& auth_path)
+               : m_ots_sig(ots_sig), m_auth_path(auth_path)
+               {}
+
+            TreeSignature(wots_keysig_t&& ots_sig,
+                          wots_keysig_t&& auth_path)
+               : m_ots_sig(std::move(ots_sig)),
+                 m_auth_path(std::move(auth_path))
+               {}
+
+            const wots_keysig_t& ots_signature() const
+               {
+               return m_ots_sig;
+               }
+
+            wots_keysig_t& ots_signature()
+               {
+               return m_ots_sig;
+               }
+
+            const wots_keysig_t& authentication_path() const
+               {
+               return m_auth_path;
+               }
+
+            wots_keysig_t& authentication_path()
+               {
+               return m_auth_path;
+               }
+
+         private:
+            wots_keysig_t m_ots_sig;
+            wots_keysig_t m_auth_path;
+         };
+
+      /**
+       * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+       * oid. The public seed for this key will be initialized with a
+       * uniformly random n-byte value, where "n" is the element size of the
+       * selected signature method.
+       *
+       * @param oid Identifier for the selected signature method.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()) {}
+
+      /**
+       * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+       * oid. The public seed for this key will be initialized with a
+       * uniformly random n-byte value, where "n" is the element size of the
+       * selected signature method.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param rng A random number generate used to generate the public seed.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                          RandomNumberGenerator& rng)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()),
+           m_public_seed(rng.random_vec(m_wots_params.element_size())) {}
+
+      /**
+       * Creates a XMSS_WOTS_PrivateKey for the signature method identified by
+       * oid, with a precomputed public seed.
+       *
+       * @param oid Identifier for the selected signature method.
+       * @param public_seed A precomputed public seed of n-bytes length.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                          secure_vector<uint8_t> public_seed)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()),
+           m_public_seed(public_seed) {}
+
+      /**
+       * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+       * oid. The public seed will be initialized with a precomputed seed and
+       * and precomputed key data which should be derived from a
+       * XMSS_WOTS_PrivateKey.
+       *
+       * @param oid Ident:s/ifier for the selected signature methods.
+       * @param public_seed A precomputed public seed of n-bytes length.
+       * @param key Precomputed raw key data of the XMSS_WOTS_PublicKey.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                          secure_vector<uint8_t>&& public_seed,
+                          wots_keysig_t&& key)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()),
+           m_key(std::move(key)),
+           m_public_seed(std::move(public_seed))
+         {}
+
+      /**
+       * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+       * oid. The public seed will be initialized with a precomputed seed and
+       * and precomputed key data which should be derived from a
+       * XMSS_WOTS_PrivateKey.
+       *
+       * @param oid Identifier for the selected signature methods.
+       * @param public_seed A precomputed public seed of n-bytes length.
+       * @param key Precomputed raw key data of the XMSS_WOTS_PublicKey.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                          const secure_vector<uint8_t>& public_seed,
+                          const wots_keysig_t& key)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()),
+           m_key(key),
+           m_public_seed(public_seed)
+         {}
+
+      /**
+       * Creates a XMSS_WOTS_PublicKey form a message and signature using
+       * Algorithm 6 WOTS_pkFromSig defined in the XMSS standard. This
+       * overload is used to verify a message using a public key.
+       *
+       * @param oid WOTSP algorithm identifier.
+       * @param msg A message.
+       * @param sig A WOTS signature for msg.
+       * @param adrs An XMSS_Address.
+       * @param public_seed The public public_seed.
+       **/
+      XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+                          const secure_vector<uint8_t>& msg,
+                          const wots_keysig_t& sig,
+                          XMSS_Address& adrs,
+                          const secure_vector<uint8_t>& public_seed)
+         : m_wots_params(oid),
+           m_hash(m_wots_params.hash_function_name()),
+           m_key(pub_key_from_signature(msg,
+                                        sig,
+                                        adrs,
+                                        public_seed)),
+           m_public_seed(public_seed)
+         {}
+
+      /**
+       * Retrieves the i-th element out of the length len chain of
+       * n-byte elements contained in the public key.
+       *
+       * @param i index of the element.
+       * @returns n-byte element addressed by i.
+       **/
+      const secure_vector<uint8_t>& operator[](size_t i) const { return m_key[i]; }
+      secure_vector<uint8_t>& operator[](size_t i) { return m_key[i]; }
+
+      /**
+       * Convert the key into the raw key data. The key becomes a length
+       * len vector of n-byte elements.
+       **/
+      operator const wots_keysig_t& () const { return m_key; }
+
+      /**
+       * Convert the key into the raw key data. The key becomes a length
+       * len vector of n-byte elements.
+       **/
+      operator wots_keysig_t& () { return m_key; }
+
+      const secure_vector<uint8_t>& public_seed() const { return m_public_seed; }
+
+      secure_vector<uint8_t>& public_seed() { return m_public_seed; }
+
+      void set_public_seed(const secure_vector<uint8_t>& public_seed)
+         {
+         m_public_seed = public_seed;
+         }
+
+      void set_public_seed(secure_vector<uint8_t>&& public_seed)
+         {
+         m_public_seed = std::move(public_seed);
+         }
+
+      const wots_keysig_t& key_data() const { return m_key; }
+
+      wots_keysig_t& key_data() { return m_key; }
+
+      void set_key_data(const wots_keysig_t& key_data)
+         {
+         m_key = key_data;
+         }
+
+      void set_key_data(wots_keysig_t&& key_data)
+         {
+         m_key = std::move(key_data);
+         }
+
+      const XMSS_WOTS_Parameters& wots_parameters() const
+         {
+         return m_wots_params;
+         }
+
+      std::string algo_name() const override
+         {
+         return m_wots_params.name();
+         }
+
+      AlgorithmIdentifier algorithm_identifier() const override
+         {
+         throw Not_Implemented("No AlgorithmIdentifier available for XMSS-WOTS.");
+         }
+
+      bool check_key(RandomNumberGenerator&, bool) const override
+         {
+         return true;
+         }
+
+      size_t estimated_strength() const override
+         {
+         return m_wots_params.estimated_strength();
+         }
+
+      size_t key_length() const override
+         {
+         return m_wots_params.estimated_strength();
+         }
+
+      std::vector<uint8_t> public_key_bits() const override
+         {
+         throw Not_Implemented("No key format defined for XMSS-WOTS");
+         }
+
+      bool operator==(const XMSS_WOTS_PublicKey& key)
+         {
+         return m_key == key.m_key;
+         }
+
+      bool operator!=(const XMSS_WOTS_PublicKey& key)
+         {
+         return !(*this == key);
+         }
+
+   protected:
+      /**
+       * Algorithm 2: Chaining Function.
+       *
+       * Takes an n-byte input string and transforms it into a the function
+       * result iterating the cryptographic hash function "F" steps times on
+       * the input x using the outputs of the PRNG "G".
+       *
+       * This overload is used in multithreaded scenarios, where it is
+       * required to provide seperate instances of XMSS_Hash to each
+       * thread.
+       *
+       * @param[out] x An n-byte input string, that will be transformed into
+       *               the chaining function result.
+       * @param start_idx The start index.
+       * @param steps A number of steps.
+       * @param adrs An OTS Hash Address.
+       * @param public_seed A public seed.
+       * @param hash Instance of XMSS_Hash, that may only by the thead
+       *        executing chain.
+       **/
+      void chain(secure_vector<uint8_t>& x,
+                 size_t start_idx,
+                 size_t steps,
+                 XMSS_Address& adrs,
+                 const secure_vector<uint8_t>& public_seed,
+                 XMSS_Hash& hash);
+
+      /**
+       * Algorithm 2: Chaining Function.
+       *
+       * Takes an n-byte input string and transforms it into a the function
+       * result iterating the cryptographic hash function "F" steps times on
+       * the input x using the outputs of the PRNG "G".
+       *
+       * @param[out] x An n-byte input string, that will be transformed into
+       *               the chaining function result.
+       * @param start_idx The start index.
+       * @param steps A number of steps.
+       * @param adrs An OTS Hash Address.
+       * @param public_seed A public seed.
+       **/
+      inline void chain(secure_vector<uint8_t>& x,
+                        size_t start_idx,
+                        size_t steps,
+                        XMSS_Address& adrs,
+                        const secure_vector<uint8_t>& public_seed)
+         {
+         chain(x, start_idx, steps, adrs, public_seed, m_hash);
+         }
+
+      XMSS_WOTS_Parameters m_wots_params;
+      XMSS_Hash m_hash;
+      wots_keysig_t m_key;
+      secure_vector<uint8_t> m_public_seed;
+
+   private:
+      /**
+       * Algorithm 6: "WOTS_pkFromSig"
+       * Computes a Winternitz One Time Signature+ public key from a message and
+       * its signature.
+       *
+       * @param msg A message.
+       * @param sig The signature for msg.
+       * @param adrs An address.
+       * @param public_seed A public_seed.
+       *
+       * @return Temporary WOTS+ public key.
+       **/
+      wots_keysig_t pub_key_from_signature(
+         const secure_vector<uint8_t>& msg,
+         const wots_keysig_t& sig,
+         XMSS_Address& adrs,
+         const secure_vector<uint8_t>& public_seed);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp b/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp
@@ -0,0 +1,112 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/auto_rng.h>
+#include <botan/entropy_src.h>
+#include <botan/hmac_drbg.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+  #include <botan/system_rng.h>
+#endif
+
+#if !defined(BOTAN_AUTO_RNG_HMAC)
+#error "No hash function defined for AutoSeeded_RNG in build.h (try enabling sha2_32)"
+#endif
+
+namespace Botan {
+
+AutoSeeded_RNG::~AutoSeeded_RNG()
+   {
+   // for unique_ptr
+   }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+                               size_t reseed_interval)
+   {
+   m_rng.reset(new HMAC_DRBG(MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+                             underlying_rng,
+                             reseed_interval));
+   force_reseed();
+   }
+
+AutoSeeded_RNG::AutoSeeded_RNG(Entropy_Sources& entropy_sources,
+                               size_t reseed_interval)
+   {
+   m_rng.reset(new HMAC_DRBG(MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+                             entropy_sources,
+                             reseed_interval));
+   force_reseed();
+   }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+                               Entropy_Sources& entropy_sources,
+                               size_t reseed_interval)
+   {
+   m_rng.reset(new HMAC_DRBG(
+                  MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+                  underlying_rng, entropy_sources, reseed_interval));
+   force_reseed();
+   }
+
+AutoSeeded_RNG::AutoSeeded_RNG(size_t reseed_interval) :
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   AutoSeeded_RNG(system_rng(), reseed_interval)
+#else
+   AutoSeeded_RNG(Entropy_Sources::global_sources(), reseed_interval)
+#endif
+   {
+   }
+
+void AutoSeeded_RNG::force_reseed()
+   {
+   m_rng->force_reseed();
+   m_rng->next_byte();
+
+   if(!m_rng->is_seeded())
+      {
+      throw Internal_Error("AutoSeeded_RNG reseeding failed");
+      }
+   }
+
+bool AutoSeeded_RNG::is_seeded() const
+   {
+   return m_rng->is_seeded();
+   }
+
+void AutoSeeded_RNG::clear()
+   {
+   m_rng->clear();
+   }
+
+std::string AutoSeeded_RNG::name() const
+   {
+   return m_rng->name();
+   }
+
+void AutoSeeded_RNG::add_entropy(const uint8_t in[], size_t len)
+   {
+   m_rng->add_entropy(in, len);
+   }
+
+size_t AutoSeeded_RNG::reseed(Entropy_Sources& srcs,
+                              size_t poll_bits,
+                              std::chrono::milliseconds poll_timeout)
+   {
+   return m_rng->reseed(srcs, poll_bits, poll_timeout);
+   }
+
+void AutoSeeded_RNG::randomize(uint8_t output[], size_t output_len)
+   {
+   m_rng->randomize_with_ts_input(output, output_len);
+   }
+
+void AutoSeeded_RNG::randomize_with_input(uint8_t output[], size_t output_len,
+                                          const uint8_t ad[], size_t ad_len)
+   {
+   m_rng->randomize_with_input(output, output_len, ad, ad_len);
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/auto_rng/auto_rng.h b/third_party/botan/src/lib/rng/auto_rng/auto_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/auto_rng/auto_rng.h
@@ -0,0 +1,102 @@
+/*
+* Auto Seeded RNG
+* (C) 2008,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AUTO_SEEDING_RNG_H_
+#define BOTAN_AUTO_SEEDING_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+class Stateful_RNG;
+
+/**
+* A userspace PRNG
+*/
+class BOTAN_PUBLIC_API(2,0) AutoSeeded_RNG final : public RandomNumberGenerator
+   {
+   public:
+      void randomize(uint8_t out[], size_t len) override;
+
+      void randomize_with_input(uint8_t output[], size_t output_len,
+                                const uint8_t input[], size_t input_len) override;
+
+      bool is_seeded() const override;
+
+      bool accepts_input() const override { return true; }
+
+      /**
+      * Mark state as requiring a reseed on next use
+      */
+      void force_reseed();
+
+      size_t reseed(Entropy_Sources& srcs,
+                    size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+                    std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override;
+
+      void add_entropy(const uint8_t in[], size_t len) override;
+
+      std::string name() const override;
+
+      void clear() override;
+
+      /**
+      * Uses the system RNG (if available) or else a default group of
+      * entropy sources (all other systems) to gather seed material.
+      *
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      AutoSeeded_RNG(size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      /**
+      * Create an AutoSeeded_RNG which will get seed material from some other
+      * RNG instance. For example you could provide a reference to the system
+      * RNG or a hardware RNG.
+      *
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+                     size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      /**
+      * Create an AutoSeeded_RNG which will get seed material from a set of
+      * entropy sources.
+      *
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      AutoSeeded_RNG(Entropy_Sources& entropy_sources,
+                     size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      /**
+      * Create an AutoSeeded_RNG which will get seed material from both an
+      * underlying RNG and a set of entropy sources.
+      *
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+                     Entropy_Sources& entropy_sources,
+                     size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      ~AutoSeeded_RNG();
+
+   private:
+      std::unique_ptr<Stateful_RNG> m_rng;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp b/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp
@@ -0,0 +1,87 @@
+/*
+* ChaCha_RNG
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha_rng.h>
+
+namespace Botan {
+
+ChaCha_RNG::ChaCha_RNG() : Stateful_RNG()
+   {
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   clear();
+   }
+
+ChaCha_RNG::ChaCha_RNG(const secure_vector<uint8_t>& seed) : Stateful_RNG()
+   {
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   clear();
+   add_entropy(seed.data(), seed.size());
+   }
+
+ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+                       size_t reseed_interval) :
+   Stateful_RNG(underlying_rng, reseed_interval)
+   {
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   clear();
+   }
+
+ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+                       Entropy_Sources& entropy_sources,
+                       size_t reseed_interval) :
+   Stateful_RNG(underlying_rng, entropy_sources, reseed_interval)
+   {
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   clear();
+   }
+
+ChaCha_RNG::ChaCha_RNG(Entropy_Sources& entropy_sources,
+                       size_t reseed_interval) :
+   Stateful_RNG(entropy_sources, reseed_interval)
+   {
+   m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   clear();
+   }
+
+void ChaCha_RNG::clear_state()
+   {
+   m_hmac->set_key(std::vector<uint8_t>(m_hmac->output_length(), 0x00));
+   m_chacha->set_key(m_hmac->final());
+   }
+
+void ChaCha_RNG::generate_output(uint8_t output[], size_t output_len,
+                                 const uint8_t input[], size_t input_len)
+   {
+   if(input_len > 0)
+      {
+      update(input, input_len);
+      }
+
+   m_chacha->write_keystream(output, output_len);
+   }
+
+void ChaCha_RNG::update(const uint8_t input[], size_t input_len)
+   {
+   m_hmac->update(input, input_len);
+   m_chacha->set_key(m_hmac->final());
+
+   secure_vector<uint8_t> mac_key(m_hmac->output_length());
+   m_chacha->write_keystream(mac_key.data(), mac_key.size());
+   m_hmac->set_key(mac_key);
+   }
+
+size_t ChaCha_RNG::security_level() const
+   {
+   return 256;
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h b/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h
@@ -0,0 +1,125 @@
+/*
+* ChaCha_RNG
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHACHA_RNG_H_
+#define BOTAN_CHACHA_RNG_H_
+
+#include <botan/stateful_rng.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* ChaCha_RNG is a very fast but completely ad-hoc RNG created by
+* creating a 256-bit random value and using it as a key for ChaCha20.
+*
+* The RNG maintains two 256-bit keys, one for HMAC_SHA256 (HK) and the
+* other for ChaCha20 (CK). To compute a new key in response to
+* reseeding request or add_entropy calls, ChaCha_RNG computes
+*   CK' = HMAC_SHA256(HK, input_material)
+* Then a new HK' is computed by running ChaCha20 with the new key to
+* output 32 bytes:
+*   HK' = ChaCha20(CK')
+*
+* Now output can be produced by continuing to produce output with ChaCha20
+* under CK'
+*
+* The first HK (before seeding occurs) is taken as the all zero value.
+*
+* @warning This RNG construction is probably fine but is non-standard.
+* The primary reason to use it is in cases where the other RNGs are
+* not fast enough.
+*/
+class BOTAN_PUBLIC_API(2,3) ChaCha_RNG final : public Stateful_RNG
+   {
+   public:
+      /**
+      * Automatic reseeding is disabled completely, as it has no access to
+      * any source for seed material.
+      *
+      * If a fork is detected, the RNG will be unable to reseed itself
+      * in response. In this case, an exception will be thrown rather
+      * than generating duplicated output.
+      */
+      ChaCha_RNG();
+
+      /**
+      * Provide an initial seed to the RNG, without providing an
+      * underlying RNG or entropy source. Automatic reseeding is
+      * disabled completely, as it has no access to any source for
+      * seed material.
+      *
+      * If a fork is detected, the RNG will be unable to reseed itself
+      * in response. In this case, an exception will be thrown rather
+      * than generating duplicated output.
+      *
+      * @param seed the seed material, should be at least 256 bits
+      */
+      ChaCha_RNG(const secure_vector<uint8_t>& seed);
+
+      /**
+      * Automatic reseeding from @p underlying_rng will take place after
+      * @p reseed_interval many requests or after a fork was detected.
+      *
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+                 size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      /**
+      * Automatic reseeding from @p entropy_sources will take place after
+      * @p reseed_interval many requests or after a fork was detected.
+      *
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed.
+      */
+      ChaCha_RNG(Entropy_Sources& entropy_sources,
+                 size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      /**
+      * Automatic reseeding from @p underlying_rng and @p entropy_sources
+      * will take place after @p reseed_interval many requests or after
+      * a fork was detected.
+      *
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed.
+      */
+      ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+                 Entropy_Sources& entropy_sources,
+                 size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+      std::string name() const override { return "ChaCha_RNG"; }
+
+      size_t security_level() const override;
+
+      size_t max_number_of_bytes_per_request() const override { return 0; }
+
+   private:
+      void update(const uint8_t input[], size_t input_len) override;
+
+      void generate_output(uint8_t output[], size_t output_len,
+                           const uint8_t input[], size_t input_len) override;
+
+      void clear_state() override;
+
+      std::unique_ptr<MessageAuthenticationCode> m_hmac;
+      std::unique_ptr<StreamCipher> m_chacha;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp
@@ -0,0 +1,197 @@
+/*
+* HMAC_DRBG
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hmac_drbg.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+size_t hmac_drbg_security_level(size_t mac_output_length)
+   {
+   // security strength of the hash function
+   // for pre-image resistance (see NIST SP 800-57)
+   // SHA-160: 128 bits
+   // SHA-224, SHA-512/224: 192 bits,
+   // SHA-256, SHA-512/256, SHA-384, SHA-512: >= 256 bits
+   // NIST SP 800-90A only supports up to 256 bits though
+
+   if(mac_output_length < 32)
+      {
+      return (mac_output_length - 4) * 8;
+      }
+   else
+      {
+      return 32 * 8;
+      }
+   }
+
+void check_limits(size_t reseed_interval,
+                  size_t max_number_of_bytes_per_request)
+   {
+   // SP800-90A permits up to 2^48, but it is not usable on 32 bit
+   // platforms, so we only allow up to 2^24, which is still reasonably high
+   if(reseed_interval == 0 || reseed_interval > static_cast<size_t>(1) << 24)
+      {
+      throw Invalid_Argument("Invalid value for reseed_interval");
+      }
+
+   if(max_number_of_bytes_per_request == 0 || max_number_of_bytes_per_request > 64 * 1024)
+      {
+      throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request");
+      }
+   }
+
+}
+
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                     RandomNumberGenerator& underlying_rng,
+                     size_t reseed_interval,
+                     size_t max_number_of_bytes_per_request) :
+   Stateful_RNG(underlying_rng, reseed_interval),
+   m_mac(std::move(prf)),
+   m_max_number_of_bytes_per_request(max_number_of_bytes_per_request),
+   m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+   {
+   BOTAN_ASSERT_NONNULL(m_mac);
+
+   check_limits(reseed_interval, max_number_of_bytes_per_request);
+
+   clear();
+   }
+
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                     RandomNumberGenerator& underlying_rng,
+                     Entropy_Sources& entropy_sources,
+                     size_t reseed_interval,
+                     size_t max_number_of_bytes_per_request) :
+   Stateful_RNG(underlying_rng, entropy_sources, reseed_interval),
+   m_mac(std::move(prf)),
+   m_max_number_of_bytes_per_request(max_number_of_bytes_per_request),
+   m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+   {
+   BOTAN_ASSERT_NONNULL(m_mac);
+
+   check_limits(reseed_interval, max_number_of_bytes_per_request);
+
+   clear();
+   }
+
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                     Entropy_Sources& entropy_sources,
+                     size_t reseed_interval,
+                     size_t max_number_of_bytes_per_request) :
+   Stateful_RNG(entropy_sources, reseed_interval),
+   m_mac(std::move(prf)),
+   m_max_number_of_bytes_per_request(max_number_of_bytes_per_request),
+   m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+   {
+   BOTAN_ASSERT_NONNULL(m_mac);
+
+   check_limits(reseed_interval, max_number_of_bytes_per_request);
+
+   clear();
+   }
+
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) :
+   Stateful_RNG(),
+   m_mac(std::move(prf)),
+   m_max_number_of_bytes_per_request(64*1024),
+   m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+   {
+   BOTAN_ASSERT_NONNULL(m_mac);
+   clear();
+   }
+
+HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash) :
+   Stateful_RNG(),
+   m_mac(MessageAuthenticationCode::create_or_throw("HMAC(" + hmac_hash + ")")),
+   m_max_number_of_bytes_per_request(64 * 1024),
+   m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+   {
+   clear();
+   }
+
+void HMAC_DRBG::clear_state()
+   {
+   if(m_V.size() == 0)
+      {
+      const size_t output_length = m_mac->output_length();
+      m_V.resize(output_length);
+      }
+
+   for(size_t i = 0; i != m_V.size(); ++i)
+      m_V[i] = 0x01;
+   m_mac->set_key(std::vector<uint8_t>(m_V.size(), 0x00));
+   }
+
+std::string HMAC_DRBG::name() const
+   {
+   return "HMAC_DRBG(" + m_mac->name() + ")";
+   }
+
+/*
+* HMAC_DRBG generation
+* See NIST SP800-90A section 10.1.2.5
+*/
+void HMAC_DRBG::generate_output(uint8_t output[], size_t output_len,
+                                const uint8_t input[], size_t input_len)
+   {
+   if(input_len > 0)
+      {
+      update(input, input_len);
+      }
+
+   while(output_len > 0)
+      {
+      const size_t to_copy = std::min(output_len, m_V.size());
+      m_mac->update(m_V.data(), m_V.size());
+      m_mac->final(m_V.data());
+      copy_mem(output, m_V.data(), to_copy);
+
+      output += to_copy;
+      output_len -= to_copy;
+      }
+
+   update(input, input_len);
+   }
+
+/*
+* Reset V and the mac key with new values
+* See NIST SP800-90A section 10.1.2.2
+*/
+void HMAC_DRBG::update(const uint8_t input[], size_t input_len)
+   {
+   secure_vector<uint8_t> T(m_V.size());
+   m_mac->update(m_V);
+   m_mac->update(0x00);
+   m_mac->update(input, input_len);
+   m_mac->final(T.data());
+   m_mac->set_key(T);
+
+   m_mac->update(m_V.data(), m_V.size());
+   m_mac->final(m_V.data());
+
+   if(input_len > 0)
+      {
+      m_mac->update(m_V);
+      m_mac->update(0x01);
+      m_mac->update(input, input_len);
+      m_mac->final(T.data());
+      m_mac->set_key(T);
+
+      m_mac->update(m_V.data(), m_V.size());
+      m_mac->final(m_V.data());
+      }
+   }
+
+size_t HMAC_DRBG::security_level() const
+   {
+   return m_security_level;
+   }
+}
diff --git a/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h b/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h
@@ -0,0 +1,150 @@
+/*
+* HMAC_DRBG (SP800-90A)
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HMAC_DRBG_H_
+#define BOTAN_HMAC_DRBG_H_
+
+#include <botan/stateful_rng.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* HMAC_DRBG from NIST SP800-90A
+*/
+class BOTAN_PUBLIC_API(2,0) HMAC_DRBG final : public Stateful_RNG
+   {
+   public:
+      /**
+      * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+      *
+      * Automatic reseeding is disabled completely, as it has no access to
+      * any source for seed material.
+      *
+      * If a fork is detected, the RNG will be unable to reseed itself
+      * in response. In this case, an exception will be thrown rather
+      * than generating duplicated output.
+      */
+      explicit HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf);
+
+      /**
+      * Constructor taking a string for the hash
+      */
+      explicit HMAC_DRBG(const std::string& hmac_hash);
+
+      /**
+      * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+      *
+      * Automatic reseeding from @p underlying_rng will take place after
+      * @p reseed_interval many requests or after a fork was detected.
+      *
+      * @param prf MAC to use as a PRF
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed (max. 2^24)
+      * @param max_number_of_bytes_per_request requests that are in size higher
+      * than max_number_of_bytes_per_request are treated as if multiple single
+      * requests of max_number_of_bytes_per_request size had been made.
+      * In theory SP 800-90A requires that we reject any request for a DRBG
+      * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+      * the caller who wants an output larger than max_number_of_bytes_per_request,
+      * instead treat these requests as if multiple requests of
+      * max_number_of_bytes_per_request size had been made. NIST requires for
+      * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+      * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+      * example every 512 bit automatic reseeding occurs.
+      */
+      HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                RandomNumberGenerator& underlying_rng,
+                size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+                size_t max_number_of_bytes_per_request = 64 * 1024);
+
+      /**
+      * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+      *
+      * Automatic reseeding from @p entropy_sources will take place after
+      * @p reseed_interval many requests or after a fork was detected.
+      *
+      * @param prf MAC to use as a PRF
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed (max. 2^24)
+      * @param max_number_of_bytes_per_request requests that are in size higher
+      * than max_number_of_bytes_per_request are treated as if multiple single
+      * requests of max_number_of_bytes_per_request size had been made.
+      * In theory SP 800-90A requires that we reject any request for a DRBG
+      * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+      * the caller who wants an output larger than max_number_of_bytes_per_request,
+      * instead treat these requests as if multiple requests of
+      * max_number_of_bytes_per_request size had been made. NIST requires for
+      * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+      * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+      * example every 512 bit automatic reseeding occurs.
+      */
+      HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                Entropy_Sources& entropy_sources,
+                size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+                size_t max_number_of_bytes_per_request = 64 * 1024);
+
+      /**
+      * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+      *
+      * Automatic reseeding from @p underlying_rng and @p entropy_sources
+      * will take place after @p reseed_interval many requests or after
+      * a fork was detected.
+      *
+      * @param prf MAC to use as a PRF
+      * @param underlying_rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed (max. 2^24)
+      * @param max_number_of_bytes_per_request requests that are in size higher
+      * than max_number_of_bytes_per_request are treated as if multiple single
+      * requests of max_number_of_bytes_per_request size had been made.
+      * In theory SP 800-90A requires that we reject any request for a DRBG
+      * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+      * the caller who wants an output larger than max_number_of_bytes_per_request,
+      * instead treat these requests as if multiple requests of
+      * max_number_of_bytes_per_request size had been made. NIST requires for
+      * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+      * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+      * example every 512 bit automatic reseeding occurs.
+      */
+      HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+                RandomNumberGenerator& underlying_rng,
+                Entropy_Sources& entropy_sources,
+                size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+                size_t max_number_of_bytes_per_request = 64 * 1024);
+
+      std::string name() const override;
+
+      size_t security_level() const override;
+
+      size_t max_number_of_bytes_per_request() const override
+         { return m_max_number_of_bytes_per_request; }
+
+   private:
+      void update(const uint8_t input[], size_t input_len) override;
+
+      void generate_output(uint8_t output[], size_t output_len,
+                           const uint8_t input[], size_t input_len) override;
+
+      void clear_state() override;
+
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+      secure_vector<uint8_t> m_V;
+      const size_t m_max_number_of_bytes_per_request;
+      const size_t m_security_level;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp b/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp
@@ -0,0 +1,157 @@
+/*
+* (C) 2016,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/processor_rng.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM)
+  #include <immintrin.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   /*
+   * According to Intel, RDRAND is guaranteed to generate a random
+   * number within 10 retries on a working CPU
+   */
+   const size_t HWRNG_RETRIES = 10;
+
+#else
+   /*
+   * Lacking specific guidance we give the CPU quite a bit of leeway
+   */
+   const size_t HWRNG_RETRIES = 512;
+#endif
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_32)
+   typedef uint32_t hwrng_output;
+#else
+   typedef uint64_t hwrng_output;
+#endif
+
+hwrng_output read_hwrng(bool& success)
+   {
+   hwrng_output output = 0;
+   success = false;
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   int cf = 0;
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+   // same asm seq works for 32 and 64 bit
+   asm volatile("rdrand %0; adcl $0,%1" :
+       "=r" (output), "=r" (cf) : "0" (output), "1" (cf) : "cc");
+#elif defined(BOTAN_TARGET_ARCH_IS_X86_32)
+   cf = _rdrand32_step(&output);
+#else
+   cf = _rdrand64_step(&output);
+#endif
+   success = (1 == cf);
+
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+   /*
+   DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy.
+   Avoid the bias by invoking it twice and, assuming both succeed, returning the
+   XOR of the two results, which should unbias the output.
+   */
+   uint64_t output2 = 0;
+   // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED)
+   asm volatile("darn %0, 1" : "=r" (output));
+   asm volatile("darn %0, 1" : "=r" (output2));
+
+   if((~output) != 0 && (~output2) != 0)
+      {
+      output ^= output2;
+      success = true;
+      }
+
+#endif
+
+   if(success)
+      return output;
+
+   return 0;
+   }
+
+hwrng_output read_hwrng()
+   {
+   for(size_t i = 0; i < HWRNG_RETRIES; ++i)
+      {
+      bool success = false;
+      hwrng_output output = read_hwrng(success);
+
+      if(success)
+         return output;
+      }
+
+   throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
+   }
+
+}
+
+//static
+bool Processor_RNG::available()
+   {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   return CPUID::has_rdrand();
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+   return CPUID::has_darn_rng();
+#else
+   return false;
+#endif
+   }
+
+std::string Processor_RNG::name() const
+   {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   return "rdrand";
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+   return "darn";
+#else
+   return "hwrng";
+#endif
+   }
+
+void Processor_RNG::randomize(uint8_t out[], size_t out_len)
+   {
+   while(out_len >= sizeof(hwrng_output))
+      {
+      const hwrng_output r = read_hwrng();
+      store_le(r, out);
+      out += sizeof(hwrng_output);
+      out_len -= sizeof(hwrng_output);
+      }
+
+   if(out_len > 0) // at most sizeof(hwrng_output)-1
+      {
+      const hwrng_output r = read_hwrng();
+      for(size_t i = 0; i != out_len; ++i)
+         out[i] = get_byte(i, r);
+      }
+   }
+
+Processor_RNG::Processor_RNG()
+   {
+   if(!Processor_RNG::available())
+      throw Invalid_State("Current CPU does not support RNG instruction");
+   }
+
+void Processor_RNG::add_entropy(const uint8_t[], size_t)
+   {
+   /* no way to add entropy */
+   }
+
+size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds)
+   {
+   /* no way to add entropy */
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/processor_rng/processor_rng.h b/third_party/botan/src/lib/rng/processor_rng/processor_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/processor_rng/processor_rng.h
@@ -0,0 +1,52 @@
+/*
+* (C) 2016,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RNG_PROCESSOR_RNG_H_
+#define BOTAN_RNG_PROCESSOR_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+/**
+* Directly invokes a CPU specific instruction to generate random numbers.
+* On x86, the RDRAND instruction is used.
+* on POWER, the DARN instruction is used.
+*/
+class BOTAN_PUBLIC_API(2,15) Processor_RNG final : public Hardware_RNG
+   {
+   public:
+      /**
+      * Constructor will throw if CPU does not have RDRAND bit set
+      */
+      Processor_RNG();
+
+      /**
+      * Return true if RNG instruction is available on the current processor
+      */
+      static bool available();
+
+      bool accepts_input() const override { return false; }
+      bool is_seeded() const override { return true; }
+
+      void randomize(uint8_t out[], size_t out_len) override;
+
+      /*
+      * No way to provide entropy to RDRAND generator, so add_entropy is ignored
+      */
+      void add_entropy(const uint8_t[], size_t) override;
+
+      /*
+      * No way to reseed processor provided generator, so reseed is ignored
+      */
+      size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override;
+
+      std::string name() const override;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp b/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp
@@ -0,0 +1,67 @@
+/*
+* RDRAND RNG
+* (C) 2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rdrand_rng.h>
+#include <botan/processor_rng.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void RDRAND_RNG::randomize(uint8_t out[], size_t out_len)
+   {
+   Processor_RNG rng;
+   rng.randomize(out, out_len);
+   }
+
+RDRAND_RNG::RDRAND_RNG()
+   {
+   // Will throw if instruction is not available
+   Processor_RNG rng;
+   }
+
+//static
+bool RDRAND_RNG::available()
+   {
+   return Processor_RNG::available();
+   }
+
+//static
+uint32_t RDRAND_RNG::rdrand()
+   {
+   Processor_RNG rng;
+
+   for(;;)
+      {
+      try
+         {
+         uint8_t out[4];
+         rng.randomize(out, 4);
+         return load_le<uint32_t>(out, 0);
+         }
+      catch(PRNG_Unseeded&) {}
+      }
+   }
+
+//static
+uint32_t RDRAND_RNG::rdrand_status(bool& ok)
+   {
+   ok = false;
+   Processor_RNG rng;
+
+   try
+      {
+      uint8_t out[4];
+      rng.randomize(out, 4);
+      ok = true;
+      return load_le<uint32_t>(out, 0);
+      }
+   catch(PRNG_Unseeded&) {}
+
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h b/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h
@@ -0,0 +1,68 @@
+/*
+* RDRAND RNG
+* (C) 2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RNG_RDRAND_H_
+#define BOTAN_RNG_RDRAND_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) RDRAND_RNG final : public Hardware_RNG
+   {
+   public:
+      /**
+      * Constructor will throw if CPU does not have RDRAND bit set
+      */
+      BOTAN_DEPRECATED("Use Processor_RNG instead") RDRAND_RNG();
+
+      /**
+      * Return true if RDRAND is available on the current processor
+      */
+      static bool available();
+
+      bool accepts_input() const override { return false; }
+
+      /**
+      * Uses RDRAND to produce output
+      */
+      void randomize(uint8_t out[], size_t out_len) override;
+
+      /*
+      * No way to provide entropy to RDRAND generator, so add_entropy is ignored
+      */
+      void add_entropy(const uint8_t[], size_t) override
+         { /* no op */ }
+
+      /*
+      * No way to reseed RDRAND generator, so reseed is ignored
+      */
+      size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
+         { return 0; /* no op */ }
+
+      std::string name() const override { return "RDRAND"; }
+
+      bool is_seeded() const override { return true; }
+
+      /**
+      * On correctly working hardware, RDRAND is always supposed to
+      * succeed within a set number of retries. If after that many
+      * retries RDRAND has still not suceeded, sets ok = false and
+      * returns 0.
+      */
+      static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand_status(bool& ok);
+
+      /*
+      * Calls RDRAND until it succeeds, this could hypothetically
+      * loop forever on broken hardware.
+      */
+      static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand();
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/rng.cpp b/third_party/botan/src/lib/rng/rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/rng.cpp
@@ -0,0 +1,91 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rng.h>
+#include <botan/entropy_src.h>
+#include <botan/loadstor.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+  #include <botan/auto_rng.h>
+#endif
+
+namespace Botan {
+
+void RandomNumberGenerator::randomize_with_ts_input(uint8_t output[], size_t output_len)
+   {
+   if(this->accepts_input())
+      {
+      /*
+      Form additional input which is provided to the PRNG implementation
+      to paramaterize the KDF output.
+      */
+      uint8_t additional_input[16] = { 0 };
+      store_le(OS::get_system_timestamp_ns(), additional_input);
+      store_le(OS::get_high_resolution_clock(), additional_input + 8);
+
+      this->randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+      }
+   else
+      {
+      this->randomize(output, output_len);
+      }
+   }
+
+void RandomNumberGenerator::randomize_with_input(uint8_t output[], size_t output_len,
+                                                 const uint8_t input[], size_t input_len)
+   {
+   this->add_entropy(input, input_len);
+   this->randomize(output, output_len);
+   }
+
+size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs,
+                                     size_t poll_bits,
+                                     std::chrono::milliseconds poll_timeout)
+   {
+   if(this->accepts_input())
+      {
+      return srcs.poll(*this, poll_bits, poll_timeout);
+      }
+   else
+      {
+      return 0;
+      }
+   }
+
+void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
+   {
+   if(this->accepts_input())
+      {
+      secure_vector<uint8_t> buf(poll_bits / 8);
+      rng.randomize(buf.data(), buf.size());
+      this->add_entropy(buf.data(), buf.size());
+      }
+   }
+
+RandomNumberGenerator* RandomNumberGenerator::make_rng()
+   {
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+   return new AutoSeeded_RNG;
+#else
+   throw Not_Implemented("make_rng failed, no AutoSeeded_RNG in this build");
+#endif
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+Serialized_RNG::Serialized_RNG() : m_rng(new AutoSeeded_RNG) {}
+#else
+Serialized_RNG::Serialized_RNG()
+   {
+   throw Not_Implemented("Serialized_RNG default constructor failed: AutoSeeded_RNG disabled in build");
+   }
+#endif
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/rng/rng.h b/third_party/botan/src/lib/rng/rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/rng.h
@@ -0,0 +1,297 @@
+/*
+* Random Number Generator base classes
+* (C) 1999-2009,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H_
+#define BOTAN_RANDOM_NUMBER_GENERATOR_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <botan/mutex.h>
+#include <type_traits>
+#include <chrono>
+#include <string>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* An interface to a cryptographic random number generator
+*/
+class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator
+   {
+   public:
+      virtual ~RandomNumberGenerator() = default;
+
+      RandomNumberGenerator() = default;
+
+      /*
+      * Never copy a RNG, create a new one
+      */
+      RandomNumberGenerator(const RandomNumberGenerator& rng) = delete;
+      RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete;
+
+      /**
+      * Randomize a byte array.
+      * @param output the byte array to hold the random output.
+      * @param length the length of the byte array output in bytes.
+      */
+      virtual void randomize(uint8_t output[], size_t length) = 0;
+
+      /**
+      * Returns false if it is known that this RNG object is not able to accept
+      * externally provided inputs (via add_entropy, randomize_with_input, etc).
+      * In this case, any such provided inputs are ignored.
+      *
+      * If this function returns true, then inputs may or may not be accepted.
+      */
+      virtual bool accepts_input() const = 0;
+
+      /**
+      * Incorporate some additional data into the RNG state. For
+      * example adding nonces or timestamps from a peer's protocol
+      * message can help hedge against VM state rollback attacks.
+      * A few RNG types do not accept any externally provided input,
+      * in which case this function is a no-op.
+      *
+      * @param input a byte array containg the entropy to be added
+      * @param length the length of the byte array in
+      */
+      virtual void add_entropy(const uint8_t input[], size_t length) = 0;
+
+      /**
+      * Incorporate some additional data into the RNG state.
+      */
+      template<typename T> void add_entropy_T(const T& t)
+         {
+         static_assert(std::is_pod<T>::value, "add_entropy_T data must be POD");
+         this->add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T));
+         }
+
+      /**
+      * Incorporate entropy into the RNG state then produce output.
+      * Some RNG types implement this using a single operation, default
+      * calls add_entropy + randomize in sequence.
+      *
+      * Use this to further bind the outputs to your current
+      * process/protocol state. For instance if generating a new key
+      * for use in a session, include a session ID or other such
+      * value. See NIST SP 800-90 A, B, C series for more ideas.
+      *
+      * @param output buffer to hold the random output
+      * @param output_len size of the output buffer in bytes
+      * @param input entropy buffer to incorporate
+      * @param input_len size of the input buffer in bytes
+      */
+      virtual void randomize_with_input(uint8_t output[], size_t output_len,
+                                        const uint8_t input[], size_t input_len);
+
+      /**
+      * This calls `randomize_with_input` using some timestamps as extra input.
+      *
+      * For a stateful RNG using non-random but potentially unique data the
+      * extra input can help protect against problems with fork, VM state
+      * rollback, or other cases where somehow an RNG state is duplicated. If
+      * both of the duplicated RNG states later incorporate a timestamp (and the
+      * timestamps don't themselves repeat), their outputs will diverge.
+      */
+      virtual void randomize_with_ts_input(uint8_t output[], size_t output_len);
+
+      /**
+      * @return the name of this RNG type
+      */
+      virtual std::string name() const = 0;
+
+      /**
+      * Clear all internally held values of this RNG
+      * @post is_seeded() == false
+      */
+      virtual void clear() = 0;
+
+      /**
+      * Check whether this RNG is seeded.
+      * @return true if this RNG was already seeded, false otherwise.
+      */
+      virtual bool is_seeded() const = 0;
+
+      /**
+      * Poll provided sources for up to poll_bits bits of entropy
+      * or until the timeout expires. Returns estimate of the number
+      * of bits collected.
+      */
+      virtual size_t reseed(Entropy_Sources& srcs,
+                            size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+                            std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT);
+
+      /**
+      * Reseed by reading specified bits from the RNG
+      */
+      virtual void reseed_from_rng(RandomNumberGenerator& rng,
+                                   size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS);
+
+      // Some utility functions built on the interface above:
+
+      /**
+      * Return a random vector
+      * @param bytes number of bytes in the result
+      * @return randomized vector of length bytes
+      */
+      secure_vector<uint8_t> random_vec(size_t bytes)
+         {
+         secure_vector<uint8_t> output;
+         random_vec(output, bytes);
+         return output;
+         }
+
+      template<typename Alloc>
+         void random_vec(std::vector<uint8_t, Alloc>& v, size_t bytes)
+         {
+         v.resize(bytes);
+         this->randomize(v.data(), v.size());
+         }
+
+      /**
+      * Return a random byte
+      * @return random byte
+      */
+      uint8_t next_byte()
+         {
+         uint8_t b;
+         this->randomize(&b, 1);
+         return b;
+         }
+
+      /**
+      * @return a random byte that is greater than zero
+      */
+      uint8_t next_nonzero_byte()
+         {
+         uint8_t b = this->next_byte();
+         while(b == 0)
+            b = this->next_byte();
+         return b;
+         }
+
+      /**
+      * Create a seeded and active RNG object for general application use
+      * Added in 1.8.0
+      * Use AutoSeeded_RNG instead
+      */
+      BOTAN_DEPRECATED("Use AutoSeeded_RNG")
+      static RandomNumberGenerator* make_rng();
+   };
+
+/**
+* Convenience typedef
+*/
+typedef RandomNumberGenerator RNG;
+
+/**
+* Hardware_RNG exists to tag hardware RNG types (PKCS11_RNG, TPM_RNG, Processor_RNG)
+*/
+class BOTAN_PUBLIC_API(2,0) Hardware_RNG : public RandomNumberGenerator
+   {
+   public:
+      virtual void clear() final override { /* no way to clear state of hardware RNG */ }
+   };
+
+/**
+* Null/stub RNG - fails if you try to use it for anything
+* This is not generally useful except for in certain tests
+*/
+class BOTAN_PUBLIC_API(2,0) Null_RNG final : public RandomNumberGenerator
+   {
+   public:
+      bool is_seeded() const override { return false; }
+
+      bool accepts_input() const override { return false; }
+
+      void clear() override {}
+
+      void randomize(uint8_t[], size_t) override
+         {
+         throw PRNG_Unseeded("Null_RNG called");
+         }
+
+      void add_entropy(const uint8_t[], size_t) override {}
+
+      std::string name() const override { return "Null_RNG"; }
+   };
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+/**
+* Wraps access to a RNG in a mutex
+* Note that most of the time it's much better to use a RNG per thread
+* otherwise the RNG will act as an unnecessary contention point
+*
+* Since 2.16.0 all Stateful_RNG instances have an internal lock, so
+* this class is no longer needed. It will be removed in a future major
+* release.
+*/
+class BOTAN_PUBLIC_API(2,0) Serialized_RNG final : public RandomNumberGenerator
+   {
+   public:
+      void randomize(uint8_t out[], size_t len) override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         m_rng->randomize(out, len);
+         }
+
+      bool accepts_input() const override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         return m_rng->accepts_input();
+         }
+
+      bool is_seeded() const override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         return m_rng->is_seeded();
+         }
+
+      void clear() override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         m_rng->clear();
+         }
+
+      std::string name() const override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         return m_rng->name();
+         }
+
+      size_t reseed(Entropy_Sources& src,
+                    size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+                    std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         return m_rng->reseed(src, poll_bits, poll_timeout);
+         }
+
+      void add_entropy(const uint8_t in[], size_t len) override
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+         m_rng->add_entropy(in, len);
+         }
+
+      BOTAN_DEPRECATED("Use Serialized_RNG(new AutoSeeded_RNG) instead") Serialized_RNG();
+
+      /*
+      * Since 2.16.0 this is no longer needed for any RNG type. This
+      * class will be removed in a future major release.
+      */
+      explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {}
+   private:
+      mutable mutex_type m_mutex;
+      std::unique_ptr<RandomNumberGenerator> m_rng;
+   };
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp b/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp
@@ -0,0 +1,190 @@
+/*
+* (C) 2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stateful_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/loadstor.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+  #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+void Stateful_RNG::clear()
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+   m_reseed_counter = 0;
+   m_last_pid = 0;
+   clear_state();
+   }
+
+void Stateful_RNG::force_reseed()
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+   m_reseed_counter = 0;
+   }
+
+bool Stateful_RNG::is_seeded() const
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+   return m_reseed_counter > 0;
+   }
+
+void Stateful_RNG::add_entropy(const uint8_t input[], size_t input_len)
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+   update(input, input_len);
+
+   if(8*input_len >= security_level())
+      {
+      reset_reseed_counter();
+      }
+   }
+
+void Stateful_RNG::initialize_with(const uint8_t input[], size_t len)
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+   clear();
+   add_entropy(input, len);
+   }
+
+void Stateful_RNG::randomize(uint8_t output[], size_t output_len)
+   {
+   randomize_with_input(output, output_len, nullptr, 0);
+   }
+
+void Stateful_RNG::randomize_with_ts_input(uint8_t output[], size_t output_len)
+   {
+   uint8_t additional_input[20] = { 0 };
+
+   store_le(OS::get_high_resolution_clock(), additional_input);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   System_RNG system_rng;
+   system_rng.randomize(additional_input + 8, sizeof(additional_input) - 8);
+#else
+   store_le(OS::get_system_timestamp_ns(), additional_input + 8);
+   store_le(OS::get_process_id(), additional_input + 16);
+#endif
+
+   randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+   }
+
+void Stateful_RNG::randomize_with_input(uint8_t output[], size_t output_len,
+                                        const uint8_t input[], size_t input_len)
+   {
+   if(output_len == 0)
+      return;
+
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+   const size_t max_per_request = max_number_of_bytes_per_request();
+
+   if(max_per_request == 0) // no limit
+      {
+      reseed_check();
+      this->generate_output(output, output_len, input, input_len);
+      }
+   else
+      {
+      while(output_len > 0)
+         {
+         const size_t this_req = std::min(max_per_request, output_len);
+
+         /*
+         * We split the request into several requests to the underlying DRBG but
+         * pass the input to each invocation. It might be more sensible to only
+         * provide it for the first invocation, however between 2.0 and 2.15
+         * HMAC_DRBG always provided it for all requests so retain that here.
+         */
+
+         reseed_check();
+         this->generate_output(output, this_req, input, input_len);
+
+         output += this_req;
+         output_len -= this_req;
+         }
+      }
+   }
+
+size_t Stateful_RNG::reseed(Entropy_Sources& srcs,
+                            size_t poll_bits,
+                            std::chrono::milliseconds poll_timeout)
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+   const size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout);
+
+   if(bits_collected >= security_level())
+      {
+      reset_reseed_counter();
+      }
+
+   return bits_collected;
+   }
+
+void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
+   {
+   lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+   RandomNumberGenerator::reseed_from_rng(rng, poll_bits);
+
+   if(poll_bits >= security_level())
+      {
+      reset_reseed_counter();
+      }
+   }
+
+void Stateful_RNG::reset_reseed_counter()
+   {
+   // Lock is held whenever this function is called
+   m_reseed_counter = 1;
+   }
+
+void Stateful_RNG::reseed_check()
+   {
+   // Lock is held whenever this function is called
+
+   const uint32_t cur_pid = OS::get_process_id();
+
+   const bool fork_detected = (m_last_pid > 0) && (cur_pid != m_last_pid);
+
+   if(is_seeded() == false ||
+      fork_detected ||
+      (m_reseed_interval > 0 && m_reseed_counter >= m_reseed_interval))
+      {
+      m_reseed_counter = 0;
+      m_last_pid = cur_pid;
+
+      if(m_underlying_rng)
+         {
+         reseed_from_rng(*m_underlying_rng, security_level());
+         }
+
+      if(m_entropy_sources)
+         {
+         reseed(*m_entropy_sources, security_level());
+         }
+
+      if(!is_seeded())
+         {
+         if(fork_detected)
+            throw Invalid_State("Detected use of fork but cannot reseed DRBG");
+         else
+            throw PRNG_Unseeded(name());
+         }
+      }
+   else
+      {
+      BOTAN_ASSERT(m_reseed_counter != 0, "RNG is seeded");
+      m_reseed_counter += 1;
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h b/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h
@@ -0,0 +1,166 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STATEFUL_RNG_H_
+#define BOTAN_STATEFUL_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+/**
+* Inherited by RNGs which maintain in-process state, like HMAC_DRBG.
+* On Unix these RNGs are vulnerable to problems with fork, where the
+* RNG state is duplicated, and the parent and child process RNGs will
+* produce identical output until one of them reseeds. Stateful_RNG
+* reseeds itself whenever a fork is detected, or after a set number of
+* bytes have been output.
+*
+* Not implemented by RNGs which access an external RNG, such as the
+* system PRNG or a hardware RNG.
+*/
+class BOTAN_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator
+   {
+   public:
+      /**
+      * @param rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      Stateful_RNG(RandomNumberGenerator& rng,
+                   Entropy_Sources& entropy_sources,
+                   size_t reseed_interval) :
+         m_underlying_rng(&rng),
+         m_entropy_sources(&entropy_sources),
+         m_reseed_interval(reseed_interval)
+         {}
+
+      /**
+      * @param rng is a reference to some RNG which will be used
+      * to perform the periodic reseeding
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      Stateful_RNG(RandomNumberGenerator& rng, size_t reseed_interval) :
+         m_underlying_rng(&rng),
+         m_reseed_interval(reseed_interval)
+         {}
+
+      /**
+      * @param entropy_sources will be polled to perform reseeding periodically
+      * @param reseed_interval specifies a limit of how many times
+      * the RNG will be called before automatic reseeding is performed
+      */
+      Stateful_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval) :
+         m_entropy_sources(&entropy_sources),
+         m_reseed_interval(reseed_interval)
+         {}
+
+      /**
+      * In this case, automatic reseeding is impossible
+      */
+      Stateful_RNG() : m_reseed_interval(0) {}
+
+      /**
+      * Consume this input and mark the RNG as initialized regardless
+      * of the length of the input or the current seeded state of
+      * the RNG.
+      */
+      void initialize_with(const uint8_t input[], size_t length);
+
+      bool is_seeded() const override final;
+
+      bool accepts_input() const override final { return true; }
+
+      /**
+      * Mark state as requiring a reseed on next use
+      */
+      void force_reseed();
+
+      void reseed_from_rng(RandomNumberGenerator& rng,
+                           size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS) override final;
+
+      void add_entropy(const uint8_t input[], size_t input_len) override final;
+
+      void randomize(uint8_t output[], size_t output_len) override final;
+
+      void randomize_with_input(uint8_t output[], size_t output_len,
+                                const uint8_t input[], size_t input_len) override final;
+
+      /**
+      * Overrides default implementation and also includes the current
+      * process ID and the reseed counter.
+      */
+      void randomize_with_ts_input(uint8_t output[], size_t output_len) override final;
+
+      /**
+      * Poll provided sources for up to poll_bits bits of entropy
+      * or until the timeout expires. Returns estimate of the number
+      * of bits collected.
+      */
+      size_t reseed(Entropy_Sources& srcs,
+                    size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+                    std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override;
+
+      /**
+      * @return intended security level of this DRBG
+      */
+      virtual size_t security_level() const = 0;
+
+      /**
+      * Some DRBGs have a notion of the maximum number of bytes per
+      * request.  Longer requests (to randomize) will be treated as
+      * multiple requests, and may initiate reseeding multiple times,
+      * depending on the values of max_number_of_bytes_per_request and
+      * reseed_interval(). This function returns zero if the RNG in
+      * question does not have such a notion.
+      *
+      * @return max number of bytes per request (or zero)
+      */
+      virtual size_t max_number_of_bytes_per_request() const = 0;
+
+      size_t reseed_interval() const { return m_reseed_interval; }
+
+      void clear() override final;
+
+   protected:
+      void reseed_check();
+
+      virtual void generate_output(uint8_t output[], size_t output_len,
+                                   const uint8_t input[], size_t input_len) = 0;
+
+      virtual void update(const uint8_t input[], size_t input_len) = 0;
+
+      virtual void clear_state() = 0;
+
+   private:
+      void reset_reseed_counter();
+
+      mutable recursive_mutex_type m_mutex;
+
+      // A non-owned and possibly null pointer to shared RNG
+      RandomNumberGenerator* m_underlying_rng = nullptr;
+
+      // A non-owned and possibly null pointer to a shared Entropy_Source
+      Entropy_Sources* m_entropy_sources = nullptr;
+
+      const size_t m_reseed_interval;
+      uint32_t m_last_pid = 0;
+
+      /*
+      * Set to 1 after a successful seeding, then incremented.  Reset
+      * to 0 by clear() or a fork. This logic is used even if
+      * automatic reseeding is disabled (via m_reseed_interval = 0)
+      */
+      size_t m_reseed_counter = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/rng/system_rng/system_rng.cpp b/third_party/botan/src/lib/rng/system_rng/system_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/system_rng/system_rng.cpp
@@ -0,0 +1,289 @@
+/*
+* System RNG
+* (C) 2014,2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/system_rng.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
+  #include <botan/dyn_load.h>
+  #define NOMINMAX 1
+  #define _WINSOCKAPI_ // stop windows.h including winsock.h
+  #include <windows.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
+  #include <bcrypt.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
+  #include <stdlib.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
+  #include <sys/random.h>
+  #include <errno.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
+  #include <sys/types.h>
+  #include <sys/stat.h>
+  #include <fcntl.h>
+  #include <unistd.h>
+  #include <errno.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+   {
+   public:
+      System_RNG_Impl() : m_advapi("advapi32.dll")
+         {
+         // This throws if the function is not found
+         m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_fptr>("SystemFunction036");
+         }
+
+      void randomize(uint8_t buf[], size_t len) override
+         {
+         bool success = m_rtlgenrandom(buf, ULONG(len)) == TRUE;
+         if(!success)
+            throw System_Error("RtlGenRandom failed");
+         }
+
+      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+      bool is_seeded() const override { return true; }
+      bool accepts_input() const override { return false; }
+      void clear() override { /* not possible */ }
+      std::string name() const override { return "RtlGenRandom"; }
+   private:
+      // Use type BYTE instead of BOOLEAN because of a naming conflict
+      // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
+      // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
+      using RtlGenRandom_fptr = BYTE (NTAPI *)(PVOID, ULONG);
+
+      Dynamically_Loaded_Library m_advapi;
+      RtlGenRandom_fptr m_rtlgenrandom;
+   };
+
+#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+   {
+   public:
+      System_RNG_Impl()
+         {
+         NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov,
+                                                      BCRYPT_RNG_ALGORITHM,
+                                                      MS_PRIMITIVE_PROVIDER, 0);
+         if(ret != STATUS_SUCCESS)
+            throw System_Error("System_RNG failed to acquire crypto provider", ret);
+         }
+
+      ~System_RNG_Impl()
+         {
+         ::BCryptCloseAlgorithmProvider(m_prov, 0);
+         }
+
+      void randomize(uint8_t buf[], size_t len) override
+         {
+         NTSTATUS ret = ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0);
+         if(ret != STATUS_SUCCESS)
+            throw System_Error("System_RNG call to BCryptGenRandom failed", ret);
+         }
+
+      void add_entropy(const uint8_t in[], size_t length) override
+         {
+         /*
+         There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide
+         entropy inputs, but it is ignored in Windows 8 and later.
+         */
+         }
+
+      bool is_seeded() const override { return true; }
+      bool accepts_input() const override { return false; }
+      void clear() override { /* not possible */ }
+      std::string name() const override { return "crypto_ng"; }
+   private:
+      BCRYPT_ALG_HANDLE m_prov;
+   };
+
+#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+   {
+   public:
+      // No constructor or destructor needed as no userland state maintained
+
+      void randomize(uint8_t buf[], size_t len) override
+         {
+         // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0
+         if(len > 0)
+            {
+            ::arc4random_buf(buf, len);
+            }
+         }
+
+      bool accepts_input() const override { return false; }
+      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+      bool is_seeded() const override { return true; }
+      void clear() override { /* not possible */ }
+      std::string name() const override { return "arc4random"; }
+   };
+
+#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+   {
+   public:
+      // No constructor or destructor needed as no userland state maintained
+
+      void randomize(uint8_t buf[], size_t len) override
+         {
+         const unsigned int flags = 0;
+
+         while(len > 0)
+            {
+            const ssize_t got = ::getrandom(buf, len, flags);
+
+            if(got < 0)
+               {
+               if(errno == EINTR)
+                  continue;
+               throw System_Error("System_RNG getrandom failed", errno);
+               }
+
+            buf += got;
+            len -= got;
+            }
+         }
+
+      bool accepts_input() const override { return false; }
+      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+      bool is_seeded() const override { return true; }
+      void clear() override { /* not possible */ }
+      std::string name() const override { return "getrandom"; }
+   };
+
+
+#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
+
+// Read a random device
+
+class System_RNG_Impl final : public RandomNumberGenerator
+   {
+   public:
+      System_RNG_Impl()
+         {
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+         m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
+
+         if(m_fd >= 0)
+            {
+            m_writable = true;
+            }
+         else
+            {
+            /*
+            Cannot open in read-write mode. Fall back to read-only,
+            calls to add_entropy will fail, but randomize will work
+            */
+            m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
+            m_writable = false;
+            }
+
+         if(m_fd < 0)
+            throw System_Error("System_RNG failed to open RNG device", errno);
+         }
+
+      ~System_RNG_Impl()
+         {
+         ::close(m_fd);
+         m_fd = -1;
+         }
+
+      void randomize(uint8_t buf[], size_t len) override;
+      void add_entropy(const uint8_t in[], size_t length) override;
+      bool is_seeded() const override { return true; }
+      bool accepts_input() const override { return m_writable; }
+      void clear() override { /* not possible */ }
+      std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
+   private:
+      int m_fd;
+      bool m_writable;
+   };
+
+void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
+   {
+   while(len)
+      {
+      ssize_t got = ::read(m_fd, buf, len);
+
+      if(got < 0)
+         {
+         if(errno == EINTR)
+            continue;
+         throw System_Error("System_RNG read failed", errno);
+         }
+      if(got == 0)
+         throw System_Error("System_RNG EOF on device"); // ?!?
+
+      buf += got;
+      len -= got;
+      }
+   }
+
+void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
+   {
+   if(!m_writable)
+      return;
+
+   while(len)
+      {
+      ssize_t got = ::write(m_fd, input, len);
+
+      if(got < 0)
+         {
+         if(errno == EINTR)
+            continue;
+
+         /*
+         * This is seen on OS X CI, despite the fact that the man page
+         * for macOS urandom explicitly states that writing to it is
+         * supported, and write(2) does not document EPERM at all.
+         * But in any case EPERM seems indicative of a policy decision
+         * by the OS or sysadmin that additional entropy is not wanted
+         * in the system pool, so we accept that and return here,
+         * since there is no corrective action possible.
+         *
+         * In Linux EBADF or EPERM is returned if m_fd is not opened for
+         * writing.
+         */
+         if(errno == EPERM || errno == EBADF)
+            return;
+
+         // maybe just ignore any failure here and return?
+         throw System_Error("System_RNG write failed", errno);
+         }
+
+      input += got;
+      len -= got;
+      }
+   }
+
+#endif
+
+}
+
+RandomNumberGenerator& system_rng()
+   {
+   static System_RNG_Impl g_system_rng;
+   return g_system_rng;
+   }
+
+}
diff --git a/third_party/botan/src/lib/rng/system_rng/system_rng.h b/third_party/botan/src/lib/rng/system_rng/system_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/rng/system_rng/system_rng.h
@@ -0,0 +1,43 @@
+/*
+* System RNG interface
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYSTEM_RNG_H_
+#define BOTAN_SYSTEM_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+/**
+* Return a shared reference to a global PRNG instance provided by the
+* operating system. For instance might be instantiated by /dev/urandom
+* or CryptGenRandom.
+*/
+BOTAN_PUBLIC_API(2,0) RandomNumberGenerator& system_rng();
+
+/*
+* Instantiable reference to the system RNG.
+*/
+class BOTAN_PUBLIC_API(2,0) System_RNG final : public RandomNumberGenerator
+   {
+   public:
+      std::string name() const override { return system_rng().name(); }
+
+      void randomize(uint8_t out[], size_t len) override { system_rng().randomize(out, len); }
+
+      void add_entropy(const uint8_t in[], size_t length) override { system_rng().add_entropy(in, length); }
+
+      bool is_seeded() const override { return system_rng().is_seeded(); }
+
+      bool accepts_input() const override { return system_rng().accepts_input(); }
+
+      void clear() override { system_rng().clear(); }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/chacha/chacha.cpp b/third_party/botan/src/lib/stream/chacha/chacha.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/chacha/chacha.cpp
@@ -0,0 +1,384 @@
+/*
+* ChaCha
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+#define CHACHA_QUARTER_ROUND(a, b, c, d) \
+      do {                               \
+      a += b; d ^= a; d = rotl<16>(d);   \
+      c += d; b ^= c; b = rotl<12>(b);   \
+      a += b; d ^= a; d = rotl<8>(d);    \
+      c += d; b ^= c; b = rotl<7>(b);    \
+      } while(0)
+
+/*
+* Generate HChaCha cipher stream (for XChaCha IV setup)
+*/
+void hchacha(uint32_t output[8], const uint32_t input[16], size_t rounds)
+   {
+   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
+
+   uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
+            x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
+            x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
+            x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
+
+   for(size_t i = 0; i != rounds / 2; ++i)
+      {
+      CHACHA_QUARTER_ROUND(x00, x04, x08, x12);
+      CHACHA_QUARTER_ROUND(x01, x05, x09, x13);
+      CHACHA_QUARTER_ROUND(x02, x06, x10, x14);
+      CHACHA_QUARTER_ROUND(x03, x07, x11, x15);
+
+      CHACHA_QUARTER_ROUND(x00, x05, x10, x15);
+      CHACHA_QUARTER_ROUND(x01, x06, x11, x12);
+      CHACHA_QUARTER_ROUND(x02, x07, x08, x13);
+      CHACHA_QUARTER_ROUND(x03, x04, x09, x14);
+      }
+
+   output[0] = x00;
+   output[1] = x01;
+   output[2] = x02;
+   output[3] = x03;
+   output[4] = x12;
+   output[5] = x13;
+   output[6] = x14;
+   output[7] = x15;
+   }
+
+}
+
+ChaCha::ChaCha(size_t rounds) : m_rounds(rounds)
+   {
+   BOTAN_ARG_CHECK(m_rounds == 8 || m_rounds == 12 || m_rounds == 20,
+                   "ChaCha only supports 8, 12 or 20 rounds");
+   }
+
+std::string ChaCha::provider() const
+   {
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return "avx2";
+      }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+   if(CPUID::has_simd_32())
+      {
+      return "simd32";
+      }
+#endif
+
+   return "base";
+   }
+
+//static
+void ChaCha::chacha_x8(uint8_t output[64*8], uint32_t input[16], size_t rounds)
+   {
+   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
+
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+   if(CPUID::has_avx2())
+      {
+      return ChaCha::chacha_avx2_x8(output, input, rounds);
+      }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+   if(CPUID::has_simd_32())
+      {
+      ChaCha::chacha_simd32_x4(output, input, rounds);
+      ChaCha::chacha_simd32_x4(output + 4*64, input, rounds);
+      return;
+      }
+#endif
+
+   // TODO interleave rounds
+   for(size_t i = 0; i != 8; ++i)
+      {
+      uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
+               x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
+               x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
+               x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
+
+      for(size_t r = 0; r != rounds / 2; ++r)
+         {
+         CHACHA_QUARTER_ROUND(x00, x04, x08, x12);
+         CHACHA_QUARTER_ROUND(x01, x05, x09, x13);
+         CHACHA_QUARTER_ROUND(x02, x06, x10, x14);
+         CHACHA_QUARTER_ROUND(x03, x07, x11, x15);
+
+         CHACHA_QUARTER_ROUND(x00, x05, x10, x15);
+         CHACHA_QUARTER_ROUND(x01, x06, x11, x12);
+         CHACHA_QUARTER_ROUND(x02, x07, x08, x13);
+         CHACHA_QUARTER_ROUND(x03, x04, x09, x14);
+         }
+
+      x00 += input[0];
+      x01 += input[1];
+      x02 += input[2];
+      x03 += input[3];
+      x04 += input[4];
+      x05 += input[5];
+      x06 += input[6];
+      x07 += input[7];
+      x08 += input[8];
+      x09 += input[9];
+      x10 += input[10];
+      x11 += input[11];
+      x12 += input[12];
+      x13 += input[13];
+      x14 += input[14];
+      x15 += input[15];
+
+      store_le(x00, output + 64 * i + 4 *  0);
+      store_le(x01, output + 64 * i + 4 *  1);
+      store_le(x02, output + 64 * i + 4 *  2);
+      store_le(x03, output + 64 * i + 4 *  3);
+      store_le(x04, output + 64 * i + 4 *  4);
+      store_le(x05, output + 64 * i + 4 *  5);
+      store_le(x06, output + 64 * i + 4 *  6);
+      store_le(x07, output + 64 * i + 4 *  7);
+      store_le(x08, output + 64 * i + 4 *  8);
+      store_le(x09, output + 64 * i + 4 *  9);
+      store_le(x10, output + 64 * i + 4 * 10);
+      store_le(x11, output + 64 * i + 4 * 11);
+      store_le(x12, output + 64 * i + 4 * 12);
+      store_le(x13, output + 64 * i + 4 * 13);
+      store_le(x14, output + 64 * i + 4 * 14);
+      store_le(x15, output + 64 * i + 4 * 15);
+
+      input[12]++;
+      input[13] += (input[12] == 0);
+      }
+   }
+
+#undef CHACHA_QUARTER_ROUND
+
+/*
+* Combine cipher stream with message
+*/
+void ChaCha::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   while(length >= m_buffer.size() - m_position)
+      {
+      const size_t available = m_buffer.size() - m_position;
+
+      xor_buf(out, in, &m_buffer[m_position], available);
+      chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+
+      length -= available;
+      in += available;
+      out += available;
+      m_position = 0;
+      }
+
+   xor_buf(out, in, &m_buffer[m_position], length);
+
+   m_position += length;
+   }
+
+void ChaCha::write_keystream(uint8_t out[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   while(length >= m_buffer.size() - m_position)
+      {
+      const size_t available = m_buffer.size() - m_position;
+
+      copy_mem(out, &m_buffer[m_position], available);
+      chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+
+      length -= available;
+      out += available;
+      m_position = 0;
+      }
+
+   copy_mem(out, &m_buffer[m_position], length);
+
+   m_position += length;
+   }
+
+void ChaCha::initialize_state()
+   {
+   static const uint32_t TAU[] =
+      { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
+
+   static const uint32_t SIGMA[] =
+      { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+   m_state[4] = m_key[0];
+   m_state[5] = m_key[1];
+   m_state[6] = m_key[2];
+   m_state[7] = m_key[3];
+
+   if(m_key.size() == 4)
+      {
+      m_state[0] = TAU[0];
+      m_state[1] = TAU[1];
+      m_state[2] = TAU[2];
+      m_state[3] = TAU[3];
+
+      m_state[8] = m_key[0];
+      m_state[9] = m_key[1];
+      m_state[10] = m_key[2];
+      m_state[11] = m_key[3];
+      }
+   else
+      {
+      m_state[0] = SIGMA[0];
+      m_state[1] = SIGMA[1];
+      m_state[2] = SIGMA[2];
+      m_state[3] = SIGMA[3];
+
+      m_state[8] = m_key[4];
+      m_state[9] = m_key[5];
+      m_state[10] = m_key[6];
+      m_state[11] = m_key[7];
+      }
+
+   m_state[12] = 0;
+   m_state[13] = 0;
+   m_state[14] = 0;
+   m_state[15] = 0;
+
+   m_position = 0;
+   }
+
+/*
+* ChaCha Key Schedule
+*/
+void ChaCha::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_key.resize(length / 4);
+   load_le<uint32_t>(m_key.data(), key, m_key.size());
+
+   m_state.resize(16);
+
+   const size_t chacha_parallelism = 8; // chacha_x8
+   const size_t chacha_block = 64;
+   m_buffer.resize(chacha_parallelism * chacha_block);
+
+   set_iv(nullptr, 0);
+   }
+
+size_t ChaCha::default_iv_length() const
+   {
+   return 24;
+   }
+
+Key_Length_Specification ChaCha::key_spec() const
+   {
+   return Key_Length_Specification(16, 32, 16);
+   }
+
+StreamCipher* ChaCha::clone() const
+   {
+   return new ChaCha(m_rounds);
+   }
+
+bool ChaCha::valid_iv_length(size_t iv_len) const
+   {
+   return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
+   }
+
+void ChaCha::set_iv(const uint8_t iv[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   if(!valid_iv_length(length))
+      throw Invalid_IV_Length(name(), length);
+
+   initialize_state();
+
+   if(length == 0)
+      {
+      // Treat zero length IV same as an all-zero IV
+      m_state[14] = 0;
+      m_state[15] = 0;
+      }
+   else if(length == 8)
+      {
+      m_state[14] = load_le<uint32_t>(iv, 0);
+      m_state[15] = load_le<uint32_t>(iv, 1);
+      }
+   else if(length == 12)
+      {
+      m_state[13] = load_le<uint32_t>(iv, 0);
+      m_state[14] = load_le<uint32_t>(iv, 1);
+      m_state[15] = load_le<uint32_t>(iv, 2);
+      }
+   else if(length == 24)
+      {
+      m_state[12] = load_le<uint32_t>(iv, 0);
+      m_state[13] = load_le<uint32_t>(iv, 1);
+      m_state[14] = load_le<uint32_t>(iv, 2);
+      m_state[15] = load_le<uint32_t>(iv, 3);
+
+      secure_vector<uint32_t> hc(8);
+      hchacha(hc.data(), m_state.data(), m_rounds);
+
+      m_state[ 4] = hc[0];
+      m_state[ 5] = hc[1];
+      m_state[ 6] = hc[2];
+      m_state[ 7] = hc[3];
+      m_state[ 8] = hc[4];
+      m_state[ 9] = hc[5];
+      m_state[10] = hc[6];
+      m_state[11] = hc[7];
+      m_state[12] = 0;
+      m_state[13] = 0;
+      m_state[14] = load_le<uint32_t>(iv, 4);
+      m_state[15] = load_le<uint32_t>(iv, 5);
+      }
+
+   chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+   m_position = 0;
+   }
+
+void ChaCha::clear()
+   {
+   zap(m_key);
+   zap(m_state);
+   zap(m_buffer);
+   m_position = 0;
+   }
+
+std::string ChaCha::name() const
+   {
+   return "ChaCha(" + std::to_string(m_rounds) + ")";
+   }
+
+void ChaCha::seek(uint64_t offset)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   // Find the block offset
+   const uint64_t counter = offset / 64;
+
+   uint8_t out[8];
+
+   store_le(counter, out);
+
+   m_state[12] = load_le<uint32_t>(out, 0);
+   m_state[13] += load_le<uint32_t>(out, 1);
+
+   chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+   m_position = offset % 64;
+   }
+}
diff --git a/third_party/botan/src/lib/stream/chacha/chacha.h b/third_party/botan/src/lib/stream/chacha/chacha.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/chacha/chacha.h
@@ -0,0 +1,82 @@
+/*
+* ChaCha20
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHACHA_H_
+#define BOTAN_CHACHA_H_
+
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(chacha.h)
+
+namespace Botan {
+
+/**
+* DJB's ChaCha (https://cr.yp.to/chacha.html)
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha final : public StreamCipher
+   {
+   public:
+      /**
+      * @param rounds number of rounds
+      * @note Currently only 8, 12 or 20 rounds are supported, all others
+      * will throw an exception
+      */
+      explicit ChaCha(size_t rounds = 20);
+
+      std::string provider() const override;
+
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void write_keystream(uint8_t out[], size_t len) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      /*
+      * ChaCha accepts 0, 8, 12 or 24 byte IVs.
+      * The default IV is a 8 zero bytes.
+      * An IV of length 0 is treated the same as the default zero IV.
+      * An IV of length 24 selects XChaCha mode
+      */
+      bool valid_iv_length(size_t iv_len) const override;
+
+      size_t default_iv_length() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      void clear() override;
+
+      StreamCipher* clone() const override;
+
+      std::string name() const override;
+
+      void seek(uint64_t offset) override;
+
+   private:
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      void initialize_state();
+
+      void chacha_x8(uint8_t output[64*8], uint32_t state[16], size_t rounds);
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+      void chacha_simd32_x4(uint8_t output[64*4], uint32_t state[16], size_t rounds);
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+      void chacha_avx2_x8(uint8_t output[64*8], uint32_t state[16], size_t rounds);
+#endif
+
+      size_t m_rounds;
+      secure_vector<uint32_t> m_key;
+      secure_vector<uint32_t> m_state;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_position = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp b/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp
@@ -0,0 +1,207 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+//static
+BOTAN_FUNC_ISA("avx2")
+void ChaCha::chacha_avx2_x8(uint8_t output[64*8], uint32_t state[16], size_t rounds)
+   {
+   SIMD_8x32::reset_registers();
+
+   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
+   const SIMD_8x32 CTR0 = SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7);
+
+   const uint32_t C = 0xFFFFFFFF - state[12];
+   const SIMD_8x32 CTR1 = SIMD_8x32(0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7);
+
+   SIMD_8x32 R00 = SIMD_8x32::splat(state[ 0]);
+   SIMD_8x32 R01 = SIMD_8x32::splat(state[ 1]);
+   SIMD_8x32 R02 = SIMD_8x32::splat(state[ 2]);
+   SIMD_8x32 R03 = SIMD_8x32::splat(state[ 3]);
+   SIMD_8x32 R04 = SIMD_8x32::splat(state[ 4]);
+   SIMD_8x32 R05 = SIMD_8x32::splat(state[ 5]);
+   SIMD_8x32 R06 = SIMD_8x32::splat(state[ 6]);
+   SIMD_8x32 R07 = SIMD_8x32::splat(state[ 7]);
+   SIMD_8x32 R08 = SIMD_8x32::splat(state[ 8]);
+   SIMD_8x32 R09 = SIMD_8x32::splat(state[ 9]);
+   SIMD_8x32 R10 = SIMD_8x32::splat(state[10]);
+   SIMD_8x32 R11 = SIMD_8x32::splat(state[11]);
+   SIMD_8x32 R12 = SIMD_8x32::splat(state[12]) + CTR0;
+   SIMD_8x32 R13 = SIMD_8x32::splat(state[13]) + CTR1;
+   SIMD_8x32 R14 = SIMD_8x32::splat(state[14]);
+   SIMD_8x32 R15 = SIMD_8x32::splat(state[15]);
+
+   for(size_t r = 0; r != rounds / 2; ++r)
+      {
+      R00 += R04;
+      R01 += R05;
+      R02 += R06;
+      R03 += R07;
+
+      R12 ^= R00;
+      R13 ^= R01;
+      R14 ^= R02;
+      R15 ^= R03;
+
+      R12 = R12.rotl<16>();
+      R13 = R13.rotl<16>();
+      R14 = R14.rotl<16>();
+      R15 = R15.rotl<16>();
+
+      R08 += R12;
+      R09 += R13;
+      R10 += R14;
+      R11 += R15;
+
+      R04 ^= R08;
+      R05 ^= R09;
+      R06 ^= R10;
+      R07 ^= R11;
+
+      R04 = R04.rotl<12>();
+      R05 = R05.rotl<12>();
+      R06 = R06.rotl<12>();
+      R07 = R07.rotl<12>();
+
+      R00 += R04;
+      R01 += R05;
+      R02 += R06;
+      R03 += R07;
+
+      R12 ^= R00;
+      R13 ^= R01;
+      R14 ^= R02;
+      R15 ^= R03;
+
+      R12 = R12.rotl<8>();
+      R13 = R13.rotl<8>();
+      R14 = R14.rotl<8>();
+      R15 = R15.rotl<8>();
+
+      R08 += R12;
+      R09 += R13;
+      R10 += R14;
+      R11 += R15;
+
+      R04 ^= R08;
+      R05 ^= R09;
+      R06 ^= R10;
+      R07 ^= R11;
+
+      R04 = R04.rotl<7>();
+      R05 = R05.rotl<7>();
+      R06 = R06.rotl<7>();
+      R07 = R07.rotl<7>();
+
+      R00 += R05;
+      R01 += R06;
+      R02 += R07;
+      R03 += R04;
+
+      R15 ^= R00;
+      R12 ^= R01;
+      R13 ^= R02;
+      R14 ^= R03;
+
+      R15 = R15.rotl<16>();
+      R12 = R12.rotl<16>();
+      R13 = R13.rotl<16>();
+      R14 = R14.rotl<16>();
+
+      R10 += R15;
+      R11 += R12;
+      R08 += R13;
+      R09 += R14;
+
+      R05 ^= R10;
+      R06 ^= R11;
+      R07 ^= R08;
+      R04 ^= R09;
+
+      R05 = R05.rotl<12>();
+      R06 = R06.rotl<12>();
+      R07 = R07.rotl<12>();
+      R04 = R04.rotl<12>();
+
+      R00 += R05;
+      R01 += R06;
+      R02 += R07;
+      R03 += R04;
+
+      R15 ^= R00;
+      R12 ^= R01;
+      R13 ^= R02;
+      R14 ^= R03;
+
+      R15 = R15.rotl<8>();
+      R12 = R12.rotl<8>();
+      R13 = R13.rotl<8>();
+      R14 = R14.rotl<8>();
+
+      R10 += R15;
+      R11 += R12;
+      R08 += R13;
+      R09 += R14;
+
+      R05 ^= R10;
+      R06 ^= R11;
+      R07 ^= R08;
+      R04 ^= R09;
+
+      R05 = R05.rotl<7>();
+      R06 = R06.rotl<7>();
+      R07 = R07.rotl<7>();
+      R04 = R04.rotl<7>();
+      }
+
+   R00 += SIMD_8x32::splat(state[0]);
+   R01 += SIMD_8x32::splat(state[1]);
+   R02 += SIMD_8x32::splat(state[2]);
+   R03 += SIMD_8x32::splat(state[3]);
+   R04 += SIMD_8x32::splat(state[4]);
+   R05 += SIMD_8x32::splat(state[5]);
+   R06 += SIMD_8x32::splat(state[6]);
+   R07 += SIMD_8x32::splat(state[7]);
+   R08 += SIMD_8x32::splat(state[8]);
+   R09 += SIMD_8x32::splat(state[9]);
+   R10 += SIMD_8x32::splat(state[10]);
+   R11 += SIMD_8x32::splat(state[11]);
+   R12 += SIMD_8x32::splat(state[12]) + CTR0;
+   R13 += SIMD_8x32::splat(state[13]) + CTR1;
+   R14 += SIMD_8x32::splat(state[14]);
+   R15 += SIMD_8x32::splat(state[15]);
+
+   SIMD_8x32::transpose(R00, R01, R02, R03, R04, R05, R06, R07);
+   SIMD_8x32::transpose(R08, R09, R10, R11, R12, R13, R14, R15);
+
+   R00.store_le(output);
+   R08.store_le(output + 32*1);
+   R01.store_le(output + 32*2);
+   R09.store_le(output + 32*3);
+   R02.store_le(output + 32*4);
+   R10.store_le(output + 32*5);
+   R03.store_le(output + 32*6);
+   R11.store_le(output + 32*7);
+   R04.store_le(output + 32*8);
+   R12.store_le(output + 32*9);
+   R05.store_le(output + 32*10);
+   R13.store_le(output + 32*11);
+   R06.store_le(output + 32*12);
+   R14.store_le(output + 32*13);
+   R07.store_le(output + 32*14);
+   R15.store_le(output + 32*15);
+
+   SIMD_8x32::zero_registers();
+
+   state[12] += 8;
+   if(state[12] < 8)
+      state[13]++;
+   }
+}
diff --git a/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp b/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp
@@ -0,0 +1,205 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+//static
+void ChaCha::chacha_simd32_x4(uint8_t output[64*4], uint32_t state[16], size_t rounds)
+   {
+   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
+   const SIMD_4x32 CTR0 = SIMD_4x32(0, 1, 2, 3);
+
+   const uint32_t C = 0xFFFFFFFF - state[12];
+   const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3);
+
+   SIMD_4x32 R00 = SIMD_4x32::splat(state[ 0]);
+   SIMD_4x32 R01 = SIMD_4x32::splat(state[ 1]);
+   SIMD_4x32 R02 = SIMD_4x32::splat(state[ 2]);
+   SIMD_4x32 R03 = SIMD_4x32::splat(state[ 3]);
+   SIMD_4x32 R04 = SIMD_4x32::splat(state[ 4]);
+   SIMD_4x32 R05 = SIMD_4x32::splat(state[ 5]);
+   SIMD_4x32 R06 = SIMD_4x32::splat(state[ 6]);
+   SIMD_4x32 R07 = SIMD_4x32::splat(state[ 7]);
+   SIMD_4x32 R08 = SIMD_4x32::splat(state[ 8]);
+   SIMD_4x32 R09 = SIMD_4x32::splat(state[ 9]);
+   SIMD_4x32 R10 = SIMD_4x32::splat(state[10]);
+   SIMD_4x32 R11 = SIMD_4x32::splat(state[11]);
+   SIMD_4x32 R12 = SIMD_4x32::splat(state[12]) + CTR0;
+   SIMD_4x32 R13 = SIMD_4x32::splat(state[13]) + CTR1;
+   SIMD_4x32 R14 = SIMD_4x32::splat(state[14]);
+   SIMD_4x32 R15 = SIMD_4x32::splat(state[15]);
+
+   for(size_t r = 0; r != rounds / 2; ++r)
+      {
+      R00 += R04;
+      R01 += R05;
+      R02 += R06;
+      R03 += R07;
+
+      R12 ^= R00;
+      R13 ^= R01;
+      R14 ^= R02;
+      R15 ^= R03;
+
+      R12 = R12.rotl<16>();
+      R13 = R13.rotl<16>();
+      R14 = R14.rotl<16>();
+      R15 = R15.rotl<16>();
+
+      R08 += R12;
+      R09 += R13;
+      R10 += R14;
+      R11 += R15;
+
+      R04 ^= R08;
+      R05 ^= R09;
+      R06 ^= R10;
+      R07 ^= R11;
+
+      R04 = R04.rotl<12>();
+      R05 = R05.rotl<12>();
+      R06 = R06.rotl<12>();
+      R07 = R07.rotl<12>();
+
+      R00 += R04;
+      R01 += R05;
+      R02 += R06;
+      R03 += R07;
+
+      R12 ^= R00;
+      R13 ^= R01;
+      R14 ^= R02;
+      R15 ^= R03;
+
+      R12 = R12.rotl<8>();
+      R13 = R13.rotl<8>();
+      R14 = R14.rotl<8>();
+      R15 = R15.rotl<8>();
+
+      R08 += R12;
+      R09 += R13;
+      R10 += R14;
+      R11 += R15;
+
+      R04 ^= R08;
+      R05 ^= R09;
+      R06 ^= R10;
+      R07 ^= R11;
+
+      R04 = R04.rotl<7>();
+      R05 = R05.rotl<7>();
+      R06 = R06.rotl<7>();
+      R07 = R07.rotl<7>();
+
+      R00 += R05;
+      R01 += R06;
+      R02 += R07;
+      R03 += R04;
+
+      R15 ^= R00;
+      R12 ^= R01;
+      R13 ^= R02;
+      R14 ^= R03;
+
+      R15 = R15.rotl<16>();
+      R12 = R12.rotl<16>();
+      R13 = R13.rotl<16>();
+      R14 = R14.rotl<16>();
+
+      R10 += R15;
+      R11 += R12;
+      R08 += R13;
+      R09 += R14;
+
+      R05 ^= R10;
+      R06 ^= R11;
+      R07 ^= R08;
+      R04 ^= R09;
+
+      R05 = R05.rotl<12>();
+      R06 = R06.rotl<12>();
+      R07 = R07.rotl<12>();
+      R04 = R04.rotl<12>();
+
+      R00 += R05;
+      R01 += R06;
+      R02 += R07;
+      R03 += R04;
+
+      R15 ^= R00;
+      R12 ^= R01;
+      R13 ^= R02;
+      R14 ^= R03;
+
+      R15 = R15.rotl<8>();
+      R12 = R12.rotl<8>();
+      R13 = R13.rotl<8>();
+      R14 = R14.rotl<8>();
+
+      R10 += R15;
+      R11 += R12;
+      R08 += R13;
+      R09 += R14;
+
+      R05 ^= R10;
+      R06 ^= R11;
+      R07 ^= R08;
+      R04 ^= R09;
+
+      R05 = R05.rotl<7>();
+      R06 = R06.rotl<7>();
+      R07 = R07.rotl<7>();
+      R04 = R04.rotl<7>();
+      }
+
+   R00 += SIMD_4x32::splat(state[0]);
+   R01 += SIMD_4x32::splat(state[1]);
+   R02 += SIMD_4x32::splat(state[2]);
+   R03 += SIMD_4x32::splat(state[3]);
+   R04 += SIMD_4x32::splat(state[4]);
+   R05 += SIMD_4x32::splat(state[5]);
+   R06 += SIMD_4x32::splat(state[6]);
+   R07 += SIMD_4x32::splat(state[7]);
+   R08 += SIMD_4x32::splat(state[8]);
+   R09 += SIMD_4x32::splat(state[9]);
+   R10 += SIMD_4x32::splat(state[10]);
+   R11 += SIMD_4x32::splat(state[11]);
+   R12 += SIMD_4x32::splat(state[12]) + CTR0;
+   R13 += SIMD_4x32::splat(state[13]) + CTR1;
+   R14 += SIMD_4x32::splat(state[14]);
+   R15 += SIMD_4x32::splat(state[15]);
+
+   SIMD_4x32::transpose(R00, R01, R02, R03);
+   SIMD_4x32::transpose(R04, R05, R06, R07);
+   SIMD_4x32::transpose(R08, R09, R10, R11);
+   SIMD_4x32::transpose(R12, R13, R14, R15);
+
+   R00.store_le(output + 0*16);
+   R04.store_le(output + 1*16);
+   R08.store_le(output + 2*16);
+   R12.store_le(output + 3*16);
+   R01.store_le(output + 4*16);
+   R05.store_le(output + 5*16);
+   R09.store_le(output + 6*16);
+   R13.store_le(output + 7*16);
+   R02.store_le(output + 8*16);
+   R06.store_le(output + 9*16);
+   R10.store_le(output + 10*16);
+   R14.store_le(output + 11*16);
+   R03.store_le(output + 12*16);
+   R07.store_le(output + 13*16);
+   R11.store_le(output + 14*16);
+   R15.store_le(output + 15*16);
+
+   state[12] += 4;
+   if(state[12] < 4)
+      state[13]++;
+   }
+
+}
diff --git a/third_party/botan/src/lib/stream/ctr/ctr.cpp b/third_party/botan/src/lib/stream/ctr/ctr.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/ctr/ctr.cpp
@@ -0,0 +1,256 @@
+/*
+* Counter mode
+* (C) 1999-2011,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ctr.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+CTR_BE::CTR_BE(BlockCipher* ciph) :
+   m_cipher(ciph),
+   m_block_size(m_cipher->block_size()),
+   m_ctr_size(m_block_size),
+   m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
+   m_counter(m_cipher->parallel_bytes()),
+   m_pad(m_counter.size()),
+   m_pad_pos(0)
+   {
+   }
+
+CTR_BE::CTR_BE(BlockCipher* cipher, size_t ctr_size) :
+   m_cipher(cipher),
+   m_block_size(m_cipher->block_size()),
+   m_ctr_size(ctr_size),
+   m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
+   m_counter(m_cipher->parallel_bytes()),
+   m_pad(m_counter.size()),
+   m_pad_pos(0)
+   {
+   BOTAN_ARG_CHECK(m_ctr_size >= 4 && m_ctr_size <= m_block_size,
+                   "Invalid CTR-BE counter size");
+   }
+
+void CTR_BE::clear()
+   {
+   m_cipher->clear();
+   zeroise(m_pad);
+   zeroise(m_counter);
+   zap(m_iv);
+   m_pad_pos = 0;
+   }
+
+size_t CTR_BE::default_iv_length() const
+   {
+   return m_block_size;
+   }
+
+bool CTR_BE::valid_iv_length(size_t iv_len) const
+   {
+   return (iv_len <= m_block_size);
+   }
+
+Key_Length_Specification CTR_BE::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+CTR_BE* CTR_BE::clone() const
+   {
+   return new CTR_BE(m_cipher->clone(), m_ctr_size);
+   }
+
+void CTR_BE::key_schedule(const uint8_t key[], size_t key_len)
+   {
+   m_cipher->set_key(key, key_len);
+
+   // Set a default all-zeros IV
+   set_iv(nullptr, 0);
+   }
+
+std::string CTR_BE::name() const
+   {
+   if(m_ctr_size == m_block_size)
+      return ("CTR-BE(" + m_cipher->name() + ")");
+   else
+      return ("CTR-BE(" + m_cipher->name() + "," + std::to_string(m_ctr_size) + ")");
+
+   }
+
+void CTR_BE::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   verify_key_set(m_iv.empty() == false);
+
+   const uint8_t* pad_bits = &m_pad[0];
+   const size_t pad_size = m_pad.size();
+
+   if(m_pad_pos > 0)
+      {
+      const size_t avail = pad_size - m_pad_pos;
+      const size_t take = std::min(length, avail);
+      xor_buf(out, in, pad_bits + m_pad_pos, take);
+      length -= take;
+      in += take;
+      out += take;
+      m_pad_pos += take;
+
+      if(take == avail)
+         {
+         add_counter(m_ctr_blocks);
+         m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+         m_pad_pos = 0;
+         }
+      }
+
+   while(length >= pad_size)
+      {
+      xor_buf(out, in, pad_bits, pad_size);
+      length -= pad_size;
+      in += pad_size;
+      out += pad_size;
+
+      add_counter(m_ctr_blocks);
+      m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+      }
+
+   xor_buf(out, in, pad_bits, length);
+   m_pad_pos += length;
+   }
+
+void CTR_BE::set_iv(const uint8_t iv[], size_t iv_len)
+   {
+   if(!valid_iv_length(iv_len))
+      throw Invalid_IV_Length(name(), iv_len);
+
+   m_iv.resize(m_block_size);
+   zeroise(m_iv);
+   buffer_insert(m_iv, 0, iv, iv_len);
+
+   seek(0);
+   }
+
+void CTR_BE::add_counter(const uint64_t counter)
+   {
+   const size_t ctr_size = m_ctr_size;
+   const size_t ctr_blocks = m_ctr_blocks;
+   const size_t BS = m_block_size;
+
+   if(ctr_size == 4)
+      {
+      const size_t off = (BS - 4);
+      const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));
+
+      for(size_t i = 0; i != ctr_blocks; ++i)
+         {
+         store_be(uint32_t(low32 + i), &m_counter[i*BS+off]);
+         }
+      }
+   else if(ctr_size == 8)
+      {
+      const size_t off = (BS - 8);
+      const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);
+
+      for(size_t i = 0; i != ctr_blocks; ++i)
+         {
+         store_be(uint64_t(low64 + i), &m_counter[i*BS+off]);
+         }
+      }
+   else if(ctr_size == 16)
+      {
+      const size_t off = (BS - 16);
+      uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);
+      uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);
+      b1 += counter;
+      b0 += (b1 < counter) ? 1 : 0; // carry
+
+      for(size_t i = 0; i != ctr_blocks; ++i)
+         {
+         store_be(b0, &m_counter[i*BS+off]);
+         store_be(b1, &m_counter[i*BS+off+8]);
+         b1 += 1;
+         b0 += (b1 == 0); // carry
+         }
+      }
+   else
+      {
+      for(size_t i = 0; i != ctr_blocks; ++i)
+         {
+         uint64_t local_counter = counter;
+         uint16_t carry = static_cast<uint8_t>(local_counter);
+         for(size_t j = 0; (carry || local_counter) && j != ctr_size; ++j)
+            {
+            const size_t off = i*BS + (BS-1-j);
+            const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;
+            m_counter[off] = static_cast<uint8_t>(cnt);
+            local_counter = (local_counter >> 8);
+            carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);
+            }
+         }
+      }
+   }
+
+void CTR_BE::seek(uint64_t offset)
+   {
+   verify_key_set(m_iv.empty() == false);
+
+   const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());
+
+   zeroise(m_counter);
+   buffer_insert(m_counter, 0, m_iv);
+
+   const size_t BS = m_block_size;
+
+   // Set m_counter blocks to IV, IV + 1, ... IV + n
+
+   if(m_ctr_size == 4 && BS >= 8)
+      {
+      const uint32_t low32 = load_be<uint32_t>(&m_counter[BS-4], 0);
+
+      if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks))
+         {
+         size_t written = 1;
+         while(written < m_ctr_blocks)
+            {
+            copy_mem(&m_counter[written*BS], &m_counter[0], BS*written);
+            written *= 2;
+            }
+         }
+      else
+         {
+         for(size_t i = 1; i != m_ctr_blocks; ++i)
+            {
+            copy_mem(&m_counter[i*BS], &m_counter[0], BS - 4);
+            }
+         }
+
+      for(size_t i = 1; i != m_ctr_blocks; ++i)
+         {
+         const uint32_t c = static_cast<uint32_t>(low32 + i);
+         store_be(c, &m_counter[(BS-4)+i*BS]);
+         }
+      }
+   else
+      {
+      // do everything sequentially:
+      for(size_t i = 1; i != m_ctr_blocks; ++i)
+         {
+         buffer_insert(m_counter, i*BS, &m_counter[(i-1)*BS], BS);
+
+         for(size_t j = 0; j != m_ctr_size; ++j)
+            if(++m_counter[i*BS + (BS - 1 - j)])
+               break;
+         }
+      }
+
+   if(base_counter > 0)
+      add_counter(base_counter);
+
+   m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+   m_pad_pos = offset % m_counter.size();
+   }
+}
diff --git a/third_party/botan/src/lib/stream/ctr/ctr.h b/third_party/botan/src/lib/stream/ctr/ctr.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/ctr/ctr.h
@@ -0,0 +1,65 @@
+/*
+* CTR-BE Mode
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CTR_BE_H_
+#define BOTAN_CTR_BE_H_
+
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ctr.h)
+
+namespace Botan {
+
+/**
+* CTR-BE (Counter mode, big-endian)
+*/
+class BOTAN_PUBLIC_API(2,0) CTR_BE final : public StreamCipher
+   {
+   public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      size_t default_iv_length() const override;
+
+      bool valid_iv_length(size_t iv_len) const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      std::string name() const override;
+
+      CTR_BE* clone() const override;
+
+      void clear() override;
+
+      /**
+      * @param cipher the block cipher to use
+      */
+      explicit CTR_BE(BlockCipher* cipher);
+
+      CTR_BE(BlockCipher* cipher, size_t ctr_size);
+
+      void seek(uint64_t offset) override;
+   private:
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+      void add_counter(const uint64_t counter);
+
+      std::unique_ptr<BlockCipher> m_cipher;
+
+      const size_t m_block_size;
+      const size_t m_ctr_size;
+      const size_t m_ctr_blocks;
+
+      secure_vector<uint8_t> m_counter, m_pad;
+      std::vector<uint8_t> m_iv;
+      size_t m_pad_pos;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/ofb/ofb.cpp b/third_party/botan/src/lib/stream/ofb/ofb.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/ofb/ofb.cpp
@@ -0,0 +1,92 @@
+/*
+* OFB Mode
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ofb.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+OFB::OFB(BlockCipher* cipher) :
+   m_cipher(cipher),
+   m_buffer(m_cipher->block_size()),
+   m_buf_pos(0)
+   {
+   }
+
+void OFB::clear()
+   {
+   m_cipher->clear();
+   zeroise(m_buffer);
+   m_buf_pos = 0;
+   }
+
+void OFB::key_schedule(const uint8_t key[], size_t key_len)
+   {
+   m_cipher->set_key(key, key_len);
+
+   // Set a default all-zeros IV
+   set_iv(nullptr, 0);
+   }
+
+std::string OFB::name() const
+   {
+   return "OFB(" + m_cipher->name() + ")";
+   }
+
+size_t OFB::default_iv_length() const
+   {
+   return m_cipher->block_size();
+   }
+
+bool OFB::valid_iv_length(size_t iv_len) const
+   {
+   return (iv_len <= m_cipher->block_size());
+   }
+
+Key_Length_Specification OFB::key_spec() const
+   {
+   return m_cipher->key_spec();
+   }
+
+OFB* OFB::clone() const
+   {
+   return new OFB(m_cipher->clone());
+   }
+
+void OFB::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   while(length >= m_buffer.size() - m_buf_pos)
+      {
+      xor_buf(out, in, &m_buffer[m_buf_pos], m_buffer.size() - m_buf_pos);
+      length -= (m_buffer.size() - m_buf_pos);
+      in += (m_buffer.size() - m_buf_pos);
+      out += (m_buffer.size() - m_buf_pos);
+      m_cipher->encrypt(m_buffer);
+      m_buf_pos = 0;
+      }
+   xor_buf(out, in, &m_buffer[m_buf_pos], length);
+   m_buf_pos += length;
+   }
+
+void OFB::set_iv(const uint8_t iv[], size_t iv_len)
+   {
+   if(!valid_iv_length(iv_len))
+      throw Invalid_IV_Length(name(), iv_len);
+
+   zeroise(m_buffer);
+   buffer_insert(m_buffer, 0, iv, iv_len);
+
+   m_cipher->encrypt(m_buffer);
+   m_buf_pos = 0;
+   }
+
+
+void OFB::seek(uint64_t)
+   {
+   throw Not_Implemented("OFB does not support seeking");
+   }
+}
diff --git a/third_party/botan/src/lib/stream/ofb/ofb.h b/third_party/botan/src/lib/stream/ofb/ofb.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/ofb/ofb.h
@@ -0,0 +1,56 @@
+/*
+* OFB Mode
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OUTPUT_FEEDBACK_MODE_H_
+#define BOTAN_OUTPUT_FEEDBACK_MODE_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ofb.h)
+
+namespace Botan {
+
+/**
+* Output Feedback Mode
+*/
+class BOTAN_PUBLIC_API(2,0) OFB final : public StreamCipher
+   {
+   public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      size_t default_iv_length() const override;
+
+      bool valid_iv_length(size_t iv_len) const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      std::string name() const override;
+
+      OFB* clone() const override;
+
+      void clear() override;
+
+      /**
+      * @param cipher the block cipher to use
+      */
+      explicit OFB(BlockCipher* cipher);
+
+      void seek(uint64_t offset) override;
+   private:
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      std::unique_ptr<BlockCipher> m_cipher;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_buf_pos;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/rc4/rc4.cpp b/third_party/botan/src/lib/stream/rc4/rc4.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/rc4/rc4.cpp
@@ -0,0 +1,133 @@
+/*
+* RC4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rc4.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Combine cipher stream with message
+*/
+void RC4::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   while(length >= m_buffer.size() - m_position)
+      {
+      xor_buf(out, in, &m_buffer[m_position], m_buffer.size() - m_position);
+      length -= (m_buffer.size() - m_position);
+      in += (m_buffer.size() - m_position);
+      out += (m_buffer.size() - m_position);
+      generate();
+      }
+   xor_buf(out, in, &m_buffer[m_position], length);
+   m_position += length;
+   }
+
+StreamCipher* RC4::clone() const
+   {
+   return new RC4(m_SKIP);
+   }
+
+Key_Length_Specification RC4::key_spec() const
+   {
+   return Key_Length_Specification(1, 256);
+   }
+
+void RC4::set_iv(const uint8_t*, size_t length)
+   {
+   if(length > 0)
+      throw Invalid_IV_Length("RC4", length);
+   }
+
+/*
+* Generate cipher stream
+*/
+void RC4::generate()
+   {
+   uint8_t SX, SY;
+   for(size_t i = 0; i != m_buffer.size(); i += 4)
+      {
+      SX = m_state[m_X+1]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+      m_state[m_X+1] = SY; m_state[m_Y] = SX;
+      m_buffer[i] = m_state[(SX + SY) % 256];
+
+      SX = m_state[m_X+2]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+      m_state[m_X+2] = SY; m_state[m_Y] = SX;
+      m_buffer[i+1] = m_state[(SX + SY) % 256];
+
+      SX = m_state[m_X+3]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+      m_state[m_X+3] = SY; m_state[m_Y] = SX;
+      m_buffer[i+2] = m_state[(SX + SY) % 256];
+
+      m_X = (m_X + 4) % 256;
+      SX = m_state[m_X]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+      m_state[m_X] = SY; m_state[m_Y] = SX;
+      m_buffer[i+3] = m_state[(SX + SY) % 256];
+      }
+   m_position = 0;
+   }
+
+/*
+* RC4 Key Schedule
+*/
+void RC4::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_state.resize(256);
+   m_buffer.resize(256);
+
+   m_position = m_X = m_Y = 0;
+
+   for(size_t i = 0; i != 256; ++i)
+      m_state[i] = static_cast<uint8_t>(i);
+
+   for(size_t i = 0, state_index = 0; i != 256; ++i)
+      {
+      state_index = (state_index + key[i % length] + m_state[i]) % 256;
+      std::swap(m_state[i], m_state[state_index]);
+      }
+
+   for(size_t i = 0; i <= m_SKIP; i += m_buffer.size())
+      generate();
+
+   m_position += (m_SKIP % m_buffer.size());
+   }
+
+/*
+* Return the name of this type
+*/
+std::string RC4::name() const
+   {
+   if(m_SKIP == 0)
+      return "RC4";
+   else if(m_SKIP == 256)
+      return "MARK-4";
+   else
+      return "RC4(" + std::to_string(m_SKIP) + ")";
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void RC4::clear()
+   {
+   zap(m_state);
+   zap(m_buffer);
+   m_position = m_X = m_Y = 0;
+   }
+
+/*
+* RC4 Constructor
+*/
+RC4::RC4(size_t s) : m_SKIP(s) {}
+
+void RC4::seek(uint64_t)
+   {
+   throw Not_Implemented("RC4 does not support seeking");
+   }
+}
diff --git a/third_party/botan/src/lib/stream/rc4/rc4.h b/third_party/botan/src/lib/stream/rc4/rc4.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/rc4/rc4.h
@@ -0,0 +1,57 @@
+/*
+* RC4
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RC4_H_
+#define BOTAN_RC4_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rc4.h)
+
+namespace Botan {
+
+/**
+* RC4 stream cipher
+*/
+class BOTAN_PUBLIC_API(2,0) RC4 final : public StreamCipher
+   {
+   public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      void clear() override;
+      std::string name() const override;
+
+      StreamCipher* clone() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      /**
+      * @param skip skip this many initial bytes in the keystream
+      */
+      explicit RC4(size_t skip = 0);
+
+      ~RC4() { clear(); }
+
+      void seek(uint64_t offset) override;
+   private:
+      void key_schedule(const uint8_t[], size_t) override;
+      void generate();
+
+      const size_t m_SKIP;
+      uint8_t m_X = 0;
+      uint8_t m_Y = 0;
+      secure_vector<uint8_t> m_state;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_position = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/salsa20/salsa20.cpp b/third_party/botan/src/lib/stream/salsa20/salsa20.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/salsa20/salsa20.cpp
@@ -0,0 +1,302 @@
+/*
+* Salsa20 / XSalsa20
+* (C) 1999-2010,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/salsa20.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+#define SALSA20_QUARTER_ROUND(x1, x2, x3, x4)    \
+   do {                                          \
+      x2 ^= rotl<7>(x1 + x4);                    \
+      x3 ^= rotl<9>(x2 + x1);                    \
+      x4 ^= rotl<13>(x3 + x2);                   \
+      x1 ^= rotl<18>(x4 + x3);                   \
+   } while(0)
+
+/*
+* Generate HSalsa20 cipher stream (for XSalsa20 IV setup)
+*/
+//static
+void Salsa20::hsalsa20(uint32_t output[8], const uint32_t input[16])
+   {
+   uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
+            x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
+            x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
+            x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
+
+   for(size_t i = 0; i != 10; ++i)
+      {
+      SALSA20_QUARTER_ROUND(x00, x04, x08, x12);
+      SALSA20_QUARTER_ROUND(x05, x09, x13, x01);
+      SALSA20_QUARTER_ROUND(x10, x14, x02, x06);
+      SALSA20_QUARTER_ROUND(x15, x03, x07, x11);
+
+      SALSA20_QUARTER_ROUND(x00, x01, x02, x03);
+      SALSA20_QUARTER_ROUND(x05, x06, x07, x04);
+      SALSA20_QUARTER_ROUND(x10, x11, x08, x09);
+      SALSA20_QUARTER_ROUND(x15, x12, x13, x14);
+      }
+
+   output[0] = x00;
+   output[1] = x05;
+   output[2] = x10;
+   output[3] = x15;
+   output[4] = x06;
+   output[5] = x07;
+   output[6] = x08;
+   output[7] = x09;
+   }
+
+/*
+* Generate Salsa20 cipher stream
+*/
+//static
+void Salsa20::salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds)
+   {
+   BOTAN_ASSERT_NOMSG(rounds % 2 == 0);
+
+   uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
+            x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
+            x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
+            x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
+
+   for(size_t i = 0; i != rounds / 2; ++i)
+      {
+      SALSA20_QUARTER_ROUND(x00, x04, x08, x12);
+      SALSA20_QUARTER_ROUND(x05, x09, x13, x01);
+      SALSA20_QUARTER_ROUND(x10, x14, x02, x06);
+      SALSA20_QUARTER_ROUND(x15, x03, x07, x11);
+
+      SALSA20_QUARTER_ROUND(x00, x01, x02, x03);
+      SALSA20_QUARTER_ROUND(x05, x06, x07, x04);
+      SALSA20_QUARTER_ROUND(x10, x11, x08, x09);
+      SALSA20_QUARTER_ROUND(x15, x12, x13, x14);
+      }
+
+   store_le(x00 + input[ 0], output + 4 *  0);
+   store_le(x01 + input[ 1], output + 4 *  1);
+   store_le(x02 + input[ 2], output + 4 *  2);
+   store_le(x03 + input[ 3], output + 4 *  3);
+   store_le(x04 + input[ 4], output + 4 *  4);
+   store_le(x05 + input[ 5], output + 4 *  5);
+   store_le(x06 + input[ 6], output + 4 *  6);
+   store_le(x07 + input[ 7], output + 4 *  7);
+   store_le(x08 + input[ 8], output + 4 *  8);
+   store_le(x09 + input[ 9], output + 4 *  9);
+   store_le(x10 + input[10], output + 4 * 10);
+   store_le(x11 + input[11], output + 4 * 11);
+   store_le(x12 + input[12], output + 4 * 12);
+   store_le(x13 + input[13], output + 4 * 13);
+   store_le(x14 + input[14], output + 4 * 14);
+   store_le(x15 + input[15], output + 4 * 15);
+   }
+
+#undef SALSA20_QUARTER_ROUND
+
+/*
+* Combine cipher stream with message
+*/
+void Salsa20::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   while(length >= m_buffer.size() - m_position)
+      {
+      const size_t available = m_buffer.size() - m_position;
+
+      xor_buf(out, in, &m_buffer[m_position], available);
+      salsa_core(m_buffer.data(), m_state.data(), 20);
+
+      ++m_state[8];
+      m_state[9] += (m_state[8] == 0);
+
+      length -= available;
+      in += available;
+      out += available;
+
+      m_position = 0;
+      }
+
+   xor_buf(out, in, &m_buffer[m_position], length);
+
+   m_position += length;
+   }
+
+void Salsa20::initialize_state()
+   {
+   static const uint32_t TAU[] =
+      { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
+
+   static const uint32_t SIGMA[] =
+      { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+   m_state[1] = m_key[0];
+   m_state[2] = m_key[1];
+   m_state[3] = m_key[2];
+   m_state[4] = m_key[3];
+
+   if(m_key.size() == 4)
+      {
+      m_state[0] = TAU[0];
+      m_state[5] = TAU[1];
+      m_state[10] = TAU[2];
+      m_state[15] = TAU[3];
+      m_state[11] = m_key[0];
+      m_state[12] = m_key[1];
+      m_state[13] = m_key[2];
+      m_state[14] = m_key[3];
+      }
+   else
+      {
+      m_state[0] = SIGMA[0];
+      m_state[5] = SIGMA[1];
+      m_state[10] = SIGMA[2];
+      m_state[15] = SIGMA[3];
+      m_state[11] = m_key[4];
+      m_state[12] = m_key[5];
+      m_state[13] = m_key[6];
+      m_state[14] = m_key[7];
+      }
+
+   m_state[6] = 0;
+   m_state[7] = 0;
+   m_state[8] = 0;
+   m_state[9] = 0;
+
+   m_position = 0;
+   }
+
+/*
+* Salsa20 Key Schedule
+*/
+void Salsa20::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_key.resize(length / 4);
+   load_le<uint32_t>(m_key.data(), key, m_key.size());
+
+   m_state.resize(16);
+   m_buffer.resize(64);
+
+   set_iv(nullptr, 0);
+   }
+
+/*
+* Set the Salsa IV
+*/
+void Salsa20::set_iv(const uint8_t iv[], size_t length)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   if(!valid_iv_length(length))
+      throw Invalid_IV_Length(name(), length);
+
+   initialize_state();
+
+   if(length == 0)
+      {
+      // Salsa20 null IV
+      m_state[6] = 0;
+      m_state[7] = 0;
+      }
+   else if(length == 8)
+      {
+      // Salsa20
+      m_state[6] = load_le<uint32_t>(iv, 0);
+      m_state[7] = load_le<uint32_t>(iv, 1);
+      }
+   else
+      {
+      // XSalsa20
+      m_state[6] = load_le<uint32_t>(iv, 0);
+      m_state[7] = load_le<uint32_t>(iv, 1);
+      m_state[8] = load_le<uint32_t>(iv, 2);
+      m_state[9] = load_le<uint32_t>(iv, 3);
+
+      secure_vector<uint32_t> hsalsa(8);
+      hsalsa20(hsalsa.data(), m_state.data());
+
+      m_state[ 1] = hsalsa[0];
+      m_state[ 2] = hsalsa[1];
+      m_state[ 3] = hsalsa[2];
+      m_state[ 4] = hsalsa[3];
+      m_state[ 6] = load_le<uint32_t>(iv, 4);
+      m_state[ 7] = load_le<uint32_t>(iv, 5);
+      m_state[11] = hsalsa[4];
+      m_state[12] = hsalsa[5];
+      m_state[13] = hsalsa[6];
+      m_state[14] = hsalsa[7];
+      }
+
+   m_state[8] = 0;
+   m_state[9] = 0;
+
+   salsa_core(m_buffer.data(), m_state.data(), 20);
+   ++m_state[8];
+   m_state[9] += (m_state[8] == 0);
+
+   m_position = 0;
+   }
+
+bool Salsa20::valid_iv_length(size_t iv_len) const
+   {
+   return (iv_len == 0 || iv_len == 8 || iv_len == 24);
+   }
+
+size_t Salsa20::default_iv_length() const
+   {
+   return 24;
+   }
+
+Key_Length_Specification Salsa20::key_spec() const
+   {
+   return Key_Length_Specification(16, 32, 16);
+   }
+
+StreamCipher* Salsa20::clone() const
+   {
+   return new Salsa20;
+   }
+
+std::string Salsa20::name() const
+   {
+   return "Salsa20";
+   }
+
+/*
+* Clear memory of sensitive data
+*/
+void Salsa20::clear()
+   {
+   zap(m_key);
+   zap(m_state);
+   zap(m_buffer);
+   m_position = 0;
+   }
+
+void Salsa20::seek(uint64_t offset)
+   {
+   verify_key_set(m_state.empty() == false);
+
+   // Find the block offset
+   const uint64_t counter = offset / 64;
+   uint8_t counter8[8];
+   store_le(counter, counter8);
+
+   m_state[8]  = load_le<uint32_t>(counter8, 0);
+   m_state[9] += load_le<uint32_t>(counter8, 1);
+
+   salsa_core(m_buffer.data(), m_state.data(), 20);
+
+   ++m_state[8];
+   m_state[9] += (m_state[8] == 0);
+
+   m_position = offset % 64;
+   }
+}
diff --git a/third_party/botan/src/lib/stream/salsa20/salsa20.h b/third_party/botan/src/lib/stream/salsa20/salsa20.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/salsa20/salsa20.h
@@ -0,0 +1,54 @@
+/*
+* Salsa20 / XSalsa20
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SALSA20_H_
+#define BOTAN_SALSA20_H_
+
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(salsa20.h)
+
+namespace Botan {
+
+/**
+* DJB's Salsa20 (and XSalsa20)
+*/
+class BOTAN_PUBLIC_API(2,0) Salsa20 final : public StreamCipher
+   {
+   public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      bool valid_iv_length(size_t iv_len) const override;
+
+      size_t default_iv_length() const override;
+
+      Key_Length_Specification key_spec() const override;
+
+      void clear() override;
+      std::string name() const override;
+      StreamCipher* clone() const override;
+
+      static void salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds);
+      static void hsalsa20(uint32_t output[8], const uint32_t input[16]);
+
+      void seek(uint64_t offset) override;
+   private:
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      void initialize_state();
+
+      secure_vector<uint32_t> m_key;
+      secure_vector<uint32_t> m_state;
+      secure_vector<uint8_t> m_buffer;
+      size_t m_position = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp b/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp
@@ -0,0 +1,90 @@
+/*
+* SHAKE-128
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shake_cipher.h>
+#include <botan/exceptn.h>
+#include <botan/sha3.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+SHAKE_128_Cipher::SHAKE_128_Cipher() :
+   m_buf_pos(0)
+   {}
+
+void SHAKE_128_Cipher::cipher(const uint8_t in[], uint8_t out[], size_t length)
+   {
+   const size_t SHAKE_128_BYTERATE = (1600-256)/8;
+
+   verify_key_set(m_state.empty() == false);
+
+   while(length >= SHAKE_128_BYTERATE - m_buf_pos)
+      {
+      xor_buf(out, in, &m_buffer[m_buf_pos], SHAKE_128_BYTERATE - m_buf_pos);
+      length -= (SHAKE_128_BYTERATE - m_buf_pos);
+      in += (SHAKE_128_BYTERATE - m_buf_pos);
+      out += (SHAKE_128_BYTERATE - m_buf_pos);
+
+      SHA_3::permute(m_state.data());
+      copy_out_le(m_buffer.data(), SHAKE_128_BYTERATE, m_state.data());
+
+      m_buf_pos = 0;
+      }
+   xor_buf(out, in, &m_buffer[m_buf_pos], length);
+   m_buf_pos += length;
+   }
+
+void SHAKE_128_Cipher::key_schedule(const uint8_t key[], size_t length)
+   {
+   const size_t SHAKE_128_BITRATE = (1600-256);
+   m_state.resize(25);
+   m_buffer.resize(SHAKE_128_BITRATE/8);
+   zeroise(m_state);
+
+   const size_t S_pos = SHA_3::absorb(SHAKE_128_BITRATE, m_state, 0, key, length);
+   SHA_3::finish(SHAKE_128_BITRATE, m_state, S_pos, 0x1F, 0x80);
+   copy_out_le(m_buffer.data(), m_buffer.size(), m_state.data());
+   }
+
+void SHAKE_128_Cipher::clear()
+   {
+   zap(m_state);
+   zap(m_buffer);
+   m_buf_pos = 0;
+   }
+
+void SHAKE_128_Cipher::set_iv(const uint8_t[], size_t length)
+   {
+   /*
+   * This could be supported in some way (say, by treating iv as
+   * a prefix or suffix of the key).
+   */
+   if(length != 0)
+      throw Invalid_IV_Length(name(), length);
+   }
+
+void SHAKE_128_Cipher::seek(uint64_t)
+   {
+   throw Not_Implemented("SHAKE_128_Cipher::seek");
+   }
+
+Key_Length_Specification SHAKE_128_Cipher::key_spec() const
+   {
+   return Key_Length_Specification(1, 160);
+   }
+
+std::string SHAKE_128_Cipher::name() const
+   {
+   return "SHAKE-128";
+   }
+
+StreamCipher* SHAKE_128_Cipher::clone() const
+   {
+   return new SHAKE_128_Cipher;
+   }
+
+}
diff --git a/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h b/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h
@@ -0,0 +1,57 @@
+/*
+* SHAKE-128 as a stream cipher
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHAKE128_CIPHER_H_
+#define BOTAN_SHAKE128_CIPHER_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/secmem.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shake_cipher.h)
+
+namespace Botan {
+
+/**
+* SHAKE-128 XOF presented as a stream cipher
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_128_Cipher final : public StreamCipher
+   {
+   public:
+      SHAKE_128_Cipher();
+
+      /**
+      * Produce more XOF output
+      */
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      /**
+      * Seeking is not supported, this function will throw
+      */
+      void seek(uint64_t offset) override;
+
+      /**
+      * IV not supported, this function will throw unless iv_len == 0
+      */
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+      Key_Length_Specification key_spec() const override;
+
+      void clear() override;
+      std::string name() const override;
+      StreamCipher* clone() const override;
+
+   private:
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      secure_vector<uint64_t> m_state; // internal state
+      secure_vector<uint8_t> m_buffer; // ciphertext buffer
+      size_t m_buf_pos; // position in m_buffer
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/stream/stream_cipher.cpp b/third_party/botan/src/lib/stream/stream_cipher.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/stream_cipher.cpp
@@ -0,0 +1,149 @@
+/*
+* Stream Ciphers
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stream_cipher.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_CHACHA)
+  #include <botan/chacha.h>
+#endif
+
+#if defined(BOTAN_HAS_SALSA20)
+  #include <botan/salsa20.h>
+#endif
+
+#if defined(BOTAN_HAS_SHAKE_CIPHER)
+  #include <botan/shake_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_CTR_BE)
+  #include <botan/ctr.h>
+#endif
+
+#if defined(BOTAN_HAS_OFB)
+  #include <botan/ofb.h>
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+  #include <botan/rc4.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+  #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<StreamCipher> StreamCipher::create(const std::string& algo_spec,
+                                                   const std::string& provider)
+   {
+   const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_CTR_BE)
+   if((req.algo_name() == "CTR-BE" || req.algo_name() == "CTR") && req.arg_count_between(1,2))
+      {
+      if(provider.empty() || provider == "base")
+         {
+         auto cipher = BlockCipher::create(req.arg(0));
+         if(cipher)
+            {
+            size_t ctr_size = req.arg_as_integer(1, cipher->block_size());
+            return std::unique_ptr<StreamCipher>(new CTR_BE(cipher.release(), ctr_size));
+            }
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA)
+   if(req.algo_name() == "ChaCha")
+      {
+      if(provider.empty() || provider == "base")
+         return std::unique_ptr<StreamCipher>(new ChaCha(req.arg_as_integer(0, 20)));
+      }
+
+   if(req.algo_name() == "ChaCha20")
+      {
+      if(provider.empty() || provider == "base")
+         return std::unique_ptr<StreamCipher>(new ChaCha(20));
+      }
+#endif
+
+#if defined(BOTAN_HAS_SALSA20)
+   if(req.algo_name() == "Salsa20")
+      {
+      if(provider.empty() || provider == "base")
+         return std::unique_ptr<StreamCipher>(new Salsa20);
+      }
+#endif
+
+#if defined(BOTAN_HAS_SHAKE_CIPHER)
+   if(req.algo_name() == "SHAKE-128" || req.algo_name() == "SHAKE-128-XOF")
+      {
+      if(provider.empty() || provider == "base")
+         return std::unique_ptr<StreamCipher>(new SHAKE_128_Cipher);
+      }
+#endif
+
+#if defined(BOTAN_HAS_OFB)
+   if(req.algo_name() == "OFB" && req.arg_count() == 1)
+      {
+      if(provider.empty() || provider == "base")
+         {
+         if(auto c = BlockCipher::create(req.arg(0)))
+            return std::unique_ptr<StreamCipher>(new OFB(c.release()));
+         }
+      }
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+
+   if(req.algo_name() == "RC4" ||
+      req.algo_name() == "ARC4" ||
+      req.algo_name() == "MARK-4")
+      {
+      const size_t skip = (req.algo_name() == "MARK-4") ? 256 : req.arg_as_integer(0, 0);
+
+#if defined(BOTAN_HAS_OPENSSL)
+      if(provider.empty() || provider == "openssl")
+         {
+         return std::unique_ptr<StreamCipher>(make_openssl_rc4(skip));
+         }
+#endif
+
+      if(provider.empty() || provider == "base")
+         {
+         return std::unique_ptr<StreamCipher>(new RC4(skip));
+         }
+      }
+
+#endif
+
+   BOTAN_UNUSED(req);
+   BOTAN_UNUSED(provider);
+
+   return nullptr;
+   }
+
+//static
+std::unique_ptr<StreamCipher>
+StreamCipher::create_or_throw(const std::string& algo,
+                             const std::string& provider)
+   {
+   if(auto sc = StreamCipher::create(algo, provider))
+      {
+      return sc;
+      }
+   throw Lookup_Error("Stream cipher", algo, provider);
+   }
+
+std::vector<std::string> StreamCipher::providers(const std::string& algo_spec)
+   {
+   return probe_providers_of<StreamCipher>(algo_spec, {"base", "openssl"});
+   }
+
+}
diff --git a/third_party/botan/src/lib/stream/stream_cipher.h b/third_party/botan/src/lib/stream/stream_cipher.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/stream/stream_cipher.h
@@ -0,0 +1,147 @@
+/*
+* Stream Cipher
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREAM_CIPHER_H_
+#define BOTAN_STREAM_CIPHER_H_
+
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Base class for all stream ciphers
+*/
+class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
+   {
+   public:
+      virtual ~StreamCipher() = default;
+
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to use
+      * @return a null pointer if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<StreamCipher>
+         create(const std::string& algo_spec,
+                const std::string& provider = "");
+
+      /**
+      * Create an instance based on a name
+      * If provider is empty then best available is chosen.
+      * @param algo_spec algorithm name
+      * @param provider provider implementation to use
+      * Throws a Lookup_Error if the algo/provider combination cannot be found
+      */
+      static std::unique_ptr<StreamCipher>
+         create_or_throw(const std::string& algo_spec,
+                         const std::string& provider = "");
+
+      /**
+      * @return list of available providers for this algorithm, empty if not available
+      */
+      static std::vector<std::string> providers(const std::string& algo_spec);
+
+      /**
+      * Encrypt or decrypt a message
+      * @param in the plaintext
+      * @param out the byte array to hold the output, i.e. the ciphertext
+      * @param len the length of both in and out in bytes
+      */
+      virtual void cipher(const uint8_t in[], uint8_t out[], size_t len) = 0;
+
+      /**
+      * Write keystream bytes to a buffer
+      * @param out the byte array to hold the keystream
+      * @param len the length of out in bytes
+      */
+      virtual void write_keystream(uint8_t out[], size_t len)
+         {
+         clear_mem(out, len);
+         cipher1(out, len);
+         }
+
+      /**
+      * Encrypt or decrypt a message
+      * The message is encrypted/decrypted in place.
+      * @param buf the plaintext / ciphertext
+      * @param len the length of buf in bytes
+      */
+      void cipher1(uint8_t buf[], size_t len)
+         { cipher(buf, buf, len); }
+
+      /**
+      * Encrypt a message
+      * The message is encrypted/decrypted in place.
+      * @param inout the plaintext / ciphertext
+      */
+      template<typename Alloc>
+         void encipher(std::vector<uint8_t, Alloc>& inout)
+         { cipher(inout.data(), inout.data(), inout.size()); }
+
+      /**
+      * Encrypt a message
+      * The message is encrypted in place.
+      * @param inout the plaintext / ciphertext
+      */
+      template<typename Alloc>
+         void encrypt(std::vector<uint8_t, Alloc>& inout)
+         { cipher(inout.data(), inout.data(), inout.size()); }
+
+      /**
+      * Decrypt a message in place
+      * The message is decrypted in place.
+      * @param inout the plaintext / ciphertext
+      */
+      template<typename Alloc>
+         void decrypt(std::vector<uint8_t, Alloc>& inout)
+         { cipher(inout.data(), inout.data(), inout.size()); }
+
+      /**
+      * Resync the cipher using the IV
+      * @param iv the initialization vector
+      * @param iv_len the length of the IV in bytes
+      */
+      virtual void set_iv(const uint8_t iv[], size_t iv_len) = 0;
+
+      /**
+      * Return the default (preferred) nonce length
+      * If this function returns 0, then this cipher does not support nonces
+      */
+      virtual size_t default_iv_length() const { return 0; }
+
+      /**
+      * @param iv_len the length of the IV in bytes
+      * @return if the length is valid for this algorithm
+      */
+      virtual bool valid_iv_length(size_t iv_len) const { return (iv_len == 0); }
+
+      /**
+      * @return a new object representing the same algorithm as *this
+      */
+      virtual StreamCipher* clone() const = 0;
+
+      /**
+      * Set the offset and the state used later to generate the keystream
+      * @param offset the offset where we begin to generate the keystream
+      */
+      virtual void seek(uint64_t offset) = 0;
+
+      /**
+      * @return provider information about this implementation. Default is "base",
+      * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+      */
+      virtual std::string provider() const { return "base"; }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/asio/asio_async_ops.h b/third_party/botan/src/lib/tls/asio/asio_async_ops.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/asio/asio_async_ops.h
@@ -0,0 +1,355 @@
+/*
+* Helpers for TLS ASIO Stream
+* (C) 2018-2020 Jack Lloyd
+*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_ASYNC_OPS_H_
+#define BOTAN_ASIO_ASYNC_OPS_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <botan/asio_error.h>
+
+// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
+// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
+#define BOOST_ASIO_DISABLE_SERIAL_PORT
+#include <boost/asio.hpp>
+#include <boost/asio/yield.hpp>
+
+namespace Botan {
+namespace TLS {
+namespace detail {
+
+/**
+ * Base class for asynchronous stream operations.
+ *
+ * Asynchronous operations, used for example to implement an interface for boost::asio::async_read_some and
+ * boost::asio::async_write_some, are based on boost::asio::coroutines.
+ * Derived operations should implement a call operator and invoke it with the correct parameters upon construction. The
+ * call operator needs to make sure that the user-provided handler is not called directly. Typically, yield / reenter is
+ * used for this in the following fashion:
+ *
+ * ```
+ * void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+ *    {
+ *    reenter(this)
+ *       {
+ *       // operation specific logic, repeatedly interacting with the stream_core and the next_layer (socket)
+ *
+ *       // make sure intermediate initiating function is called
+ *       if(!isContinuation)
+ *          {
+ *          yield next_layer.async_operation(empty_buffer, this);
+ *          }
+ *
+ *       // call the completion handler
+ *       complete_now(error_code, bytes_transferred);
+ *       }
+ *    }
+ * ```
+ *
+ * Once the operation is completed and ready to call the completion handler it checks if an intermediate initiating
+ * function has been called using the `isContinuation` parameter. If not, it will call an asynchronous operation, such
+ * as `async_read_some`, with and empty buffer, set the object itself as the handler, and `yield`. As a result, the call
+ * operator will be invoked again, this time as a continuation, and will jump to the location where it yielded before
+ * using `reenter`. It is now safe to call the handler function via `complete_now`.
+ *
+ * \tparam Handler Type of the completion handler
+ * \tparam Executor1 Type of the asio executor (usually derived from the lower layer)
+ * \tparam Allocator Type of the allocator to be used
+ */
+template <class Handler, class Executor1, class Allocator>
+class AsyncBase : public boost::asio::coroutine
+   {
+   public:
+      using allocator_type = boost::asio::associated_allocator_t<Handler, Allocator>;
+      using executor_type = boost::asio::associated_executor_t<Handler, Executor1>;
+
+      allocator_type get_allocator() const noexcept
+         {
+         return boost::asio::get_associated_allocator(m_handler);
+         }
+
+      executor_type get_executor() const noexcept
+         {
+         return boost::asio::get_associated_executor(m_handler, m_work_guard_1.get_executor());
+         }
+
+   protected:
+      template <class HandlerT>
+      AsyncBase(HandlerT&& handler, const Executor1& executor)
+         : m_handler(std::forward<HandlerT>(handler))
+         , m_work_guard_1(executor)
+         {
+         }
+
+      /**
+       * Call the completion handler.
+       *
+       * This function should only be called after an intermediate initiating function has been called.
+       *
+       * @param args Arguments forwarded to the completion handler function.
+       */
+      template<class... Args>
+      void complete_now(Args&& ... args)
+         {
+         m_work_guard_1.reset();
+         m_handler(std::forward<Args>(args)...);
+         }
+
+      Handler m_handler;
+      boost::asio::executor_work_guard<Executor1> m_work_guard_1;
+   };
+
+template <class Handler, class Stream, class MutableBufferSequence, class Allocator = std::allocator<void>>
+class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+   {
+   public:
+      /**
+       * Construct and invoke an AsyncReadOperation.
+       *
+       * @param handler Handler function to be called upon completion.
+       * @param stream The stream from which the data will be read
+       * @param buffers The buffers into which the data will be read.
+       * @param ec Optional error code; used to report an error to the handler function.
+       */
+      template <class HandlerT>
+      AsyncReadOperation(HandlerT&& handler,
+                         Stream& stream,
+                         const MutableBufferSequence& buffers,
+                         const boost::system::error_code& ec = {})
+         : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+              std::forward<HandlerT>(handler),
+              stream.get_executor())
+         , m_stream(stream)
+         , m_buffers(buffers)
+         , m_decodedBytes(0)
+         {
+         this->operator()(ec, std::size_t(0), false);
+         }
+
+      AsyncReadOperation(AsyncReadOperation&&) = default;
+
+      void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+         {
+         reenter(this)
+            {
+            if(bytes_transferred > 0 && !ec)
+               {
+               // We have received encrypted data from the network, now hand it to TLS::Channel for decryption.
+               boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
+               m_stream.process_encrypted_data(read_buffer, ec);
+               }
+
+            if (m_stream.shutdown_received())
+               {
+               // we just received a 'close_notify' from the peer and don't expect any more data
+               ec = boost::asio::error::eof;
+               }
+            else if (ec == boost::asio::error::eof)
+               {
+               // we did not expect this disconnection from the peer
+               ec = StreamError::StreamTruncated;
+               }
+
+            if(!m_stream.has_received_data() && !ec && boost::asio::buffer_size(m_buffers) > 0)
+               {
+               // The channel did not decrypt a complete record yet, we need more data from the socket.
+               m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
+               return;
+               }
+
+            if(m_stream.has_received_data() && !ec)
+               {
+               // The channel has decrypted a TLS record, now copy it to the output buffers.
+               m_decodedBytes = m_stream.copy_received_data(m_buffers);
+               }
+
+            if(!isContinuation)
+               {
+               // Make sure the handler is not called without an intermediate initiating function.
+               // "Reading" into a zero-byte buffer will complete immediately.
+               m_ec = ec;
+               yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
+               ec = m_ec;
+               }
+
+            this->complete_now(ec, m_decodedBytes);
+            }
+         }
+
+   private:
+      Stream&                   m_stream;
+      MutableBufferSequence     m_buffers;
+      std::size_t               m_decodedBytes;
+      boost::system::error_code m_ec;
+   };
+
+template <typename Handler, class Stream, class Allocator = std::allocator<void>>
+class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+   {
+   public:
+      /**
+       * Construct and invoke an AsyncWriteOperation.
+       *
+       * @param handler Handler function to be called upon completion.
+       * @param stream The stream from which the data will be read
+       * @param plainBytesTransferred Number of bytes to be reported to the user-provided handler function as
+       *                              bytes_transferred. This needs to be provided since the amount of plaintext data
+       *                              consumed from the input buffer can differ from the amount of encrypted data written
+       *                              to the next layer.
+       * @param ec Optional error code; used to report an error to the handler function.
+       */
+      template <class HandlerT>
+      AsyncWriteOperation(HandlerT&& handler,
+                          Stream& stream,
+                          std::size_t plainBytesTransferred,
+                          const boost::system::error_code& ec = {})
+         : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+              std::forward<HandlerT>(handler),
+              stream.get_executor())
+         , m_stream(stream)
+         , m_plainBytesTransferred(plainBytesTransferred)
+         {
+         this->operator()(ec, std::size_t(0), false);
+         }
+
+      AsyncWriteOperation(AsyncWriteOperation&&) = default;
+
+      void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+         {
+         reenter(this)
+            {
+            // mark the number of encrypted bytes sent to the network as "consumed"
+            // Note: bytes_transferred will be zero on first call
+            m_stream.consume_send_buffer(bytes_transferred);
+
+            if(m_stream.has_data_to_send() && !ec)
+               {
+               m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*this));
+               return;
+               }
+
+            if (ec == boost::asio::error::eof && !m_stream.shutdown_received())
+               {
+               // transport layer was closed by peer without receiving 'close_notify'
+               ec = StreamError::StreamTruncated;
+               }
+
+            if(!isContinuation)
+               {
+               // Make sure the handler is not called without an intermediate initiating function.
+               // "Writing" to a zero-byte buffer will complete immediately.
+               m_ec = ec;
+               yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*this));
+               ec = m_ec;
+               }
+
+            // The size of the sent TLS record can differ from the size of the payload due to TLS encryption. We need to
+            // tell the handler how many bytes of the original data we already processed.
+            this->complete_now(ec, m_plainBytesTransferred);
+            }
+         }
+
+   private:
+      Stream&                   m_stream;
+      std::size_t               m_plainBytesTransferred;
+      boost::system::error_code m_ec;
+   };
+
+template <class Handler, class Stream, class Allocator = std::allocator<void>>
+class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+   {
+   public:
+      /**
+       * Construct and invoke an AsyncHandshakeOperation.
+       *
+       * @param handler Handler function to be called upon completion.
+       * @param stream The stream from which the data will be read
+       * @param ec Optional error code; used to report an error to the handler function.
+       */
+      template<class HandlerT>
+      AsyncHandshakeOperation(
+         HandlerT&& handler,
+         Stream& stream,
+         const boost::system::error_code& ec = {})
+         : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+              std::forward<HandlerT>(handler),
+              stream.get_executor())
+         , m_stream(stream)
+         {
+         this->operator()(ec, std::size_t(0), false);
+         }
+
+      AsyncHandshakeOperation(AsyncHandshakeOperation&&) = default;
+
+      void operator()(boost::system::error_code ec, std::size_t bytesTransferred, bool isContinuation = true)
+         {
+         reenter(this)
+            {
+            if(ec == boost::asio::error::eof)
+               {
+               ec = StreamError::StreamTruncated;
+               }
+
+            if(bytesTransferred > 0 && !ec)
+               {
+               // Provide encrypted TLS data received from the network to TLS::Channel for decryption
+               boost::asio::const_buffer read_buffer {m_stream.input_buffer().data(), bytesTransferred};
+               m_stream.process_encrypted_data(read_buffer, ec);
+               }
+
+            if(m_stream.has_data_to_send() && !ec)
+               {
+               // Write encrypted TLS data provided by the TLS::Channel on the wire
+
+               // Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`). This
+               // operation will eventually call `*this` as its own handler, passing the 0 back to this call operator.
+               // This is necessary because the check of `bytesTransferred > 0` assumes that `bytesTransferred` bytes
+               // were just read and are available in input_buffer for further processing.
+               AsyncWriteOperation<AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
+                                   Stream,
+                                   Allocator>
+                                   op{std::move(*this), m_stream, 0};
+               return;
+               }
+
+            if(!m_stream.native_handle()->is_active() && !ec)
+               {
+               // Read more encrypted TLS data from the network
+               m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
+               return;
+               }
+
+            if(!isContinuation)
+               {
+               // Make sure the handler is not called without an intermediate initiating function.
+               // "Reading" into a zero-byte buffer will complete immediately.
+               m_ec = ec;
+               yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
+               ec = m_ec;
+               }
+
+            this->complete_now(ec);
+            }
+         }
+
+   private:
+      Stream&                   m_stream;
+      boost::system::error_code m_ec;
+   };
+
+}  // namespace detail
+}  // namespace TLS
+}  // namespace Botan
+
+#include <boost/asio/unyield.hpp>
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_ASYNC_OPS_H_
diff --git a/third_party/botan/src/lib/tls/asio/asio_context.h b/third_party/botan/src/lib/tls/asio/asio_context.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/asio/asio_context.h
@@ -0,0 +1,120 @@
+/*
+ * TLS Context
+ * (C) 2018-2020 Jack Lloyd
+ *     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#ifndef BOTAN_ASIO_TLS_CONTEXT_H_
+#define BOTAN_ASIO_TLS_CONTEXT_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <functional>
+
+#include <botan/credentials_manager.h>
+#include <botan/ocsp.h>
+#include <botan/rng.h>
+#include <botan/tls_callbacks.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_server_info.h>
+#include <botan/tls_session_manager.h>
+
+namespace Botan {
+namespace TLS {
+
+namespace detail {
+template <typename FunT>
+struct fn_signature_helper : public std::false_type {};
+
+template <typename R, typename D, typename... Args>
+struct fn_signature_helper<R(D::*)(Args...)>
+   {
+   using type = std::function<R(Args...)>;
+   };
+}  // namespace detail
+
+/**
+ * A helper class to initialize and configure Botan::TLS::Stream
+ */
+class Context
+   {
+   public:
+      // statically extract the function signature type from Callbacks::tls_verify_cert_chain
+      // and reuse it as an std::function<> for the verify callback signature
+      /**
+       * The signature of the callback function should correspond to the signature of
+       * Callbacks::tls_verify_cert_chain
+       */
+      using Verify_Callback =
+         detail::fn_signature_helper<decltype(&Callbacks::tls_verify_cert_chain)>::type;
+
+      Context(Credentials_Manager&   credentials_manager,
+              RandomNumberGenerator& rng,
+              Session_Manager&       session_manager,
+              Policy&                policy,
+              Server_Information     server_info = Server_Information()) :
+         m_credentials_manager(credentials_manager),
+         m_rng(rng),
+         m_session_manager(session_manager),
+         m_policy(policy),
+         m_server_info(server_info)
+         {}
+
+      virtual ~Context() = default;
+
+      Context(Context&&)                 = default;
+      Context(const Context&)            = delete;
+      Context& operator=(const Context&) = delete;
+      Context& operator=(Context&&)      = delete;
+
+      /**
+       * @brief Override the tls_verify_cert_chain callback
+       *
+       * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+       * used in the handshake.
+       * Using this function is equivalent to setting the callback via @see Botan::TLS::Stream::set_verify_callback
+       *
+       * @note This function should only be called before initiating the TLS handshake
+       */
+      void set_verify_callback(Verify_Callback callback)
+         {
+         m_verify_callback = std::move(callback);
+         }
+
+      bool has_verify_callback() const
+         {
+         return static_cast<bool>(m_verify_callback);
+         }
+
+      const Verify_Callback& get_verify_callback() const
+         {
+         return m_verify_callback;
+         }
+
+      void set_server_info(const Server_Information& server_info)
+         {
+         m_server_info = server_info;
+         }
+
+   protected:
+      template <class S, class C> friend class Stream;
+
+      Credentials_Manager&   m_credentials_manager;
+      RandomNumberGenerator& m_rng;
+      Session_Manager&       m_session_manager;
+      Policy&                m_policy;
+
+      Server_Information     m_server_info;
+      Verify_Callback        m_verify_callback;
+   };
+
+}  // namespace TLS
+}  // namespace Botan
+
+#endif  // BOOST_VERSION
+#endif  // BOTAN_ASIO_TLS_CONTEXT_H_
diff --git a/third_party/botan/src/lib/tls/asio/asio_error.h b/third_party/botan/src/lib/tls/asio/asio_error.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/asio/asio_error.h
@@ -0,0 +1,151 @@
+/*
+* TLS Stream Errors
+* (C) 2018-2020 Jack Lloyd
+*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_ERROR_H_
+#define BOTAN_ASIO_ERROR_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <boost/system/system_error.hpp>
+
+#include <botan/exceptn.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+/*
+ * This file defines Botan-specific subclasses of boost::system::error_category.
+ * In addition to the class definition, each category class is accompanied by function `make_error_code` used to create
+ * a `boost::system::error_code` of the category from some other kind of error in Botan (for example, a TLS alert).
+ * Since error_category instances should be singletons, there's also a method to get/create the instance for each class.
+ */
+
+namespace Botan {
+namespace TLS {
+
+enum StreamError
+   {
+   StreamTruncated = 1
+   };
+
+//! @brief An error category for errors from the TLS::Stream
+struct StreamCategory : public boost::system::error_category
+   {
+   public:
+      const char* name() const noexcept override
+         {
+         return "Botan TLS Stream";
+         }
+
+      std::string message(int value) const override
+         {
+         switch(value)
+            {
+            case StreamTruncated:
+               return "stream truncated";
+            default:
+               return "generic error";
+            }
+         }
+   };
+
+inline const StreamCategory& botan_stream_category()
+   {
+   static StreamCategory category;
+   return category;
+   }
+
+inline boost::system::error_code make_error_code(Botan::TLS::StreamError e)
+   {
+   return boost::system::error_code(static_cast<int>(e), Botan::TLS::botan_stream_category());
+   }
+
+//! @brief An error category for TLS alerts
+struct BotanAlertCategory : boost::system::error_category
+   {
+   const char* name() const noexcept override
+      {
+      return "Botan TLS Alert";
+      }
+
+   std::string message(int ev) const override
+      {
+      Botan::TLS::Alert alert(static_cast<Botan::TLS::Alert::Type>(ev));
+      return alert.type_string();
+      }
+   };
+
+inline const BotanAlertCategory& botan_alert_category() noexcept
+   {
+   static BotanAlertCategory category;
+   return category;
+   }
+
+inline boost::system::error_code make_error_code(Botan::TLS::Alert::Type c)
+   {
+   return boost::system::error_code(static_cast<int>(c), Botan::TLS::botan_alert_category());
+   }
+
+}  // namespace TLS
+
+//! @brief An error category for errors from Botan (other than TLS alerts)
+struct BotanErrorCategory : boost::system::error_category
+   {
+   const char* name() const noexcept override
+      {
+      return "Botan";
+      }
+
+   std::string message(int ev) const override
+      {
+      return Botan::to_string(static_cast<Botan::ErrorType>(ev));
+      }
+   };
+
+inline const BotanErrorCategory& botan_category() noexcept
+   {
+   static BotanErrorCategory category;
+   return category;
+   }
+
+inline boost::system::error_code make_error_code(Botan::ErrorType e)
+   {
+   return boost::system::error_code(static_cast<int>(e), Botan::botan_category());
+   }
+
+}  // namespace Botan
+
+ /*
+ * Add a template specialization of `is_error_code_enum` for each kind of error to allow automatic conversion to an
+ * error code.
+ */
+namespace boost {
+namespace system {
+
+template<> struct is_error_code_enum<Botan::TLS::Alert::Type>
+   {
+   static const bool value = true;
+   };
+
+template<> struct is_error_code_enum<Botan::TLS::StreamError>
+   {
+   static const bool value = true;
+   };
+
+template<> struct is_error_code_enum<Botan::ErrorType>
+   {
+   static const bool value = true;
+   };
+
+}  // namespace system
+}  // namespace boost
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_ERROR_H_
diff --git a/third_party/botan/src/lib/tls/asio/asio_stream.h b/third_party/botan/src/lib/tls/asio/asio_stream.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/asio/asio_stream.h
@@ -0,0 +1,827 @@
+/*
+* TLS ASIO Stream
+* (C) 2018-2020 Jack Lloyd
+*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_STREAM_H_
+#define BOTAN_ASIO_STREAM_H_
+
+#include <botan/build.h>
+
+// first version to be compatible with Networking TS (N4656) and boost::beast
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <botan/asio_async_ops.h>
+#include <botan/asio_context.h>
+#include <botan/asio_error.h>
+
+#include <botan/tls_callbacks.h>
+#include <botan/tls_channel.h>
+#include <botan/tls_client.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_server.h>
+
+// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
+// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
+#define BOOST_ASIO_DISABLE_SERIAL_PORT
+#include <boost/asio.hpp>
+#include <boost/beast/core.hpp>
+
+#include <algorithm>
+#include <memory>
+#include <type_traits>
+
+namespace Botan {
+namespace TLS {
+
+/**
+ * @brief boost::asio compatible SSL/TLS stream
+ *
+ * @tparam StreamLayer type of the next layer, usually a network socket
+ * @tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
+ */
+template <class StreamLayer, class ChannelT = Channel>
+class Stream
+   {
+   public:
+      //! \name construction
+      //! @{
+
+      /**
+       * @brief Construct a new Stream
+       *
+       * @param context The context parameter is used to set up the underlying native handle. Using code is
+       *                responsible for lifetime management of the context and must ensure that it is available for the
+       *                lifetime of the stream.
+       * @param args Arguments to be forwarded to the construction of the next layer.
+       */
+      template <typename... Args>
+      explicit Stream(Context& context, Args&& ... args)
+         : m_context(context)
+         , m_nextLayer(std::forward<Args>(args)...)
+         , m_core(*this)
+         , m_shutdown_received(false)
+         , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
+         , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
+         {}
+
+      /**
+       * @brief Construct a new Stream
+       *
+       * Convenience overload for boost::asio::ssl::stream compatibility.
+       *
+       * @param arg This argument is forwarded to the construction of the next layer.
+       * @param context The context parameter is used to set up the underlying native handle. Using code is
+       *                responsible for lifetime management of the context and must ensure that is available for the
+       *                lifetime of the stream.
+       */
+      template <typename Arg>
+      explicit Stream(Arg&& arg, Context& context)
+         : m_context(context)
+         , m_nextLayer(std::forward<Arg>(arg))
+         , m_core(*this)
+         , m_shutdown_received(false)
+         , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
+         , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
+         {}
+
+      virtual ~Stream() = default;
+
+      Stream(Stream&& other) = default;
+      Stream& operator=(Stream&& other) = default;
+
+      Stream(const Stream& other) = delete;
+      Stream& operator=(const Stream& other) = delete;
+
+      //! @}
+      //! \name boost::asio accessor methods
+      //! @{
+
+      using next_layer_type = typename std::remove_reference<StreamLayer>::type;
+
+      const next_layer_type& next_layer() const { return m_nextLayer; }
+      next_layer_type& next_layer() { return m_nextLayer; }
+
+#if BOOST_VERSION >= 107000
+      /*
+       * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
+       * Instead, the new free-standing functions in Beast need to be used.
+       * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
+       */
+      using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
+
+      lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
+      const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
+#else
+      using lowest_layer_type = typename next_layer_type::lowest_layer_type;
+
+      lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
+      const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
+#endif
+
+      using executor_type = typename next_layer_type::executor_type;
+      executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
+
+      using native_handle_type = typename std::add_pointer<ChannelT>::type;
+      native_handle_type native_handle()
+         {
+         if(m_native_handle == nullptr)
+            { throw Invalid_State("Invalid handshake state"); }
+         return m_native_handle.get();
+         }
+
+      //! @}
+      //! \name configuration and callback setters
+      //! @{
+
+      /**
+       * @brief Override the tls_verify_cert_chain callback
+       *
+       * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+       * used in the handshake.
+       * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
+       *
+       * @note This function should only be called before initiating the TLS handshake
+       */
+      void set_verify_callback(Context::Verify_Callback callback)
+         {
+         m_context.set_verify_callback(std::move(callback));
+         }
+
+      /**
+       * @brief Compatibility overload of @ref set_verify_callback
+       *
+       * @param callback the callback implementation
+       * @param ec This parameter is unused.
+       */
+      void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec)
+         {
+         BOTAN_UNUSED(ec);
+         m_context.set_verify_callback(std::move(callback));
+         }
+
+      //! @throws Not_Implemented
+      void set_verify_depth(int depth)
+         {
+         BOTAN_UNUSED(depth);
+         throw Not_Implemented("set_verify_depth is not implemented");
+         }
+
+      /**
+       * Not Implemented.
+       * @param depth the desired verification depth
+       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
+       */
+      void set_verify_depth(int depth, boost::system::error_code& ec)
+         {
+         BOTAN_UNUSED(depth);
+         ec = Botan::ErrorType::NotImplemented;
+         }
+
+      //! @throws Not_Implemented
+      template <typename verify_mode>
+      void set_verify_mode(verify_mode v)
+         {
+         BOTAN_UNUSED(v);
+         throw Not_Implemented("set_verify_mode is not implemented");
+         }
+
+      /**
+       * Not Implemented.
+       * @param v the desired verify mode
+       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
+       */
+      template <typename verify_mode>
+      void set_verify_mode(verify_mode v, boost::system::error_code& ec)
+         {
+         BOTAN_UNUSED(v);
+         ec = Botan::ErrorType::NotImplemented;
+         }
+
+      //! @}
+      //! \name handshake methods
+      //! @{
+
+      /**
+       * @brief Performs SSL handshaking.
+       *
+       * The function call will block until handshaking is complete or an error occurs.
+       *
+       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
+       * @throws boost::system::system_error if error occured
+       */
+      void handshake(Connection_Side side)
+         {
+         boost::system::error_code ec;
+         handshake(side, ec);
+         boost::asio::detail::throw_error(ec, "handshake");
+         }
+
+      /**
+       * @brief Performs SSL handshaking.
+       *
+       * The function call will block until handshaking is complete or an error occurs.
+       *
+       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
+       * @param ec Set to indicate what error occurred, if any.
+       */
+      void handshake(Connection_Side side, boost::system::error_code& ec)
+         {
+         setup_native_handle(side, ec);
+
+         if(side == CLIENT)
+            {
+            // send client hello, which was written to the send buffer on client instantiation
+            send_pending_encrypted_data(ec);
+            }
+
+         while(!native_handle()->is_active() && !ec)
+            {
+            boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
+            if(ec)
+               { return; }
+
+            process_encrypted_data(read_buffer, ec);
+
+            send_pending_encrypted_data(ec);
+            }
+         }
+
+      /**
+       * @brief Starts an asynchronous SSL handshake.
+       *
+       * This function call always returns immediately.
+       *
+       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
+       * @param handler The handler to be called when the handshake operation completes.
+       *                The equivalent function signature of the handler must be: void(boost::system::error_code)
+       */
+      template <typename HandshakeHandler>
+      auto async_handshake(Connection_Side side, HandshakeHandler&& handler) ->
+      BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, void(boost::system::error_code))
+         {
+         BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check;
+
+         boost::system::error_code ec;
+         setup_native_handle(side, ec);
+         // If ec is set by setup_native_handle, the AsyncHandshakeOperation created below will do nothing but call the
+         // handler with the error_code set appropriately - no need to early return here.
+
+         boost::asio::async_completion<HandshakeHandler, void(boost::system::error_code)> init(handler);
+
+         detail::AsyncHandshakeOperation<typename std::decay<HandshakeHandler>::type, Stream>
+         op{std::move(init.completion_handler), *this, ec};
+
+         return init.result.get();
+         }
+
+      //! @throws Not_Implemented
+      template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
+      BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
+                                    void(boost::system::error_code, std::size_t))
+      async_handshake(Connection_Side side, const ConstBufferSequence& buffers,
+                      BufferedHandshakeHandler&& handler)
+         {
+         BOTAN_UNUSED(side, buffers, handler);
+         BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
+         throw Not_Implemented("buffered async handshake is not implemented");
+         }
+
+      //! @}
+      //! \name shutdown methods
+      //! @{
+
+      /**
+       * @brief Shut down SSL on the stream.
+       *
+       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
+       * or an error occurs. Note that this will not close the lowest layer.
+       *
+       * Note that this can be used in reaction of a received shutdown alert from the peer.
+       *
+       * @param ec Set to indicate what error occured, if any.
+       */
+      void shutdown(boost::system::error_code& ec)
+         {
+         try_with_error_code([&]
+            {
+            native_handle()->close();
+            }, ec);
+
+         send_pending_encrypted_data(ec);
+         }
+
+      /**
+       * @brief Shut down SSL on the stream.
+       *
+       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
+       * or an error occurs. Note that this will not close the lowest layer.
+       *
+       * Note that this can be used in reaction of a received shutdown alert from the peer.
+       *
+       * @throws boost::system::system_error if error occured
+       */
+      void shutdown()
+         {
+         boost::system::error_code ec;
+         shutdown(ec);
+         boost::asio::detail::throw_error(ec, "shutdown");
+         }
+
+   private:
+      /**
+       * @brief Internal wrapper type to adapt the expected signature of `async_shutdown`
+       *        to the completion handler signature of `AsyncWriteOperation`.
+       *
+       * This is boilerplate to ignore the `size_t` parameter that is passed to the
+       * completion handler of `AsyncWriteOperation`.
+       *
+       * @todo in C++14 and above this could be implemented as a mutable lambda expression
+       *       that captures `handler` by perfect forwarding, like so:
+       *
+       *       [h = std::forward<Handler>(handler)] (...) mutable { return h(ec); }
+       */
+      template <typename Handler>
+      class Wrapper
+         {
+         public:
+            Wrapper(Handler&& handler) : _handler(std::forward<Handler>(handler)) {}
+            void operator()(boost::system::error_code ec, size_t)
+               {
+               _handler(ec);
+               }
+         private:
+            Handler _handler;
+         };
+
+   public:
+      /**
+       * @brief Asynchronously shut down SSL on the stream.
+       *
+       * This function call always returns immediately.
+       *
+       * Note that this can be used in reaction of a received shutdown alert from the peer.
+       *
+       * @param handler The handler to be called when the shutdown operation completes.
+       *                The equivalent function signature of the handler must be: void(boost::system::error_code)
+       */
+      template <typename ShutdownHandler>
+      void async_shutdown(ShutdownHandler&& handler)
+         {
+         boost::system::error_code ec;
+         try_with_error_code([&]
+            {
+            native_handle()->close();
+            }, ec);
+         // If ec is set by native_handle->close(), the AsyncWriteOperation created below will do nothing but call the
+         // handler with the error_code set appropriately - no need to early return here.
+
+         using ShutdownHandlerWrapper = Wrapper<ShutdownHandler>;
+
+         ShutdownHandlerWrapper w(std::forward<ShutdownHandler>(handler));
+         BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, w) type_check;
+
+         boost::asio::async_completion<ShutdownHandlerWrapper, void(boost::system::error_code, std::size_t)>
+         init(w);
+
+         detail::AsyncWriteOperation<typename std::decay<ShutdownHandlerWrapper>::type, Stream>
+         op{std::move(init.completion_handler), *this, boost::asio::buffer_size(send_buffer())};
+
+         return init.result.get();
+         }
+
+      //! @}
+      //! \name I/O methods
+      //! @{
+
+      /**
+       * @brief Read some data from the stream.
+       *
+       * The function call will block until one or more bytes of data has been read successfully, or until an error
+       * occurs.
+       *
+       * @param buffers The buffers into which the data will be read.
+       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
+       *           has closed the connection but did not properly shut down the SSL connection.
+       * @return The number of bytes read. Returns 0 if an error occurred.
+       */
+      template <typename MutableBufferSequence>
+      std::size_t read_some(const MutableBufferSequence& buffers,
+                            boost::system::error_code& ec)
+         {
+         if(has_received_data())
+            { return copy_received_data(buffers); }
+
+         boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
+         if(ec)
+            { return 0; }
+
+         process_encrypted_data(read_buffer, ec);
+
+         if(ec)  // something went wrong in process_encrypted_data()
+            { return 0; }
+
+         if(shutdown_received())
+            {
+            // we just received a 'close_notify' from the peer and don't expect any more data
+            ec = boost::asio::error::eof;
+            }
+         else if(ec == boost::asio::error::eof)
+            {
+            // we did not expect this disconnection from the peer
+            ec = StreamError::StreamTruncated;
+            }
+
+         return !ec ? copy_received_data(buffers) : 0;
+         }
+
+      /**
+       * @brief Read some data from the stream.
+       *
+       * The function call will block until one or more bytes of data has been read successfully, or until an error
+       * occurs.
+       *
+       * @param buffers The buffers into which the data will be read.
+       * @return The number of bytes read. Returns 0 if an error occurred.
+       * @throws boost::system::system_error if error occured
+       */
+      template <typename MutableBufferSequence>
+      std::size_t read_some(const MutableBufferSequence& buffers)
+         {
+         boost::system::error_code ec;
+         auto const n = read_some(buffers, ec);
+         boost::asio::detail::throw_error(ec, "read_some");
+         return n;
+         }
+
+      /**
+       * @brief Write some data to the stream.
+       *
+       * The function call will block until one or more bytes of data has been written successfully, or until an error
+       * occurs.
+       *
+       * @param buffers The data to be written.
+       * @param ec Set to indicate what error occurred, if any.
+       * @return The number of bytes processed from the input buffers.
+       */
+      template <typename ConstBufferSequence>
+      std::size_t write_some(const ConstBufferSequence& buffers,
+                             boost::system::error_code& ec)
+         {
+         tls_encrypt(buffers, ec);
+         send_pending_encrypted_data(ec);
+         return !ec ? boost::asio::buffer_size(buffers) : 0;
+         }
+
+      /**
+       * @brief Write some data to the stream.
+       *
+       * The function call will block until one or more bytes of data has been written successfully, or until an error
+       * occurs.
+       *
+       * @param buffers The data to be written.
+       * @return The number of bytes written.
+       * @throws boost::system::system_error if error occured
+       */
+      template <typename ConstBufferSequence>
+      std::size_t write_some(const ConstBufferSequence& buffers)
+         {
+         boost::system::error_code ec;
+         auto const n = write_some(buffers, ec);
+         boost::asio::detail::throw_error(ec, "write_some");
+         return n;
+         }
+
+      /**
+       * @brief Start an asynchronous write. The function call always returns immediately.
+       *
+       * @param buffers The data to be written.
+       * @param handler The handler to be called when the write operation completes. Copies will be made of the handler
+       *        as required. The equivalent function signature of the handler must be:
+       *        void(boost::system::error_code, std::size_t)
+       */
+      template <typename ConstBufferSequence, typename WriteHandler>
+      auto async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) ->
+      BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
+                                    void(boost::system::error_code, std::size_t))
+         {
+         BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
+
+         boost::asio::async_completion<WriteHandler, void(boost::system::error_code, std::size_t)> init(handler);
+
+         boost::system::error_code ec;
+         tls_encrypt(buffers, ec);
+         if(ec)
+            {
+            // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
+            // AsyncWriteOperation call the handler with the error_code set
+            consume_send_buffer(m_send_buffer.size());
+            detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
+            op{std::move(init.completion_handler), *this, std::size_t(0), ec};
+            return init.result.get();
+            }
+
+         detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
+         op{std::move(init.completion_handler), *this, boost::asio::buffer_size(buffers)};
+
+         return init.result.get();
+         }
+
+      /**
+       * @brief Start an asynchronous read. The function call always returns immediately.
+       *
+       * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
+       *                necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
+       *                that they remain valid until the handler is called.
+       * @param handler The handler to be called when the read operation completes. The equivalent function signature of
+       *                the handler must be:
+       *                void(boost::system::error_code, std::size_t)
+       */
+      template <typename MutableBufferSequence, typename ReadHandler>
+      auto async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) ->
+      BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
+                                    void(boost::system::error_code, std::size_t))
+         {
+         BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
+
+         boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
+
+         detail::AsyncReadOperation<typename std::decay<ReadHandler>::type, Stream, MutableBufferSequence>
+         op{std::move(init.completion_handler), *this, buffers};
+         return init.result.get();
+         }
+
+      //! @}
+
+      //! @brief Indicates whether a close_notify alert has been received from the peer.
+      bool shutdown_received() const
+         {
+         return m_shutdown_received;
+         }
+
+   protected:
+      template <class H, class S, class M, class A> friend class detail::AsyncReadOperation;
+      template <class H, class S, class A> friend class detail::AsyncWriteOperation;
+      template <class H, class S, class A> friend class detail::AsyncHandshakeOperation;
+
+      /**
+       * @brief Helper class that implements Botan::TLS::Callbacks
+       *
+       * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
+       * functions triggered by the native_handle.
+       *
+       * @param receive_buffer reference to the buffer where decrypted data should be placed
+       * @param send_buffer reference to the buffer where encrypted data should be placed
+       */
+      class StreamCore : public Botan::TLS::Callbacks
+         {
+         public:
+            StreamCore(Stream& stream) : m_stream(stream) {}
+
+            virtual ~StreamCore() = default;
+
+            void tls_emit_data(const uint8_t data[], std::size_t size) override
+               {
+               m_stream.m_send_buffer.commit(
+                  boost::asio::buffer_copy(m_stream.m_send_buffer.prepare(size), boost::asio::buffer(data, size))
+               );
+               }
+
+            void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
+               {
+               m_stream.m_receive_buffer.commit(
+                  boost::asio::buffer_copy(m_stream.m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
+               );
+               }
+
+            void tls_alert(Botan::TLS::Alert alert) override
+               {
+               if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+                  {
+                  m_stream.set_shutdown_received();
+                  // Channel::process_alert will automatically write the corresponding close_notify response to the
+                  // send_buffer and close the native_handle after this function returns.
+                  }
+               }
+
+            std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
+               {
+               return std::chrono::milliseconds(1000);
+               }
+
+            bool tls_session_established(const Botan::TLS::Session&) override
+               {
+               // TODO: it should be possible to configure this in the using application (via callback?)
+               return true;
+               }
+
+            void tls_verify_cert_chain(
+               const std::vector<X509_Certificate>& cert_chain,
+               const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+               const std::vector<Certificate_Store*>& trusted_roots,
+               Usage_Type usage,
+               const std::string& hostname,
+               const TLS::Policy& policy) override
+               {
+               if(m_stream.m_context.has_verify_callback())
+                  {
+                  m_stream.m_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+                  }
+               else
+                  {
+                  Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+                  }
+               }
+
+         private:
+            Stream& m_stream;
+         };
+
+      const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
+      boost::asio::const_buffer send_buffer() const { return m_send_buffer.data(); }
+
+      //! @brief Check if decrypted data is available in the receive buffer
+      bool has_received_data() const { return m_receive_buffer.size() > 0; }
+
+      //! @brief Copy decrypted data into the user-provided buffer
+      template <typename MutableBufferSequence>
+      std::size_t copy_received_data(MutableBufferSequence buffers)
+         {
+         // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
+         // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
+         // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
+         // than the decrypted record, so this optimization might not be worth the additional complexity.
+         const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
+         m_receive_buffer.consume(copiedBytes);
+         return copiedBytes;
+         }
+
+      //! @brief Check if encrypted data is available in the send buffer
+      bool has_data_to_send() const { return m_send_buffer.size() > 0; }
+
+      //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
+      void consume_send_buffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
+
+      // This is a helper construct to allow mocking the native_handle in test code. It is activated by explicitly
+      // specifying a (mocked) channel type template parameter when constructing the stream and does not attempt to
+      // instantiate the native_handle.
+      // Note: once we have C++17 we can achieve this much more elegantly using constexpr if.
+      template<class T = ChannelT>
+      typename std::enable_if<!std::is_same<Channel, T>::value>::type
+      setup_native_handle(Connection_Side, boost::system::error_code&) {}
+
+      /**
+       * @brief Create the native handle.
+       *
+       * Depending on the desired connection side, this function will create a Botan::TLS::Client or a
+       * Botan::TLS::Server.
+       *
+       * @param side The desired connection side (client or server)
+       * @param ec Set to indicate what error occurred, if any.
+       */
+      template<class T = ChannelT>
+      typename std::enable_if<std::is_same<Channel, T>::value>::type
+      setup_native_handle(Connection_Side side, boost::system::error_code& ec)
+         {
+         try_with_error_code([&]
+            {
+            if(side == CLIENT)
+               {
+               m_native_handle = std::unique_ptr<Client>(
+                  new Client(m_core,
+                             m_context.m_session_manager,
+                             m_context.m_credentials_manager,
+                             m_context.m_policy,
+                             m_context.m_rng,
+                             m_context.m_server_info,
+                             Protocol_Version::latest_tls_version()));
+               }
+            else
+               {
+               m_native_handle = std::unique_ptr<Server>(
+                  new Server(m_core,
+                             m_context.m_session_manager,
+                             m_context.m_credentials_manager,
+                             m_context.m_policy,
+                             m_context.m_rng,
+                             false /* no DTLS */));
+               }
+            }, ec);
+         }
+
+      /** @brief Synchronously write encrypted data from the send buffer to the next layer.
+       *
+       * If this function is called with an error code other than 'Success', it will do nothing and return 0.
+       *
+       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
+       *           has closed the connection but did not properly shut down the SSL connection.
+       * @return The number of bytes written.
+       */
+      size_t send_pending_encrypted_data(boost::system::error_code& ec)
+         {
+         if(ec)
+            { return 0; }
+
+         auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
+         consume_send_buffer(writtenBytes);
+
+         if(ec == boost::asio::error::eof && !shutdown_received())
+            {
+            // transport layer was closed by peer without receiving 'close_notify'
+            ec = StreamError::StreamTruncated;
+            }
+
+         return writtenBytes;
+         }
+
+      /**
+       * @brief Pass plaintext data to the native handle for processing.
+       *
+       * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
+       */
+      template <typename ConstBufferSequence>
+      void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec)
+         {
+         // NOTE: This is not asynchronous: it encrypts the data synchronously.
+         // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
+         // but is not actually written to the wire, yet.
+         for(auto it = boost::asio::buffer_sequence_begin(buffers);
+               !ec && it != boost::asio::buffer_sequence_end(buffers);
+               it++)
+            {
+            const boost::asio::const_buffer buffer = *it;
+            try_with_error_code([&]
+               {
+               native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size());
+               }, ec);
+            }
+         }
+
+      /**
+       * @brief Pass encrypted data to the native handle for processing.
+       *
+       * If an exception occurs while processing the data, an error code will be set.
+       *
+       * @param read_buffer Input buffer containing the encrypted data.
+       * @param ec Set to indicate what error occurred, if any.
+       */
+      void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
+         {
+         try_with_error_code([&]
+            {
+            native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
+            }, ec);
+         }
+
+      //! @brief Catch exceptions and set an error_code
+      template <typename Fun>
+      void try_with_error_code(Fun f, boost::system::error_code& ec)
+         {
+         try
+            {
+            f();
+            }
+         catch(const TLS_Exception& e)
+            {
+            ec = e.type();
+            }
+         catch(const Botan::Exception& e)
+            {
+            ec = e.error_type();
+            }
+         catch(const std::exception&)
+            {
+            ec = Botan::ErrorType::Unknown;
+            }
+         }
+
+      void set_shutdown_received()
+         {
+         m_shutdown_received = true;
+         }
+
+      Context&                  m_context;
+      StreamLayer               m_nextLayer;
+
+      boost::beast::flat_buffer m_receive_buffer;
+      boost::beast::flat_buffer m_send_buffer;
+
+      StreamCore                m_core;
+      std::unique_ptr<ChannelT> m_native_handle;
+
+      bool m_shutdown_received;
+
+      // Buffer space used to read input intended for the core
+      std::vector<uint8_t>              m_input_buffer_space;
+      const boost::asio::mutable_buffer m_input_buffer;
+   };
+
+}  // namespace TLS
+}  // namespace Botan
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_STREAM_H_
diff --git a/third_party/botan/src/lib/tls/credentials_manager.cpp b/third_party/botan/src/lib/tls/credentials_manager.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/credentials_manager.cpp
@@ -0,0 +1,104 @@
+/*
+* Credentials Manager
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/credentials_manager.h>
+
+namespace Botan {
+
+std::string Credentials_Manager::psk_identity_hint(const std::string&,
+                                                   const std::string&)
+   {
+   return "";
+   }
+
+std::string Credentials_Manager::psk_identity(const std::string&,
+                                              const std::string&,
+                                              const std::string&)
+   {
+   return "";
+   }
+
+SymmetricKey Credentials_Manager::psk(const std::string&,
+                                      const std::string&,
+                                      const std::string& identity)
+   {
+   throw Internal_Error("No PSK set for identity " + identity);
+   }
+
+bool Credentials_Manager::attempt_srp(const std::string&,
+                                      const std::string&)
+   {
+   return false;
+   }
+
+std::string Credentials_Manager::srp_identifier(const std::string&,
+                                                const std::string&)
+   {
+   return "";
+   }
+
+std::string Credentials_Manager::srp_password(const std::string&,
+                                              const std::string&,
+                                              const std::string&)
+   {
+   return "";
+   }
+
+bool Credentials_Manager::srp_verifier(const std::string&,
+                                       const std::string&,
+                                       const std::string&,
+                                       std::string&,
+                                       BigInt&,
+                                       std::vector<uint8_t>&,
+                                       bool)
+   {
+   return false;
+   }
+
+std::vector<X509_Certificate> Credentials_Manager::find_cert_chain(
+   const std::vector<std::string>& key_types,
+   const std::vector<X509_DN>&,
+   const std::string& type,
+   const std::string& context)
+   {
+   return cert_chain(key_types, type, context);
+   }
+
+std::vector<X509_Certificate> Credentials_Manager::cert_chain(
+   const std::vector<std::string>&,
+   const std::string&,
+   const std::string&)
+   {
+   return std::vector<X509_Certificate>();
+   }
+
+std::vector<X509_Certificate> Credentials_Manager::cert_chain_single_type(
+   const std::string& cert_key_type,
+   const std::string& type,
+   const std::string& context)
+   {
+   std::vector<std::string> cert_types;
+   cert_types.push_back(cert_key_type);
+   return find_cert_chain(cert_types, std::vector<X509_DN>(), type, context);
+   }
+
+Private_Key* Credentials_Manager::private_key_for(const X509_Certificate&,
+                                                  const std::string&,
+                                                  const std::string&)
+   {
+   return nullptr;
+   }
+
+std::vector<Certificate_Store*>
+Credentials_Manager::trusted_certificate_authorities(
+   const std::string&,
+   const std::string&)
+   {
+   return std::vector<Certificate_Store*>();
+   }
+
+}
diff --git a/third_party/botan/src/lib/tls/credentials_manager.h b/third_party/botan/src/lib/tls/credentials_manager.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/credentials_manager.h
@@ -0,0 +1,196 @@
+/*
+* Credentials Manager
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CREDENTIALS_MANAGER_H_
+#define BOTAN_CREDENTIALS_MANAGER_H_
+
+#include <botan/pk_keys.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/symkey.h>
+#include <string>
+
+namespace Botan {
+
+class X509_DN;
+class BigInt;
+
+/**
+* Interface for a credentials manager.
+*
+* A type is a fairly static value that represents the general nature
+* of the transaction occurring. Currently used values are "tls-client"
+* and "tls-server". Context represents a hostname, email address,
+* username, or other identifier.
+*/
+class BOTAN_PUBLIC_API(2,0) Credentials_Manager
+   {
+   public:
+      virtual ~Credentials_Manager() = default;
+
+      /**
+      * Return a list of the certificates of CAs that we trust in this
+      * type/context.
+      *
+      * @param type specifies the type of operation occurring
+      *
+      * @param context specifies a context relative to type. For instance
+      *        for type "tls-client", context specifies the servers name.
+      */
+      virtual std::vector<Certificate_Store*> trusted_certificate_authorities(
+         const std::string& type,
+         const std::string& context);
+
+      /**
+      * Return a cert chain we can use, ordered from leaf to root,
+      * or else an empty vector.
+      *
+      * It is assumed that the caller can get the private key of the
+      * leaf with private_key_for
+      *
+      * @param cert_key_types specifies the key types desired ("RSA",
+      *                       "DSA", "ECDSA", etc), or empty if there
+      *                       is no preference by the caller.
+      *
+      * @param acceptable_CAs the CAs the requestor will accept (possibly empty)
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      */
+      virtual std::vector<X509_Certificate> find_cert_chain(
+         const std::vector<std::string>& cert_key_types,
+         const std::vector<X509_DN>& acceptable_CAs,
+         const std::string& type,
+         const std::string& context);
+
+      /**
+      * Return a cert chain we can use, ordered from leaf to root,
+      * or else an empty vector.
+      *
+      * This virtual function is deprecated, and will be removed in a
+      * future release. Use (and override) find_cert_chain instead.
+      *
+      * It is assumed that the caller can get the private key of the
+      * leaf with private_key_for
+      *
+      * @param cert_key_types specifies the key types desired ("RSA",
+      *                       "DSA", "ECDSA", etc), or empty if there
+      *                       is no preference by the caller.
+      *
+      * @param type specifies the type of operation occurring
+      *
+      * @param context specifies a context relative to type.
+      */
+      virtual std::vector<X509_Certificate> cert_chain(
+         const std::vector<std::string>& cert_key_types,
+         const std::string& type,
+         const std::string& context);
+
+      /**
+      * Return a cert chain we can use, ordered from leaf to root,
+      * or else an empty vector.
+      *
+      * It is assumed that the caller can get the private key of the
+      * leaf with private_key_for
+      *
+      * @param cert_key_type specifies the type of key requested
+      *                      ("RSA", "DSA", "ECDSA", etc)
+      *
+      * @param type specifies the type of operation occurring
+      *
+      * @param context specifies a context relative to type.
+      */
+      std::vector<X509_Certificate> cert_chain_single_type(
+         const std::string& cert_key_type,
+         const std::string& type,
+         const std::string& context);
+
+      /**
+      * @return private key associated with this certificate if we should
+      *         use it with this context. cert was returned by cert_chain
+      * @note this object should retain ownership of the returned key;
+      *       it should not be deleted by the caller.
+      */
+      virtual Private_Key* private_key_for(const X509_Certificate& cert,
+                                           const std::string& type,
+                                           const std::string& context);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @return true if we should attempt SRP authentication
+      */
+      virtual bool attempt_srp(const std::string& type,
+                               const std::string& context);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @return identifier for client-side SRP auth, if available
+                for this type/context. Should return empty string
+                if password auth not desired/available.
+      */
+      virtual std::string srp_identifier(const std::string& type,
+                                         const std::string& context);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @param identifier specifies what identifier we want the
+      *        password for. This will be a value previously returned
+      *        by srp_identifier.
+      * @return password for client-side SRP auth, if available
+                for this identifier/type/context.
+      */
+      virtual std::string srp_password(const std::string& type,
+                                       const std::string& context,
+                                       const std::string& identifier);
+
+      /**
+      * Retrieve SRP verifier parameters
+      */
+      virtual bool srp_verifier(const std::string& type,
+                                const std::string& context,
+                                const std::string& identifier,
+                                std::string& group_name,
+                                BigInt& verifier,
+                                std::vector<uint8_t>& salt,
+                                bool generate_fake_on_unknown);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @return the PSK identity hint for this type/context
+      */
+      virtual std::string psk_identity_hint(const std::string& type,
+                                            const std::string& context);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @param identity_hint was passed by the server (but may be empty)
+      * @return the PSK identity we want to use
+      */
+      virtual std::string psk_identity(const std::string& type,
+                                       const std::string& context,
+                                       const std::string& identity_hint);
+
+      /**
+      * @param type specifies the type of operation occurring
+      * @param context specifies a context relative to type.
+      * @param identity is a PSK identity previously returned by
+               psk_identity for the same type and context.
+      * @return the PSK used for identity, or throw an exception if no
+      * key exists
+      */
+      virtual SymmetricKey psk(const std::string& type,
+                               const std::string& context,
+                               const std::string& identity);
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/msg_cert_req.cpp b/third_party/botan/src/lib/tls/msg_cert_req.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_cert_req.cpp
@@ -0,0 +1,156 @@
+/*
+* Certificate Request Message
+* (C) 2004-2006,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+std::string cert_type_code_to_name(uint8_t code)
+   {
+   switch(code)
+      {
+      case 1:
+         return "RSA";
+      case 2:
+         return "DSA";
+      case 64:
+         return "ECDSA";
+      default:
+         return ""; // DH or something else
+      }
+   }
+
+uint8_t cert_type_name_to_code(const std::string& name)
+   {
+   if(name == "RSA")
+      return 1;
+   if(name == "DSA")
+      return 2;
+   if(name == "ECDSA")
+      return 64;
+
+   throw Invalid_Argument("Unknown cert type " + name);
+   }
+
+}
+
+/**
+* Create a new Certificate Request message
+*/
+Certificate_Req::Certificate_Req(Handshake_IO& io,
+                                 Handshake_Hash& hash,
+                                 const Policy& policy,
+                                 const std::vector<X509_DN>& ca_certs,
+                                 Protocol_Version version) :
+   m_names(ca_certs),
+   m_cert_key_types({ "RSA", "ECDSA", "DSA" })
+   {
+   if(version.supports_negotiable_signature_algorithms())
+      {
+      m_schemes = policy.allowed_signature_schemes();
+      }
+
+   hash.update(io.send(*this));
+   }
+
+/**
+* Deserialize a Certificate Request message
+*/
+Certificate_Req::Certificate_Req(const std::vector<uint8_t>& buf,
+                                 Protocol_Version version)
+   {
+   if(buf.size() < 4)
+      throw Decoding_Error("Certificate_Req: Bad certificate request");
+
+   TLS_Data_Reader reader("CertificateRequest", buf);
+
+   std::vector<uint8_t> cert_type_codes = reader.get_range_vector<uint8_t>(1, 1, 255);
+
+   for(size_t i = 0; i != cert_type_codes.size(); ++i)
+      {
+      const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]);
+
+      if(cert_type_name.empty()) // something we don't know
+         continue;
+
+      m_cert_key_types.emplace_back(cert_type_name);
+      }
+
+   if(version.supports_negotiable_signature_algorithms())
+      {
+      const std::vector<uint8_t> algs = reader.get_range_vector<uint8_t>(2, 2, 65534);
+
+      if(algs.size() % 2 != 0)
+         throw Decoding_Error("Bad length for signature IDs in certificate request");
+
+      for(size_t i = 0; i != algs.size(); i += 2)
+         {
+         m_schemes.push_back(static_cast<Signature_Scheme>(make_uint16(algs[i], algs[i+1])));
+         }
+      }
+
+   const uint16_t purported_size = reader.get_uint16_t();
+
+   if(reader.remaining_bytes() != purported_size)
+      throw Decoding_Error("Inconsistent length in certificate request");
+
+   while(reader.has_remaining())
+      {
+      std::vector<uint8_t> name_bits = reader.get_range_vector<uint8_t>(2, 0, 65535);
+
+      BER_Decoder decoder(name_bits.data(), name_bits.size());
+      X509_DN name;
+      decoder.decode(name);
+      m_names.emplace_back(name);
+      }
+   }
+
+/**
+* Serialize a Certificate Request message
+*/
+std::vector<uint8_t> Certificate_Req::serialize() const
+   {
+   std::vector<uint8_t> buf;
+
+   std::vector<uint8_t> cert_types;
+
+   for(size_t i = 0; i != m_cert_key_types.size(); ++i)
+      cert_types.push_back(cert_type_name_to_code(m_cert_key_types[i]));
+
+   append_tls_length_value(buf, cert_types, 1);
+
+   if(m_schemes.size() > 0)
+      buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::SERVER);
+
+   std::vector<uint8_t> encoded_names;
+
+   for(size_t i = 0; i != m_names.size(); ++i)
+      {
+      DER_Encoder encoder;
+      encoder.encode(m_names[i]);
+
+      append_tls_length_value(encoded_names, encoder.get_contents(), 2);
+      }
+
+   append_tls_length_value(buf, encoded_names, 2);
+
+   return buf;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_cert_status.cpp b/third_party/botan/src/lib/tls/msg_cert_status.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_cert_status.cpp
@@ -0,0 +1,71 @@
+/*
+* Certificate Status
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Certificate_Status::Certificate_Status(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size() < 5)
+      throw Decoding_Error("Invalid Certificate_Status message: too small");
+
+   if(buf[0] != 1) // not OCSP
+      throw Decoding_Error("Unexpected Certificate_Status message: unexpected response type");
+
+   size_t len = make_uint32(0, buf[1], buf[2], buf[3]);
+
+   // Verify the redundant length field...
+   if(buf.size() != len + 4)
+      throw Decoding_Error("Invalid Certificate_Status: invalid length field");
+
+   m_response.assign(buf.begin() + 4, buf.end());
+   }
+
+Certificate_Status::Certificate_Status(Handshake_IO& io,
+                                       Handshake_Hash& hash,
+                                       std::shared_ptr<const OCSP::Response> ocsp) :
+   m_response(ocsp->raw_bits())
+   {
+   hash.update(io.send(*this));
+   }
+
+Certificate_Status::Certificate_Status(Handshake_IO& io,
+                                       Handshake_Hash& hash,
+                                       const std::vector<uint8_t>& raw_response_bytes) :
+   m_response(raw_response_bytes)
+   {
+   hash.update(io.send(*this));
+   }
+
+std::vector<uint8_t> Certificate_Status::serialize() const
+   {
+   if(m_response.size() > 0xFFFFFF) // unlikely
+      throw Encoding_Error("OCSP response too long to encode in TLS");
+
+   const uint32_t response_len = static_cast<uint32_t>(m_response.size());
+
+   std::vector<uint8_t> buf;
+   buf.push_back(1); // type OCSP
+   for(size_t i = 1; i < 4; ++i)
+      buf.push_back(get_byte(i, response_len));
+
+   buf += m_response;
+   return buf;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_cert_verify.cpp b/third_party/botan/src/lib/tls/msg_cert_verify.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_cert_verify.cpp
@@ -0,0 +1,110 @@
+/*
+* Certificate Verify Message
+* (C) 2004,2006,2011,2012 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* Create a new Certificate Verify message
+*/
+Certificate_Verify::Certificate_Verify(Handshake_IO& io,
+                                       Handshake_State& state,
+                                       const Policy& policy,
+                                       RandomNumberGenerator& rng,
+                                       const Private_Key* priv_key)
+   {
+   BOTAN_ASSERT_NONNULL(priv_key);
+
+   std::pair<std::string, Signature_Format> format =
+      state.choose_sig_format(*priv_key, m_scheme, true, policy);
+
+   m_signature =
+      state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second,
+                                         state.hash().get_contents());
+
+   state.hash().update(io.send(*this));
+   }
+
+/*
+* Deserialize a Certificate Verify message
+*/
+Certificate_Verify::Certificate_Verify(const std::vector<uint8_t>& buf,
+                                       Protocol_Version version)
+   {
+   TLS_Data_Reader reader("CertificateVerify", buf);
+
+   if(version.supports_negotiable_signature_algorithms())
+      {
+      m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t());
+      }
+
+   m_signature = reader.get_range<uint8_t>(2, 0, 65535);
+   reader.assert_done();
+   }
+
+/*
+* Serialize a Certificate Verify message
+*/
+std::vector<uint8_t> Certificate_Verify::serialize() const
+   {
+   std::vector<uint8_t> buf;
+
+   if(m_scheme != Signature_Scheme::NONE)
+      {
+      const uint16_t scheme_code = static_cast<uint16_t>(m_scheme);
+      buf.push_back(get_byte(0, scheme_code));
+      buf.push_back(get_byte(1, scheme_code));
+      }
+
+   if(m_signature.size() > 0xFFFF)
+      throw Encoding_Error("Certificate_Verify signature too long to encode");
+
+   const uint16_t sig_len = static_cast<uint16_t>(m_signature.size());
+   buf.push_back(get_byte(0, sig_len));
+   buf.push_back(get_byte(1, sig_len));
+   buf += m_signature;
+
+   return buf;
+   }
+
+/*
+* Verify a Certificate Verify message
+*/
+bool Certificate_Verify::verify(const X509_Certificate& cert,
+                                const Handshake_State& state,
+                                const Policy& policy) const
+   {
+   std::unique_ptr<Public_Key> key(cert.subject_public_key());
+
+   policy.check_peer_key_acceptable(*key);
+
+   std::pair<std::string, Signature_Format> format =
+      state.parse_sig_format(*key.get(), m_scheme, true, policy);
+
+   const bool signature_valid =
+      state.callbacks().tls_verify_message(*key, format.first, format.second,
+                                           state.hash().get_contents(), m_signature);
+
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+   BOTAN_UNUSED(signature_valid);
+   return true;
+#else
+   return signature_valid;
+#endif
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_certificate.cpp b/third_party/botan/src/lib/tls/msg_certificate.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_certificate.cpp
@@ -0,0 +1,109 @@
+/*
+* Certificate Message
+* (C) 2004-2006,2012,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_alert.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/loadstor.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Create a new Certificate message
+*/
+Certificate::Certificate(Handshake_IO& io,
+                         Handshake_Hash& hash,
+                         const std::vector<X509_Certificate>& cert_list) :
+   m_certs(cert_list)
+   {
+   hash.update(io.send(*this));
+   }
+
+/**
+* Deserialize a Certificate message
+*/
+Certificate::Certificate(const std::vector<uint8_t>& buf, const Policy& policy)
+   {
+   if(buf.size() < 3)
+      throw Decoding_Error("Certificate: Message malformed");
+
+   const size_t total_size = make_uint32(0, buf[0], buf[1], buf[2]);
+
+   if(total_size != buf.size() - 3)
+      throw Decoding_Error("Certificate: Message malformed");
+
+   const size_t max_size = policy.maximum_certificate_chain_size();
+   if(max_size > 0 && total_size > max_size)
+      throw Decoding_Error("Certificate chain exceeds policy specified maximum size");
+
+   const uint8_t* certs = buf.data() + 3;
+
+   while(size_t remaining_bytes = buf.data() + buf.size() - certs)
+      {
+      if(remaining_bytes < 3)
+         throw Decoding_Error("Certificate: Message malformed");
+
+      const size_t cert_size = make_uint32(0, certs[0], certs[1], certs[2]);
+
+      if(remaining_bytes < (3 + cert_size))
+         throw Decoding_Error("Certificate: Message malformed");
+
+      DataSource_Memory cert_buf(&certs[3], cert_size);
+      m_certs.push_back(X509_Certificate(cert_buf));
+
+      certs += cert_size + 3;
+      }
+
+   /*
+   * TLS 1.0 through 1.2 all seem to require that the certificate be
+   * precisely a v3 certificate. In fact the strict wording would seem
+   * to require that every certificate in the chain be v3. But often
+   * the intermediates are outside of the control of the server.
+   * But, require that the leaf certificate be v3
+   */
+   if(m_certs.size() > 0 && m_certs[0].x509_version() != 3)
+      {
+      throw TLS_Exception(Alert::BAD_CERTIFICATE,
+                          "The leaf certificate must be v3");
+      }
+   }
+
+/**
+* Serialize a Certificate message
+*/
+std::vector<uint8_t> Certificate::serialize() const
+   {
+   std::vector<uint8_t> buf(3);
+
+   for(size_t i = 0; i != m_certs.size(); ++i)
+      {
+      std::vector<uint8_t> raw_cert = m_certs[i].BER_encode();
+      const size_t cert_size = raw_cert.size();
+      for(size_t j = 0; j != 3; ++j)
+         {
+         buf.push_back(get_byte(j+1, static_cast<uint32_t>(cert_size)));
+         }
+      buf += raw_cert;
+      }
+
+   const size_t buf_size = buf.size() - 3;
+   for(size_t i = 0; i != 3; ++i)
+      buf[i] = get_byte(i+1, static_cast<uint32_t>(buf_size));
+
+   return buf;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_client_hello.cpp b/third_party/botan/src/lib/tls/msg_client_hello.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_client_hello.cpp
@@ -0,0 +1,465 @@
+/*
+* TLS Hello Request and Client Hello Messages
+* (C) 2004-2011,2015,2016 Jack Lloyd
+*     2016 Matthias Gierlings
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_callbacks.h>
+#include <botan/rng.h>
+#include <botan/hash.h>
+
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/stl_util.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+enum {
+   TLS_EMPTY_RENEGOTIATION_INFO_SCSV        = 0x00FF,
+   TLS_FALLBACK_SCSV                        = 0x5600
+};
+
+std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng,
+                                       const Policy& policy)
+   {
+   std::vector<uint8_t> buf(32);
+   rng.randomize(buf.data(), buf.size());
+
+   std::unique_ptr<HashFunction> sha256 = HashFunction::create_or_throw("SHA-256");
+   sha256->update(buf);
+   sha256->final(buf);
+
+   if(policy.include_time_in_hello_random())
+      {
+      const uint32_t time32 = static_cast<uint32_t>(
+         std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
+
+      store_be(time32, buf.data());
+      }
+
+   return buf;
+   }
+
+/*
+* Create a new Hello Request message
+*/
+Hello_Request::Hello_Request(Handshake_IO& io)
+   {
+   io.send(*this);
+   }
+
+/*
+* Deserialize a Hello Request message
+*/
+Hello_Request::Hello_Request(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size())
+      throw Decoding_Error("Bad Hello_Request, has non-zero size");
+   }
+
+/*
+* Serialize a Hello Request message
+*/
+std::vector<uint8_t> Hello_Request::serialize() const
+   {
+   return std::vector<uint8_t>();
+   }
+
+/*
+* Create a new Client Hello message
+*/
+Client_Hello::Client_Hello(Handshake_IO& io,
+                           Handshake_Hash& hash,
+                           const Policy& policy,
+                           Callbacks& cb,
+                           RandomNumberGenerator& rng,
+                           const std::vector<uint8_t>& reneg_info,
+                           const Client_Hello::Settings& client_settings,
+                           const std::vector<std::string>& next_protocols) :
+   m_version(client_settings.protocol_version()),
+   m_random(make_hello_random(rng, policy)),
+   m_suites(policy.ciphersuite_list(m_version, !client_settings.srp_identifier().empty())),
+   m_comp_methods(1)
+   {
+   if(!policy.acceptable_protocol_version(m_version))
+      throw Internal_Error("Offering " + m_version.to_string() +
+                           " but our own policy does not accept it");
+
+   /*
+   * Place all empty extensions in front to avoid a bug in some systems
+   * which reject hellos when the last extension in the list is empty.
+   */
+   m_extensions.add(new Extended_Master_Secret);
+   m_extensions.add(new Session_Ticket());
+
+   if(policy.negotiate_encrypt_then_mac())
+      m_extensions.add(new Encrypt_then_MAC);
+
+   m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+   m_extensions.add(new Supported_Versions(m_version, policy));
+
+   if(client_settings.hostname() != "")
+      m_extensions.add(new Server_Name_Indicator(client_settings.hostname()));
+
+   if(policy.support_cert_status_message())
+      m_extensions.add(new Certificate_Status_Request({}, {}));
+
+   if(reneg_info.empty() && !next_protocols.empty())
+      m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols));
+
+   if(m_version.supports_negotiable_signature_algorithms())
+      m_extensions.add(new Signature_Algorithms(policy.allowed_signature_schemes()));
+
+   if(m_version.is_datagram_protocol())
+      m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles()));
+
+#if defined(BOTAN_HAS_SRP6)
+   m_extensions.add(new SRP_Identifier(client_settings.srp_identifier()));
+#else
+   if(!client_settings.srp_identifier().empty())
+      {
+      throw Invalid_State("Attempting to initiate SRP session but TLS-SRP support disabled");
+      }
+#endif
+
+   std::unique_ptr<Supported_Groups> supported_groups(new Supported_Groups(policy.key_exchange_groups()));
+
+   if(supported_groups->ec_groups().size() > 0)
+      {
+      m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+      }
+
+   m_extensions.add(supported_groups.release());
+
+   cb.tls_modify_extensions(m_extensions, CLIENT);
+
+   if(policy.send_fallback_scsv(client_settings.protocol_version()))
+      m_suites.push_back(TLS_FALLBACK_SCSV);
+
+   hash.update(io.send(*this));
+   }
+
+/*
+* Create a new Client Hello message (session resumption case)
+*/
+Client_Hello::Client_Hello(Handshake_IO& io,
+                           Handshake_Hash& hash,
+                           const Policy& policy,
+                           Callbacks& cb,
+                           RandomNumberGenerator& rng,
+                           const std::vector<uint8_t>& reneg_info,
+                           const Session& session,
+                           const std::vector<std::string>& next_protocols) :
+   m_version(session.version()),
+   m_session_id(session.session_id()),
+   m_random(make_hello_random(rng, policy)),
+   m_suites(policy.ciphersuite_list(m_version, (session.srp_identifier() != ""))),
+   m_comp_methods(1)
+   {
+   if(!policy.acceptable_protocol_version(m_version))
+      throw Internal_Error("Offering " + m_version.to_string() +
+                           " but our own policy does not accept it");
+
+   if(!value_exists(m_suites, session.ciphersuite_code()))
+      m_suites.push_back(session.ciphersuite_code());
+
+   /*
+   We always add the EMS extension, even if not used in the original session.
+   If the server understands it and follows the RFC it should reject our resume
+   attempt and upgrade us to a new session with the EMS protection.
+   */
+   m_extensions.add(new Extended_Master_Secret);
+
+   m_extensions.add(new Renegotiation_Extension(reneg_info));
+   m_extensions.add(new Server_Name_Indicator(session.server_info().hostname()));
+   m_extensions.add(new Session_Ticket(session.session_ticket()));
+
+   if(policy.support_cert_status_message())
+      m_extensions.add(new Certificate_Status_Request({}, {}));
+
+   std::unique_ptr<Supported_Groups> supported_groups(new Supported_Groups(policy.key_exchange_groups()));
+
+   if(supported_groups->ec_groups().size() > 0)
+      {
+      m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+      }
+
+   m_extensions.add(supported_groups.release());
+
+   if(session.supports_encrypt_then_mac())
+      m_extensions.add(new Encrypt_then_MAC);
+
+#if defined(BOTAN_HAS_SRP6)
+   m_extensions.add(new SRP_Identifier(session.srp_identifier()));
+#else
+   if(!session.srp_identifier().empty())
+      {
+      throw Invalid_State("Attempting to resume SRP session but TLS-SRP support disabled");
+      }
+#endif
+
+   if(m_version.supports_negotiable_signature_algorithms())
+      m_extensions.add(new Signature_Algorithms(policy.allowed_signature_schemes()));
+
+   if(reneg_info.empty() && !next_protocols.empty())
+      m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols));
+
+   cb.tls_modify_extensions(m_extensions, CLIENT);
+
+   hash.update(io.send(*this));
+   }
+
+void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify)
+   {
+   if(!m_version.is_datagram_protocol())
+      throw Invalid_State("Cannot use hello cookie with stream protocol");
+
+   m_hello_cookie = hello_verify.cookie();
+   }
+
+/*
+* Serialize a Client Hello message
+*/
+std::vector<uint8_t> Client_Hello::serialize() const
+   {
+   std::vector<uint8_t> buf;
+
+   buf.push_back(m_version.major_version());
+   buf.push_back(m_version.minor_version());
+   buf += m_random;
+
+   append_tls_length_value(buf, m_session_id, 1);
+
+   if(m_version.is_datagram_protocol())
+      append_tls_length_value(buf, m_hello_cookie, 1);
+
+   append_tls_length_value(buf, m_suites, 2);
+   append_tls_length_value(buf, m_comp_methods, 1);
+
+   /*
+   * May not want to send extensions at all in some cases. If so,
+   * should include SCSV value (if reneg info is empty, if not we are
+   * renegotiating with a modern server)
+   */
+
+   buf += m_extensions.serialize(Connection_Side::CLIENT);
+
+   return buf;
+   }
+
+std::vector<uint8_t> Client_Hello::cookie_input_data() const
+   {
+   std::vector<uint8_t> buf;
+
+   buf.push_back(m_version.major_version());
+   buf.push_back(m_version.minor_version());
+   buf += m_random;
+
+   append_tls_length_value(buf, m_session_id, 1);
+
+   append_tls_length_value(buf, m_suites, 2);
+   append_tls_length_value(buf, m_comp_methods, 1);
+
+   // Here we don't serialize the extensions since the client extensions
+   // may contain values we don't know how to serialize back.
+
+   return buf;
+   }
+
+/*
+* Read a counterparty client hello
+*/
+Client_Hello::Client_Hello(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size() < 41)
+      throw Decoding_Error("Client_Hello: Packet corrupted");
+
+   TLS_Data_Reader reader("ClientHello", buf);
+
+   const uint8_t major_version = reader.get_byte();
+   const uint8_t minor_version = reader.get_byte();
+
+   m_version = Protocol_Version(major_version, minor_version);
+
+   m_random = reader.get_fixed<uint8_t>(32);
+
+   m_session_id = reader.get_range<uint8_t>(1, 0, 32);
+
+   if(m_version.is_datagram_protocol())
+      m_hello_cookie = reader.get_range<uint8_t>(1, 0, 255);
+
+   m_suites = reader.get_range_vector<uint16_t>(2, 1, 32767);
+
+   m_comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255);
+
+   m_extensions.deserialize(reader, Connection_Side::CLIENT);
+
+   if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)))
+      {
+      if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+         {
+         if(!reneg->renegotiation_info().empty())
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Client sent renegotiation SCSV and non-empty extension");
+         }
+      else
+         {
+         // add fake extension
+         m_extensions.add(new Renegotiation_Extension());
+         }
+      }
+   }
+
+bool Client_Hello::sent_fallback_scsv() const
+   {
+   return offered_suite(static_cast<uint16_t>(TLS_FALLBACK_SCSV));
+   }
+
+/*
+* Check if we offered this ciphersuite
+*/
+bool Client_Hello::offered_suite(uint16_t ciphersuite) const
+   {
+   for(size_t i = 0; i != m_suites.size(); ++i)
+      if(m_suites[i] == ciphersuite)
+         return true;
+   return false;
+   }
+
+std::vector<Signature_Scheme> Client_Hello::signature_schemes() const
+   {
+   std::vector<Signature_Scheme> schemes;
+
+   if(Signature_Algorithms* sigs = m_extensions.get<Signature_Algorithms>())
+      {
+      schemes = sigs->supported_schemes();
+      }
+
+   return schemes;
+   }
+
+std::vector<Group_Params> Client_Hello::supported_ecc_curves() const
+   {
+   if(Supported_Groups* groups = m_extensions.get<Supported_Groups>())
+      return groups->ec_groups();
+   return std::vector<Group_Params>();
+   }
+
+std::vector<Group_Params> Client_Hello::supported_dh_groups() const
+   {
+   if(Supported_Groups* groups = m_extensions.get<Supported_Groups>())
+      return groups->dh_groups();
+   return std::vector<Group_Params>();
+   }
+
+bool Client_Hello::prefers_compressed_ec_points() const
+   {
+   if(Supported_Point_Formats* ecc_formats = m_extensions.get<Supported_Point_Formats>())
+      {
+      return ecc_formats->prefers_compressed();
+      }
+   return false;
+   }
+
+std::string Client_Hello::sni_hostname() const
+   {
+   if(Server_Name_Indicator* sni = m_extensions.get<Server_Name_Indicator>())
+      return sni->host_name();
+   return "";
+   }
+
+#if defined(BOTAN_HAS_SRP6)
+std::string Client_Hello::srp_identifier() const
+   {
+   if(SRP_Identifier* srp = m_extensions.get<SRP_Identifier>())
+      return srp->identifier();
+   return "";
+   }
+#endif
+
+bool Client_Hello::secure_renegotiation() const
+   {
+   return m_extensions.has<Renegotiation_Extension>();
+   }
+
+std::vector<uint8_t> Client_Hello::renegotiation_info() const
+   {
+   if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+      return reneg->renegotiation_info();
+   return std::vector<uint8_t>();
+   }
+
+std::vector<Protocol_Version> Client_Hello::supported_versions() const
+   {
+   if(Supported_Versions* versions = m_extensions.get<Supported_Versions>())
+      return versions->versions();
+   return {};
+   }
+
+bool Client_Hello::supports_session_ticket() const
+   {
+   return m_extensions.has<Session_Ticket>();
+   }
+
+std::vector<uint8_t> Client_Hello::session_ticket() const
+   {
+   if(Session_Ticket* ticket = m_extensions.get<Session_Ticket>())
+      return ticket->contents();
+   return std::vector<uint8_t>();
+   }
+
+bool Client_Hello::supports_alpn() const
+   {
+   return m_extensions.has<Application_Layer_Protocol_Notification>();
+   }
+
+bool Client_Hello::supports_extended_master_secret() const
+   {
+   return m_extensions.has<Extended_Master_Secret>();
+   }
+
+bool Client_Hello::supports_cert_status_message() const
+   {
+   return m_extensions.has<Certificate_Status_Request>();
+   }
+
+bool Client_Hello::supports_encrypt_then_mac() const
+   {
+   return m_extensions.has<Encrypt_then_MAC>();
+   }
+
+bool Client_Hello::sent_signature_algorithms() const
+   {
+   return m_extensions.has<Signature_Algorithms>();
+   }
+
+std::vector<std::string> Client_Hello::next_protocols() const
+   {
+   if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>())
+      return alpn->protocols();
+   return std::vector<std::string>();
+   }
+
+std::vector<uint16_t> Client_Hello::srtp_profiles() const
+   {
+   if(SRTP_Protection_Profiles* srtp = m_extensions.get<SRTP_Protection_Profiles>())
+      return srtp->profiles();
+   return std::vector<uint16_t>();
+   }
+
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_client_kex.cpp b/third_party/botan/src/lib/tls/msg_client_kex.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_client_kex.cpp
@@ -0,0 +1,404 @@
+/*
+* Client Key Exchange Message
+* (C) 2004-2010,2016 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/rng.h>
+
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/credentials_manager.h>
+#include <botan/internal/ct_utils.h>
+
+#include <botan/rsa.h>
+
+#if defined(BOTAN_HAS_CECPQ1)
+  #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+  #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* Create a new Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io,
+                                         Handshake_State& state,
+                                         const Policy& policy,
+                                         Credentials_Manager& creds,
+                                         const Public_Key* server_public_key,
+                                         const std::string& hostname,
+                                         RandomNumberGenerator& rng)
+   {
+   const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+   if(kex_algo == Kex_Algo::PSK)
+      {
+      std::string identity_hint = "";
+
+      if(state.server_kex())
+         {
+         TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params());
+         identity_hint = reader.get_string(2, 0, 65535);
+         }
+
+      const std::string psk_identity =
+         creds.psk_identity("tls-client", hostname, identity_hint);
+
+      append_tls_length_value(m_key_material, psk_identity, 2);
+
+      SymmetricKey psk = creds.psk("tls-client", hostname, psk_identity);
+
+      std::vector<uint8_t> zeros(psk.length());
+
+      append_tls_length_value(m_pre_master, zeros, 2);
+      append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+      }
+   else if(state.server_kex())
+      {
+      TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params());
+
+      SymmetricKey psk;
+
+      if(kex_algo == Kex_Algo::DHE_PSK ||
+         kex_algo == Kex_Algo::ECDHE_PSK)
+         {
+         std::string identity_hint = reader.get_string(2, 0, 65535);
+
+         const std::string psk_identity =
+            creds.psk_identity("tls-client", hostname, identity_hint);
+
+         append_tls_length_value(m_key_material, psk_identity, 2);
+
+         psk = creds.psk("tls-client", hostname, psk_identity);
+         }
+
+      if(kex_algo == Kex_Algo::DH ||
+         kex_algo == Kex_Algo::DHE_PSK)
+         {
+         const std::vector<uint8_t> modulus = reader.get_range<uint8_t>(2, 1, 65535);
+         const std::vector<uint8_t> generator = reader.get_range<uint8_t>(2, 1, 65535);
+         const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(2, 1, 65535);
+
+         if(reader.remaining_bytes())
+            throw Decoding_Error("Bad params size for DH key exchange");
+
+         const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> dh_result =
+            state.callbacks().tls_dh_agree(modulus, generator, peer_public_value, policy, rng);
+
+         if(kex_algo == Kex_Algo::DH)
+            m_pre_master = dh_result.first;
+         else
+            {
+            append_tls_length_value(m_pre_master, dh_result.first, 2);
+            append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+            }
+
+         append_tls_length_value(m_key_material, dh_result.second, 2);
+         }
+      else if(kex_algo == Kex_Algo::ECDH ||
+              kex_algo == Kex_Algo::ECDHE_PSK)
+         {
+         const uint8_t curve_type = reader.get_byte();
+         if(curve_type != 3)
+            throw Decoding_Error("Server sent non-named ECC curve");
+
+         const Group_Params curve_id = static_cast<Group_Params>(reader.get_uint16_t());
+         const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(1, 1, 255);
+
+         if(policy.choose_key_exchange_group({curve_id}) != curve_id)
+            {
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server sent ECC curve prohibited by policy");
+            }
+
+         const std::string curve_name = state.callbacks().tls_decode_group_param(curve_id);
+
+         if(curve_name == "")
+            throw Decoding_Error("Server sent unknown named curve " +
+                                 std::to_string(static_cast<uint16_t>(curve_id)));
+
+         const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> ecdh_result =
+            state.callbacks().tls_ecdh_agree(curve_name, peer_public_value, policy, rng,
+                                             state.server_hello()->prefers_compressed_ec_points());
+
+         if(kex_algo == Kex_Algo::ECDH)
+            {
+            m_pre_master = ecdh_result.first;
+            }
+         else
+            {
+            append_tls_length_value(m_pre_master, ecdh_result.first, 2);
+            append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+            }
+
+         append_tls_length_value(m_key_material, ecdh_result.second, 1);
+         }
+#if defined(BOTAN_HAS_SRP6)
+      else if(kex_algo == Kex_Algo::SRP_SHA)
+         {
+         const BigInt N = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+         const BigInt g = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+         std::vector<uint8_t> salt = reader.get_range<uint8_t>(1, 1, 255);
+         const BigInt B = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+
+         const std::string srp_group = srp6_group_identifier(N, g);
+
+         const std::string srp_identifier =
+            creds.srp_identifier("tls-client", hostname);
+
+         const std::string srp_password =
+            creds.srp_password("tls-client", hostname, srp_identifier);
+
+         std::pair<BigInt, SymmetricKey> srp_vals =
+            srp6_client_agree(srp_identifier,
+                              srp_password,
+                              srp_group,
+                              "SHA-1",
+                              salt,
+                              B,
+                              rng);
+
+         append_tls_length_value(m_key_material, BigInt::encode(srp_vals.first), 2);
+         m_pre_master = srp_vals.second.bits_of();
+         }
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+      else if(kex_algo == Kex_Algo::CECPQ1)
+         {
+         const std::vector<uint8_t> cecpq1_offer = reader.get_range<uint8_t>(2, 1, 65535);
+
+         if(cecpq1_offer.size() != CECPQ1_OFFER_BYTES)
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid CECPQ1 key size");
+
+         std::vector<uint8_t> newhope_accept(CECPQ1_ACCEPT_BYTES);
+         secure_vector<uint8_t> shared_secret(CECPQ1_SHARED_KEY_BYTES);
+         CECPQ1_accept(shared_secret.data(), newhope_accept.data(), cecpq1_offer.data(), rng);
+         append_tls_length_value(m_key_material, newhope_accept, 2);
+         m_pre_master = shared_secret;
+         }
+#endif
+      else
+         {
+         throw Internal_Error("Client_Key_Exchange: Unknown key exchange method was negotiated");
+         }
+
+      reader.assert_done();
+      }
+   else
+      {
+      // No server key exchange msg better mean RSA kex + RSA key in cert
+
+      if(kex_algo != Kex_Algo::STATIC_RSA)
+         throw Unexpected_Message("No server kex message, but negotiated a key exchange that required it");
+
+      if(!server_public_key)
+         throw Internal_Error("No server public key for RSA exchange");
+
+      if(auto rsa_pub = dynamic_cast<const RSA_PublicKey*>(server_public_key))
+         {
+         const Protocol_Version offered_version = state.client_hello()->version();
+
+         rng.random_vec(m_pre_master, 48);
+         m_pre_master[0] = offered_version.major_version();
+         m_pre_master[1] = offered_version.minor_version();
+
+         PK_Encryptor_EME encryptor(*rsa_pub, rng, "PKCS1v15");
+
+         const std::vector<uint8_t> encrypted_key = encryptor.encrypt(m_pre_master, rng);
+
+         append_tls_length_value(m_key_material, encrypted_key, 2);
+         }
+      else
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Expected a RSA key in server cert but got " +
+                             server_public_key->algo_name());
+      }
+
+   state.hash().update(io.send(*this));
+   }
+
+/*
+* Read a Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(const std::vector<uint8_t>& contents,
+                                         const Handshake_State& state,
+                                         const Private_Key* server_rsa_kex_key,
+                                         Credentials_Manager& creds,
+                                         const Policy& policy,
+                                         RandomNumberGenerator& rng)
+   {
+   const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+   if(kex_algo == Kex_Algo::STATIC_RSA)
+      {
+      BOTAN_ASSERT(state.server_certs() && !state.server_certs()->cert_chain().empty(),
+                   "RSA key exchange negotiated so server sent a certificate");
+
+      if(!server_rsa_kex_key)
+         throw Internal_Error("Expected RSA kex but no server kex key set");
+
+      if(!dynamic_cast<const RSA_PrivateKey*>(server_rsa_kex_key))
+         throw Internal_Error("Expected RSA key but got " + server_rsa_kex_key->algo_name());
+
+      TLS_Data_Reader reader("ClientKeyExchange", contents);
+      const std::vector<uint8_t> encrypted_pre_master = reader.get_range<uint8_t>(2, 0, 65535);
+      reader.assert_done();
+
+      PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15");
+
+      const uint8_t client_major = state.client_hello()->version().major_version();
+      const uint8_t client_minor = state.client_hello()->version().minor_version();
+
+      /*
+      * PK_Decryptor::decrypt_or_random will return a random value if
+      * either the length does not match the expected value or if the
+      * version number embedded in the PMS does not match the one sent
+      * in the client hello.
+      */
+      const size_t expected_plaintext_size = 48;
+      const size_t expected_content_size = 2;
+      const uint8_t expected_content_bytes[expected_content_size] = { client_major, client_minor };
+      const uint8_t expected_content_pos[expected_content_size] = { 0, 1 };
+
+      m_pre_master =
+         decryptor.decrypt_or_random(encrypted_pre_master.data(),
+                                     encrypted_pre_master.size(),
+                                     expected_plaintext_size,
+                                     rng,
+                                     expected_content_bytes,
+                                     expected_content_pos,
+                                     expected_content_size);
+      }
+   else
+      {
+      TLS_Data_Reader reader("ClientKeyExchange", contents);
+
+      SymmetricKey psk;
+
+      if(key_exchange_is_psk(kex_algo))
+         {
+         const std::string psk_identity = reader.get_string(2, 0, 65535);
+
+         psk = creds.psk("tls-server",
+                         state.client_hello()->sni_hostname(),
+                         psk_identity);
+
+         if(psk.length() == 0)
+            {
+            if(policy.hide_unknown_users())
+               psk = SymmetricKey(rng, 16);
+            else
+               throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+                                   "No PSK for identifier " + psk_identity);
+            }
+         }
+
+      if(kex_algo == Kex_Algo::PSK)
+         {
+         std::vector<uint8_t> zeros(psk.length());
+         append_tls_length_value(m_pre_master, zeros, 2);
+         append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+         }
+#if defined(BOTAN_HAS_SRP6)
+      else if(kex_algo == Kex_Algo::SRP_SHA)
+         {
+         SRP6_Server_Session& srp = state.server_kex()->server_srp_params();
+
+         m_pre_master = srp.step2(BigInt::decode(reader.get_range<uint8_t>(2, 0, 65535))).bits_of();
+         }
+#endif
+#if defined(BOTAN_HAS_CECPQ1)
+      else if(kex_algo == Kex_Algo::CECPQ1)
+         {
+         const CECPQ1_key& cecpq1_offer = state.server_kex()->cecpq1_key();
+
+         const std::vector<uint8_t> cecpq1_accept = reader.get_range<uint8_t>(2, 0, 65535);
+         if(cecpq1_accept.size() != CECPQ1_ACCEPT_BYTES)
+            throw Decoding_Error("Invalid size for CECPQ1 accept message");
+
+         m_pre_master.resize(CECPQ1_SHARED_KEY_BYTES);
+         CECPQ1_finish(m_pre_master.data(), cecpq1_offer, cecpq1_accept.data());
+         }
+#endif
+      else if(kex_algo == Kex_Algo::DH ||
+              kex_algo == Kex_Algo::DHE_PSK ||
+              kex_algo == Kex_Algo::ECDH ||
+              kex_algo == Kex_Algo::ECDHE_PSK)
+         {
+         const Private_Key& private_key = state.server_kex()->server_kex_key();
+
+         const PK_Key_Agreement_Key* ka_key =
+            dynamic_cast<const PK_Key_Agreement_Key*>(&private_key);
+
+         if(!ka_key)
+            throw Internal_Error("Expected key agreement key type but got " +
+                                 private_key.algo_name());
+
+         std::vector<uint8_t> client_pubkey;
+
+         if(ka_key->algo_name() == "DH")
+            {
+            client_pubkey = reader.get_range<uint8_t>(2, 0, 65535);
+            }
+         else
+            {
+            client_pubkey = reader.get_range<uint8_t>(1, 1, 255);
+            }
+
+         try
+            {
+            PK_Key_Agreement ka(*ka_key, rng, "Raw");
+
+            secure_vector<uint8_t> shared_secret = ka.derive_key(0, client_pubkey).bits_of();
+
+            if(ka_key->algo_name() == "DH")
+               shared_secret = CT::strip_leading_zeros(shared_secret);
+
+            if(kex_algo == Kex_Algo::DHE_PSK ||
+               kex_algo == Kex_Algo::ECDHE_PSK)
+               {
+               append_tls_length_value(m_pre_master, shared_secret, 2);
+               append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+               }
+            else
+               m_pre_master = shared_secret;
+            }
+         catch(Invalid_Argument& e)
+            {
+            throw TLS_Exception(Alert::ILLEGAL_PARAMETER, e.what());
+            }
+         catch(std::exception&)
+            {
+            /*
+            * Something failed in the DH/ECDH computation. To avoid possible
+            * attacks which are based on triggering and detecting some edge
+            * failure condition, randomize the pre-master output and carry on,
+            * allowing the protocol to fail later in the finished checks.
+            */
+            rng.random_vec(m_pre_master, ka_key->public_value().size());
+            }
+
+         reader.assert_done();
+         }
+      else
+         throw Internal_Error("Client_Key_Exchange: Unknown key exchange negotiated");
+      }
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_finished.cpp b/third_party/botan/src/lib/tls/msg_finished.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_finished.cpp
@@ -0,0 +1,91 @@
+/*
+* Finished Message
+* (C) 2004-2006,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+/*
+* Compute the verify_data
+*/
+std::vector<uint8_t> finished_compute_verify(const Handshake_State& state,
+                                          Connection_Side side)
+   {
+   const uint8_t TLS_CLIENT_LABEL[] = {
+      0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69,
+      0x73, 0x68, 0x65, 0x64 };
+
+   const uint8_t TLS_SERVER_LABEL[] = {
+      0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69,
+      0x73, 0x68, 0x65, 0x64 };
+
+   std::unique_ptr<KDF> prf(state.protocol_specific_prf());
+
+   std::vector<uint8_t> input;
+   std::vector<uint8_t> label;
+   if(side == CLIENT)
+      label += std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL));
+   else
+      label += std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL));
+
+   input += state.hash().final(state.version(), state.ciphersuite().prf_algo());
+
+   return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label));
+   }
+
+}
+
+/*
+* Create a new Finished message
+*/
+Finished::Finished(Handshake_IO& io,
+                   Handshake_State& state,
+                   Connection_Side side) : m_verification_data(finished_compute_verify( state, side ))
+   {
+   state.hash().update(io.send(*this));
+   }
+
+/*
+* Serialize a Finished message
+*/
+std::vector<uint8_t> Finished::serialize() const
+   {
+   return m_verification_data;
+   }
+
+/*
+* Deserialize a Finished message
+*/
+Finished::Finished(const std::vector<uint8_t>& buf) : m_verification_data(buf)
+   {}
+
+/*
+* Verify a Finished message
+*/
+bool Finished::verify(const Handshake_State& state,
+                      Connection_Side side) const
+   {
+   std::vector<byte> computed_verify = finished_compute_verify(state, side);
+
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+   return true;
+#else
+   return (m_verification_data.size() == computed_verify.size()) &&
+      constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size());
+#endif
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_hello_verify.cpp b/third_party/botan/src/lib/tls/msg_hello_verify.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_hello_verify.cpp
@@ -0,0 +1,69 @@
+/*
+* DTLS Hello Verify Request
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Hello_Verify_Request::Hello_Verify_Request(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size() < 3)
+      throw Decoding_Error("Hello verify request too small");
+
+   Protocol_Version version(buf[0], buf[1]);
+
+   if(version != Protocol_Version::DTLS_V10 &&
+      version != Protocol_Version::DTLS_V12)
+      {
+      throw Decoding_Error("Unknown version from server in hello verify request");
+      }
+
+   if(static_cast<size_t>(buf[2]) + 3 != buf.size())
+      throw Decoding_Error("Bad length in hello verify request");
+
+   m_cookie.assign(buf.begin() + 3, buf.end());
+   }
+
+Hello_Verify_Request::Hello_Verify_Request(const std::vector<uint8_t>& client_hello_bits,
+                                           const std::string& client_identity,
+                                           const SymmetricKey& secret_key)
+   {
+   std::unique_ptr<MessageAuthenticationCode> hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   hmac->set_key(secret_key);
+
+   hmac->update_be(static_cast<uint64_t>(client_hello_bits.size()));
+   hmac->update(client_hello_bits);
+   hmac->update_be(static_cast<uint64_t>(client_identity.size()));
+   hmac->update(client_identity);
+
+   m_cookie.resize(hmac->output_length());
+   hmac->final(m_cookie.data());
+   }
+
+std::vector<uint8_t> Hello_Verify_Request::serialize() const
+   {
+   /* DTLS 1.2 server implementations SHOULD use DTLS version 1.0
+      regardless of the version of TLS that is expected to be
+      negotiated (RFC 6347, section 4.2.1)
+   */
+
+   Protocol_Version format_version(Protocol_Version::DTLS_V10);
+
+   std::vector<uint8_t> bits;
+   bits.push_back(format_version.major_version());
+   bits.push_back(format_version.minor_version());
+   bits.push_back(static_cast<uint8_t>(m_cookie.size()));
+   bits += m_cookie;
+   return bits;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_server_hello.cpp b/third_party/botan/src/lib/tls/msg_server_hello.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_server_hello.cpp
@@ -0,0 +1,251 @@
+/*
+* TLS Server Hello and Server Hello Done
+* (C) 2004-2011,2015,2016,2019 Jack Lloyd
+*     2016 Matthias Gierlings
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/tls_callbacks.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/stl_util.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400;
+//const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401;
+
+std::vector<uint8_t>
+make_server_hello_random(RandomNumberGenerator& rng,
+                         Protocol_Version offered_version,
+                         const Policy& policy)
+   {
+   auto random = make_hello_random(rng, policy);
+
+   if((offered_version == Protocol_Version::TLS_V10 ||
+       offered_version == Protocol_Version::TLS_V11) &&
+      policy.allow_tls12())
+      {
+      store_be(DOWNGRADE_TLS11, &random[24]);
+      }
+
+   if(offered_version == Protocol_Version::DTLS_V10 && policy.allow_dtls12())
+      {
+      store_be(DOWNGRADE_TLS11, &random[24]);
+      }
+
+   return random;
+   }
+
+}
+
+// New session case
+Server_Hello::Server_Hello(Handshake_IO& io,
+                           Handshake_Hash& hash,
+                           const Policy& policy,
+                           Callbacks& cb,
+                           RandomNumberGenerator& rng,
+                           const std::vector<uint8_t>& reneg_info,
+                           const Client_Hello& client_hello,
+                           const Server_Hello::Settings& server_settings,
+                           const std::string next_protocol) :
+   m_version(server_settings.protocol_version()),
+   m_session_id(server_settings.session_id()),
+   m_random(make_server_hello_random(rng, m_version, policy)),
+   m_ciphersuite(server_settings.ciphersuite()),
+   m_comp_method(0)
+   {
+   if(client_hello.supports_extended_master_secret())
+      m_extensions.add(new Extended_Master_Secret);
+
+   // Sending the extension back does not commit us to sending a stapled response
+   if(client_hello.supports_cert_status_message() && policy.support_cert_status_message())
+      m_extensions.add(new Certificate_Status_Request);
+
+   Ciphersuite c = Ciphersuite::by_id(m_ciphersuite);
+
+   if(c.cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac())
+      {
+      m_extensions.add(new Encrypt_then_MAC);
+      }
+
+   if(c.ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS))
+      {
+      m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+      }
+
+   if(client_hello.secure_renegotiation())
+      m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+   if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket())
+      m_extensions.add(new Session_Ticket());
+
+   if(!next_protocol.empty() && client_hello.supports_alpn())
+      m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
+
+   if(m_version.is_datagram_protocol())
+      {
+      const std::vector<uint16_t> server_srtp = policy.srtp_profiles();
+      const std::vector<uint16_t> client_srtp = client_hello.srtp_profiles();
+
+      if(!server_srtp.empty() && !client_srtp.empty())
+         {
+         uint16_t shared = 0;
+         // always using server preferences for now
+         for(auto s_srtp : server_srtp)
+            for(auto c_srtp : client_srtp)
+               {
+               if(shared == 0 && s_srtp == c_srtp)
+                  shared = s_srtp;
+               }
+
+         if(shared)
+            m_extensions.add(new SRTP_Protection_Profiles(shared));
+         }
+      }
+
+   cb.tls_modify_extensions(m_extensions, SERVER);
+
+   hash.update(io.send(*this));
+   }
+
+// Resuming
+Server_Hello::Server_Hello(Handshake_IO& io,
+                           Handshake_Hash& hash,
+                           const Policy& policy,
+                           Callbacks& cb,
+                           RandomNumberGenerator& rng,
+                           const std::vector<uint8_t>& reneg_info,
+                           const Client_Hello& client_hello,
+                           Session& resumed_session,
+                           bool offer_session_ticket,
+                           const std::string& next_protocol) :
+   m_version(resumed_session.version()),
+   m_session_id(client_hello.session_id()),
+   m_random(make_hello_random(rng, policy)),
+   m_ciphersuite(resumed_session.ciphersuite_code()),
+   m_comp_method(0)
+   {
+   if(client_hello.supports_extended_master_secret())
+      m_extensions.add(new Extended_Master_Secret);
+
+   if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac())
+      {
+      Ciphersuite c = resumed_session.ciphersuite();
+      if(c.cbc_ciphersuite())
+         m_extensions.add(new Encrypt_then_MAC);
+      }
+
+   if(resumed_session.ciphersuite().ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS))
+      {
+      m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+      }
+
+   if(client_hello.secure_renegotiation())
+      m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+   if(client_hello.supports_session_ticket() && offer_session_ticket)
+      m_extensions.add(new Session_Ticket());
+
+   if(!next_protocol.empty() && client_hello.supports_alpn())
+      m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
+
+   cb.tls_modify_extensions(m_extensions, SERVER);
+
+   hash.update(io.send(*this));
+   }
+
+/*
+* Deserialize a Server Hello message
+*/
+Server_Hello::Server_Hello(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size() < 38)
+      throw Decoding_Error("Server_Hello: Packet corrupted");
+
+   TLS_Data_Reader reader("ServerHello", buf);
+
+   const uint8_t major_version = reader.get_byte();
+   const uint8_t minor_version = reader.get_byte();
+
+   m_version = Protocol_Version(major_version, minor_version);
+
+   m_random = reader.get_fixed<uint8_t>(32);
+
+   m_session_id = reader.get_range<uint8_t>(1, 0, 32);
+
+   m_ciphersuite = reader.get_uint16_t();
+
+   m_comp_method = reader.get_byte();
+
+   m_extensions.deserialize(reader, Connection_Side::SERVER);
+   }
+
+/*
+* Serialize a Server Hello message
+*/
+std::vector<uint8_t> Server_Hello::serialize() const
+   {
+   std::vector<uint8_t> buf;
+
+   buf.push_back(m_version.major_version());
+   buf.push_back(m_version.minor_version());
+   buf += m_random;
+
+   append_tls_length_value(buf, m_session_id, 1);
+
+   buf.push_back(get_byte(0, m_ciphersuite));
+   buf.push_back(get_byte(1, m_ciphersuite));
+
+   buf.push_back(m_comp_method);
+
+   buf += m_extensions.serialize(Connection_Side::SERVER);
+
+   return buf;
+   }
+
+bool Server_Hello::random_signals_downgrade() const
+   {
+   const uint64_t last8 = load_be<uint64_t>(m_random.data(), 3);
+   return (last8 == DOWNGRADE_TLS11);
+   }
+
+/*
+* Create a new Server Hello Done message
+*/
+Server_Hello_Done::Server_Hello_Done(Handshake_IO& io,
+                                     Handshake_Hash& hash)
+   {
+   hash.update(io.send(*this));
+   }
+
+/*
+* Deserialize a Server Hello Done message
+*/
+Server_Hello_Done::Server_Hello_Done(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size())
+      throw Decoding_Error("Server_Hello_Done: Must be empty, and is not");
+   }
+
+/*
+* Serialize a Server Hello Done message
+*/
+std::vector<uint8_t> Server_Hello_Done::serialize() const
+   {
+   return std::vector<uint8_t>();
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_server_kex.cpp b/third_party/botan/src/lib/tls/msg_server_kex.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_server_kex.cpp
@@ -0,0 +1,334 @@
+/*
+* Server Key Exchange Message
+* (C) 2004-2010,2012,2015,2016 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/credentials_manager.h>
+#include <botan/loadstor.h>
+#include <botan/pubkey.h>
+
+#include <botan/dh.h>
+#include <botan/ecdh.h>
+
+#if defined(BOTAN_HAS_CURVE_25519)
+  #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+  #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+  #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Create a new Server Key Exchange message
+*/
+Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io,
+                                         Handshake_State& state,
+                                         const Policy& policy,
+                                         Credentials_Manager& creds,
+                                         RandomNumberGenerator& rng,
+                                         const Private_Key* signing_key)
+   {
+   const std::string hostname = state.client_hello()->sni_hostname();
+   const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK)
+      {
+      std::string identity_hint =
+         creds.psk_identity_hint("tls-server", hostname);
+
+      append_tls_length_value(m_params, identity_hint, 2);
+      }
+
+   if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK)
+      {
+      const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups();
+
+      Group_Params shared_group = Group_Params::NONE;
+
+      /*
+      If the client does not send any DH groups in the supported groups
+      extension, but does offer DH ciphersuites, we select a group arbitrarily
+      */
+
+      if(dh_groups.empty())
+         {
+         shared_group = policy.default_dh_group();
+         }
+      else
+         {
+         shared_group = policy.choose_key_exchange_group(dh_groups);
+         }
+
+      if(shared_group == Group_Params::NONE)
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+               "Could not agree on a DH group with the client");
+
+      BOTAN_ASSERT(group_param_is_dh(shared_group), "DH groups for the DH ciphersuites god");
+
+      const std::string group_name = state.callbacks().tls_decode_group_param(shared_group);
+      std::unique_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, DL_Group(group_name)));
+
+      append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2);
+      append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2);
+      append_tls_length_value(m_params, dh->public_value(), 2);
+      m_kex_key.reset(dh.release());
+      }
+   else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK)
+      {
+      const std::vector<Group_Params> ec_groups = state.client_hello()->supported_ecc_curves();
+
+      if(ec_groups.empty())
+         throw Internal_Error("Client sent no ECC extension but we negotiated ECDH");
+
+      Group_Params shared_group = policy.choose_key_exchange_group(ec_groups);
+
+      if(shared_group == Group_Params::NONE)
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "No shared ECC group with client");
+
+      std::vector<uint8_t> ecdh_public_val;
+
+      if(shared_group == Group_Params::X25519)
+         {
+#if defined(BOTAN_HAS_CURVE_25519)
+         std::unique_ptr<Curve25519_PrivateKey> x25519(new Curve25519_PrivateKey(rng));
+         ecdh_public_val = x25519->public_value();
+         m_kex_key.reset(x25519.release());
+#else
+         throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
+#endif
+         }
+      else
+         {
+         Group_Params curve = policy.choose_key_exchange_group(ec_groups);
+
+         const std::string curve_name = state.callbacks().tls_decode_group_param(curve);
+
+         EC_Group ec_group(curve_name);
+         std::unique_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group));
+
+         // follow client's preference for point compression
+         ecdh_public_val = ecdh->public_value(
+            state.client_hello()->prefers_compressed_ec_points() ?
+            PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
+
+         m_kex_key.reset(ecdh.release());
+         }
+
+      const uint16_t named_curve_id = static_cast<uint16_t>(shared_group);
+      m_params.push_back(3); // named curve
+      m_params.push_back(get_byte(0, named_curve_id));
+      m_params.push_back(get_byte(1, named_curve_id));
+
+      append_tls_length_value(m_params, ecdh_public_val, 1);
+      }
+#if defined(BOTAN_HAS_SRP6)
+   else if(kex_algo == Kex_Algo::SRP_SHA)
+      {
+      const std::string srp_identifier = state.client_hello()->srp_identifier();
+
+      std::string group_id;
+      BigInt v;
+      std::vector<uint8_t> salt;
+
+      const bool found = creds.srp_verifier("tls-server", hostname,
+                                            srp_identifier,
+                                            group_id, v, salt,
+                                            policy.hide_unknown_users());
+
+      if(!found)
+         throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+                             "Unknown SRP user " + srp_identifier);
+
+      m_srp_params.reset(new SRP6_Server_Session);
+
+      BigInt B = m_srp_params->step1(v, group_id,
+                                     "SHA-1", rng);
+
+      DL_Group group(group_id);
+
+      append_tls_length_value(m_params, BigInt::encode(group.get_p()), 2);
+      append_tls_length_value(m_params, BigInt::encode(group.get_g()), 2);
+      append_tls_length_value(m_params, salt, 1);
+      append_tls_length_value(m_params, BigInt::encode(B), 2);
+      }
+#endif
+#if defined(BOTAN_HAS_CECPQ1)
+   else if(kex_algo == Kex_Algo::CECPQ1)
+      {
+      std::vector<uint8_t> cecpq1_offer(CECPQ1_OFFER_BYTES);
+      m_cecpq1_key.reset(new CECPQ1_key);
+      CECPQ1_offer(cecpq1_offer.data(), m_cecpq1_key.get(), rng);
+      append_tls_length_value(m_params, cecpq1_offer, 2);
+      }
+#endif
+   else if(kex_algo != Kex_Algo::PSK)
+      {
+      throw Internal_Error("Server_Key_Exchange: Unknown kex type " +
+                           kex_method_to_string(kex_algo));
+      }
+
+   if(state.ciphersuite().signature_used())
+      {
+      BOTAN_ASSERT(signing_key, "Signing key was set");
+
+      std::pair<std::string, Signature_Format> format =
+         state.choose_sig_format(*signing_key, m_scheme, false, policy);
+
+      std::vector<uint8_t> buf = state.client_hello()->random();
+
+      buf += state.server_hello()->random();
+      buf += params();
+
+      m_signature =
+         state.callbacks().tls_sign_message(*signing_key, rng,
+                                            format.first, format.second, buf);
+      }
+
+   state.hash().update(io.send(*this));
+   }
+
+/**
+* Deserialize a Server Key Exchange message
+*/
+Server_Key_Exchange::Server_Key_Exchange(const std::vector<uint8_t>& buf,
+                                         const Kex_Algo kex_algo,
+                                         const Auth_Method auth_method,
+                                         Protocol_Version version)
+   {
+   TLS_Data_Reader reader("ServerKeyExchange", buf);
+
+   /*
+   * Here we are deserializing enough to find out what offset the
+   * signature is at. All processing is done when the Client Key Exchange
+   * is prepared.
+   */
+
+   if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK)
+      {
+      reader.get_string(2, 0, 65535); // identity hint
+      }
+
+   if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK)
+      {
+      // 3 bigints, DH p, g, Y
+
+      for(size_t i = 0; i != 3; ++i)
+         {
+         reader.get_range<uint8_t>(2, 1, 65535);
+         }
+      }
+   else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK)
+      {
+      reader.get_byte(); // curve type
+      reader.get_uint16_t(); // curve id
+      reader.get_range<uint8_t>(1, 1, 255); // public key
+      }
+   else if(kex_algo == Kex_Algo::SRP_SHA)
+      {
+      // 2 bigints (N,g) then salt, then server B
+
+      reader.get_range<uint8_t>(2, 1, 65535);
+      reader.get_range<uint8_t>(2, 1, 65535);
+      reader.get_range<uint8_t>(1, 1, 255);
+      reader.get_range<uint8_t>(2, 1, 65535);
+      }
+   else if(kex_algo == Kex_Algo::CECPQ1)
+      {
+      // u16 blob
+      reader.get_range<uint8_t>(2, 1, 65535);
+      }
+   else if(kex_algo != Kex_Algo::PSK)
+      throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " +
+                           kex_method_to_string(kex_algo));
+
+   m_params.assign(buf.data(), buf.data() + reader.read_so_far());
+
+   if(auth_method != Auth_Method::ANONYMOUS && auth_method != Auth_Method::IMPLICIT)
+      {
+      if(version.supports_negotiable_signature_algorithms())
+         {
+         m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t());
+         }
+
+      m_signature = reader.get_range<uint8_t>(2, 0, 65535);
+      }
+
+   reader.assert_done();
+   }
+
+/**
+* Serialize a Server Key Exchange message
+*/
+std::vector<uint8_t> Server_Key_Exchange::serialize() const
+   {
+   std::vector<uint8_t> buf = params();
+
+   if(m_signature.size())
+      {
+      if(m_scheme != Signature_Scheme::NONE)
+         {
+         const uint16_t scheme_code = static_cast<uint16_t>(m_scheme);
+         buf.push_back(get_byte(0, scheme_code));
+         buf.push_back(get_byte(1, scheme_code));
+         }
+
+      append_tls_length_value(buf, m_signature, 2);
+      }
+
+   return buf;
+   }
+
+/**
+* Verify a Server Key Exchange message
+*/
+bool Server_Key_Exchange::verify(const Public_Key& server_key,
+                                 const Handshake_State& state,
+                                 const Policy& policy) const
+   {
+   policy.check_peer_key_acceptable(server_key);
+
+   std::pair<std::string, Signature_Format> format =
+      state.parse_sig_format(server_key, m_scheme, false, policy);
+
+   std::vector<uint8_t> buf = state.client_hello()->random();
+
+   buf += state.server_hello()->random();
+   buf += params();
+
+   const bool signature_valid =
+      state.callbacks().tls_verify_message(server_key, format.first, format.second,
+                                           buf, m_signature);
+
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+   BOTAN_UNUSED(signature_valid);
+   return true;
+#else
+   return signature_valid;
+#endif
+   }
+
+const Private_Key& Server_Key_Exchange::server_kex_key() const
+   {
+   BOTAN_ASSERT_NONNULL(m_kex_key);
+   return *m_kex_key;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/msg_session_ticket.cpp b/third_party/botan/src/lib/tls/msg_session_ticket.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/msg_session_ticket.cpp
@@ -0,0 +1,56 @@
+/*
+* Session Tickets
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace TLS {
+
+New_Session_Ticket::New_Session_Ticket(Handshake_IO& io,
+                                       Handshake_Hash& hash,
+                                       const std::vector<uint8_t>& ticket,
+                                       uint32_t lifetime) :
+   m_ticket_lifetime_hint(lifetime),
+   m_ticket(ticket)
+   {
+   hash.update(io.send(*this));
+   }
+
+New_Session_Ticket::New_Session_Ticket(Handshake_IO& io,
+                                       Handshake_Hash& hash)
+   {
+   hash.update(io.send(*this));
+   }
+
+New_Session_Ticket::New_Session_Ticket(const std::vector<uint8_t>& buf)
+   {
+   if(buf.size() < 6)
+      throw Decoding_Error("Session ticket message too short to be valid");
+
+   TLS_Data_Reader reader("SessionTicket", buf);
+
+   m_ticket_lifetime_hint = reader.get_uint32_t();
+   m_ticket = reader.get_range<uint8_t>(2, 0, 65535);
+   reader.assert_done();
+   }
+
+std::vector<uint8_t> New_Session_Ticket::serialize() const
+   {
+   std::vector<uint8_t> buf(4);
+   store_be(m_ticket_lifetime_hint, buf.data());
+   append_tls_length_value(buf, m_ticket, 2);
+   return buf;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp b/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp
@@ -0,0 +1,213 @@
+/*
+* SQL TLS Session Manager
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager_sql.h>
+#include <botan/database.h>
+#include <botan/pbkdf.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
+                                         const std::string& passphrase,
+                                         RandomNumberGenerator& rng,
+                                         size_t max_sessions,
+                                         std::chrono::seconds session_lifetime) :
+   m_db(db),
+   m_rng(rng),
+   m_max_sessions(max_sessions),
+   m_session_lifetime(session_lifetime)
+   {
+   m_db->create_table(
+      "create table if not exists tls_sessions "
+      "("
+      "session_id TEXT PRIMARY KEY, "
+      "session_start INTEGER, "
+      "hostname TEXT, "
+      "hostport INTEGER, "
+      "session BLOB"
+      ")");
+
+   m_db->create_table(
+      "create table if not exists tls_sessions_metadata "
+      "("
+      "passphrase_salt BLOB, "
+      "passphrase_iterations INTEGER, "
+      "passphrase_check INTEGER "
+      ")");
+
+   const size_t salts = m_db->row_count("tls_sessions_metadata");
+
+   std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
+
+   if(salts == 1)
+      {
+      // existing db
+      auto stmt = m_db->new_statement("select * from tls_sessions_metadata");
+
+      if(stmt->step())
+         {
+         std::pair<const uint8_t*, size_t> salt = stmt->get_blob(0);
+         const size_t iterations = stmt->get_size_t(1);
+         const size_t check_val_db = stmt->get_size_t(2);
+
+         secure_vector<uint8_t> x = pbkdf->pbkdf_iterations(32 + 2,
+                                                         passphrase,
+                                                         salt.first, salt.second,
+                                                         iterations);
+
+         const size_t check_val_created = make_uint16(x[0], x[1]);
+         m_session_key.assign(x.begin() + 2, x.end());
+
+         if(check_val_created != check_val_db)
+            throw Invalid_Argument("Session database password not valid");
+         }
+      }
+   else
+      {
+      // maybe just zap the salts + sessions tables in this case?
+      if(salts != 0)
+         throw Internal_Error("Seemingly corrupted TLS session db, multiple salts found");
+
+      // new database case
+
+      std::vector<uint8_t> salt;
+      rng.random_vec(salt, 16);
+      size_t iterations = 0;
+
+      secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2,
+                                                 passphrase,
+                                                 salt.data(), salt.size(),
+                                                 std::chrono::milliseconds(100),
+                                                 iterations);
+
+      size_t check_val = make_uint16(x[0], x[1]);
+      m_session_key.assign(x.begin() + 2, x.end());
+
+      auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
+
+      stmt->bind(1, salt);
+      stmt->bind(2, iterations);
+      stmt->bind(3, check_val);
+
+      stmt->spin();
+      }
+   }
+
+bool Session_Manager_SQL::load_from_session_id(const std::vector<uint8_t>& session_id,
+                                               Session& session)
+   {
+   auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
+
+   stmt->bind(1, hex_encode(session_id));
+
+   while(stmt->step())
+      {
+      std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
+
+      try
+         {
+         session = Session::decrypt(blob.first, blob.second, m_session_key);
+         return true;
+         }
+      catch(...)
+         {
+         }
+      }
+
+   return false;
+   }
+
+bool Session_Manager_SQL::load_from_server_info(const Server_Information& server,
+                                                Session& session)
+   {
+   auto stmt = m_db->new_statement("select session from tls_sessions"
+                                   " where hostname = ?1 and hostport = ?2"
+                                   " order by session_start desc");
+
+   stmt->bind(1, server.hostname());
+   stmt->bind(2, server.port());
+
+   while(stmt->step())
+      {
+      std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
+
+      try
+         {
+         session = Session::decrypt(blob.first, blob.second, m_session_key);
+         return true;
+         }
+      catch(...)
+         {
+         }
+      }
+
+   return false;
+   }
+
+void Session_Manager_SQL::remove_entry(const std::vector<uint8_t>& session_id)
+   {
+   auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
+
+   stmt->bind(1, hex_encode(session_id));
+
+   stmt->spin();
+   }
+
+size_t Session_Manager_SQL::remove_all()
+   {
+   auto stmt = m_db->new_statement("delete from tls_sessions");
+   return stmt->spin();
+   }
+
+void Session_Manager_SQL::save(const Session& session)
+   {
+   if(session.server_info().hostname().empty())
+      return;
+
+   auto stmt = m_db->new_statement("insert or replace into tls_sessions"
+                                   " values(?1, ?2, ?3, ?4, ?5)");
+
+   stmt->bind(1, hex_encode(session.session_id()));
+   stmt->bind(2, session.start_time());
+   stmt->bind(3, session.server_info().hostname());
+   stmt->bind(4, session.server_info().port());
+   stmt->bind(5, session.encrypt(m_session_key, m_rng));
+
+   stmt->spin();
+
+   prune_session_cache();
+   }
+
+void Session_Manager_SQL::prune_session_cache()
+   {
+   // First expire old sessions
+   auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1");
+   remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime);
+   remove_expired->spin();
+
+   const size_t sessions = m_db->row_count("tls_sessions");
+
+   // Then if needed expire some more sessions at random
+   if(sessions > m_max_sessions)
+      {
+      auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in "
+                                             "(select session_id from tls_sessions limit ?1)");
+
+      remove_some->bind(1, sessions - m_max_sessions);
+      remove_some->spin();
+      }
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h b/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h
@@ -0,0 +1,81 @@
+/*
+* TLS Session Manager storing to encrypted SQL db table
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SQL_SESSION_MANAGER_H_
+#define BOTAN_TLS_SQL_SESSION_MANAGER_H_
+
+#include <botan/tls_session_manager.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace TLS {
+
+/**
+* An implementation of Session_Manager that saves values in a SQL
+* database file, with the session data encrypted using a passphrase.
+*
+* @warning For clients, the hostnames associated with the saved
+* sessions are stored in the database in plaintext. This may be a
+* serious privacy risk in some situations.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_SQL : public Session_Manager
+   {
+   public:
+      /**
+      * @param db A connection to the database to use
+               The table names botan_tls_sessions and
+               botan_tls_sessions_metadata will be used
+      * @param passphrase used to encrypt the session data
+      * @param rng a random number generator
+      * @param max_sessions a hint on the maximum number of sessions
+      *        to keep in memory at any one time. (If zero, don't cap)
+      * @param session_lifetime sessions are expired after this many
+      *        seconds have elapsed from initial handshake.
+      */
+      Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
+                          const std::string& passphrase,
+                          RandomNumberGenerator& rng,
+                          size_t max_sessions = 1000,
+                          std::chrono::seconds session_lifetime = std::chrono::seconds(7200));
+
+      Session_Manager_SQL(const Session_Manager_SQL&) = delete;
+
+      Session_Manager_SQL& operator=(const Session_Manager_SQL&) = delete;
+
+      bool load_from_session_id(const std::vector<uint8_t>& session_id,
+                                Session& session) override;
+
+      bool load_from_server_info(const Server_Information& info,
+                                 Session& session) override;
+
+      void remove_entry(const std::vector<uint8_t>& session_id) override;
+
+      size_t remove_all() override;
+
+      void save(const Session& session_data) override;
+
+      std::chrono::seconds session_lifetime() const override
+         { return m_session_lifetime; }
+
+   private:
+      void prune_session_cache();
+
+      std::shared_ptr<SQL_Database> m_db;
+      secure_vector<uint8_t> m_session_key;
+      RandomNumberGenerator& m_rng;
+      size_t m_max_sessions;
+      std::chrono::seconds m_session_lifetime;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp b/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp
@@ -0,0 +1,29 @@
+/*
+* SQLite TLS Session Manager
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase,
+                                               RandomNumberGenerator& rng,
+                                               const std::string& db_filename,
+                                               size_t max_sessions,
+                                               std::chrono::seconds session_lifetime) :
+   Session_Manager_SQL(std::make_shared<Sqlite3_Database>(db_filename),
+                       passphrase,
+                       rng,
+                       max_sessions,
+                       session_lifetime)
+   {}
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h b/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h
@@ -0,0 +1,53 @@
+/*
+* SQLite3 TLS Session Manager
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SQLITE3_SESSION_MANAGER_H_
+#define BOTAN_TLS_SQLITE3_SESSION_MANAGER_H_
+
+#include <botan/tls_session_manager_sql.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace TLS {
+
+/**
+* An implementation of Session_Manager that saves values in a SQLite3
+* database file, with the session data encrypted using a passphrase.
+*
+* @warning For clients, the hostnames associated with the saved
+* sessions are stored in the database in plaintext. This may be a
+* serious privacy risk in some situations.
+*/
+class BOTAN_PUBLIC_API(2,0)
+Session_Manager_SQLite final : public Session_Manager_SQL
+   {
+   public:
+      /**
+      * @param passphrase used to encrypt the session data
+      * @param rng a random number generator
+      * @param db_filename filename of the SQLite database file.
+               The table names tls_sessions and tls_sessions_metadata
+               will be used
+      * @param max_sessions a hint on the maximum number of sessions
+      *        to keep in memory at any one time. (If zero, don't cap)
+      * @param session_lifetime sessions are expired after this many
+      *        seconds have elapsed from initial handshake.
+      */
+      Session_Manager_SQLite(const std::string& passphrase,
+                             RandomNumberGenerator& rng,
+                             const std::string& db_filename,
+                             size_t max_sessions = 1000,
+                             std::chrono::seconds session_lifetime = std::chrono::seconds(7200));
+};
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_alert.cpp b/third_party/botan/src/lib/tls/tls_alert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_alert.cpp
@@ -0,0 +1,126 @@
+/*
+* Alert Message
+* (C) 2004-2006,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Alert::Alert(const secure_vector<uint8_t>& buf)
+   {
+   if(buf.size() != 2)
+      throw Decoding_Error("Bad size (" + std::to_string(buf.size()) +
+                           ") for TLS alert message");
+
+   if(buf[0] == 1)      m_fatal = false;
+   else if(buf[0] == 2) m_fatal = true;
+   else
+      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Bad code for TLS alert level");
+
+   const uint8_t dc = buf[1];
+
+   m_type_code = static_cast<Type>(dc);
+   }
+
+std::vector<uint8_t> Alert::serialize() const
+   {
+   return std::vector<uint8_t>({
+      static_cast<uint8_t>(is_fatal() ? 2 : 1),
+      static_cast<uint8_t>(type())
+      });
+   }
+
+std::string Alert::type_string() const
+   {
+   switch(type())
+      {
+      case CLOSE_NOTIFY:
+         return "close_notify";
+      case UNEXPECTED_MESSAGE:
+         return "unexpected_message";
+      case BAD_RECORD_MAC:
+         return "bad_record_mac";
+      case DECRYPTION_FAILED:
+         return "decryption_failed";
+      case RECORD_OVERFLOW:
+         return "record_overflow";
+      case DECOMPRESSION_FAILURE:
+         return "decompression_failure";
+      case HANDSHAKE_FAILURE:
+         return "handshake_failure";
+      case NO_CERTIFICATE:
+         return "no_certificate";
+      case BAD_CERTIFICATE:
+         return "bad_certificate";
+      case UNSUPPORTED_CERTIFICATE:
+         return "unsupported_certificate";
+      case CERTIFICATE_REVOKED:
+         return "certificate_revoked";
+      case CERTIFICATE_EXPIRED:
+         return "certificate_expired";
+      case CERTIFICATE_UNKNOWN:
+         return "certificate_unknown";
+      case ILLEGAL_PARAMETER:
+         return "illegal_parameter";
+      case UNKNOWN_CA:
+         return "unknown_ca";
+      case ACCESS_DENIED:
+         return "access_denied";
+      case DECODE_ERROR:
+         return "decode_error";
+      case DECRYPT_ERROR:
+         return "decrypt_error";
+      case EXPORT_RESTRICTION:
+         return "export_restriction";
+      case PROTOCOL_VERSION:
+         return "protocol_version";
+      case INSUFFICIENT_SECURITY:
+         return "insufficient_security";
+      case INTERNAL_ERROR:
+         return "internal_error";
+      case INAPPROPRIATE_FALLBACK:
+         return "inappropriate_fallback";
+      case USER_CANCELED:
+         return "user_canceled";
+      case NO_RENEGOTIATION:
+         return "no_renegotiation";
+
+      case UNSUPPORTED_EXTENSION:
+         return "unsupported_extension";
+      case CERTIFICATE_UNOBTAINABLE:
+         return "certificate_unobtainable";
+      case UNRECOGNIZED_NAME:
+         return "unrecognized_name";
+      case BAD_CERTIFICATE_STATUS_RESPONSE:
+         return "bad_certificate_status_response";
+      case BAD_CERTIFICATE_HASH_VALUE:
+         return "bad_certificate_hash_value";
+      case UNKNOWN_PSK_IDENTITY:
+         return "unknown_psk_identity";
+      case CERTIFICATE_REQUIRED:
+         return "certificate_required";
+      case NO_APPLICATION_PROTOCOL:
+         return "no_application_protocol";
+
+      case NULL_ALERT:
+         return "none";
+      }
+
+   /*
+   * This is effectively the default case for the switch above, but we
+   * leave it out so that when an alert type is added to the enum the
+   * compiler can warn us that it is not included in the switch
+   * statement.
+   */
+   return "unrecognized_alert_" + std::to_string(type());
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_alert.h b/third_party/botan/src/lib/tls/tls_alert.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_alert.h
@@ -0,0 +1,116 @@
+/*
+* Alert Message
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_ALERT_H_
+#define BOTAN_TLS_ALERT_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* SSL/TLS Alert Message
+*/
+class BOTAN_PUBLIC_API(2,0) Alert final
+   {
+   public:
+      /**
+      * Type codes for TLS alerts
+      */
+      enum Type {
+         CLOSE_NOTIFY                    = 0,
+         UNEXPECTED_MESSAGE              = 10,
+         BAD_RECORD_MAC                  = 20,
+         DECRYPTION_FAILED               = 21,
+         RECORD_OVERFLOW                 = 22,
+         DECOMPRESSION_FAILURE           = 30,
+         HANDSHAKE_FAILURE               = 40,
+         NO_CERTIFICATE                  = 41, // SSLv3 only
+         BAD_CERTIFICATE                 = 42,
+         UNSUPPORTED_CERTIFICATE         = 43,
+         CERTIFICATE_REVOKED             = 44,
+         CERTIFICATE_EXPIRED             = 45,
+         CERTIFICATE_UNKNOWN             = 46,
+         ILLEGAL_PARAMETER               = 47,
+         UNKNOWN_CA                      = 48,
+         ACCESS_DENIED                   = 49,
+         DECODE_ERROR                    = 50,
+         DECRYPT_ERROR                   = 51,
+         EXPORT_RESTRICTION              = 60,
+         PROTOCOL_VERSION                = 70,
+         INSUFFICIENT_SECURITY           = 71,
+         INTERNAL_ERROR                  = 80,
+         INAPPROPRIATE_FALLBACK          = 86,
+         USER_CANCELED                   = 90,
+         NO_RENEGOTIATION                = 100,
+         UNSUPPORTED_EXTENSION           = 110,
+         CERTIFICATE_UNOBTAINABLE        = 111,
+         UNRECOGNIZED_NAME               = 112,
+         BAD_CERTIFICATE_STATUS_RESPONSE = 113,
+         BAD_CERTIFICATE_HASH_VALUE      = 114,
+         UNKNOWN_PSK_IDENTITY            = 115,
+         CERTIFICATE_REQUIRED            = 116, // RFC 8446
+
+         NO_APPLICATION_PROTOCOL         = 120, // RFC 7301
+
+         // pseudo alert values
+         NULL_ALERT                      = 256
+      };
+
+      /**
+      * @return true iff this alert is non-empty
+      */
+      bool is_valid() const { return (m_type_code != NULL_ALERT); }
+
+      /**
+      * @return if this alert is a fatal one or not
+      */
+      bool is_fatal() const { return m_fatal; }
+
+      /**
+      * @return type of alert
+      */
+      Type type() const { return m_type_code; }
+
+      /**
+      * @return type of alert
+      */
+      std::string type_string() const;
+
+      /**
+      * Serialize an alert
+      */
+      std::vector<uint8_t> serialize() const;
+
+      /**
+      * Deserialize an Alert message
+      * @param buf the serialized alert
+      */
+      explicit Alert(const secure_vector<uint8_t>& buf);
+
+      /**
+      * Create a new Alert
+      * @param type_code the type of alert
+      * @param fatal specifies if this is a fatal alert
+      */
+      Alert(Type type_code, bool fatal = false) :
+         m_fatal(fatal), m_type_code(type_code) {}
+
+      Alert() : m_fatal(false), m_type_code(NULL_ALERT) {}
+   private:
+      bool m_fatal;
+      Type m_type_code;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_algos.cpp b/third_party/botan/src/lib/tls/tls_algos.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_algos.cpp
@@ -0,0 +1,426 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_algos.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string kdf_algo_to_string(KDF_Algo algo)
+   {
+   switch(algo)
+      {
+      case KDF_Algo::SHA_1:
+         return "SHA-1";
+      case KDF_Algo::SHA_256:
+         return "SHA-256";
+      case KDF_Algo::SHA_384:
+         return "SHA-384";
+      }
+
+   throw Invalid_State("kdf_algo_to_string unknown enum value");
+   }
+
+std::string kex_method_to_string(Kex_Algo method)
+   {
+   switch(method)
+      {
+      case Kex_Algo::STATIC_RSA:
+         return "RSA";
+      case Kex_Algo::DH:
+         return "DH";
+      case Kex_Algo::ECDH:
+         return "ECDH";
+      case Kex_Algo::CECPQ1:
+         return "CECPQ1";
+      case Kex_Algo::SRP_SHA:
+         return "SRP_SHA";
+      case Kex_Algo::PSK:
+         return "PSK";
+      case Kex_Algo::DHE_PSK:
+         return "DHE_PSK";
+      case Kex_Algo::ECDHE_PSK:
+         return "ECDHE_PSK";
+      }
+
+   throw Invalid_State("kex_method_to_string unknown enum value");
+   }
+
+Kex_Algo kex_method_from_string(const std::string& str)
+   {
+   if(str == "RSA")
+      return Kex_Algo::STATIC_RSA;
+
+   if(str == "DH")
+      return Kex_Algo::DH;
+
+   if(str == "ECDH")
+      return Kex_Algo::ECDH;
+
+   if(str == "CECPQ1")
+      return Kex_Algo::CECPQ1;
+
+   if(str == "SRP_SHA")
+      return Kex_Algo::SRP_SHA;
+
+   if(str == "PSK")
+      return Kex_Algo::PSK;
+
+   if(str == "DHE_PSK")
+      return Kex_Algo::DHE_PSK;
+
+   if(str == "ECDHE_PSK")
+      return Kex_Algo::ECDHE_PSK;
+
+   throw Invalid_Argument("Unknown kex method " + str);
+   }
+
+std::string auth_method_to_string(Auth_Method method)
+   {
+   switch(method)
+      {
+      case Auth_Method::RSA:
+         return "RSA";
+      case Auth_Method::DSA:
+         return "DSA";
+      case Auth_Method::ECDSA:
+         return "ECDSA";
+      case Auth_Method::IMPLICIT:
+         return "IMPLICIT";
+      case Auth_Method::ANONYMOUS:
+         return "ANONYMOUS";
+      }
+
+    throw Invalid_State("auth_method_to_string unknown enum value");
+   }
+
+Auth_Method auth_method_from_string(const std::string& str)
+   {
+   if(str == "RSA")
+      return Auth_Method::RSA;
+   if(str == "DSA")
+      return Auth_Method::DSA;
+   if(str == "ECDSA")
+      return Auth_Method::ECDSA;
+   if(str == "IMPLICIT")
+      return Auth_Method::IMPLICIT;
+   if(str == "ANONYMOUS" || str == "")
+      return Auth_Method::ANONYMOUS;
+
+   throw Invalid_Argument("Bad signature method " + str);
+   }
+
+bool group_param_is_dh(Group_Params group)
+   {
+   uint16_t group_id = static_cast<uint16_t>(group);
+   return (group_id >= 256 && group_id < 512);
+   }
+
+Group_Params group_param_from_string(const std::string& group_name)
+   {
+   if(group_name == "secp256r1")
+      return Group_Params::SECP256R1;
+   if(group_name == "secp384r1")
+      return Group_Params::SECP384R1;
+   if(group_name == "secp521r1")
+      return Group_Params::SECP521R1;
+   if(group_name == "brainpool256r1")
+      return Group_Params::BRAINPOOL256R1;
+   if(group_name == "brainpool384r1")
+      return Group_Params::BRAINPOOL384R1;
+   if(group_name == "brainpool512r1")
+      return Group_Params::BRAINPOOL512R1;
+   if(group_name == "x25519")
+      return Group_Params::X25519;
+
+   if(group_name == "ffdhe/ietf/2048")
+      return Group_Params::FFDHE_2048;
+   if(group_name == "ffdhe/ietf/3072")
+      return Group_Params::FFDHE_3072;
+   if(group_name == "ffdhe/ietf/4096")
+      return Group_Params::FFDHE_4096;
+   if(group_name == "ffdhe/ietf/6144")
+      return Group_Params::FFDHE_6144;
+   if(group_name == "ffdhe/ietf/8192")
+      return Group_Params::FFDHE_8192;
+
+   return Group_Params::NONE; // unknown
+   }
+
+std::string group_param_to_string(Group_Params group)
+   {
+   switch(group)
+      {
+      case Group_Params::SECP256R1:
+         return "secp256r1";
+      case Group_Params::SECP384R1:
+         return "secp384r1";
+      case Group_Params::SECP521R1:
+         return "secp521r1";
+      case Group_Params::BRAINPOOL256R1:
+         return "brainpool256r1";
+      case Group_Params::BRAINPOOL384R1:
+         return "brainpool384r1";
+      case Group_Params::BRAINPOOL512R1:
+         return "brainpool512r1";
+      case Group_Params::X25519:
+         return "x25519";
+
+      case Group_Params::FFDHE_2048:
+         return "ffdhe/ietf/2048";
+      case Group_Params::FFDHE_3072:
+         return "ffdhe/ietf/3072";
+      case Group_Params::FFDHE_4096:
+         return "ffdhe/ietf/4096";
+      case Group_Params::FFDHE_6144:
+         return "ffdhe/ietf/6144";
+      case Group_Params::FFDHE_8192:
+         return "ffdhe/ietf/8192";
+
+      default:
+         return "";
+      }
+   }
+
+
+std::string hash_function_of_scheme(Signature_Scheme scheme)
+   {
+   switch(scheme)
+      {
+      case Signature_Scheme::DSA_SHA1:
+      case Signature_Scheme::ECDSA_SHA1:
+      case Signature_Scheme::RSA_PKCS1_SHA1:
+         return "SHA-1";
+
+      case Signature_Scheme::DSA_SHA256:
+      case Signature_Scheme::ECDSA_SHA256:
+      case Signature_Scheme::RSA_PKCS1_SHA256:
+      case Signature_Scheme::RSA_PSS_SHA256:
+         return "SHA-256";
+
+      case Signature_Scheme::DSA_SHA384:
+      case Signature_Scheme::ECDSA_SHA384:
+      case Signature_Scheme::RSA_PKCS1_SHA384:
+      case Signature_Scheme::RSA_PSS_SHA384:
+         return "SHA-384";
+
+      case Signature_Scheme::DSA_SHA512:
+      case Signature_Scheme::ECDSA_SHA512:
+      case Signature_Scheme::RSA_PKCS1_SHA512:
+      case Signature_Scheme::RSA_PSS_SHA512:
+         return "SHA-512";
+
+      case Signature_Scheme::EDDSA_25519:
+      case Signature_Scheme::EDDSA_448:
+         return "Pure";
+
+      case Signature_Scheme::NONE:
+         return "";
+      }
+
+   throw Invalid_State("hash_function_of_scheme: Unknown signature algorithm enum");
+   }
+
+const std::vector<Signature_Scheme>& all_signature_schemes()
+   {
+   /*
+   * This is ordered in some approximate order of preference
+   */
+   static const std::vector<Signature_Scheme> all_schemes = {
+      //Signature_Scheme::EDDSA_448,
+      //Signature_Scheme::EDDSA_25519,
+
+      Signature_Scheme::RSA_PSS_SHA384,
+      Signature_Scheme::RSA_PSS_SHA256,
+      Signature_Scheme::RSA_PSS_SHA512,
+
+      Signature_Scheme::RSA_PKCS1_SHA384,
+      Signature_Scheme::RSA_PKCS1_SHA512,
+      Signature_Scheme::RSA_PKCS1_SHA256,
+
+      Signature_Scheme::ECDSA_SHA384,
+      Signature_Scheme::ECDSA_SHA512,
+      Signature_Scheme::ECDSA_SHA256,
+
+      Signature_Scheme::DSA_SHA384,
+      Signature_Scheme::DSA_SHA512,
+      Signature_Scheme::DSA_SHA256,
+
+      Signature_Scheme::RSA_PKCS1_SHA1,
+      Signature_Scheme::ECDSA_SHA1,
+      Signature_Scheme::DSA_SHA1,
+   };
+
+   return all_schemes;
+   }
+
+bool signature_scheme_is_known(Signature_Scheme scheme)
+   {
+   switch(scheme)
+      {
+      case Signature_Scheme::RSA_PKCS1_SHA1:
+      case Signature_Scheme::RSA_PKCS1_SHA256:
+      case Signature_Scheme::RSA_PKCS1_SHA384:
+      case Signature_Scheme::RSA_PKCS1_SHA512:
+      case Signature_Scheme::RSA_PSS_SHA256:
+      case Signature_Scheme::RSA_PSS_SHA384:
+      case Signature_Scheme::RSA_PSS_SHA512:
+
+      case Signature_Scheme::DSA_SHA1:
+      case Signature_Scheme::DSA_SHA256:
+      case Signature_Scheme::DSA_SHA384:
+      case Signature_Scheme::DSA_SHA512:
+
+      case Signature_Scheme::ECDSA_SHA1:
+      case Signature_Scheme::ECDSA_SHA256:
+      case Signature_Scheme::ECDSA_SHA384:
+      case Signature_Scheme::ECDSA_SHA512:
+         return true;
+
+      default:
+         return false;
+      }
+
+   }
+
+std::string signature_algorithm_of_scheme(Signature_Scheme scheme)
+   {
+   switch(scheme)
+      {
+      case Signature_Scheme::RSA_PKCS1_SHA1:
+      case Signature_Scheme::RSA_PKCS1_SHA256:
+      case Signature_Scheme::RSA_PKCS1_SHA384:
+      case Signature_Scheme::RSA_PKCS1_SHA512:
+      case Signature_Scheme::RSA_PSS_SHA256:
+      case Signature_Scheme::RSA_PSS_SHA384:
+      case Signature_Scheme::RSA_PSS_SHA512:
+         return "RSA";
+
+      case Signature_Scheme::DSA_SHA1:
+      case Signature_Scheme::DSA_SHA256:
+      case Signature_Scheme::DSA_SHA384:
+      case Signature_Scheme::DSA_SHA512:
+         return "DSA";
+
+      case Signature_Scheme::ECDSA_SHA1:
+      case Signature_Scheme::ECDSA_SHA256:
+      case Signature_Scheme::ECDSA_SHA384:
+      case Signature_Scheme::ECDSA_SHA512:
+         return "ECDSA";
+
+      case Signature_Scheme::EDDSA_25519:
+         return "Ed25519";
+
+      case Signature_Scheme::EDDSA_448:
+         return "Ed448";
+
+      case Signature_Scheme::NONE:
+         return "";
+      }
+
+   throw Invalid_State("signature_algorithm_of_scheme: Unknown signature algorithm enum");
+   }
+
+std::string sig_scheme_to_string(Signature_Scheme scheme)
+   {
+   switch(scheme)
+      {
+      case Signature_Scheme::RSA_PKCS1_SHA1:
+         return "RSA_PKCS1_SHA1";
+      case Signature_Scheme::RSA_PKCS1_SHA256:
+         return "RSA_PKCS1_SHA256";
+      case Signature_Scheme::RSA_PKCS1_SHA384:
+         return "RSA_PKCS1_SHA384";
+      case Signature_Scheme::RSA_PKCS1_SHA512:
+         return "RSA_PKCS1_SHA512";
+
+      case Signature_Scheme::DSA_SHA1:
+         return "DSA_SHA1";
+      case Signature_Scheme::DSA_SHA256:
+         return "DSA_SHA256";
+      case Signature_Scheme::DSA_SHA384:
+         return "DSA_SHA384";
+      case Signature_Scheme::DSA_SHA512:
+         return "DSA_SHA512";
+
+      case Signature_Scheme::ECDSA_SHA1:
+         return "ECDSA_SHA1";
+      case Signature_Scheme::ECDSA_SHA256:
+         return "ECDSA_SHA256";
+      case Signature_Scheme::ECDSA_SHA384:
+         return "ECDSA_SHA384";
+      case Signature_Scheme::ECDSA_SHA512:
+         return "ECDSA_SHA512";
+
+      case Signature_Scheme::RSA_PSS_SHA256:
+         return "RSA_PSS_SHA256";
+      case Signature_Scheme::RSA_PSS_SHA384:
+         return "RSA_PSS_SHA384";
+      case Signature_Scheme::RSA_PSS_SHA512:
+         return "RSA_PSS_SHA512";
+
+      case Signature_Scheme::EDDSA_25519:
+         return "EDDSA_25519";
+      case Signature_Scheme::EDDSA_448:
+         return "EDDSA_448";
+
+      case Signature_Scheme::NONE:
+         return "";
+      }
+
+   throw Invalid_State("sig_scheme_to_string: Unknown signature algorithm enum");
+   }
+
+std::string padding_string_for_scheme(Signature_Scheme scheme)
+   {
+   switch(scheme)
+      {
+      case Signature_Scheme::RSA_PKCS1_SHA1:
+         return "EMSA_PKCS1(SHA-1)";
+      case Signature_Scheme::RSA_PKCS1_SHA256:
+         return "EMSA_PKCS1(SHA-256)";
+      case Signature_Scheme::RSA_PKCS1_SHA384:
+         return "EMSA_PKCS1(SHA-384)";
+      case Signature_Scheme::RSA_PKCS1_SHA512:
+         return "EMSA_PKCS1(SHA-512)";
+
+      case Signature_Scheme::DSA_SHA1:
+      case Signature_Scheme::ECDSA_SHA1:
+         return "EMSA1(SHA-1)";
+      case Signature_Scheme::DSA_SHA256:
+      case Signature_Scheme::ECDSA_SHA256:
+         return "EMSA1(SHA-256)";
+      case Signature_Scheme::DSA_SHA384:
+      case Signature_Scheme::ECDSA_SHA384:
+         return "EMSA1(SHA-384)";
+      case Signature_Scheme::DSA_SHA512:
+      case Signature_Scheme::ECDSA_SHA512:
+         return "EMSA1(SHA-512)";
+
+      case Signature_Scheme::RSA_PSS_SHA256:
+         return "PSSR(SHA-256,MGF1,32)";
+      case Signature_Scheme::RSA_PSS_SHA384:
+         return "PSSR(SHA-384,MGF1,48)";
+      case Signature_Scheme::RSA_PSS_SHA512:
+         return "PSSR(SHA-512,MGF1,64)";
+
+      case Signature_Scheme::EDDSA_25519:
+         return "Pure";
+      case Signature_Scheme::EDDSA_448:
+         return "Pure";
+
+      case Signature_Scheme::NONE:
+         return "";
+      }
+
+   throw Invalid_State("padding_string_for_scheme: Unknown signature algorithm enum");
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_algos.h b/third_party/botan/src/lib/tls/tls_algos.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_algos.h
@@ -0,0 +1,171 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_ALGO_IDS_H_
+#define BOTAN_TLS_ALGO_IDS_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(tls_algos.h)
+
+namespace Botan {
+
+namespace TLS {
+
+enum class Cipher_Algo {
+   CHACHA20_POLY1305,
+
+   AES_128_CBC_HMAC_SHA1 = 100,
+   AES_128_CBC_HMAC_SHA256,
+   AES_128_CCM,
+   AES_128_CCM_8,
+   AES_128_GCM,
+   AES_128_OCB,
+
+   AES_256_CBC_HMAC_SHA1 = 200,
+   AES_256_CBC_HMAC_SHA256,
+   AES_256_CBC_HMAC_SHA384,
+   AES_256_CCM,
+   AES_256_CCM_8,
+   AES_256_GCM,
+   AES_256_OCB,
+
+   CAMELLIA_128_CBC_HMAC_SHA1 = 300,
+   CAMELLIA_128_CBC_HMAC_SHA256,
+   CAMELLIA_128_GCM,
+
+   CAMELLIA_256_CBC_HMAC_SHA1 = 400,
+   CAMELLIA_256_CBC_HMAC_SHA256,
+   CAMELLIA_256_CBC_HMAC_SHA384,
+   CAMELLIA_256_GCM,
+
+   ARIA_128_GCM = 500,
+   ARIA_256_GCM,
+
+   DES_EDE_CBC_HMAC_SHA1 = 1000,
+   SEED_CBC_HMAC_SHA1,
+};
+
+enum class KDF_Algo {
+   SHA_1,
+   SHA_256,
+   SHA_384,
+};
+
+std::string BOTAN_DLL kdf_algo_to_string(KDF_Algo algo);
+
+enum class Nonce_Format {
+   CBC_MODE,
+   AEAD_IMPLICIT_4,
+   AEAD_XOR_12,
+};
+
+// TODO encoding should match signature_algorithms extension
+// TODO this should include hash etc as in TLS v1.3
+enum class Auth_Method {
+   RSA,
+   DSA,
+   ECDSA,
+
+   // These are placed outside the encodable range
+   IMPLICIT = 0x10000,
+   ANONYMOUS
+};
+
+std::string BOTAN_TEST_API auth_method_to_string(Auth_Method method);
+Auth_Method BOTAN_TEST_API auth_method_from_string(const std::string& str);
+
+/*
+* This matches the wire encoding
+*/
+enum class Signature_Scheme : uint16_t {
+   NONE             = 0x0000,
+
+   RSA_PKCS1_SHA1   = 0x0201,
+   RSA_PKCS1_SHA256 = 0x0401,
+   RSA_PKCS1_SHA384 = 0x0501,
+   RSA_PKCS1_SHA512 = 0x0601,
+
+   DSA_SHA1   = 0x0202,
+   DSA_SHA256 = 0x0402,
+   DSA_SHA384 = 0x0502,
+   DSA_SHA512 = 0x0602,
+
+   ECDSA_SHA1   = 0x0203,
+   ECDSA_SHA256 = 0x0403,
+   ECDSA_SHA384 = 0x0503,
+   ECDSA_SHA512 = 0x0603,
+
+   RSA_PSS_SHA256 = 0x0804,
+   RSA_PSS_SHA384 = 0x0805,
+   RSA_PSS_SHA512 = 0x0806,
+
+   EDDSA_25519 = 0x0807,
+   EDDSA_448   = 0x0808,
+};
+
+BOTAN_UNSTABLE_API const std::vector<Signature_Scheme>& all_signature_schemes();
+
+bool BOTAN_UNSTABLE_API signature_scheme_is_known(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API sig_scheme_to_string(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API hash_function_of_scheme(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API padding_string_for_scheme(Signature_Scheme scheme);
+std::string signature_algorithm_of_scheme(Signature_Scheme scheme);
+
+/*
+* Matches with wire encoding
+*/
+enum class Group_Params : uint16_t {
+   NONE = 0,
+
+   SECP256R1 = 23,
+   SECP384R1 = 24,
+   SECP521R1 = 25,
+   BRAINPOOL256R1 = 26,
+   BRAINPOOL384R1 = 27,
+   BRAINPOOL512R1 = 28,
+
+   X25519 = 29,
+
+   FFDHE_2048 = 256,
+   FFDHE_3072 = 257,
+   FFDHE_4096 = 258,
+   FFDHE_6144 = 259,
+   FFDHE_8192 = 260,
+};
+
+std::string group_param_to_string(Group_Params group);
+Group_Params group_param_from_string(const std::string& group_name);
+bool group_param_is_dh(Group_Params group);
+
+enum class Kex_Algo {
+   STATIC_RSA,
+   DH,
+   ECDH,
+   CECPQ1,
+   SRP_SHA,
+   PSK,
+   DHE_PSK,
+   ECDHE_PSK,
+};
+
+std::string BOTAN_TEST_API kex_method_to_string(Kex_Algo method);
+Kex_Algo BOTAN_TEST_API kex_method_from_string(const std::string& str);
+
+inline bool key_exchange_is_psk(Kex_Algo m)
+   {
+   return (m == Kex_Algo::PSK ||
+           m == Kex_Algo::DHE_PSK ||
+           m == Kex_Algo::ECDHE_PSK);
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_blocking.cpp b/third_party/botan/src/lib/tls/tls_blocking.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_blocking.cpp
@@ -0,0 +1,97 @@
+/*
+* TLS Blocking API
+* (C) 2013 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_blocking.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Blocking_Client::Blocking_Client(read_fn reader,
+                                 write_fn writer,
+                                 Session_Manager& session_manager,
+                                 Credentials_Manager& creds,
+                                 const Policy& policy,
+                                 RandomNumberGenerator& rng,
+                                 const Server_Information& server_info,
+                                 const Protocol_Version& offer_version,
+                                 const std::vector<std::string>& next) :
+   m_read(reader),
+   m_callbacks(new TLS::Compat_Callbacks(
+                  /*
+                  we are ok using deprecated features here because the whole Blocking_Client class
+                  is also deprecated, so just silence the warning.
+                  */
+               TLS::Compat_Callbacks::SILENCE_DEPRECATION_WARNING::PLEASE,
+               writer,
+               std::bind(&Blocking_Client::data_cb, this, std::placeholders::_1, std::placeholders::_2),
+               std::function<void (Alert)>(std::bind(&Blocking_Client::alert_cb, this, std::placeholders::_1)),
+               std::bind(&Blocking_Client::handshake_cb, this, std::placeholders::_1)
+             )),
+   m_channel(*m_callbacks.get(),
+             session_manager,
+             creds,
+             policy,
+             rng,
+             server_info,
+             offer_version,
+             next)
+   {
+   }
+
+bool Blocking_Client::handshake_cb(const Session& session)
+   {
+   return this->handshake_complete(session);
+   }
+
+void Blocking_Client::alert_cb(const Alert& alert)
+   {
+   this->alert_notification(alert);
+   }
+
+void Blocking_Client::data_cb(const uint8_t data[], size_t data_len)
+   {
+   m_plaintext.insert(m_plaintext.end(), data, data + data_len);
+   }
+
+void Blocking_Client::do_handshake()
+   {
+   std::vector<uint8_t> readbuf(4096);
+
+   while(!m_channel.is_closed() && !m_channel.is_active())
+      {
+      const size_t from_socket = m_read(readbuf.data(), readbuf.size());
+      m_channel.received_data(readbuf.data(), from_socket);
+      }
+   }
+
+size_t Blocking_Client::read(uint8_t buf[], size_t buf_len)
+   {
+   std::vector<uint8_t> readbuf(4096);
+
+   while(m_plaintext.empty() && !m_channel.is_closed())
+      {
+      const size_t from_socket = m_read(readbuf.data(), readbuf.size());
+      m_channel.received_data(readbuf.data(), from_socket);
+      }
+
+   const size_t returned = std::min(buf_len, m_plaintext.size());
+
+   for(size_t i = 0; i != returned; ++i)
+      buf[i] = m_plaintext[i];
+   m_plaintext.erase(m_plaintext.begin(), m_plaintext.begin() + returned);
+
+   BOTAN_ASSERT_IMPLICATION(returned == 0, m_channel.is_closed(),
+                            "Only return zero if channel is closed");
+
+   return returned;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_blocking.h b/third_party/botan/src/lib/tls/tls_blocking.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_blocking.h
@@ -0,0 +1,103 @@
+/*
+* TLS Blocking API
+* (C) 2013 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_BLOCKING_CHANNELS_H_
+#define BOTAN_TLS_BLOCKING_CHANNELS_H_
+
+#include <botan/tls_client.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Blocking TLS Client
+* Can be used directly, or subclass to get handshake and alert notifications
+*/
+class BOTAN_PUBLIC_API(2,0) Blocking_Client
+   {
+   public:
+      /*
+      * These functions are expected to block until completing entirely, or
+      * fail by throwing an exception.
+      */
+      typedef std::function<size_t (uint8_t[], size_t)> read_fn;
+      typedef std::function<void (const uint8_t[], size_t)> write_fn;
+
+      BOTAN_DEPRECATED("Use the regular TLS::Client interface")
+      Blocking_Client(read_fn reader,
+                      write_fn writer,
+                      Session_Manager& session_manager,
+                      Credentials_Manager& creds,
+                      const Policy& policy,
+                      RandomNumberGenerator& rng,
+                      const Server_Information& server_info = Server_Information(),
+                      const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+                      const std::vector<std::string>& next_protos = {});
+
+      /**
+      * Completes full handshake then returns
+      */
+      void do_handshake();
+
+      /**
+      * Number of bytes pending read in the plaintext buffer (bytes
+      * readable without blocking)
+      */
+      size_t pending() const { return m_plaintext.size(); }
+
+      /**
+      * Blocking read, will return at least 1 byte (eventually) or else 0 if the connection
+      * is closed.
+      */
+      size_t read(uint8_t buf[], size_t buf_len);
+
+      void write(const uint8_t buf[], size_t buf_len) { m_channel.send(buf, buf_len); }
+
+      const TLS::Channel& underlying_channel() const { return m_channel; }
+      TLS::Channel& underlying_channel() { return m_channel; }
+
+      void close() { m_channel.close(); }
+
+      bool is_closed() const { return m_channel.is_closed(); }
+
+      std::vector<X509_Certificate> peer_cert_chain() const
+         { return m_channel.peer_cert_chain(); }
+
+      virtual ~Blocking_Client() = default;
+
+   protected:
+      /**
+      * Application can override to get the handshake complete notification
+      */
+      virtual bool handshake_complete(const Session&) { return true; }
+
+      /**
+      * Application can override to get notification of alerts
+      */
+      virtual void alert_notification(const Alert&) {}
+
+   private:
+
+      bool handshake_cb(const Session&);
+
+      void data_cb(const uint8_t data[], size_t data_len);
+
+      void alert_cb(const Alert& alert);
+
+      read_fn m_read;
+      std::unique_ptr<Compat_Callbacks> m_callbacks;
+      TLS::Client m_channel;
+      secure_vector<uint8_t> m_plaintext;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_callbacks.cpp b/third_party/botan/src/lib/tls/tls_callbacks.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_callbacks.cpp
@@ -0,0 +1,191 @@
+/*
+* TLS Callbacks
+* (C) 2016 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_callbacks.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_algos.h>
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/dh.h>
+#include <botan/ecdh.h>
+#include <botan/tls_exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+#if defined(BOTAN_HAS_CURVE_25519)
+  #include <botan/curve25519.h>
+#endif
+
+namespace Botan {
+
+void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message&)
+   {
+   // default is no op
+   }
+
+std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>&)
+   {
+   return "";
+   }
+
+std::string TLS::Callbacks::tls_peer_network_identity()
+   {
+   return "";
+   }
+
+void TLS::Callbacks::tls_modify_extensions(Extensions&, Connection_Side)
+   {
+   }
+
+void TLS::Callbacks::tls_examine_extensions(const Extensions&, Connection_Side)
+   {
+   }
+
+std::string TLS::Callbacks::tls_decode_group_param(Group_Params group_param)
+   {
+   return group_param_to_string(group_param);
+   }
+
+void TLS::Callbacks::tls_verify_cert_chain(
+   const std::vector<X509_Certificate>& cert_chain,
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+   const std::vector<Certificate_Store*>& trusted_roots,
+   Usage_Type usage,
+   const std::string& hostname,
+   const TLS::Policy& policy)
+   {
+   if(cert_chain.empty())
+      throw Invalid_Argument("Certificate chain was empty");
+
+   Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
+                                             policy.minimum_signature_strength());
+
+   Path_Validation_Result result =
+      x509_path_validate(cert_chain,
+                         restrictions,
+                         trusted_roots,
+                         (usage == Usage_Type::TLS_SERVER_AUTH ? hostname : ""),
+                         usage,
+                         std::chrono::system_clock::now(),
+                         tls_verify_cert_chain_ocsp_timeout(),
+                         ocsp_responses);
+
+   if(!result.successful_validation())
+      {
+      throw TLS_Exception(Alert::BAD_CERTIFICATE,
+                          "Certificate validation failure: " + result.result_string());
+      }
+   }
+
+std::vector<uint8_t> TLS::Callbacks::tls_sign_message(
+   const Private_Key& key,
+   RandomNumberGenerator& rng,
+   const std::string& emsa,
+   Signature_Format format,
+   const std::vector<uint8_t>& msg)
+   {
+   PK_Signer signer(key, rng, emsa, format);
+
+   return signer.sign_message(msg, rng);
+   }
+
+bool TLS::Callbacks::tls_verify_message(
+   const Public_Key& key,
+   const std::string& emsa,
+   Signature_Format format,
+   const std::vector<uint8_t>& msg,
+   const std::vector<uint8_t>& sig)
+   {
+   PK_Verifier verifier(key, emsa, format);
+
+   return verifier.verify_message(msg, sig);
+   }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_dh_agree(
+   const std::vector<uint8_t>& modulus,
+   const std::vector<uint8_t>& generator,
+   const std::vector<uint8_t>& peer_public_value,
+   const Policy& policy,
+   RandomNumberGenerator& rng)
+   {
+   BigInt p = BigInt::decode(modulus);
+   BigInt g = BigInt::decode(generator);
+   BigInt Y = BigInt::decode(peer_public_value);
+
+   /*
+    * A basic check for key validity. As we do not know q here we
+    * cannot check that Y is in the right subgroup. However since
+    * our key is ephemeral there does not seem to be any
+    * advantage to bogus keys anyway.
+    */
+   if(Y <= 1 || Y >= p - 1)
+      throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+                          "Server sent bad DH key for DHE exchange");
+
+   DL_Group group(p, g);
+
+   if(!group.verify_group(rng, false))
+      throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+                          "DH group validation failed");
+
+   DH_PublicKey peer_key(group, Y);
+
+   policy.check_peer_key_acceptable(peer_key);
+
+   DH_PrivateKey priv_key(rng, group);
+   PK_Key_Agreement ka(priv_key, rng, "Raw");
+   secure_vector<uint8_t> dh_secret = CT::strip_leading_zeros(
+      ka.derive_key(0, peer_key.public_value()).bits_of());
+
+   return std::make_pair(dh_secret, priv_key.public_value());
+   }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_ecdh_agree(
+   const std::string& curve_name,
+   const std::vector<uint8_t>& peer_public_value,
+   const Policy& policy,
+   RandomNumberGenerator& rng,
+   bool compressed)
+   {
+   secure_vector<uint8_t> ecdh_secret;
+   std::vector<uint8_t> our_public_value;
+
+   if(curve_name == "x25519")
+      {
+#if defined(BOTAN_HAS_CURVE_25519)
+      if(peer_public_value.size() != 32)
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
+         }
+
+      Curve25519_PublicKey peer_key(peer_public_value);
+      policy.check_peer_key_acceptable(peer_key);
+      Curve25519_PrivateKey priv_key(rng);
+      PK_Key_Agreement ka(priv_key, rng, "Raw");
+      ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+
+      // X25519 is always compressed but sent as "uncompressed" in TLS
+      our_public_value = priv_key.public_value();
+#else
+      throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
+#endif
+      }
+   else
+      {
+      EC_Group group(OID::from_string(curve_name));
+      ECDH_PublicKey peer_key(group, group.OS2ECP(peer_public_value));
+      policy.check_peer_key_acceptable(peer_key);
+      ECDH_PrivateKey priv_key(rng, group);
+      PK_Key_Agreement ka(priv_key, rng, "Raw");
+      ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+      our_public_value = priv_key.public_value(compressed ? PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
+      }
+
+   return std::make_pair(ecdh_secret, our_public_value);
+   }
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_callbacks.h b/third_party/botan/src/lib/tls/tls_callbacks.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_callbacks.h
@@ -0,0 +1,484 @@
+/*
+* TLS Callbacks
+* (C) 2016 Matthias Gierlings
+*     2016 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CALLBACKS_H_
+#define BOTAN_TLS_CALLBACKS_H_
+
+#include <botan/tls_session.h>
+#include <botan/tls_alert.h>
+#include <botan/pubkey.h>
+#include <functional>
+
+namespace Botan {
+
+class Certificate_Store;
+class X509_Certificate;
+
+namespace OCSP {
+
+class Response;
+
+}
+
+namespace TLS {
+
+class Handshake_Message;
+class Policy;
+class Extensions;
+class Certificate_Status_Request;
+
+/**
+* Encapsulates the callbacks that a TLS channel will make which are due to
+* channel specific operations.
+*/
+class BOTAN_PUBLIC_API(2,0) Callbacks
+   {
+   public:
+       virtual ~Callbacks() = default;
+
+       /**
+       * Mandatory callback: output function
+       * The channel will call this with data which needs to be sent to the peer
+       * (eg, over a socket or some other form of IPC). The array will be overwritten
+       * when the function returns so a copy must be made if the data cannot be
+       * sent immediately.
+       *
+       * @param data the vector of data to send
+       *
+       * @param size the number of bytes to send
+       */
+       virtual void tls_emit_data(const uint8_t data[], size_t size) = 0;
+
+       /**
+       * Mandatory callback: process application data
+       * Called when application data record is received from the peer.
+       * Again the array is overwritten immediately after the function returns.
+       *
+       * @param seq_no the underlying TLS/DTLS record sequence number
+       *
+       * @param data the vector containing the received record
+       *
+       * @param size the length of the received record, in bytes
+       */
+       virtual void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) = 0;
+
+       /**
+       * Mandatory callback: alert received
+       * Called when an alert is received from the peer
+       * If fatal, the connection is closing. If not fatal, the connection may
+       * still be closing (depending on the error and the peer).
+       *
+       * @param alert the source of the alert
+       */
+       virtual void tls_alert(Alert alert) = 0;
+
+       /**
+       * Mandatory callback: session established
+       * Called when a session is established. Throw an exception to abort
+       * the connection.
+       *
+       * @param session the session descriptor
+       *
+       * @return return false to prevent the session from being cached,
+       * return true to cache the session in the configured session manager
+       */
+       virtual bool tls_session_established(const Session& session) = 0;
+
+       /**
+       * Optional callback: session activated
+       * Called when a session is active and can be written to
+       */
+       virtual void tls_session_activated() {}
+
+       /**
+       * Optional callback with default impl: verify cert chain
+       *
+       * Default implementation performs a standard PKIX validation
+       * and initiates network OCSP request for end-entity cert.
+       * Override to provide different behavior.
+       *
+       * Check the certificate chain is valid up to a trusted root, and
+       * optionally (if hostname != "") that the hostname given is
+       * consistent with the leaf certificate.
+       *
+       * This function should throw an exception derived from
+       * std::exception with an informative what() result if the
+       * certificate chain cannot be verified.
+       *
+       * @param cert_chain specifies a certificate chain leading to a
+       *        trusted root CA certificate.
+       * @param ocsp_responses the server may have provided some
+       * @param trusted_roots the list of trusted certificates
+       * @param usage what this cert chain is being used for
+       *        Usage_Type::TLS_SERVER_AUTH for server chains,
+       *        Usage_Type::TLS_CLIENT_AUTH for client chains,
+       *        Usage_Type::UNSPECIFIED for other uses
+       * @param hostname when authenticating a server, this is the hostname
+       *        the client requested (eg via SNI). When authenticating a client,
+       *        this is the server name the client is authenticating *to*.
+       *        Empty in other cases or if no hostname was used.
+       * @param policy the TLS policy associated with the session being authenticated
+       *        using the certificate chain
+       */
+       virtual void tls_verify_cert_chain(
+          const std::vector<X509_Certificate>& cert_chain,
+          const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+          const std::vector<Certificate_Store*>& trusted_roots,
+          Usage_Type usage,
+          const std::string& hostname,
+          const TLS::Policy& policy);
+
+       /**
+       * Called by default `tls_verify_cert_chain` to get the timeout to use for OCSP
+       * requests. Return 0 to disable online OCSP checks.
+       *
+       * This function should not be "const" since the implementation might need
+       * to perform some side effecting operation to compute the result.
+       */
+       virtual std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
+          {
+          return std::chrono::milliseconds(0);
+          }
+
+      /**
+       * Called by the TLS server whenever the client included the
+       * status_request extension (see RFC 6066, a.k.a OCSP stapling)
+       * in the ClientHello.
+       *
+       * @return the encoded OCSP response to be sent to the client which
+       * indicates the revocation status of the server certificate. Return an
+       * empty vector to indicate that no response is available, and thus
+       * suppress the Certificate_Status message.
+       */
+       virtual std::vector<uint8_t> tls_provide_cert_status(const std::vector<X509_Certificate>& chain,
+                                                            const Certificate_Status_Request& csr)
+          {
+          BOTAN_UNUSED(chain);
+          BOTAN_UNUSED(csr);
+          return std::vector<uint8_t>();
+          }
+
+       /**
+       * Optional callback with default impl: sign a message
+       *
+       * Default implementation uses PK_Signer::sign_message().
+       * Override to provide a different approach, e.g. using an external device.
+       *
+       * @param key the private key of the signer
+       * @param rng a random number generator
+       * @param emsa the encoding method to be applied to the message
+       * @param format the signature format
+       * @param msg the input data for the signature
+       *
+       * @return the signature
+       */
+       virtual std::vector<uint8_t> tls_sign_message(
+          const Private_Key& key,
+          RandomNumberGenerator& rng,
+          const std::string& emsa,
+          Signature_Format format,
+          const std::vector<uint8_t>& msg);
+
+       /**
+       * Optional callback with default impl: verify a message signature
+       *
+       * Default implementation uses PK_Verifier::verify_message().
+       * Override to provide a different approach, e.g. using an external device.
+       *
+       * @param key the public key of the signer
+       * @param emsa the encoding method to be applied to the message
+       * @param format the signature format
+       * @param msg the input data for the signature
+       * @param sig the signature to be checked
+       *
+       * @return true if the signature is valid, false otherwise
+       */
+       virtual bool tls_verify_message(
+          const Public_Key& key,
+          const std::string& emsa,
+          Signature_Format format,
+          const std::vector<uint8_t>& msg,
+          const std::vector<uint8_t>& sig);
+
+       /**
+       * Optional callback with default impl: client side DH agreement
+       *
+       * Default implementation uses PK_Key_Agreement::derive_key().
+       * Override to provide a different approach, e.g. using an external device.
+       *
+       * @param modulus the modulus p of the discrete logarithm group
+       * @param generator the generator of the DH subgroup
+       * @param peer_public_value the public value of the peer
+       * @param policy the TLS policy associated with the session being established
+       * @param rng a random number generator
+       *
+       * @return a pair consisting of the agreed raw secret and our public value
+       */
+       virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_dh_agree(
+          const std::vector<uint8_t>& modulus,
+          const std::vector<uint8_t>& generator,
+          const std::vector<uint8_t>& peer_public_value,
+          const Policy& policy,
+          RandomNumberGenerator& rng);
+
+       /**
+       * Optional callback with default impl: client side ECDH agreement
+       *
+       * Default implementation uses PK_Key_Agreement::derive_key().
+       * Override to provide a different approach, e.g. using an external device.
+       *
+       * @param curve_name the name of the elliptic curve
+       * @param peer_public_value the public value of the peer
+       * @param policy the TLS policy associated with the session being established
+       * @param rng a random number generator
+       * @param compressed the compression preference for our public value
+       *
+       * @return a pair consisting of the agreed raw secret and our public value
+       */
+       virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_ecdh_agree(
+          const std::string& curve_name,
+          const std::vector<uint8_t>& peer_public_value,
+          const Policy& policy,
+          RandomNumberGenerator& rng,
+          bool compressed);
+
+       /**
+       * Optional callback: inspect handshake message
+       * Throw an exception to abort the handshake.
+       * Default simply ignores the message.
+       *
+       * @param message the handshake message
+       */
+       virtual void tls_inspect_handshake_msg(const Handshake_Message& message);
+
+       /**
+       * Optional callback for server: choose ALPN protocol
+       * ALPN (RFC 7301) works by the client sending a list of application
+       * protocols it is willing to negotiate. The server then selects which
+       * protocol to use, which is not necessarily even on the list that
+       * the client sent.
+       *
+       * @param client_protos the vector of protocols the client is willing to negotiate
+       *
+       * @return the protocol selected by the server, which need not be on the
+       * list that the client sent; if this is the empty string, the server ignores the
+       * client ALPN extension. Default return value is empty string.
+       */
+       virtual std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos);
+
+       /**
+       * Optional callback: examine/modify Extensions before sending.
+       *
+       * Both client and server will call this callback on the Extensions object
+       * before serializing it in the client/server hellos. This allows an
+       * application to modify which extensions are sent during the
+       * handshake.
+       *
+       * Default implementation does nothing.
+       *
+       * @param extn the extensions
+       * @param which_side will be CLIENT or SERVER which is the current
+       * applications role in the exchange.
+       */
+       virtual void tls_modify_extensions(Extensions& extn, Connection_Side which_side);
+
+       /**
+       * Optional callback: examine peer extensions.
+       *
+       * Both client and server will call this callback with the Extensions
+       * object after receiving it from the peer. This allows examining the
+       * Extensions, for example to implement a custom extension. It also allows
+       * an application to require that a particular extension be implemented;
+       * throw an exception from this function to abort the handshake.
+       *
+       * Default implementation does nothing.
+       *
+       * @param extn the extensions
+       * @param which_side will be CLIENT if these are are the clients extensions (ie we are
+       *        the server) or SERVER if these are the server extensions (we are the client).
+       */
+       virtual void tls_examine_extensions(const Extensions& extn, Connection_Side which_side);
+
+       /**
+       * Optional callback: decode TLS group ID
+       *
+       * TLS uses a 16-bit field to identify ECC and DH groups. This callback
+       * handles the decoding. You only need to implement this if you are using
+       * a custom ECC or DH group (this is extremely uncommon).
+       *
+       * Default implementation uses the standard (IETF-defined) mappings.
+       */
+       virtual std::string tls_decode_group_param(Group_Params group_param);
+
+       /**
+       * Optional callback: return peer network identity
+       *
+       * There is no expected or specified format. The only expectation is this
+       * function will return a unique value. For example returning the peer
+       * host IP and port.
+       *
+       * This is used to bind the DTLS cookie to a particular network identity.
+       * It is only called if the dtls-cookie-secret PSK is also defined.
+       */
+       virtual std::string tls_peer_network_identity();
+
+       /**
+       * Optional callback: error logging. (not currently called)
+       * @param err An error message related to this connection.
+       */
+       virtual void tls_log_error(const char* err)
+          {
+          BOTAN_UNUSED(err);
+          }
+
+       /**
+       * Optional callback: debug logging. (not currently called)
+       * @param what Some hopefully informative string
+       */
+       virtual void tls_log_debug(const char* what)
+          {
+          BOTAN_UNUSED(what);
+          }
+
+       /**
+       * Optional callback: debug logging taking a buffer. (not currently called)
+       * @param descr What this buffer is
+       * @param val the bytes
+       * @param val_len length of val
+       */
+       virtual void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len)
+          {
+          BOTAN_UNUSED(descr, val, val_len);
+          }
+   };
+
+/**
+* TLS::Callbacks using std::function for compatability with the old API signatures.
+* This type is only provided for backward compatibility.
+* New implementations should derive from TLS::Callbacks instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Compat_Callbacks final : public Callbacks
+   {
+   public:
+      typedef std::function<void (const uint8_t[], size_t)> output_fn;
+      typedef std::function<void (const uint8_t[], size_t)> data_cb;
+      typedef std::function<void (Alert, const uint8_t[], size_t)> alert_cb;
+      typedef std::function<bool (const Session&)> handshake_cb;
+      typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
+      typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn;
+
+      /**
+       * @param data_output_fn is called with data for the outbound socket
+       *
+       * @param app_data_cb is called when new application data is received
+       *
+       * @param recv_alert_cb is called when a TLS alert is received
+       *
+       * @param hs_cb is called when a handshake is completed
+       *
+       * @param hs_msg_cb is called for each handshake message received
+       *
+       * @param next_proto is called with ALPN protocol data sent by the client
+       */
+       BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+       Compat_Callbacks(output_fn data_output_fn, data_cb app_data_cb, alert_cb recv_alert_cb,
+                        handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr,
+                        next_protocol_fn next_proto = nullptr)
+          : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+            m_alert_cb(std::bind(recv_alert_cb, std::placeholders::_1, nullptr, 0)),
+            m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+       BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+       Compat_Callbacks(output_fn data_output_fn, data_cb app_data_cb,
+                        std::function<void (Alert)> recv_alert_cb,
+                        handshake_cb hs_cb,
+                        handshake_msg_cb hs_msg_cb = nullptr,
+                        next_protocol_fn next_proto = nullptr)
+          : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+            m_alert_cb(recv_alert_cb),
+            m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+       enum class SILENCE_DEPRECATION_WARNING { PLEASE = 0 };
+       Compat_Callbacks(SILENCE_DEPRECATION_WARNING,
+                        output_fn data_output_fn, data_cb app_data_cb,
+                        std::function<void (Alert)> recv_alert_cb,
+                        handshake_cb hs_cb,
+                        handshake_msg_cb hs_msg_cb = nullptr,
+                        next_protocol_fn next_proto = nullptr)
+          : m_output_function(data_output_fn),
+            m_app_data_cb(app_data_cb),
+            m_alert_cb(recv_alert_cb),
+            m_hs_cb(hs_cb),
+            m_hs_msg_cb(hs_msg_cb),
+            m_next_proto(next_proto) {}
+
+       Compat_Callbacks(SILENCE_DEPRECATION_WARNING,
+                        output_fn data_output_fn, data_cb app_data_cb, alert_cb recv_alert_cb,
+                        handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr,
+                        next_protocol_fn next_proto = nullptr)
+          : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+            m_alert_cb(std::bind(recv_alert_cb, std::placeholders::_1, nullptr, 0)),
+            m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+
+       void tls_emit_data(const uint8_t data[], size_t size) override
+          {
+          BOTAN_ASSERT(m_output_function != nullptr,
+                       "Invalid TLS output function callback.");
+          m_output_function(data, size);
+          }
+
+       void tls_record_received(uint64_t /*seq_no*/, const uint8_t data[], size_t size) override
+          {
+          BOTAN_ASSERT(m_app_data_cb != nullptr,
+                       "Invalid TLS app data callback.");
+          m_app_data_cb(data, size);
+          }
+
+       void tls_alert(Alert alert) override
+          {
+          BOTAN_ASSERT(m_alert_cb != nullptr,
+                       "Invalid TLS alert callback.");
+          m_alert_cb(alert);
+          }
+
+       bool tls_session_established(const Session& session) override
+          {
+          BOTAN_ASSERT(m_hs_cb != nullptr,
+                       "Invalid TLS handshake callback.");
+          return m_hs_cb(session);
+          }
+
+       std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+          {
+          if(m_next_proto != nullptr) { return m_next_proto(client_protos); }
+          return "";
+          }
+
+       void tls_inspect_handshake_msg(const Handshake_Message& hmsg) override
+          {
+          // The handshake message callback is optional so we can
+          // not assume it has been set.
+          if(m_hs_msg_cb != nullptr) { m_hs_msg_cb(hmsg); }
+          }
+
+    private:
+         const output_fn m_output_function;
+         const data_cb m_app_data_cb;
+         const std::function<void (Alert)> m_alert_cb;
+         const handshake_cb m_hs_cb;
+         const handshake_msg_cb m_hs_msg_cb;
+         const next_protocol_fn m_next_proto;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp b/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp
@@ -0,0 +1,499 @@
+/*
+* TLS CBC Record Handling
+* (C) 2012,2013,2014,2015,2016,2020 Jack Lloyd
+* (C) 2016 Juraj Somorovsky
+* (C) 2016 Matthias Gierlings
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_cbc.h>
+#include <botan/cbc.h>
+
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* TLS_CBC_HMAC_AEAD_Mode Constructor
+*/
+TLS_CBC_HMAC_AEAD_Mode::TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir dir,
+                                               std::unique_ptr<BlockCipher> cipher,
+                                               std::unique_ptr<MessageAuthenticationCode> mac,
+                                               size_t cipher_keylen,
+                                               size_t mac_keylen,
+                                               Protocol_Version version,
+                                               bool use_encrypt_then_mac) :
+   m_cipher_name(cipher->name()),
+   m_mac_name(mac->name()),
+   m_cipher_keylen(cipher_keylen),
+   m_mac_keylen(mac_keylen),
+   m_use_encrypt_then_mac(use_encrypt_then_mac)
+   {
+   m_tag_size = mac->output_length();
+   m_block_size = cipher->block_size();
+
+   m_iv_size = version.supports_explicit_cbc_ivs() ? m_block_size : 0;
+
+   m_is_datagram = version.is_datagram_protocol();
+
+   m_mac = std::move(mac);
+
+   if(dir == ENCRYPTION)
+      m_cbc.reset(new CBC_Encryption(cipher.release(), new Null_Padding));
+   else
+      m_cbc.reset(new CBC_Decryption(cipher.release(), new Null_Padding));
+   }
+
+void TLS_CBC_HMAC_AEAD_Mode::clear()
+   {
+   cbc().clear();
+   mac().clear();
+   reset();
+   }
+
+void TLS_CBC_HMAC_AEAD_Mode::reset()
+   {
+   cbc_state().clear();
+   m_ad.clear();
+   m_msg.clear();
+   }
+
+std::string TLS_CBC_HMAC_AEAD_Mode::name() const
+   {
+   return "TLS_CBC(" + m_cipher_name + "," + m_mac_name + ")";
+   }
+
+size_t TLS_CBC_HMAC_AEAD_Mode::update_granularity() const
+   {
+   return 1; // just buffers anyway
+   }
+
+bool TLS_CBC_HMAC_AEAD_Mode::valid_nonce_length(size_t nl) const
+   {
+   if(m_cbc_state.empty())
+      return nl == block_size();
+   return nl == iv_size();
+   }
+
+Key_Length_Specification TLS_CBC_HMAC_AEAD_Mode::key_spec() const
+   {
+   return Key_Length_Specification(m_cipher_keylen + m_mac_keylen);
+   }
+
+void TLS_CBC_HMAC_AEAD_Mode::key_schedule(const uint8_t key[], size_t keylen)
+   {
+   // Both keys are of fixed length specified by the ciphersuite
+
+   if(keylen != m_cipher_keylen + m_mac_keylen)
+      throw Invalid_Key_Length(name(), keylen);
+
+   mac().set_key(&key[0], m_mac_keylen);
+   cbc().set_key(&key[m_mac_keylen], m_cipher_keylen);
+   }
+
+void TLS_CBC_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+   {
+   if(!valid_nonce_length(nonce_len))
+      {
+      throw Invalid_IV_Length(name(), nonce_len);
+      }
+
+   m_msg.clear();
+
+   if(nonce_len > 0)
+      {
+      m_cbc_state.assign(nonce, nonce + nonce_len);
+      }
+   }
+
+size_t TLS_CBC_HMAC_AEAD_Mode::process(uint8_t buf[], size_t sz)
+   {
+   m_msg.insert(m_msg.end(), buf, buf + sz);
+   return 0;
+   }
+
+std::vector<uint8_t> TLS_CBC_HMAC_AEAD_Mode::assoc_data_with_len(uint16_t len)
+   {
+   std::vector<uint8_t> ad = m_ad;
+   BOTAN_ASSERT(ad.size() == 13, "Expected AAD size");
+   ad[11] = get_byte(0, len);
+   ad[12] = get_byte(1, len);
+   return ad;
+   }
+
+void TLS_CBC_HMAC_AEAD_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+   {
+   if(ad_len != 13)
+      throw Invalid_Argument("Invalid TLS AEAD associated data length");
+   m_ad.assign(ad, ad + ad_len);
+   }
+
+void TLS_CBC_HMAC_AEAD_Encryption::set_associated_data(const uint8_t ad[], size_t ad_len)
+   {
+   TLS_CBC_HMAC_AEAD_Mode::set_associated_data(ad, ad_len);
+
+   if(use_encrypt_then_mac())
+      {
+      // AAD hack for EtM
+      // EtM uses ciphertext size instead of plaintext size for AEAD input
+      const uint16_t pt_size = make_uint16(assoc_data()[11], assoc_data()[12]);
+      const uint16_t enc_size = static_cast<uint16_t>(round_up(iv_size() + pt_size + 1, block_size()));
+      assoc_data()[11] = get_byte<uint16_t>(0, enc_size);
+      assoc_data()[12] = get_byte<uint16_t>(1, enc_size);
+      }
+   }
+
+void TLS_CBC_HMAC_AEAD_Encryption::cbc_encrypt_record(
+   secure_vector<uint8_t>& buffer, size_t offset, size_t padding_length)
+   {
+   // We always do short padding:
+   BOTAN_ASSERT_NOMSG(padding_length <= 16);
+
+   buffer.resize(buffer.size() + padding_length);
+
+   const uint8_t padding_val = static_cast<uint8_t>(padding_length - 1);
+
+   CT::poison(&padding_val, 1);
+   CT::poison(&padding_length, 1);
+   CT::poison(buffer.data(), buffer.size());
+
+   const size_t last_block_starts = buffer.size() - block_size();
+   const size_t padding_starts = buffer.size() - padding_length;
+   for(size_t i = last_block_starts; i != buffer.size(); ++i)
+      {
+      auto add_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, padding_starts));
+      buffer[i] = add_padding.select(padding_val, buffer[i]);
+      }
+
+   CT::unpoison(padding_val);
+   CT::unpoison(padding_length);
+   CT::unpoison(buffer.data(), buffer.size());
+
+   cbc().start(cbc_state());
+   cbc().process(&buffer[offset], buffer.size() - offset);
+
+   cbc_state().assign(buffer.data() + (buffer.size() - block_size()), buffer.data() + buffer.size());
+   }
+
+size_t TLS_CBC_HMAC_AEAD_Encryption::output_length(size_t input_length) const
+   {
+   return round_up(input_length + 1 + (use_encrypt_then_mac() ? 0 : tag_size()), block_size()) +
+      (use_encrypt_then_mac() ? tag_size() : 0);
+   }
+
+void TLS_CBC_HMAC_AEAD_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   update(buffer, offset);
+
+   const size_t msg_size = msg().size();
+
+   const size_t input_size = msg_size + 1 + (use_encrypt_then_mac() ? 0 : tag_size());
+   const size_t enc_size = round_up(input_size, block_size());
+   BOTAN_DEBUG_ASSERT(enc_size % block_size() == 0);
+
+   const uint8_t padding_val = static_cast<uint8_t>(enc_size - input_size);
+   const size_t padding_length = static_cast<size_t>(padding_val) + 1;
+
+   buffer.reserve(offset + msg_size + padding_length + tag_size());
+   buffer.resize(offset + msg_size);
+   copy_mem(&buffer[offset], msg().data(), msg_size);
+
+   mac().update(assoc_data());
+
+   if(use_encrypt_then_mac())
+      {
+      if(iv_size() > 0)
+         {
+         mac().update(cbc_state());
+         }
+
+      cbc_encrypt_record(buffer, offset, padding_length);
+      mac().update(&buffer[offset], enc_size);
+      buffer.resize(buffer.size() + tag_size());
+      mac().final(&buffer[buffer.size() - tag_size()]);
+      }
+   else
+      {
+      mac().update(&buffer[offset], msg_size);
+      buffer.resize(buffer.size() + tag_size());
+      mac().final(&buffer[buffer.size() - tag_size()]);
+      cbc_encrypt_record(buffer, offset, padding_length);
+      }
+   }
+
+/*
+* Checks the TLS padding. Returns 0 if the padding is invalid (we
+* count the padding_length field as part of the padding size so a
+* valid padding will always be at least one byte long), or the length
+* of the padding otherwise. This is actually padding_length + 1
+* because both the padding and padding_length fields are padding from
+* our perspective.
+*
+* Returning 0 in the error case should ensure the MAC check will fail.
+* This approach is suggested in section 6.2.3.2 of RFC 5246.
+*/
+uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len)
+   {
+   if(record_len == 0 || record_len > 0xFFFF)
+      return 0;
+
+   const uint16_t rec16 = static_cast<uint16_t>(record_len);
+
+   /*
+   * TLS v1.0 and up require all the padding bytes be the same value
+   * and allows up to 255 bytes.
+   */
+
+   const uint16_t to_check = std::min<uint16_t>(256, static_cast<uint16_t>(record_len));
+   const uint8_t pad_byte = record[record_len-1];
+   const uint16_t pad_bytes = 1 + pad_byte;
+
+   auto pad_invalid = CT::Mask<uint16_t>::is_lt(rec16, pad_bytes);
+
+   for(uint16_t i = rec16 - to_check; i != rec16; ++i)
+      {
+      const uint16_t offset = rec16 - i;
+      const auto in_pad_range = CT::Mask<uint16_t>::is_lte(offset, pad_bytes);
+      const auto pad_correct = CT::Mask<uint16_t>::is_equal(record[i], pad_byte);
+      pad_invalid |= in_pad_range & ~pad_correct;
+      }
+
+   return pad_invalid.if_not_set_return(pad_bytes);
+   }
+
+void TLS_CBC_HMAC_AEAD_Decryption::cbc_decrypt_record(uint8_t record_contents[], size_t record_len)
+   {
+   if(record_len == 0 || record_len % block_size() != 0)
+      throw Decoding_Error("Received TLS CBC ciphertext with invalid length");
+
+   cbc().start(cbc_state());
+   cbc_state().assign(record_contents + record_len - block_size(),
+                      record_contents + record_len);
+
+   cbc().process(record_contents, record_len);
+   }
+
+size_t TLS_CBC_HMAC_AEAD_Decryption::output_length(size_t) const
+   {
+   /*
+   * We don't know this because the padding is arbitrary
+   */
+   return 0;
+   }
+
+/*
+* This function performs additional compression calls in order
+* to protect from the Lucky 13 attack. It adds new compression
+* function calls over dummy data, by computing additional HMAC updates.
+*
+* The countermeasure was described (in a similar way) in the Lucky 13 paper.
+*
+* Background:
+* - One SHA-1/SHA-256 compression is performed with 64 bytes of data.
+* - HMAC adds 8 byte length field and padding (at least 1 byte) so that we have:
+*   - 0 - 55 bytes: 1 compression
+*   - 56 - 55+64 bytes: 2 compressions
+*   - 56+64 - 55+2*64 bytes: 3 compressions ...
+* - For SHA-384, this works similarly, but we have 128 byte blocks and 16 byte
+*   long length field. This results in:
+*   - 0 - 111 bytes: 1 compression
+*   - 112 - 111+128 bytes: 2 compressions ...
+*
+* The implemented countermeasure works as follows:
+* 1) It computes max_compressions: number of maximum compressions performed on
+*    the decrypted data
+* 2) It computes current_compressions: number of compressions performed on the
+*    decrypted data, after padding has been removed
+* 3) If current_compressions != max_compressions: It invokes an HMAC update
+*    over dummy data so that (max_compressions - current_compressions)
+*    compressions are performed. Otherwise, it invokes an HMAC update so that
+*    no compressions are performed.
+*
+* Note that the padding validation in Botan is always performed over
+* min(plen,256) bytes, see the function check_tls_cbc_padding. This differs
+* from the countermeasure described in the paper.
+*
+* Note that the padding length padlen does also count the last byte
+* of the decrypted plaintext. This is different from the Lucky 13 paper.
+*
+* This countermeasure leaves a difference of about 100 clock cycles (in
+* comparison to >1000 clock cycles observed without it).
+*
+* plen represents the length of the decrypted plaintext message P
+* padlen represents the padding length
+*
+*/
+void TLS_CBC_HMAC_AEAD_Decryption::perform_additional_compressions(size_t plen, size_t padlen)
+   {
+   uint16_t block_size;
+   uint16_t max_bytes_in_first_block;
+   if(mac().name() == "HMAC(SHA-384)")
+      {
+      block_size = 128;
+      max_bytes_in_first_block = 111;
+      }
+   else
+      {
+      block_size = 64;
+      max_bytes_in_first_block = 55;
+      }
+   // number of maximum MACed bytes
+   const uint16_t L1 = static_cast<uint16_t>(13 + plen - tag_size());
+   // number of current MACed bytes (L1 - padlen)
+   // Here the Lucky 13 paper is different because the padlen length in the paper
+   // does not count the last message byte.
+   const uint16_t L2 = static_cast<uint16_t>(13 + plen - padlen - tag_size());
+   // From the paper, for SHA-256/SHA-1 compute: ceil((L1-55)/64) and ceil((L2-55)/64)
+   // ceil((L1-55)/64) = floor((L1+64-1-55)/64)
+   // Here we compute number of compressions for SHA-* in general
+   const uint16_t max_compresssions = ( (L1 + block_size - 1 - max_bytes_in_first_block) / block_size);
+   const uint16_t current_compressions = ((L2 + block_size - 1 - max_bytes_in_first_block) / block_size);
+   // number of additional compressions we have to perform
+   const uint16_t add_compressions = max_compresssions - current_compressions;
+   const uint16_t equal = CT::Mask<uint16_t>::is_equal(max_compresssions, current_compressions).if_set_return(1);
+   // We compute the data length we need to achieve the number of compressions.
+   // If there are no compressions, we just add 55/111 dummy bytes so that no
+   // compression is performed.
+   const uint16_t data_len = block_size * add_compressions + equal * max_bytes_in_first_block;
+   std::vector<uint8_t> data(data_len);
+   mac().update(data);
+   // we do not need to clear the MAC since the connection is broken anyway
+   }
+
+void TLS_CBC_HMAC_AEAD_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+   {
+   update(buffer, offset);
+   buffer.resize(offset);
+
+   const size_t record_len = msg().size();
+   uint8_t* record_contents = msg().data();
+
+   // This early exit does not leak info because all the values compared are public
+   if(record_len < tag_size() ||
+      (record_len - (use_encrypt_then_mac() ? tag_size() : 0)) % block_size() != 0)
+      {
+      throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+      }
+
+   if(use_encrypt_then_mac())
+      {
+      const size_t enc_size = record_len - tag_size();
+      const size_t enc_iv_size = enc_size + iv_size();
+
+      BOTAN_ASSERT_NOMSG(enc_iv_size <= 0xFFFF);
+
+      mac().update(assoc_data_with_len(static_cast<uint16_t>(enc_iv_size)));
+      if(iv_size() > 0)
+         {
+         mac().update(cbc_state());
+         }
+      mac().update(record_contents, enc_size);
+
+      std::vector<uint8_t> mac_buf(tag_size());
+      mac().final(mac_buf.data());
+
+      const size_t mac_offset = enc_size;
+
+      const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+
+      if(!mac_ok)
+         {
+         throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+         }
+
+      cbc_decrypt_record(record_contents, enc_size);
+
+      // 0 if padding was invalid, otherwise 1 + padding_bytes
+      const uint16_t pad_size = check_tls_cbc_padding(record_contents, enc_size);
+
+      // No oracle here, whoever sent us this had the key since MAC check passed
+      if(pad_size == 0)
+         {
+         throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+         }
+
+      const uint8_t* plaintext_block = &record_contents[0];
+      const size_t plaintext_length = enc_size - pad_size;
+
+      buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
+      }
+   else
+      {
+      cbc_decrypt_record(record_contents, record_len);
+
+      CT::poison(record_contents, record_len);
+
+      // 0 if padding was invalid, otherwise 1 + padding_bytes
+      uint16_t pad_size = check_tls_cbc_padding(record_contents, record_len);
+
+      /*
+      This mask is zero if there is not enough room in the packet to get a valid MAC.
+
+      We have to accept empty packets, since otherwise we are not compatible
+      with how OpenSSL's countermeasure for fixing BEAST in TLS 1.0 CBC works
+      (sending empty records, instead of 1/(n-1) splitting)
+      */
+
+      // We know the cast cannot overflow as pad_size <= 256 && tag_size <= 32
+      const auto size_ok_mask = CT::Mask<uint16_t>::is_lte(
+         static_cast<uint16_t>(tag_size() + pad_size),
+         static_cast<uint16_t>(record_len));
+
+      pad_size = size_ok_mask.if_set_return(pad_size);
+
+      CT::unpoison(record_contents, record_len);
+
+      /*
+      This is unpoisoned sooner than it should. The pad_size leaks to plaintext_length and
+      then to the timing channel in the MAC computation described in the Lucky 13 paper.
+      */
+      CT::unpoison(pad_size);
+
+      const uint8_t* plaintext_block = &record_contents[0];
+      const uint16_t plaintext_length = static_cast<uint16_t>(record_len - tag_size() - pad_size);
+
+      mac().update(assoc_data_with_len(plaintext_length));
+      mac().update(plaintext_block, plaintext_length);
+
+      std::vector<uint8_t> mac_buf(tag_size());
+      mac().final(mac_buf.data());
+
+      const size_t mac_offset = record_len - (tag_size() + pad_size);
+
+      const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+
+      const auto ok_mask = size_ok_mask & CT::Mask<uint16_t>::expand(mac_ok) & CT::Mask<uint16_t>::expand(pad_size);
+
+      CT::unpoison(ok_mask);
+
+      if(ok_mask.is_set())
+         {
+         buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
+         }
+      else
+         {
+         perform_additional_compressions(record_len, pad_size);
+
+         /*
+         * In DTLS case we have to finish computing the MAC since we require the
+         * MAC state be reset for future packets. This extra timing channel may
+         * be exploitable in a Lucky13 variant.
+         */
+         if(is_datagram_protocol())
+            mac().final(mac_buf);
+         throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+         }
+      }
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h b/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h
@@ -0,0 +1,186 @@
+/*
+* TLS CBC+HMAC AEAD
+* (C) 2016 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CBC_HMAC_AEAD_H_
+#define BOTAN_TLS_CBC_HMAC_AEAD_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+#include <botan/mac.h>
+#include <botan/tls_version.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS CBC+HMAC AEAD base class (GenericBlockCipher in TLS spec)
+* This is the weird TLS-specific mode, not for general consumption.
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode
+   {
+   public:
+      size_t process(uint8_t buf[], size_t sz) override final;
+
+      std::string name() const override final;
+
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      size_t update_granularity() const override final;
+
+      Key_Length_Specification key_spec() const override final;
+
+      bool valid_nonce_length(size_t nl) const override final;
+
+      size_t tag_size() const override final { return m_tag_size; }
+
+      size_t default_nonce_length() const override final { return m_iv_size; }
+
+      void clear() override final;
+
+      void reset() override final;
+
+ protected:
+      TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir direction,
+                             std::unique_ptr<BlockCipher> cipher,
+                             std::unique_ptr<MessageAuthenticationCode> mac,
+                             size_t cipher_keylen,
+                             size_t mac_keylen,
+                             Protocol_Version version,
+                             bool use_encrypt_then_mac);
+
+      size_t cipher_keylen() const { return m_cipher_keylen; }
+      size_t mac_keylen() const { return m_mac_keylen; }
+      size_t iv_size() const { return m_iv_size; }
+      size_t block_size() const { return m_block_size; }
+
+      bool use_encrypt_then_mac() const { return m_use_encrypt_then_mac; }
+
+      bool is_datagram_protocol() const { return m_is_datagram; }
+
+      Cipher_Mode& cbc() const { return *m_cbc; }
+
+      MessageAuthenticationCode& mac() const
+         {
+         BOTAN_ASSERT_NONNULL(m_mac);
+         return *m_mac;
+         }
+
+      secure_vector<uint8_t>& cbc_state() { return m_cbc_state; }
+      std::vector<uint8_t>& assoc_data() { return m_ad; }
+      secure_vector<uint8_t>& msg() { return m_msg; }
+
+      std::vector<uint8_t> assoc_data_with_len(uint16_t len);
+
+   private:
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
+
+      void key_schedule(const uint8_t key[], size_t length) override final;
+
+      const std::string m_cipher_name;
+      const std::string m_mac_name;
+      size_t m_cipher_keylen;
+      size_t m_mac_keylen;
+      size_t m_iv_size;
+      size_t m_tag_size;
+      size_t m_block_size;
+      bool m_use_encrypt_then_mac;
+      bool m_is_datagram;
+
+      std::unique_ptr<Cipher_Mode> m_cbc;
+      std::unique_ptr<MessageAuthenticationCode> m_mac;
+
+      secure_vector<uint8_t> m_cbc_state;
+      std::vector<uint8_t> m_ad;
+      secure_vector<uint8_t> m_msg;
+   };
+
+/**
+* TLS_CBC_HMAC_AEAD Encryption
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Encryption final : public TLS_CBC_HMAC_AEAD_Mode
+   {
+   public:
+      /**
+      */
+      TLS_CBC_HMAC_AEAD_Encryption(
+                             std::unique_ptr<BlockCipher> cipher,
+                             std::unique_ptr<MessageAuthenticationCode> mac,
+                             const size_t cipher_keylen,
+                             const size_t mac_keylen,
+                             const Protocol_Version version,
+                             bool use_encrypt_then_mac) :
+         TLS_CBC_HMAC_AEAD_Mode(ENCRYPTION,
+                                std::move(cipher),
+                                std::move(mac),
+                                cipher_keylen,
+                                mac_keylen,
+                                version,
+                                use_encrypt_then_mac)
+         {}
+
+      void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+      size_t output_length(size_t input_length) const override;
+
+      size_t minimum_final_size() const override { return 0; }
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+   private:
+      void cbc_encrypt_record(secure_vector<uint8_t>& buffer, size_t offset,
+                              size_t padding_length);
+   };
+
+/**
+* TLS_CBC_HMAC_AEAD Decryption
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Decryption final : public TLS_CBC_HMAC_AEAD_Mode
+   {
+   public:
+      /**
+      */
+      TLS_CBC_HMAC_AEAD_Decryption(std::unique_ptr<BlockCipher> cipher,
+                                   std::unique_ptr<MessageAuthenticationCode> mac,
+                                   const size_t cipher_keylen,
+                                   const size_t mac_keylen,
+                                   const Protocol_Version version,
+                                   bool use_encrypt_then_mac) :
+         TLS_CBC_HMAC_AEAD_Mode(DECRYPTION,
+                                std::move(cipher),
+                                std::move(mac),
+                                cipher_keylen,
+                                mac_keylen,
+                                version,
+                                use_encrypt_then_mac)
+         {}
+
+      size_t output_length(size_t input_length) const override;
+
+      size_t minimum_final_size() const override { return tag_size(); }
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+   private:
+      void cbc_decrypt_record(uint8_t record_contents[], size_t record_len);
+
+      void perform_additional_compressions(size_t plen, size_t padlen);
+   };
+
+/**
+* Check the TLS padding of a record
+* @param record the record bits
+* @param record_len length of record
+* @return 0 if padding is invalid, otherwise padding_bytes + 1
+*/
+BOTAN_TEST_API uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_channel.cpp b/third_party/botan/src/lib/tls/tls_channel.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_channel.cpp
@@ -0,0 +1,795 @@
+/*
+* TLS Channels
+* (C) 2011,2012,2014,2015,2016 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_record.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/stl_util.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace TLS {
+
+size_t TLS::Channel::IO_BUF_DEFAULT_SIZE = 10*1024;
+
+Channel::Channel(Callbacks& callbacks,
+                 Session_Manager& session_manager,
+                 RandomNumberGenerator& rng,
+                 const Policy& policy,
+                 bool is_server,
+                 bool is_datagram,
+                 size_t reserved_io_buffer_size) :
+   m_is_server(is_server),
+   m_is_datagram(is_datagram),
+   m_callbacks(callbacks),
+   m_session_manager(session_manager),
+   m_policy(policy),
+   m_rng(rng),
+   m_has_been_closed(false)
+   {
+   init(reserved_io_buffer_size);
+   }
+
+Channel::Channel(output_fn out,
+                 data_cb app_data_cb,
+                 alert_cb recv_alert_cb,
+                 handshake_cb hs_cb,
+                 handshake_msg_cb hs_msg_cb,
+                 Session_Manager& session_manager,
+                 RandomNumberGenerator& rng,
+                 const Policy& policy,
+                 bool is_server,
+                 bool is_datagram,
+                 size_t io_buf_sz) :
+    m_is_server(is_server),
+    m_is_datagram(is_datagram),
+    m_compat_callbacks(new Compat_Callbacks(
+                          /*
+                          this Channel constructor is also deprecated so its ok that it
+                          relies on a deprecated API
+                          */
+                          Compat_Callbacks::SILENCE_DEPRECATION_WARNING::PLEASE,
+                          out, app_data_cb, recv_alert_cb, hs_cb, hs_msg_cb)),
+    m_callbacks(*m_compat_callbacks.get()),
+    m_session_manager(session_manager),
+    m_policy(policy),
+    m_rng(rng),
+    m_has_been_closed(false)
+    {
+    init(io_buf_sz);
+    }
+
+void Channel::init(size_t io_buf_sz)
+   {
+   /* epoch 0 is plaintext, thus null cipher state */
+   m_write_cipher_states[0] = nullptr;
+   m_read_cipher_states[0] = nullptr;
+
+   m_writebuf.reserve(io_buf_sz);
+   m_readbuf.reserve(io_buf_sz);
+   }
+
+void Channel::reset_state()
+   {
+   m_active_state.reset();
+   m_pending_state.reset();
+   m_readbuf.clear();
+   m_write_cipher_states.clear();
+   m_read_cipher_states.clear();
+   }
+
+void Channel::reset_active_association_state()
+   {
+   // This operation only makes sense for DTLS
+   BOTAN_ASSERT_NOMSG(m_is_datagram);
+   m_active_state.reset();
+   m_read_cipher_states.clear();
+   m_write_cipher_states.clear();
+
+   m_write_cipher_states[0] = nullptr;
+   m_read_cipher_states[0] = nullptr;
+
+   if(m_sequence_numbers)
+      m_sequence_numbers->reset();
+   }
+
+Channel::~Channel()
+   {
+   // So unique_ptr destructors run correctly
+   }
+
+Connection_Sequence_Numbers& Channel::sequence_numbers() const
+   {
+   BOTAN_ASSERT(m_sequence_numbers, "Have a sequence numbers object");
+   return *m_sequence_numbers;
+   }
+
+std::shared_ptr<Connection_Cipher_State> Channel::read_cipher_state_epoch(uint16_t epoch) const
+   {
+   auto i = m_read_cipher_states.find(epoch);
+   if(i == m_read_cipher_states.end())
+      throw Internal_Error("TLS::Channel No read cipherstate for epoch " + std::to_string(epoch));
+   return i->second;
+   }
+
+std::shared_ptr<Connection_Cipher_State> Channel::write_cipher_state_epoch(uint16_t epoch) const
+   {
+   auto i = m_write_cipher_states.find(epoch);
+   if(i == m_write_cipher_states.end())
+      throw Internal_Error("TLS::Channel No write cipherstate for epoch " + std::to_string(epoch));
+   return i->second;
+   }
+
+std::vector<X509_Certificate> Channel::peer_cert_chain() const
+   {
+   if(auto active = active_state())
+      return get_peer_cert_chain(*active);
+   return std::vector<X509_Certificate>();
+   }
+
+bool Channel::save_session(const Session& session)
+   {
+   return callbacks().tls_session_established(session);
+   }
+
+Handshake_State& Channel::create_handshake_state(Protocol_Version version)
+   {
+   if(pending_state())
+      throw Internal_Error("create_handshake_state called during handshake");
+
+   if(auto active = active_state())
+      {
+      Protocol_Version active_version = active->version();
+
+      if(active_version.is_datagram_protocol() != version.is_datagram_protocol())
+         {
+         throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                             "Active state using version " + active_version.to_string() +
+                             " cannot change to " + version.to_string() + " in pending");
+         }
+      }
+
+   if(!m_sequence_numbers)
+      {
+      if(version.is_datagram_protocol())
+         m_sequence_numbers.reset(new Datagram_Sequence_Numbers);
+      else
+         m_sequence_numbers.reset(new Stream_Sequence_Numbers);
+      }
+
+   using namespace std::placeholders;
+
+   std::unique_ptr<Handshake_IO> io;
+   if(version.is_datagram_protocol())
+      {
+      io.reset(new Datagram_Handshake_IO(
+                  std::bind(&Channel::send_record_under_epoch, this, _1, _2, _3),
+                  sequence_numbers(),
+                  static_cast<uint16_t>(m_policy.dtls_default_mtu()),
+                  m_policy.dtls_initial_timeout(),
+                  m_policy.dtls_maximum_timeout()));
+      }
+   else
+      {
+      io.reset(new Stream_Handshake_IO(std::bind(&Channel::send_record, this, _1, _2)));
+      }
+
+   m_pending_state.reset(new_handshake_state(io.release()));
+
+   if(auto active = active_state())
+      m_pending_state->set_version(active->version());
+
+   return *m_pending_state.get();
+   }
+
+bool Channel::timeout_check()
+   {
+   if(m_pending_state)
+      return m_pending_state->handshake_io().timeout_check();
+
+   //FIXME: scan cipher suites and remove epochs older than 2*MSL
+   return false;
+   }
+
+void Channel::renegotiate(bool force_full_renegotiation)
+   {
+   if(pending_state()) // currently in handshake?
+      return;
+
+   if(auto active = active_state())
+      {
+      if(force_full_renegotiation == false)
+         force_full_renegotiation = !policy().allow_resumption_for_renegotiation();
+
+      initiate_handshake(create_handshake_state(active->version()),
+                         force_full_renegotiation);
+      }
+   else
+      throw Invalid_State("Cannot renegotiate on inactive connection");
+   }
+
+void Channel::change_cipher_spec_reader(Connection_Side side)
+   {
+   auto pending = pending_state();
+
+   BOTAN_ASSERT(pending && pending->server_hello(),
+                "Have received server hello");
+
+   if(pending->server_hello()->compression_method() != 0)
+      throw Internal_Error("Negotiated unknown compression algorithm");
+
+   sequence_numbers().new_read_cipher_state();
+
+   const uint16_t epoch = sequence_numbers().current_read_epoch();
+
+   BOTAN_ASSERT(m_read_cipher_states.count(epoch) == 0,
+                "No read cipher state currently set for next epoch");
+
+   // flip side as we are reading
+   std::shared_ptr<Connection_Cipher_State> read_state(
+      new Connection_Cipher_State(pending->version(),
+                                  (side == CLIENT) ? SERVER : CLIENT,
+                                  false,
+                                  pending->ciphersuite(),
+                                  pending->session_keys(),
+                                  pending->server_hello()->supports_encrypt_then_mac()));
+
+   m_read_cipher_states[epoch] = read_state;
+   }
+
+void Channel::change_cipher_spec_writer(Connection_Side side)
+   {
+   auto pending = pending_state();
+
+   BOTAN_ASSERT(pending && pending->server_hello(),
+                "Have received server hello");
+
+   if(pending->server_hello()->compression_method() != 0)
+      throw Internal_Error("Negotiated unknown compression algorithm");
+
+   sequence_numbers().new_write_cipher_state();
+
+   const uint16_t epoch = sequence_numbers().current_write_epoch();
+
+   BOTAN_ASSERT(m_write_cipher_states.count(epoch) == 0,
+                "No write cipher state currently set for next epoch");
+
+   std::shared_ptr<Connection_Cipher_State> write_state(
+      new Connection_Cipher_State(pending->version(),
+                                  side,
+                                  true,
+                                  pending->ciphersuite(),
+                                  pending->session_keys(),
+                                  pending->server_hello()->supports_encrypt_then_mac()));
+
+   m_write_cipher_states[epoch] = write_state;
+   }
+
+bool Channel::is_active() const
+   {
+   if(is_closed())
+      return false;
+   return (active_state() != nullptr);
+   }
+
+bool Channel::is_closed() const
+   {
+   return m_has_been_closed;
+   }
+
+void Channel::activate_session()
+   {
+   std::swap(m_active_state, m_pending_state);
+   m_pending_state.reset();
+
+   if(!m_active_state->version().is_datagram_protocol())
+      {
+      // TLS is easy just remove all but the current state
+      const uint16_t current_epoch = sequence_numbers().current_write_epoch();
+
+      const auto not_current_epoch =
+         [current_epoch](uint16_t epoch) { return (epoch != current_epoch); };
+
+      map_remove_if(not_current_epoch, m_write_cipher_states);
+      map_remove_if(not_current_epoch, m_read_cipher_states);
+      }
+
+   callbacks().tls_session_activated();
+   }
+
+size_t Channel::received_data(const std::vector<uint8_t>& buf)
+   {
+   return this->received_data(buf.data(), buf.size());
+   }
+
+size_t Channel::received_data(const uint8_t input[], size_t input_size)
+   {
+   const bool allow_epoch0_restart = m_is_datagram && m_is_server && policy().allow_dtls_epoch0_restart();
+
+   try
+      {
+      while(input_size)
+         {
+         size_t consumed = 0;
+
+         auto get_epoch = [this](uint16_t epoch) { return read_cipher_state_epoch(epoch); };
+
+         const Record_Header record =
+            read_record(m_is_datagram,
+                        m_readbuf,
+                        input,
+                        input_size,
+                        consumed,
+                        m_record_buf,
+                        m_sequence_numbers.get(),
+                        get_epoch,
+                        allow_epoch0_restart);
+
+         const size_t needed = record.needed();
+
+         BOTAN_ASSERT(consumed > 0, "Got to eat something");
+
+         BOTAN_ASSERT(consumed <= input_size,
+                      "Record reader consumed sane amount");
+
+         input += consumed;
+         input_size -= consumed;
+
+         BOTAN_ASSERT(input_size == 0 || needed == 0,
+                      "Got a full record or consumed all input");
+
+         if(input_size == 0 && needed != 0)
+            return needed; // need more data to complete record
+
+         // Ignore invalid records in DTLS
+         if(m_is_datagram && record.type() == NO_RECORD)
+            {
+            return 0;
+            }
+
+         if(m_record_buf.size() > MAX_PLAINTEXT_SIZE)
+            throw TLS_Exception(Alert::RECORD_OVERFLOW,
+                                "TLS plaintext record is larger than allowed maximum");
+
+
+         const bool epoch0_restart = m_is_datagram && record.epoch() == 0 && active_state();
+         BOTAN_ASSERT_IMPLICATION(epoch0_restart, allow_epoch0_restart, "Allowed state");
+
+         const bool initial_record = epoch0_restart || (!pending_state() && !active_state());
+
+         if(record.type() != ALERT)
+            {
+            if(initial_record)
+               {
+               // For initial records just check for basic sanity
+               if(record.version().major_version() != 3 &&
+                  record.version().major_version() != 0xFE)
+                  {
+                  throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                      "Received unexpected record version in initial record");
+                  }
+               }
+            else if(auto pending = pending_state())
+               {
+               if(pending->server_hello() != nullptr && record.version() != pending->version())
+                  {
+                  if(record.version() != pending->version())
+                     {
+                     throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                         "Received unexpected record version");
+                     }
+                  }
+               }
+            else if(auto active = active_state())
+               {
+               if(record.version() != active->version())
+                  {
+                  throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                      "Received unexpected record version");
+                  }
+               }
+            }
+
+         if(record.type() == HANDSHAKE || record.type() == CHANGE_CIPHER_SPEC)
+            {
+            if(m_has_been_closed)
+               throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure");
+            process_handshake_ccs(m_record_buf, record.sequence(), record.type(), record.version(), epoch0_restart);
+            }
+         else if(record.type() == APPLICATION_DATA)
+            {
+            if(m_has_been_closed)
+               throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received application data after connection closure");
+            if(pending_state() != nullptr)
+               throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Can't interleave application and handshake data");
+            process_application_data(record.sequence(), m_record_buf);
+            }
+         else if(record.type() == ALERT)
+            {
+            process_alert(m_record_buf);
+            }
+         else if(record.type() != NO_RECORD)
+            throw Unexpected_Message("Unexpected record type " +
+                                     std::to_string(record.type()) +
+                                     " from counterparty");
+         }
+
+      return 0; // on a record boundary
+      }
+   catch(TLS_Exception& e)
+      {
+      send_fatal_alert(e.type());
+      throw;
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      send_fatal_alert(Alert::BAD_RECORD_MAC);
+      throw;
+      }
+   catch(Decoding_Error&)
+      {
+      send_fatal_alert(Alert::DECODE_ERROR);
+      throw;
+      }
+   catch(...)
+      {
+      send_fatal_alert(Alert::INTERNAL_ERROR);
+      throw;
+      }
+   }
+
+void Channel::process_handshake_ccs(const secure_vector<uint8_t>& record,
+                                    uint64_t record_sequence,
+                                    Record_Type record_type,
+                                    Protocol_Version record_version,
+                                    bool epoch0_restart)
+   {
+   if(!m_pending_state)
+      {
+      // No pending handshake, possibly new:
+      if(record_version.is_datagram_protocol() && !epoch0_restart)
+         {
+         if(m_sequence_numbers)
+            {
+            /*
+            * Might be a peer retransmit under epoch - 1 in which
+            * case we must retransmit last flight
+            */
+            sequence_numbers().read_accept(record_sequence);
+
+            const uint16_t epoch = record_sequence >> 48;
+
+            if(epoch == sequence_numbers().current_read_epoch())
+               {
+               create_handshake_state(record_version);
+               }
+            else if(epoch == sequence_numbers().current_read_epoch() - 1)
+               {
+               BOTAN_ASSERT(m_active_state, "Have active state here");
+               m_active_state->handshake_io().add_record(record.data(),
+                                                         record.size(),
+                                                         record_type,
+                                                         record_sequence);
+               }
+            }
+         else
+            {
+            create_handshake_state(record_version);
+            }
+         }
+      else
+         {
+         create_handshake_state(record_version);
+         }
+      }
+
+   // May have been created in above conditional
+   if(m_pending_state)
+      {
+      m_pending_state->handshake_io().add_record(record.data(),
+                                                 record.size(),
+                                                 record_type,
+                                                 record_sequence);
+
+      while(auto pending = m_pending_state.get())
+         {
+         auto msg = pending->get_next_handshake_msg();
+
+         if(msg.first == HANDSHAKE_NONE) // no full handshake yet
+            break;
+
+         process_handshake_msg(active_state(), *pending,
+                               msg.first, msg.second, epoch0_restart);
+
+         if(!m_pending_state)
+            break;
+         }
+      }
+   }
+
+void Channel::process_application_data(uint64_t seq_no, const secure_vector<uint8_t>& record)
+   {
+   if(!active_state())
+      throw Unexpected_Message("Application data before handshake done");
+
+   callbacks().tls_record_received(seq_no, record.data(), record.size());
+   }
+
+void Channel::process_alert(const secure_vector<uint8_t>& record)
+    {
+    Alert alert_msg(record);
+
+    if(alert_msg.type() == Alert::NO_RENEGOTIATION)
+       m_pending_state.reset();
+
+    callbacks().tls_alert(alert_msg);
+
+    if(alert_msg.is_fatal())
+       {
+       if(auto active = active_state())
+          m_session_manager.remove_entry(active->server_hello()->session_id());
+       }
+
+    if(alert_msg.type() == Alert::CLOSE_NOTIFY)
+       send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind
+
+    if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal())
+       {
+       m_has_been_closed = true;
+       }
+    }
+
+void Channel::write_record(Connection_Cipher_State* cipher_state, uint16_t epoch,
+                           uint8_t record_type, const uint8_t input[], size_t length)
+   {
+   BOTAN_ASSERT(m_pending_state || m_active_state, "Some connection state exists");
+
+   const Protocol_Version record_version =
+      (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version());
+
+   const uint64_t next_seq = sequence_numbers().next_write_sequence(epoch);
+
+   if(cipher_state == nullptr)
+      {
+      TLS::write_unencrypted_record(m_writebuf, record_type, record_version, next_seq,
+                                    input, length);
+      }
+   else
+      {
+      TLS::write_record(m_writebuf, record_type, record_version, next_seq,
+                        input, length, *cipher_state, m_rng);
+      }
+
+   callbacks().tls_emit_data(m_writebuf.data(), m_writebuf.size());
+   }
+
+void Channel::send_record_array(uint16_t epoch, uint8_t type, const uint8_t input[], size_t length)
+   {
+   if(length == 0)
+      return;
+
+   /*
+   * In versions without an explicit IV field (only TLS v1.0 now that
+   * SSLv3 has been removed) send a single byte record first to randomize
+   * the following (implicit) IV of the following record.
+   *
+   * This isn't needed in TLS v1.1 or higher.
+   *
+   * An empty record also works but apparently some implementations do
+   * not like this (https://bugzilla.mozilla.org/show_bug.cgi?id=665814)
+   *
+   * See https://www.openssl.org/~bodo/tls-cbc.txt for background.
+   */
+
+   auto cipher_state = write_cipher_state_epoch(epoch);
+
+   if(type == APPLICATION_DATA && m_active_state->version().supports_explicit_cbc_ivs() == false)
+      {
+      while(length)
+         {
+         write_record(cipher_state.get(), epoch, type, input, 1);
+         input += 1;
+         length -= 1;
+
+         const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+         write_record(cipher_state.get(), epoch, type, input, sending);
+
+         input += sending;
+         length -= sending;
+         }
+      }
+   else
+      {
+      while(length)
+         {
+         const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+         write_record(cipher_state.get(), epoch, type, input, sending);
+
+         input += sending;
+         length -= sending;
+         }
+      }
+   }
+
+void Channel::send_record(uint8_t record_type, const std::vector<uint8_t>& record)
+   {
+   send_record_array(sequence_numbers().current_write_epoch(),
+                     record_type, record.data(), record.size());
+   }
+
+void Channel::send_record_under_epoch(uint16_t epoch, uint8_t record_type,
+                                      const std::vector<uint8_t>& record)
+   {
+   send_record_array(epoch, record_type, record.data(), record.size());
+   }
+
+void Channel::send(const uint8_t buf[], size_t buf_size)
+   {
+   if(!is_active())
+      throw Invalid_State("Data cannot be sent on inactive TLS connection");
+
+   send_record_array(sequence_numbers().current_write_epoch(),
+                     APPLICATION_DATA, buf, buf_size);
+   }
+
+void Channel::send(const std::string& string)
+   {
+   this->send(cast_char_ptr_to_uint8(string.data()), string.size());
+   }
+
+void Channel::send_alert(const Alert& alert)
+   {
+   if(alert.is_valid() && !is_closed())
+      {
+      try
+         {
+         send_record(ALERT, alert.serialize());
+         }
+      catch(...) { /* swallow it */ }
+      }
+
+   if(alert.type() == Alert::NO_RENEGOTIATION)
+      m_pending_state.reset();
+
+   if(alert.is_fatal())
+   {
+      if(auto active = active_state())
+      {
+         m_session_manager.remove_entry(active->server_hello()->session_id());
+      }
+      reset_state();
+   }
+
+   if(alert.type() == Alert::CLOSE_NOTIFY || alert.is_fatal())
+      {
+      m_has_been_closed = true;
+      }
+   }
+
+void Channel::secure_renegotiation_check(const Client_Hello* client_hello)
+   {
+   const bool secure_renegotiation = client_hello->secure_renegotiation();
+
+   if(auto active = active_state())
+      {
+      const bool active_sr = active->client_hello()->secure_renegotiation();
+
+      if(active_sr != secure_renegotiation)
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Client changed its mind about secure renegotiation");
+      }
+
+   if(secure_renegotiation)
+      {
+      const std::vector<uint8_t>& data = client_hello->renegotiation_info();
+
+      if(data != secure_renegotiation_data_for_client_hello())
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Client sent bad values for secure renegotiation");
+      }
+   }
+
+void Channel::secure_renegotiation_check(const Server_Hello* server_hello)
+   {
+   const bool secure_renegotiation = server_hello->secure_renegotiation();
+
+   if(auto active = active_state())
+      {
+      const bool active_sr = active->server_hello()->secure_renegotiation();
+
+      if(active_sr != secure_renegotiation)
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Server changed its mind about secure renegotiation");
+      }
+
+   if(secure_renegotiation)
+      {
+      const std::vector<uint8_t>& data = server_hello->renegotiation_info();
+
+      if(data != secure_renegotiation_data_for_server_hello())
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Server sent bad values for secure renegotiation");
+      }
+   }
+
+std::vector<uint8_t> Channel::secure_renegotiation_data_for_client_hello() const
+   {
+   if(auto active = active_state())
+      return active->client_finished()->verify_data();
+   return std::vector<uint8_t>();
+   }
+
+std::vector<uint8_t> Channel::secure_renegotiation_data_for_server_hello() const
+   {
+   if(auto active = active_state())
+      {
+      std::vector<uint8_t> buf = active->client_finished()->verify_data();
+      buf += active->server_finished()->verify_data();
+      return buf;
+      }
+
+   return std::vector<uint8_t>();
+   }
+
+bool Channel::secure_renegotiation_supported() const
+   {
+   if(auto active = active_state())
+      return active->server_hello()->secure_renegotiation();
+
+   if(auto pending = pending_state())
+      if(auto hello = pending->server_hello())
+         return hello->secure_renegotiation();
+
+   return false;
+   }
+
+SymmetricKey Channel::key_material_export(const std::string& label,
+                                          const std::string& context,
+                                          size_t length) const
+   {
+   if(auto active = active_state())
+      {
+      if(pending_state() != nullptr)
+         throw Invalid_State("Channel::key_material_export cannot export during renegotiation");
+
+      std::unique_ptr<KDF> prf(active->protocol_specific_prf());
+
+      const secure_vector<uint8_t>& master_secret =
+         active->session_keys().master_secret();
+
+      std::vector<uint8_t> salt;
+      salt += active->client_hello()->random();
+      salt += active->server_hello()->random();
+
+      if(context != "")
+         {
+         size_t context_size = context.length();
+         if(context_size > 0xFFFF)
+            throw Invalid_Argument("key_material_export context is too long");
+         salt.push_back(get_byte(0, static_cast<uint16_t>(context_size)));
+         salt.push_back(get_byte(1, static_cast<uint16_t>(context_size)));
+         salt += to_byte_vector(context);
+         }
+
+      return prf->derive_key(length, master_secret, salt, to_byte_vector(label));
+      }
+   else
+      {
+      throw Invalid_State("Channel::key_material_export connection not active");
+      }
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_channel.h b/third_party/botan/src/lib/tls/tls_channel.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_channel.h
@@ -0,0 +1,318 @@
+/*
+* TLS Channel
+* (C) 2011,2012,2014,2015 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CHANNEL_H_
+#define BOTAN_TLS_CHANNEL_H_
+
+#include <botan/tls_session.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_session_manager.h>
+#include <botan/tls_callbacks.h>
+#include <botan/x509cert.h>
+#include <functional>
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+class Connection_Cipher_State;
+class Connection_Sequence_Numbers;
+class Handshake_State;
+class Handshake_Message;
+class Client_Hello;
+class Server_Hello;
+class Policy;
+
+/**
+* Generic interface for TLS endpoint
+*/
+class BOTAN_PUBLIC_API(2,0) Channel
+   {
+   public:
+      typedef std::function<void (const uint8_t[], size_t)> output_fn;
+      typedef std::function<void (const uint8_t[], size_t)> data_cb;
+      typedef std::function<void (Alert, const uint8_t[], size_t)> alert_cb;
+      typedef std::function<bool (const Session&)> handshake_cb;
+      typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
+      static size_t IO_BUF_DEFAULT_SIZE;
+
+      /**
+      * Set up a new TLS session
+      *
+      * @param callbacks contains a set of callback function references
+      *        required by the TLS endpoint.
+      * @param session_manager manages session state
+      * @param rng a random number generator
+      * @param policy specifies other connection policy information
+      * @param is_server whether this is a server session or not
+      * @param is_datagram whether this is a DTLS session
+      * @param io_buf_sz This many bytes of memory will
+      *        be preallocated for the read and write buffers. Smaller
+      *        values just mean reallocations and copies are more likely.
+      */
+      Channel(Callbacks& callbacks,
+              Session_Manager& session_manager,
+              RandomNumberGenerator& rng,
+              const Policy& policy,
+              bool is_server,
+              bool is_datagram,
+              size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
+
+      /**
+       * DEPRECATED. This constructor is only provided for backward
+       * compatibility and should not be used in new implementations.
+       * (Not marked deprecated since it is only called internally, by
+       * other deprecated constructors)
+       */
+      Channel(output_fn out,
+              data_cb app_data_cb,
+              alert_cb alert_cb,
+              handshake_cb hs_cb,
+              handshake_msg_cb hs_msg_cb,
+              Session_Manager& session_manager,
+              RandomNumberGenerator& rng,
+              const Policy& policy,
+              bool is_server,
+              bool is_datagram,
+              size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
+
+      Channel(const Channel&) = delete;
+
+      Channel& operator=(const Channel&) = delete;
+
+      virtual ~Channel();
+
+      /**
+      * Inject TLS traffic received from counterparty
+      * @return a hint as the how many more bytes we need to process the
+      *         current record (this may be 0 if on a record boundary)
+      */
+      size_t received_data(const uint8_t buf[], size_t buf_size);
+
+      /**
+      * Inject TLS traffic received from counterparty
+      * @return a hint as the how many more bytes we need to process the
+      *         current record (this may be 0 if on a record boundary)
+      */
+      size_t received_data(const std::vector<uint8_t>& buf);
+
+      /**
+      * Inject plaintext intended for counterparty
+      * Throws an exception if is_active() is false
+      */
+      void send(const uint8_t buf[], size_t buf_size);
+
+      /**
+      * Inject plaintext intended for counterparty
+      * Throws an exception if is_active() is false
+      */
+      void send(const std::string& val);
+
+      /**
+      * Inject plaintext intended for counterparty
+      * Throws an exception if is_active() is false
+      */
+      template<typename Alloc>
+         void send(const std::vector<unsigned char, Alloc>& val)
+         {
+         send(val.data(), val.size());
+         }
+
+      /**
+      * Send a TLS alert message. If the alert is fatal, the internal
+      * state (keys, etc) will be reset.
+      * @param alert the Alert to send
+      */
+      void send_alert(const Alert& alert);
+
+      /**
+      * Send a warning alert
+      */
+      void send_warning_alert(Alert::Type type) { send_alert(Alert(type, false)); }
+
+      /**
+      * Send a fatal alert
+      */
+      void send_fatal_alert(Alert::Type type) { send_alert(Alert(type, true)); }
+
+      /**
+      * Send a close notification alert
+      */
+      void close() { send_warning_alert(Alert::CLOSE_NOTIFY); }
+
+      /**
+      * @return true iff the connection is active for sending application data
+      */
+      bool is_active() const;
+
+      /**
+      * @return true iff the connection has been definitely closed
+      */
+      bool is_closed() const;
+
+      /**
+      * @return certificate chain of the peer (may be empty)
+      */
+      std::vector<X509_Certificate> peer_cert_chain() const;
+
+      /**
+      * Key material export (RFC 5705)
+      * @param label a disambiguating label string
+      * @param context a per-association context value
+      * @param length the length of the desired key in bytes
+      * @return key of length bytes
+      */
+      SymmetricKey key_material_export(const std::string& label,
+                                       const std::string& context,
+                                       size_t length) const;
+
+      /**
+      * Attempt to renegotiate the session
+      * @param force_full_renegotiation if true, require a full renegotiation,
+      * otherwise allow session resumption
+      */
+      void renegotiate(bool force_full_renegotiation = false);
+
+      /**
+      * @return true iff the counterparty supports the secure
+      * renegotiation extensions.
+      */
+      bool secure_renegotiation_supported() const;
+
+      /**
+      * Perform a handshake timeout check. This does nothing unless
+      * this is a DTLS channel with a pending handshake state, in
+      * which case we check for timeout and potentially retransmit
+      * handshake packets.
+      */
+      bool timeout_check();
+
+      virtual std::string application_protocol() const = 0;
+
+   protected:
+
+      virtual void process_handshake_msg(const Handshake_State* active_state,
+                                         Handshake_State& pending_state,
+                                         Handshake_Type type,
+                                         const std::vector<uint8_t>& contents,
+                                         bool epoch0_restart) = 0;
+
+      virtual void initiate_handshake(Handshake_State& state,
+                                      bool force_full_renegotiation) = 0;
+
+      virtual std::vector<X509_Certificate>
+         get_peer_cert_chain(const Handshake_State& state) const = 0;
+
+      virtual Handshake_State* new_handshake_state(class Handshake_IO* io) = 0;
+
+      Handshake_State& create_handshake_state(Protocol_Version version);
+
+      void inspect_handshake_message(const Handshake_Message& msg);
+
+      void activate_session();
+
+      void change_cipher_spec_reader(Connection_Side side);
+
+      void change_cipher_spec_writer(Connection_Side side);
+
+      /* secure renegotiation handling */
+
+      void secure_renegotiation_check(const Client_Hello* client_hello);
+      void secure_renegotiation_check(const Server_Hello* server_hello);
+
+      std::vector<uint8_t> secure_renegotiation_data_for_client_hello() const;
+      std::vector<uint8_t> secure_renegotiation_data_for_server_hello() const;
+
+      RandomNumberGenerator& rng() { return m_rng; }
+
+      Session_Manager& session_manager() { return m_session_manager; }
+
+      const Policy& policy() const { return m_policy; }
+
+      bool save_session(const Session& session);
+
+      Callbacks& callbacks() const { return m_callbacks; }
+
+      void reset_active_association_state();
+
+   private:
+      void init(size_t io_buf_sze);
+
+      void send_record(uint8_t record_type, const std::vector<uint8_t>& record);
+
+      void send_record_under_epoch(uint16_t epoch, uint8_t record_type,
+                                   const std::vector<uint8_t>& record);
+
+      void send_record_array(uint16_t epoch, uint8_t record_type,
+                             const uint8_t input[], size_t length);
+
+      void write_record(Connection_Cipher_State* cipher_state,
+                        uint16_t epoch, uint8_t type, const uint8_t input[], size_t length);
+
+      void reset_state();
+
+      Connection_Sequence_Numbers& sequence_numbers() const;
+
+      std::shared_ptr<Connection_Cipher_State> read_cipher_state_epoch(uint16_t epoch) const;
+
+      std::shared_ptr<Connection_Cipher_State> write_cipher_state_epoch(uint16_t epoch) const;
+
+      const Handshake_State* active_state() const { return m_active_state.get(); }
+
+      const Handshake_State* pending_state() const { return m_pending_state.get(); }
+
+      /* methods to handle incoming traffic through Channel::receive_data. */
+      void process_handshake_ccs(const secure_vector<uint8_t>& record,
+                                 uint64_t record_sequence,
+                                 Record_Type record_type,
+                                 Protocol_Version record_version,
+                                 bool epoch0_restart);
+
+      void process_application_data(uint64_t req_no, const secure_vector<uint8_t>& record);
+
+      void process_alert(const secure_vector<uint8_t>& record);
+
+      const bool m_is_server;
+      const bool m_is_datagram;
+
+      /* callbacks */
+      std::unique_ptr<Compat_Callbacks> m_compat_callbacks;
+      Callbacks& m_callbacks;
+
+      /* external state */
+      Session_Manager& m_session_manager;
+      const Policy& m_policy;
+      RandomNumberGenerator& m_rng;
+
+      /* sequence number state */
+      std::unique_ptr<Connection_Sequence_Numbers> m_sequence_numbers;
+
+      /* pending and active connection states */
+      std::unique_ptr<Handshake_State> m_active_state;
+      std::unique_ptr<Handshake_State> m_pending_state;
+
+      /* cipher states for each epoch */
+      std::map<uint16_t, std::shared_ptr<Connection_Cipher_State>> m_write_cipher_states;
+      std::map<uint16_t, std::shared_ptr<Connection_Cipher_State>> m_read_cipher_states;
+
+      /* I/O buffers */
+      secure_vector<uint8_t> m_writebuf;
+      secure_vector<uint8_t> m_readbuf;
+      secure_vector<uint8_t> m_record_buf;
+
+      bool m_has_been_closed;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_ciphersuite.cpp b/third_party/botan/src/lib/tls/tls_ciphersuite.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_ciphersuite.cpp
@@ -0,0 +1,253 @@
+/*
+* TLS Cipher Suite
+* (C) 2004-2010,2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_ciphersuite.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/hash.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace TLS {
+
+size_t Ciphersuite::nonce_bytes_from_handshake() const
+   {
+   switch(m_nonce_format)
+      {
+      case Nonce_Format::CBC_MODE:
+         {
+         if(cipher_algo() == "3DES")
+            return 8;
+         else
+            return 16;
+         }
+      case Nonce_Format::AEAD_IMPLICIT_4:
+         return 4;
+      case Nonce_Format::AEAD_XOR_12:
+         return 12;
+      }
+
+   throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
+   }
+
+size_t Ciphersuite::nonce_bytes_from_record(Protocol_Version version) const
+   {
+   switch(m_nonce_format)
+      {
+      case Nonce_Format::CBC_MODE:
+         {
+         if(version.supports_explicit_cbc_ivs())
+            {
+            return cipher_algo() == "3DES" ? 8 : 16;
+            }
+         else
+            {
+            return 0;
+            }
+         }
+      case Nonce_Format::AEAD_IMPLICIT_4:
+         return 8;
+      case Nonce_Format::AEAD_XOR_12:
+         return 0;
+      }
+
+   throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
+   }
+
+bool Ciphersuite::is_scsv(uint16_t suite)
+   {
+   // TODO: derive from IANA file in script
+   return (suite == 0x00FF || suite == 0x5600);
+   }
+
+bool Ciphersuite::psk_ciphersuite() const
+   {
+   return kex_method() == Kex_Algo::PSK ||
+          kex_method() == Kex_Algo::DHE_PSK ||
+          kex_method() == Kex_Algo::ECDHE_PSK;
+   }
+
+bool Ciphersuite::ecc_ciphersuite() const
+   {
+   return kex_method() == Kex_Algo::ECDH ||
+          kex_method() == Kex_Algo::ECDHE_PSK ||
+          auth_method() == Auth_Method::ECDSA;
+   }
+
+bool Ciphersuite::usable_in_version(Protocol_Version version) const
+   {
+   if(!version.supports_aead_modes())
+      {
+      // Old versions do not support AEAD, or any MAC but SHA-1
+      if(mac_algo() != "SHA-1")
+         return false;
+      }
+
+   return true;
+   }
+
+bool Ciphersuite::cbc_ciphersuite() const
+   {
+   return (mac_algo() != "AEAD");
+   }
+
+bool Ciphersuite::signature_used() const
+   {
+   return auth_method() != Auth_Method::ANONYMOUS &&
+          auth_method() != Auth_Method::IMPLICIT;
+   }
+
+Ciphersuite Ciphersuite::by_id(uint16_t suite)
+   {
+   const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
+   auto s = std::lower_bound(all_suites.begin(), all_suites.end(), suite);
+
+   if(s != all_suites.end() && s->ciphersuite_code() == suite)
+      {
+      return *s;
+      }
+
+   return Ciphersuite(); // some unknown ciphersuite
+   }
+
+Ciphersuite Ciphersuite::from_name(const std::string& name)
+   {
+   const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
+
+   for(auto suite : all_suites)
+      {
+      if(suite.to_string() == name)
+         return suite;
+      }
+
+   return Ciphersuite(); // some unknown ciphersuite
+   }
+
+namespace {
+
+bool have_hash(const std::string& prf)
+   {
+   return (HashFunction::providers(prf).size() > 0);
+   }
+
+bool have_cipher(const std::string& cipher)
+   {
+   return (BlockCipher::providers(cipher).size() > 0) ||
+      (StreamCipher::providers(cipher).size() > 0);
+   }
+
+}
+
+bool Ciphersuite::is_usable() const
+   {
+   if(!m_cipher_keylen) // uninitialized object
+      return false;
+
+   if(!have_hash(prf_algo()))
+      return false;
+
+#if !defined(BOTAN_HAS_TLS_CBC)
+   if(cbc_ciphersuite())
+      return false;
+#endif
+
+   if(mac_algo() == "AEAD")
+      {
+      if(cipher_algo() == "ChaCha20Poly1305")
+         {
+#if !defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+         return false;
+#endif
+         }
+      else
+         {
+         auto cipher_and_mode = split_on(cipher_algo(), '/');
+         BOTAN_ASSERT(cipher_and_mode.size() == 2, "Expected format for AEAD algo");
+         if(!have_cipher(cipher_and_mode[0]))
+            return false;
+
+         const auto mode = cipher_and_mode[1];
+
+#if !defined(BOTAN_HAS_AEAD_CCM)
+         if(mode == "CCM" || mode == "CCM-8")
+            return false;
+#endif
+
+#if !defined(BOTAN_HAS_AEAD_GCM)
+         if(mode == "GCM")
+            return false;
+#endif
+
+#if !defined(BOTAN_HAS_AEAD_OCB)
+         if(mode == "OCB(12)" || mode == "OCB")
+            return false;
+#endif
+         }
+      }
+   else
+      {
+      // Old non-AEAD schemes
+      if(!have_cipher(cipher_algo()))
+         return false;
+      if(!have_hash(mac_algo())) // HMAC
+         return false;
+      }
+
+   if(kex_method() == Kex_Algo::SRP_SHA)
+      {
+#if !defined(BOTAN_HAS_SRP6)
+      return false;
+#endif
+      }
+   else if(kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK)
+      {
+#if !defined(BOTAN_HAS_ECDH)
+      return false;
+#endif
+      }
+   else if(kex_method() == Kex_Algo::DH || kex_method() == Kex_Algo::DHE_PSK)
+      {
+#if !defined(BOTAN_HAS_DIFFIE_HELLMAN)
+      return false;
+#endif
+      }
+   else if(kex_method() == Kex_Algo::CECPQ1)
+      {
+#if !defined(BOTAN_HAS_CECPQ1)
+      return false;
+#endif
+      }
+
+   if(auth_method() == Auth_Method::DSA)
+      {
+#if !defined(BOTAN_HAS_DSA)
+      return false;
+#endif
+      }
+   else if(auth_method() == Auth_Method::ECDSA)
+      {
+#if !defined(BOTAN_HAS_ECDSA)
+      return false;
+#endif
+      }
+   else if(auth_method() == Auth_Method::RSA)
+      {
+#if !defined(BOTAN_HAS_RSA)
+      return false;
+#endif
+      }
+
+   return true;
+   }
+
+}
+
+}
+
diff --git a/third_party/botan/src/lib/tls/tls_ciphersuite.h b/third_party/botan/src/lib/tls/tls_ciphersuite.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_ciphersuite.h
@@ -0,0 +1,189 @@
+/*
+* TLS Cipher Suites
+* (C) 2004-2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CIPHER_SUITES_H_
+#define BOTAN_TLS_CIPHER_SUITES_H_
+
+#include <botan/types.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_version.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Ciphersuite Information
+*/
+class BOTAN_PUBLIC_API(2,0) Ciphersuite final
+   {
+   public:
+      /**
+      * Convert an SSL/TLS ciphersuite to algorithm fields
+      * @param suite the ciphersuite code number
+      * @return ciphersuite object
+      */
+      static Ciphersuite by_id(uint16_t suite);
+
+      /**
+      * Convert an SSL/TLS ciphersuite name to algorithm fields
+      * @param name the IANA name for the desired ciphersuite
+      * @return ciphersuite object
+      */
+      static Ciphersuite from_name(const std::string& name);
+
+      /**
+      * Returns true iff this suite is a known SCSV
+      */
+      static bool is_scsv(uint16_t suite);
+
+      /**
+      * Generate a static list of all known ciphersuites and return it.
+      *
+      * @return list of all known ciphersuites
+      */
+      static const std::vector<Ciphersuite>& all_known_ciphersuites();
+
+      /**
+      * Formats the ciphersuite back to an RFC-style ciphersuite string
+      * @return RFC ciphersuite string identifier
+      */
+      std::string to_string() const { return m_iana_id; }
+
+      /**
+      * @return ciphersuite number
+      */
+      uint16_t ciphersuite_code() const { return m_ciphersuite_code; }
+
+      /**
+      * @return true if this is a PSK ciphersuite
+      */
+      bool psk_ciphersuite() const;
+
+      /**
+      * @return true if this is an ECC ciphersuite
+      */
+      bool ecc_ciphersuite() const;
+
+      /**
+       * @return true if this suite uses a CBC cipher
+       */
+      bool cbc_ciphersuite() const;
+
+      bool signature_used() const;
+
+      /**
+      * @return key exchange algorithm used by this ciphersuite
+      */
+      std::string kex_algo() const { return kex_method_to_string(kex_method()); }
+
+      Kex_Algo kex_method() const { return m_kex_algo; }
+
+      /**
+      * @return signature algorithm used by this ciphersuite
+      */
+      std::string sig_algo() const { return auth_method_to_string(auth_method()); }
+
+      Auth_Method auth_method() const { return m_auth_method; }
+
+      /**
+      * @return symmetric cipher algorithm used by this ciphersuite
+      */
+      std::string cipher_algo() const { return m_cipher_algo; }
+
+      /**
+      * @return message authentication algorithm used by this ciphersuite
+      */
+      std::string mac_algo() const { return m_mac_algo; }
+
+      std::string prf_algo() const
+         {
+         return kdf_algo_to_string(m_prf_algo);
+         }
+
+      /**
+      * @return cipher key length used by this ciphersuite
+      */
+      size_t cipher_keylen() const { return m_cipher_keylen; }
+
+      size_t nonce_bytes_from_handshake() const;
+
+      size_t nonce_bytes_from_record(Protocol_Version version) const;
+
+      Nonce_Format nonce_format() const { return m_nonce_format; }
+
+      size_t mac_keylen() const { return m_mac_keylen; }
+
+      /**
+      * @return true if this is a valid/known ciphersuite
+      */
+      bool valid() const { return m_usable; }
+
+      bool usable_in_version(Protocol_Version version) const;
+
+      bool operator<(const Ciphersuite& o) const { return ciphersuite_code() < o.ciphersuite_code(); }
+      bool operator<(const uint16_t c) const { return ciphersuite_code() < c; }
+
+      Ciphersuite() = default;
+
+   private:
+
+      bool is_usable() const;
+
+      Ciphersuite(uint16_t ciphersuite_code,
+                  const char* iana_id,
+                  Auth_Method auth_method,
+                  Kex_Algo kex_algo,
+                  const char* cipher_algo,
+                  size_t cipher_keylen,
+                  const char* mac_algo,
+                  size_t mac_keylen,
+                  KDF_Algo prf_algo,
+                  Nonce_Format nonce_format) :
+         m_ciphersuite_code(ciphersuite_code),
+         m_iana_id(iana_id),
+         m_auth_method(auth_method),
+         m_kex_algo(kex_algo),
+         m_prf_algo(prf_algo),
+         m_nonce_format(nonce_format),
+         m_cipher_algo(cipher_algo),
+         m_mac_algo(mac_algo),
+         m_cipher_keylen(cipher_keylen),
+         m_mac_keylen(mac_keylen)
+         {
+         m_usable = is_usable();
+         }
+
+      uint16_t m_ciphersuite_code = 0;
+
+      /*
+      All of these const char* strings are references to compile time
+      constants in tls_suite_info.cpp
+      */
+      const char* m_iana_id = nullptr;
+
+      Auth_Method m_auth_method = Auth_Method::ANONYMOUS;
+      Kex_Algo m_kex_algo = Kex_Algo::STATIC_RSA;
+      KDF_Algo m_prf_algo = KDF_Algo::SHA_1;
+      Nonce_Format m_nonce_format = Nonce_Format::CBC_MODE;
+
+      const char* m_cipher_algo = nullptr;
+      const char* m_mac_algo = nullptr;
+
+      size_t m_cipher_keylen = 0;
+      size_t m_mac_keylen = 0;
+
+      bool m_usable = false;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_client.cpp b/third_party/botan/src/lib/tls/tls_client.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_client.cpp
@@ -0,0 +1,780 @@
+/*
+* TLS Client
+* (C) 2004-2011,2012,2015,2016 Jack Lloyd
+*     2016 Matthias Gierlings
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_client.h>
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/stl_util.h>
+#include <iterator>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+class Client_Handshake_State final : public Handshake_State
+   {
+   public:
+      Client_Handshake_State(Handshake_IO* io, Callbacks& cb) :
+         Handshake_State(io, cb),
+         m_is_reneg(false)
+         {}
+
+      const Public_Key& get_server_public_key() const
+         {
+         BOTAN_ASSERT(server_public_key, "Server sent us a certificate");
+         return *server_public_key.get();
+         }
+
+      bool is_a_resumption() const { return (resumed_session != nullptr); }
+
+      bool is_a_renegotiation() const { return m_is_reneg; }
+
+      const secure_vector<uint8_t>& resume_master_secret() const
+         {
+         BOTAN_STATE_CHECK(is_a_resumption());
+         return resumed_session->master_secret();
+         }
+
+      const std::vector<X509_Certificate>& resume_peer_certs() const
+         {
+         BOTAN_STATE_CHECK(is_a_resumption());
+         return resumed_session->peer_certs();
+         }
+
+      std::unique_ptr<Public_Key> server_public_key;
+      // Used during session resumption
+      std::unique_ptr<Session> resumed_session;
+      bool m_is_reneg = false;
+   };
+
+}
+
+/*
+* TLS Client Constructor
+*/
+Client::Client(Callbacks& callbacks,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               const Server_Information& info,
+               const Protocol_Version& offer_version,
+               const std::vector<std::string>& next_protos,
+               size_t io_buf_sz) :
+   Channel(callbacks, session_manager, rng, policy,
+           false, offer_version.is_datagram_protocol(), io_buf_sz),
+   m_creds(creds),
+   m_info(info)
+   {
+   init(offer_version, next_protos);
+   }
+
+Client::Client(output_fn data_output_fn,
+               data_cb proc_cb,
+               alert_cb recv_alert_cb,
+               handshake_cb hs_cb,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               const Server_Information& info,
+               const Protocol_Version& offer_version,
+               const std::vector<std::string>& next_protos,
+               size_t io_buf_sz) :
+   Channel(data_output_fn, proc_cb, recv_alert_cb, hs_cb, Channel::handshake_msg_cb(),
+           session_manager, rng, policy, false, offer_version.is_datagram_protocol(), io_buf_sz),
+   m_creds(creds),
+   m_info(info)
+   {
+   init(offer_version, next_protos);
+   }
+
+Client::Client(output_fn data_output_fn,
+               data_cb proc_cb,
+               alert_cb recv_alert_cb,
+               handshake_cb hs_cb,
+               handshake_msg_cb hs_msg_cb,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               const Server_Information& info,
+               const Protocol_Version& offer_version,
+               const std::vector<std::string>& next_protos) :
+   Channel(data_output_fn, proc_cb, recv_alert_cb, hs_cb, hs_msg_cb,
+           session_manager, rng, policy, false, offer_version.is_datagram_protocol()),
+   m_creds(creds),
+   m_info(info)
+   {
+   init(offer_version, next_protos);
+   }
+
+void Client::init(const Protocol_Version& protocol_version,
+                  const std::vector<std::string>& next_protocols)
+   {
+   const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname());
+
+   Handshake_State& state = create_handshake_state(protocol_version);
+   send_client_hello(state, false, protocol_version,
+                     srp_identifier, next_protocols);
+   }
+
+Handshake_State* Client::new_handshake_state(Handshake_IO* io)
+   {
+   return new Client_Handshake_State(io, callbacks());
+   }
+
+std::vector<X509_Certificate>
+Client::get_peer_cert_chain(const Handshake_State& state) const
+   {
+   const Client_Handshake_State& cstate = dynamic_cast<const Client_Handshake_State&>(state);
+
+   if(cstate.is_a_resumption())
+      return cstate.resume_peer_certs();
+
+   if(state.server_certs())
+      return state.server_certs()->cert_chain();
+   return std::vector<X509_Certificate>();
+   }
+
+/*
+* Send a new client hello to renegotiate
+*/
+void Client::initiate_handshake(Handshake_State& state,
+                                bool force_full_renegotiation)
+   {
+   send_client_hello(state, force_full_renegotiation,
+                     policy().latest_supported_version(state.version().is_datagram_protocol()));
+   }
+
+void Client::send_client_hello(Handshake_State& state_base,
+                               bool force_full_renegotiation,
+                               Protocol_Version version,
+                               const std::string& srp_identifier,
+                               const std::vector<std::string>& next_protocols)
+   {
+   Client_Handshake_State& state = dynamic_cast<Client_Handshake_State&>(state_base);
+
+   if(state.version().is_datagram_protocol())
+      state.set_expected_next(HELLO_VERIFY_REQUEST); // optional
+   state.set_expected_next(SERVER_HELLO);
+
+   if(!force_full_renegotiation && !m_info.empty())
+      {
+      std::unique_ptr<Session> session_info(new Session);;
+      if(session_manager().load_from_server_info(m_info, *session_info))
+         {
+         /*
+         Ensure that the session protocol cipher and version are acceptable
+         If not skip the resume and establish a new session
+         */
+         const bool exact_version = session_info->version() == version;
+         const bool ok_version =
+            (session_info->version().is_datagram_protocol() == version.is_datagram_protocol()) &&
+            policy().acceptable_protocol_version(session_info->version());
+
+         const bool session_version_ok = policy().only_resume_with_exact_version() ? exact_version : ok_version;
+
+         if(policy().acceptable_ciphersuite(session_info->ciphersuite()) && session_version_ok)
+            {
+            if(srp_identifier == "" || session_info->srp_identifier() == srp_identifier)
+               {
+               state.client_hello(
+                  new Client_Hello(state.handshake_io(),
+                                   state.hash(),
+                                   policy(),
+                                   callbacks(),
+                                   rng(),
+                                   secure_renegotiation_data_for_client_hello(),
+                                   *session_info,
+                                   next_protocols));
+
+               state.resumed_session = std::move(session_info);
+               }
+            }
+         }
+      }
+
+   if(!state.client_hello()) // not resuming
+      {
+      Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier);
+      state.client_hello(new Client_Hello(
+         state.handshake_io(),
+         state.hash(),
+         policy(),
+         callbacks(),
+         rng(),
+         secure_renegotiation_data_for_client_hello(),
+         client_settings,
+         next_protocols));
+      }
+
+   secure_renegotiation_check(state.client_hello());
+   }
+
+namespace {
+
+bool key_usage_matches_ciphersuite(Key_Constraints usage,
+                                   const Ciphersuite& suite)
+   {
+   if(usage == NO_CONSTRAINTS)
+      return true; // anything goes ...
+
+   if(suite.kex_method() == Kex_Algo::STATIC_RSA)
+      {
+      return (usage & KEY_ENCIPHERMENT) | (usage & DATA_ENCIPHERMENT);
+      }
+   else
+      {
+      return (usage & DIGITAL_SIGNATURE) | (usage & NON_REPUDIATION);
+      }
+   }
+
+}
+
+/*
+* Process a handshake message
+*/
+void Client::process_handshake_msg(const Handshake_State* active_state,
+                                   Handshake_State& state_base,
+                                   Handshake_Type type,
+                                   const std::vector<uint8_t>& contents,
+                                   bool epoch0_restart)
+   {
+   BOTAN_ASSERT_NOMSG(epoch0_restart == false); // only happens on server side
+
+   Client_Handshake_State& state = dynamic_cast<Client_Handshake_State&>(state_base);
+
+   if(type == HELLO_REQUEST && active_state)
+      {
+      Hello_Request hello_request(contents);
+
+      if(state.client_hello())
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Cannot renegotiate during a handshake");
+         }
+
+      if(policy().allow_server_initiated_renegotiation())
+         {
+         if(secure_renegotiation_supported() || policy().allow_insecure_renegotiation())
+            {
+            state.m_is_reneg = true;
+            this->initiate_handshake(state, true);
+            }
+         else
+            {
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Client policy prohibits insecure renegotiation");
+            }
+         }
+      else
+         {
+         if(policy().abort_connection_on_undesired_renegotiation())
+            {
+            throw TLS_Exception(Alert::NO_RENEGOTIATION, "Client policy prohibits renegotiation");
+            }
+         else
+            {
+            // RFC 5746 section 4.2
+            send_warning_alert(Alert::NO_RENEGOTIATION);
+            }
+         }
+
+      return;
+      }
+
+   state.confirm_transition_to(type);
+
+   if(type != HANDSHAKE_CCS && type != FINISHED && type != HELLO_VERIFY_REQUEST)
+      state.hash().update(state.handshake_io().format(contents, type));
+
+   if(type == HELLO_VERIFY_REQUEST)
+      {
+      state.set_expected_next(SERVER_HELLO);
+      state.set_expected_next(HELLO_VERIFY_REQUEST); // might get it again
+
+      Hello_Verify_Request hello_verify_request(contents);
+      state.hello_verify_request(hello_verify_request);
+      }
+   else if(type == SERVER_HELLO)
+      {
+      state.server_hello(new Server_Hello(contents));
+
+      if(!state.client_hello()->offered_suite(state.server_hello()->ciphersuite()))
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Server replied with ciphersuite we didn't send");
+         }
+
+      if(!Ciphersuite::by_id(state.server_hello()->ciphersuite()).usable_in_version(state.server_hello()->version()))
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Server replied using a ciphersuite not allowed in version it offered");
+         }
+
+      if(Ciphersuite::is_scsv(state.server_hello()->ciphersuite()))
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Server replied with a signaling ciphersuite");
+         }
+
+      if(state.server_hello()->compression_method() != 0)
+         {
+         throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+                             "Server replied with non-null compression method");
+         }
+
+      if(state.client_hello()->version() > state.server_hello()->version())
+         {
+         if(state.server_hello()->random_signals_downgrade())
+            throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Downgrade attack detected");
+         }
+
+      auto client_extn = state.client_hello()->extension_types();
+      auto server_extn = state.server_hello()->extension_types();
+
+      std::vector<Handshake_Extension_Type> diff;
+
+      std::set_difference(server_extn.begin(), server_extn.end(),
+                          client_extn.begin(), client_extn.end(),
+                          std::back_inserter(diff));
+
+      if(!diff.empty())
+         {
+         // Server sent us back an extension we did not send!
+
+         std::ostringstream msg;
+         msg << "Server replied with unsupported extensions:";
+         for(auto&& d : diff)
+            msg << " " << static_cast<int>(d);
+         throw TLS_Exception(Alert::UNSUPPORTED_EXTENSION, msg.str());
+         }
+
+      if(uint16_t srtp = state.server_hello()->srtp_profile())
+         {
+         if(!value_exists(state.client_hello()->srtp_profiles(), srtp))
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server replied with DTLS-SRTP alg we did not send");
+         }
+
+      callbacks().tls_examine_extensions(state.server_hello()->extensions(), SERVER);
+
+      state.set_version(state.server_hello()->version());
+      m_application_protocol = state.server_hello()->next_protocol();
+
+      secure_renegotiation_check(state.server_hello());
+
+      const bool server_returned_same_session_id =
+         !state.server_hello()->session_id().empty() &&
+         (state.server_hello()->session_id() == state.client_hello()->session_id());
+
+      if(server_returned_same_session_id)
+         {
+         // successful resumption
+
+         /*
+         * In this case, we offered the version used in the original
+         * session, and the server must resume with the same version.
+         */
+         if(state.server_hello()->version() != state.client_hello()->version())
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server resumed session but with wrong version");
+
+         if(state.server_hello()->supports_extended_master_secret() &&
+            !state.resumed_session->supports_extended_master_secret())
+            {
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server resumed session but added extended master secret");
+            }
+
+         if(!state.server_hello()->supports_extended_master_secret() &&
+            state.resumed_session->supports_extended_master_secret())
+            {
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server resumed session and removed extended master secret");
+            }
+
+         state.compute_session_keys(state.resume_master_secret());
+
+         if(state.server_hello()->supports_session_ticket())
+            {
+            state.set_expected_next(NEW_SESSION_TICKET);
+            }
+         else
+            {
+            state.set_expected_next(HANDSHAKE_CCS);
+            }
+         }
+      else
+         {
+         // new session
+
+         if(active_state)
+            {
+            // Here we are testing things that should not change during a renegotation,
+            // even if the server creates a new session. Howerver they might change
+            // in a resumption scenario.
+
+            if(active_state->version() != state.server_hello()->version())
+               throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                   "Server changed version after renegotiation");
+
+            if(state.server_hello()->supports_extended_master_secret() !=
+               active_state->server_hello()->supports_extended_master_secret())
+               {
+               throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                   "Server changed its mind about extended master secret");
+               }
+            }
+
+         state.resumed_session.reset(); // non-null if we were attempting a resumption
+
+         if(state.client_hello()->version().is_datagram_protocol() !=
+            state.server_hello()->version().is_datagram_protocol())
+            {
+            throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                "Server replied with different protocol type than we offered");
+            }
+
+         if(state.version() > state.client_hello()->version())
+            {
+            throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                "Server replied with later version than client offered");
+            }
+
+         if(state.version().major_version() == 3 && state.version().minor_version() == 0)
+            {
+            throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                "Server attempting to negotiate SSLv3 which is not supported");
+            }
+
+         if(!policy().acceptable_protocol_version(state.version()))
+            {
+            throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                                "Server version " + state.version().to_string() +
+                                " is unacceptable by policy");
+            }
+
+         if(state.ciphersuite().signature_used() || state.ciphersuite().kex_method() == Kex_Algo::STATIC_RSA)
+            {
+            state.set_expected_next(CERTIFICATE);
+            }
+         else if(state.ciphersuite().kex_method() == Kex_Algo::PSK)
+            {
+            /* PSK is anonymous so no certificate/cert req message is
+               ever sent. The server may or may not send a server kex,
+               depending on if it has an identity hint for us.
+
+               (EC)DHE_PSK always sends a server key exchange for the
+               DH exchange portion, and is covered by block below
+            */
+
+            state.set_expected_next(SERVER_KEX);
+            state.set_expected_next(SERVER_HELLO_DONE);
+            }
+         else if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+            {
+            state.set_expected_next(SERVER_KEX);
+            }
+         else
+            {
+            state.set_expected_next(CERTIFICATE_REQUEST); // optional
+            state.set_expected_next(SERVER_HELLO_DONE);
+            }
+         }
+      }
+   else if(type == CERTIFICATE)
+      {
+      state.server_certs(new Certificate(contents, policy()));
+
+      const std::vector<X509_Certificate>& server_certs =
+         state.server_certs()->cert_chain();
+
+      if(server_certs.empty())
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Client: No certificates sent by server");
+
+      /*
+      If the server supports certificate status messages,
+      certificate verification happens after we receive the server hello done,
+      in case an OCSP response was also available
+      */
+
+      X509_Certificate server_cert = server_certs[0];
+
+      if(active_state && active_state->server_certs())
+         {
+         X509_Certificate current_cert = active_state->server_certs()->cert_chain().at(0);
+
+         if(current_cert != server_cert)
+            throw TLS_Exception(Alert::BAD_CERTIFICATE, "Server certificate changed during renegotiation");
+         }
+
+      std::unique_ptr<Public_Key> peer_key(server_cert.subject_public_key());
+
+      const std::string expected_key_type =
+         state.ciphersuite().signature_used() ? state.ciphersuite().sig_algo() : "RSA";
+
+      if(peer_key->algo_name() != expected_key_type)
+         throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+                             "Certificate key type did not match ciphersuite");
+
+      if(!key_usage_matches_ciphersuite(server_cert.constraints(), state.ciphersuite()))
+         throw TLS_Exception(Alert::BAD_CERTIFICATE,
+                             "Certificate usage constraints do not allow this ciphersuite");
+
+      state.server_public_key.reset(peer_key.release());
+
+      if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+         {
+         state.set_expected_next(SERVER_KEX);
+         }
+      else
+         {
+         state.set_expected_next(CERTIFICATE_REQUEST); // optional
+         state.set_expected_next(SERVER_HELLO_DONE);
+         }
+
+      if(state.server_hello()->supports_certificate_status_message())
+         {
+         state.set_expected_next(CERTIFICATE_STATUS); // optional
+         }
+      else
+         {
+         try
+            {
+            auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname());
+
+            callbacks().tls_verify_cert_chain(server_certs,
+                                              {},
+                                              trusted_CAs,
+                                              Usage_Type::TLS_SERVER_AUTH,
+                                              m_info.hostname(),
+                                              policy());
+            }
+         catch(TLS_Exception&)
+            {
+            throw;
+            }
+         catch(std::exception& e)
+            {
+            throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
+            }
+         }
+      }
+   else if(type == CERTIFICATE_STATUS)
+      {
+      state.server_cert_status(new Certificate_Status(contents));
+
+      if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+         {
+         state.set_expected_next(SERVER_KEX);
+         }
+      else
+         {
+         state.set_expected_next(CERTIFICATE_REQUEST); // optional
+         state.set_expected_next(SERVER_HELLO_DONE);
+         }
+      }
+   else if(type == SERVER_KEX)
+      {
+      if(state.ciphersuite().psk_ciphersuite() == false)
+         state.set_expected_next(CERTIFICATE_REQUEST); // optional
+      state.set_expected_next(SERVER_HELLO_DONE);
+
+      state.server_kex(
+         new Server_Key_Exchange(contents,
+                                 state.ciphersuite().kex_method(),
+                                 state.ciphersuite().auth_method(),
+                                 state.version())
+         );
+
+      if(state.ciphersuite().signature_used())
+         {
+         const Public_Key& server_key = state.get_server_public_key();
+
+         if(!state.server_kex()->verify(server_key, state, policy()))
+            {
+            throw TLS_Exception(Alert::DECRYPT_ERROR,
+                                "Bad signature on server key exchange");
+            }
+         }
+      }
+   else if(type == CERTIFICATE_REQUEST)
+      {
+      state.set_expected_next(SERVER_HELLO_DONE);
+      state.cert_req(new Certificate_Req(contents, state.version()));
+      }
+   else if(type == SERVER_HELLO_DONE)
+      {
+      state.server_hello_done(new Server_Hello_Done(contents));
+
+      if(state.server_certs() != nullptr &&
+         state.server_hello()->supports_certificate_status_message())
+         {
+         try
+            {
+            auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname());
+
+            std::vector<std::shared_ptr<const OCSP::Response>> ocsp;
+            if(state.server_cert_status() != nullptr)
+               {
+               try {
+                   ocsp.push_back(std::make_shared<OCSP::Response>(state.server_cert_status()->response()));
+               }
+               catch(Decoding_Error&)
+                  {
+                  // ignore it here because it might be our fault
+                  }
+               }
+
+            callbacks().tls_verify_cert_chain(state.server_certs()->cert_chain(),
+                                              ocsp,
+                                              trusted_CAs,
+                                              Usage_Type::TLS_SERVER_AUTH,
+                                              m_info.hostname(),
+                                              policy());
+            }
+         catch(TLS_Exception&)
+            {
+            throw;
+            }
+         catch(std::exception& e)
+            {
+            throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
+            }
+         }
+
+      if(state.received_handshake_msg(CERTIFICATE_REQUEST))
+         {
+         const auto& types = state.cert_req()->acceptable_cert_types();
+
+         std::vector<X509_Certificate> client_certs =
+            m_creds.find_cert_chain(types,
+                                    state.cert_req()->acceptable_CAs(),
+                                    "tls-client",
+                                    m_info.hostname());
+
+         state.client_certs(new Certificate(state.handshake_io(),
+                                            state.hash(),
+                                            client_certs));
+         }
+
+      state.client_kex(
+         new Client_Key_Exchange(state.handshake_io(),
+                                 state,
+                                 policy(),
+                                 m_creds,
+                                 state.server_public_key.get(),
+                                 m_info.hostname(),
+                                 rng())
+         );
+
+      state.compute_session_keys();
+
+      if(state.received_handshake_msg(CERTIFICATE_REQUEST) &&
+         !state.client_certs()->empty())
+         {
+         Private_Key* private_key =
+            m_creds.private_key_for(state.client_certs()->cert_chain()[0],
+                                    "tls-client",
+                                    m_info.hostname());
+
+         state.client_verify(
+            new Certificate_Verify(state.handshake_io(),
+                                   state,
+                                   policy(),
+                                   rng(),
+                                   private_key)
+            );
+         }
+
+      state.handshake_io().send(Change_Cipher_Spec());
+
+      change_cipher_spec_writer(CLIENT);
+
+      state.client_finished(new Finished(state.handshake_io(), state, CLIENT));
+
+      if(state.server_hello()->supports_session_ticket())
+         state.set_expected_next(NEW_SESSION_TICKET);
+      else
+         state.set_expected_next(HANDSHAKE_CCS);
+      }
+   else if(type == NEW_SESSION_TICKET)
+      {
+      state.new_session_ticket(new New_Session_Ticket(contents));
+
+      state.set_expected_next(HANDSHAKE_CCS);
+      }
+   else if(type == HANDSHAKE_CCS)
+      {
+      state.set_expected_next(FINISHED);
+
+      change_cipher_spec_reader(CLIENT);
+      }
+   else if(type == FINISHED)
+      {
+      state.server_finished(new Finished(contents));
+
+      if(!state.server_finished()->verify(state, SERVER))
+         throw TLS_Exception(Alert::DECRYPT_ERROR,
+                             "Finished message didn't verify");
+
+      state.hash().update(state.handshake_io().format(contents, type));
+
+      if(!state.client_finished()) // session resume case
+         {
+         state.handshake_io().send(Change_Cipher_Spec());
+         change_cipher_spec_writer(CLIENT);
+         state.client_finished(new Finished(state.handshake_io(), state, CLIENT));
+         }
+
+      std::vector<uint8_t> session_id = state.server_hello()->session_id();
+
+      const std::vector<uint8_t>& session_ticket = state.session_ticket();
+
+      if(session_id.empty() && !session_ticket.empty())
+         session_id = make_hello_random(rng(), policy());
+
+      Session session_info(
+         session_id,
+         state.session_keys().master_secret(),
+         state.server_hello()->version(),
+         state.server_hello()->ciphersuite(),
+         CLIENT,
+         state.server_hello()->supports_extended_master_secret(),
+         state.server_hello()->supports_encrypt_then_mac(),
+         get_peer_cert_chain(state),
+         session_ticket,
+         m_info,
+         "",
+         state.server_hello()->srtp_profile()
+         );
+
+      const bool should_save = save_session(session_info);
+
+      if(session_id.size() > 0 && state.is_a_resumption() == false)
+         {
+         if(should_save)
+            session_manager().save(session_info);
+         else
+            session_manager().remove_entry(session_info.session_id());
+         }
+
+      activate_session();
+      }
+   else
+      throw Unexpected_Message("Unknown handshake message received");
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_client.h b/third_party/botan/src/lib/tls/tls_client.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_client.h
@@ -0,0 +1,169 @@
+/*
+* TLS Client
+* (C) 2004-2011 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CLIENT_H_
+#define BOTAN_TLS_CLIENT_H_
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/credentials_manager.h>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* SSL/TLS Client
+*/
+class BOTAN_PUBLIC_API(2,0) Client final : public Channel
+   {
+   public:
+
+      /**
+      * Set up a new TLS client session
+      *
+      * @param callbacks contains a set of callback function references
+      *        required by the TLS client.
+      *
+      * @param session_manager manages session state
+      *
+      * @param creds manages application/user credentials
+      *
+      * @param policy specifies other connection policy information
+      *
+      * @param rng a random number generator
+      *
+      * @param server_info is identifying information about the TLS server
+      *
+      * @param offer_version specifies which version we will offer
+      *        to the TLS server.
+      *
+      * @param next_protocols specifies protocols to advertise with ALPN
+      *
+      * @param reserved_io_buffer_size This many bytes of memory will
+      *        be preallocated for the read and write buffers. Smaller
+      *        values just mean reallocations and copies are more likely.
+      */
+     Client(Callbacks& callbacks,
+            Session_Manager& session_manager,
+            Credentials_Manager& creds,
+            const Policy& policy,
+            RandomNumberGenerator& rng,
+            const Server_Information& server_info = Server_Information(),
+            const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+            const std::vector<std::string>& next_protocols = {},
+            size_t reserved_io_buffer_size = TLS::Client::IO_BUF_DEFAULT_SIZE
+         );
+
+      /**
+      * DEPRECATED. This constructor is only provided for backward
+      * compatibility and should not be used in new code. It will be
+      * removed in a future release.
+      *
+      * Set up a new TLS client session
+      *
+      * @param data_output_fn is called with data for the outbound socket
+      *
+      * @param app_data_cb is called when new application data is received
+      *
+      * @param recv_alert_cb is called when a TLS alert is received
+      *
+      * @param hs_cb is called when a handshake is completed
+      *
+      * @param session_manager manages session state
+      *
+      * @param creds manages application/user credentials
+      *
+      * @param policy specifies other connection policy information
+      *
+      * @param rng a random number generator
+      *
+      * @param server_info is identifying information about the TLS server
+      *
+      * @param offer_version specifies which version we will offer
+      *        to the TLS server.
+      *
+      * @param next_protocols specifies protocols to advertise with ALPN
+      *
+      * @param reserved_io_buffer_size This many bytes of memory will
+      *        be preallocated for the read and write buffers. Smaller
+      *        values just mean reallocations and copies are more likely.
+      */
+      BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
+      Client(output_fn data_output_fn,
+             data_cb app_data_cb,
+             alert_cb recv_alert_cb,
+             handshake_cb hs_cb,
+             Session_Manager& session_manager,
+             Credentials_Manager& creds,
+             const Policy& policy,
+             RandomNumberGenerator& rng,
+             const Server_Information& server_info = Server_Information(),
+             const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+             const std::vector<std::string>& next_protocols = {},
+             size_t reserved_io_buffer_size = TLS::Client::IO_BUF_DEFAULT_SIZE
+         );
+
+      /**
+       * DEPRECATED. This constructor is only provided for backward
+       * compatibility and should not be used in new implementations.
+       */
+      BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
+      Client(output_fn out,
+             data_cb app_data_cb,
+             alert_cb alert_cb,
+             handshake_cb hs_cb,
+             handshake_msg_cb hs_msg_cb,
+             Session_Manager& session_manager,
+             Credentials_Manager& creds,
+             const Policy& policy,
+             RandomNumberGenerator& rng,
+             const Server_Information& server_info = Server_Information(),
+             const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+             const std::vector<std::string>& next_protocols = {}
+         );
+
+      /**
+      * @return network protocol as advertised by the TLS server, if server sent the ALPN extension
+      */
+      std::string application_protocol() const override { return m_application_protocol; }
+   private:
+      void init(const Protocol_Version& protocol_version,
+                const std::vector<std::string>& next_protocols);
+
+      std::vector<X509_Certificate>
+         get_peer_cert_chain(const Handshake_State& state) const override;
+
+      void initiate_handshake(Handshake_State& state,
+                              bool force_full_renegotiation) override;
+
+      void send_client_hello(Handshake_State& state,
+                             bool force_full_renegotiation,
+                             Protocol_Version version,
+                             const std::string& srp_identifier = "",
+                             const std::vector<std::string>& next_protocols = {});
+
+      void process_handshake_msg(const Handshake_State* active_state,
+                                 Handshake_State& pending_state,
+                                 Handshake_Type type,
+                                 const std::vector<uint8_t>& contents,
+                                 bool epoch0_restart) override;
+
+      Handshake_State* new_handshake_state(Handshake_IO* io) override;
+
+      Credentials_Manager& m_creds;
+      const Server_Information m_info;
+      std::string m_application_protocol;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_exceptn.h b/third_party/botan/src/lib/tls/tls_exceptn.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_exceptn.h
@@ -0,0 +1,52 @@
+/*
+* Exceptions
+* (C) 2004-2006 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_EXCEPTION_H_
+#define BOTAN_TLS_EXCEPTION_H_
+
+#include <botan/exceptn.h>
+#include <botan/tls_alert.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Exception Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_Exception : public Exception
+   {
+   public:
+      Alert::Type type() const { return m_alert_type; }
+
+      TLS_Exception(Alert::Type type,
+                    const std::string& err_msg = "Unknown error") :
+         Exception(err_msg), m_alert_type(type) {}
+
+      int error_code() const noexcept override { return static_cast<int>(m_alert_type); }
+
+      ErrorType error_type() const noexcept override { return ErrorType::TLSError; }
+
+   private:
+      Alert::Type m_alert_type;
+   };
+
+/**
+* Unexpected_Message Exception
+*/
+class BOTAN_PUBLIC_API(2,0) Unexpected_Message final : public TLS_Exception
+   {
+   public:
+      explicit Unexpected_Message(const std::string& err) :
+         TLS_Exception(Alert::UNEXPECTED_MESSAGE, err) {}
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_extensions.cpp b/third_party/botan/src/lib/tls/tls_extensions.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_extensions.cpp
@@ -0,0 +1,660 @@
+/*
+* TLS Extensions
+* (C) 2011,2012,2015,2016 Jack Lloyd
+*     2016 Juraj Somorovsky
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_policy.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, Connection_Side from)
+   {
+   switch(code)
+      {
+      case TLSEXT_SERVER_NAME_INDICATION:
+         return new Server_Name_Indicator(reader, size);
+
+#if defined(BOTAN_HAS_SRP6)
+      case TLSEXT_SRP_IDENTIFIER:
+         return new SRP_Identifier(reader, size);
+#endif
+
+      case TLSEXT_SUPPORTED_GROUPS:
+         return new Supported_Groups(reader, size);
+
+      case TLSEXT_CERT_STATUS_REQUEST:
+         return new Certificate_Status_Request(reader, size, from);
+
+      case TLSEXT_EC_POINT_FORMATS:
+         return new Supported_Point_Formats(reader, size);
+
+      case TLSEXT_SAFE_RENEGOTIATION:
+         return new Renegotiation_Extension(reader, size);
+
+      case TLSEXT_SIGNATURE_ALGORITHMS:
+         return new Signature_Algorithms(reader, size);
+
+      case TLSEXT_USE_SRTP:
+          return new SRTP_Protection_Profiles(reader, size);
+
+      case TLSEXT_ALPN:
+         return new Application_Layer_Protocol_Notification(reader, size);
+
+      case TLSEXT_EXTENDED_MASTER_SECRET:
+         return new Extended_Master_Secret(reader, size);
+
+      case TLSEXT_ENCRYPT_THEN_MAC:
+         return new Encrypt_then_MAC(reader, size);
+
+      case TLSEXT_SESSION_TICKET:
+         return new Session_Ticket(reader, size);
+
+      case TLSEXT_SUPPORTED_VERSIONS:
+         return new Supported_Versions(reader, size, from);
+      }
+
+   return new Unknown_Extension(static_cast<Handshake_Extension_Type>(code),
+                                reader, size);
+   }
+
+}
+
+void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from)
+   {
+   if(reader.has_remaining())
+      {
+      const uint16_t all_extn_size = reader.get_uint16_t();
+
+      if(reader.remaining_bytes() != all_extn_size)
+         throw Decoding_Error("Bad extension size");
+
+      while(reader.has_remaining())
+         {
+         const uint16_t extension_code = reader.get_uint16_t();
+         const uint16_t extension_size = reader.get_uint16_t();
+
+         const auto type = static_cast<Handshake_Extension_Type>(extension_code);
+
+         if(m_extensions.find(type) != m_extensions.end())
+            throw TLS_Exception(TLS::Alert::DECODE_ERROR,
+                                "Peer sent duplicated extensions");
+
+         Extension* extn = make_extension(
+            reader, extension_code, extension_size, from);
+
+         this->add(extn);
+         }
+      }
+   }
+
+std::vector<uint8_t> Extensions::serialize(Connection_Side whoami) const
+   {
+   std::vector<uint8_t> buf(2); // 2 bytes for length field
+
+   for(auto& extn : m_extensions)
+      {
+      if(extn.second->empty())
+         continue;
+
+      const uint16_t extn_code = static_cast<uint16_t>(extn.second->type());
+
+      const std::vector<uint8_t> extn_val = extn.second->serialize(whoami);
+
+      buf.push_back(get_byte(0, extn_code));
+      buf.push_back(get_byte(1, extn_code));
+
+      buf.push_back(get_byte(0, static_cast<uint16_t>(extn_val.size())));
+      buf.push_back(get_byte(1, static_cast<uint16_t>(extn_val.size())));
+
+      buf += extn_val;
+      }
+
+   const uint16_t extn_size = static_cast<uint16_t>(buf.size() - 2);
+
+   buf[0] = get_byte(0, extn_size);
+   buf[1] = get_byte(1, extn_size);
+
+   // avoid sending a completely empty extensions block
+   if(buf.size() == 2)
+      return std::vector<uint8_t>();
+
+   return buf;
+   }
+
+bool Extensions::remove_extension(Handshake_Extension_Type typ)
+   {
+   auto i = m_extensions.find(typ);
+   if(i == m_extensions.end())
+      return false;
+   m_extensions.erase(i);
+   return true;
+   }
+
+std::set<Handshake_Extension_Type> Extensions::extension_types() const
+   {
+   std::set<Handshake_Extension_Type> offers;
+   for(auto i = m_extensions.begin(); i != m_extensions.end(); ++i)
+      offers.insert(i->first);
+   return offers;
+   }
+
+Unknown_Extension::Unknown_Extension(Handshake_Extension_Type type,
+                                     TLS_Data_Reader& reader,
+                                     uint16_t extension_size) :
+   m_type(type),
+   m_value(reader.get_fixed<uint8_t>(extension_size))
+   {
+   }
+
+std::vector<uint8_t> Unknown_Extension::serialize(Connection_Side /*whoami*/) const
+   {
+   throw Invalid_State("Cannot encode an unknown TLS extension");
+   }
+
+Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader,
+                                             uint16_t extension_size)
+   {
+   /*
+   * This is used by the server to confirm that it knew the name
+   */
+   if(extension_size == 0)
+      return;
+
+   uint16_t name_bytes = reader.get_uint16_t();
+
+   if(name_bytes + 2 != extension_size)
+      throw Decoding_Error("Bad encoding of SNI extension");
+
+   while(name_bytes)
+      {
+      uint8_t name_type = reader.get_byte();
+      name_bytes--;
+
+      if(name_type == 0) // DNS
+         {
+         m_sni_host_name = reader.get_string(2, 1, 65535);
+         name_bytes -= static_cast<uint16_t>(2 + m_sni_host_name.size());
+         }
+      else // some other unknown name type
+         {
+         reader.discard_next(name_bytes);
+         name_bytes = 0;
+         }
+      }
+   }
+
+std::vector<uint8_t> Server_Name_Indicator::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf;
+
+   size_t name_len = m_sni_host_name.size();
+
+   buf.push_back(get_byte(0, static_cast<uint16_t>(name_len+3)));
+   buf.push_back(get_byte(1, static_cast<uint16_t>(name_len+3)));
+   buf.push_back(0); // DNS
+
+   buf.push_back(get_byte(0, static_cast<uint16_t>(name_len)));
+   buf.push_back(get_byte(1, static_cast<uint16_t>(name_len)));
+
+   buf += std::make_pair(
+      cast_char_ptr_to_uint8(m_sni_host_name.data()),
+      m_sni_host_name.size());
+
+   return buf;
+   }
+
+#if defined(BOTAN_HAS_SRP6)
+
+SRP_Identifier::SRP_Identifier(TLS_Data_Reader& reader,
+                               uint16_t extension_size) : m_srp_identifier(reader.get_string(1, 1, 255))
+   {
+   if(m_srp_identifier.size() + 1 != extension_size)
+      throw Decoding_Error("Bad encoding for SRP identifier extension");
+   }
+
+std::vector<uint8_t> SRP_Identifier::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf;
+
+   const uint8_t* srp_bytes = cast_char_ptr_to_uint8(m_srp_identifier.data());
+   append_tls_length_value(buf, srp_bytes, m_srp_identifier.size(), 1);
+
+   return buf;
+   }
+
+#endif
+
+Renegotiation_Extension::Renegotiation_Extension(TLS_Data_Reader& reader,
+                                                 uint16_t extension_size) : m_reneg_data(reader.get_range<uint8_t>(1, 0, 255))
+   {
+   if(m_reneg_data.size() + 1 != extension_size)
+      throw Decoding_Error("Bad encoding for secure renegotiation extn");
+   }
+
+std::vector<uint8_t> Renegotiation_Extension::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf;
+   append_tls_length_value(buf, m_reneg_data, 1);
+   return buf;
+   }
+
+Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification(TLS_Data_Reader& reader,
+                                                                                 uint16_t extension_size)
+   {
+   if(extension_size == 0)
+      return; // empty extension
+
+   const uint16_t name_bytes = reader.get_uint16_t();
+
+   size_t bytes_remaining = extension_size - 2;
+
+   if(name_bytes != bytes_remaining)
+      throw Decoding_Error("Bad encoding of ALPN extension, bad length field");
+
+   while(bytes_remaining)
+      {
+      const std::string p = reader.get_string(1, 0, 255);
+
+      if(bytes_remaining < p.size() + 1)
+         throw Decoding_Error("Bad encoding of ALPN, length field too long");
+
+      if(p.empty())
+         throw Decoding_Error("Empty ALPN protocol not allowed");
+
+      bytes_remaining -= (p.size() + 1);
+
+      m_protocols.push_back(p);
+      }
+   }
+
+const std::string& Application_Layer_Protocol_Notification::single_protocol() const
+   {
+   if(m_protocols.size() != 1)
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                          "Server sent " + std::to_string(m_protocols.size()) +
+                          " protocols in ALPN extension response");
+   return m_protocols[0];
+   }
+
+std::vector<uint8_t> Application_Layer_Protocol_Notification::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf(2);
+
+   for(auto&& p: m_protocols)
+      {
+      if(p.length() >= 256)
+         throw TLS_Exception(Alert::INTERNAL_ERROR, "ALPN name too long");
+      if(p != "")
+         append_tls_length_value(buf,
+                                 cast_char_ptr_to_uint8(p.data()),
+                                 p.size(),
+                                 1);
+      }
+
+   buf[0] = get_byte(0, static_cast<uint16_t>(buf.size()-2));
+   buf[1] = get_byte(1, static_cast<uint16_t>(buf.size()-2));
+
+   return buf;
+   }
+
+Supported_Groups::Supported_Groups(const std::vector<Group_Params>& groups) : m_groups(groups)
+   {
+   }
+
+std::vector<Group_Params> Supported_Groups::ec_groups() const
+   {
+   std::vector<Group_Params> ec;
+   for(auto g : m_groups)
+      {
+      if(group_param_is_dh(g) == false)
+         ec.push_back(g);
+      }
+   return ec;
+   }
+
+std::vector<Group_Params> Supported_Groups::dh_groups() const
+   {
+   std::vector<Group_Params> dh;
+   for(auto g : m_groups)
+      {
+      if(group_param_is_dh(g) == true)
+         dh.push_back(g);
+      }
+   return dh;
+   }
+
+std::vector<uint8_t> Supported_Groups::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf(2);
+
+   for(auto g : m_groups)
+      {
+      const uint16_t id = static_cast<uint16_t>(g);
+
+      if(id > 0)
+         {
+         buf.push_back(get_byte(0, id));
+         buf.push_back(get_byte(1, id));
+         }
+      }
+
+   buf[0] = get_byte(0, static_cast<uint16_t>(buf.size()-2));
+   buf[1] = get_byte(1, static_cast<uint16_t>(buf.size()-2));
+
+   return buf;
+   }
+
+Supported_Groups::Supported_Groups(TLS_Data_Reader& reader,
+                                   uint16_t extension_size)
+   {
+   const uint16_t len = reader.get_uint16_t();
+
+   if(len + 2 != extension_size)
+      throw Decoding_Error("Inconsistent length field in supported groups list");
+
+   if(len % 2 == 1)
+      throw Decoding_Error("Supported groups list of strange size");
+
+   const size_t elems = len / 2;
+
+   for(size_t i = 0; i != elems; ++i)
+      {
+      const uint16_t id = reader.get_uint16_t();
+      m_groups.push_back(static_cast<Group_Params>(id));
+      }
+   }
+
+std::vector<uint8_t> Supported_Point_Formats::serialize(Connection_Side /*whoami*/) const
+   {
+   // if this extension is sent, it MUST include uncompressed (RFC 4492, section 5.1)
+   if(m_prefers_compressed)
+      {
+      return std::vector<uint8_t>{2, ANSIX962_COMPRESSED_PRIME, UNCOMPRESSED};
+      }
+   else
+      {
+      return std::vector<uint8_t>{1, UNCOMPRESSED};
+      }
+   }
+
+Supported_Point_Formats::Supported_Point_Formats(TLS_Data_Reader& reader,
+                                                 uint16_t extension_size)
+   {
+   uint8_t len = reader.get_byte();
+
+   if(len + 1 != extension_size)
+      throw Decoding_Error("Inconsistent length field in supported point formats list");
+
+   for(size_t i = 0; i != len; ++i)
+      {
+      uint8_t format = reader.get_byte();
+
+      if(static_cast<ECPointFormat>(format) == UNCOMPRESSED)
+         {
+         m_prefers_compressed = false;
+         reader.discard_next(len-i-1);
+         return;
+         }
+      else if(static_cast<ECPointFormat>(format) == ANSIX962_COMPRESSED_PRIME)
+         {
+         m_prefers_compressed = true;
+         reader.discard_next(len-i-1);
+         return;
+         }
+
+      // ignore ANSIX962_COMPRESSED_CHAR2, we don't support these curves
+      }
+   }
+
+std::vector<uint8_t> Signature_Algorithms::serialize(Connection_Side /*whoami*/) const
+   {
+   BOTAN_ASSERT(m_schemes.size() < 256, "Too many signature schemes");
+
+   std::vector<uint8_t> buf;
+
+   const uint16_t len = static_cast<uint16_t>(m_schemes.size() * 2);
+
+   buf.push_back(get_byte(0, len));
+   buf.push_back(get_byte(1, len));
+
+   for(Signature_Scheme scheme : m_schemes)
+      {
+      const uint16_t scheme_code = static_cast<uint16_t>(scheme);
+
+      buf.push_back(get_byte(0, scheme_code));
+      buf.push_back(get_byte(1, scheme_code));
+      }
+
+   return buf;
+   }
+
+Signature_Algorithms::Signature_Algorithms(TLS_Data_Reader& reader,
+                                           uint16_t extension_size)
+   {
+   uint16_t len = reader.get_uint16_t();
+
+   if(len + 2 != extension_size || len % 2 == 1 || len == 0)
+      {
+      throw Decoding_Error("Bad encoding on signature algorithms extension");
+      }
+
+   while(len)
+      {
+      const uint16_t scheme_code = reader.get_uint16_t();
+      m_schemes.push_back(static_cast<Signature_Scheme>(scheme_code));
+      len -= 2;
+      }
+   }
+
+Session_Ticket::Session_Ticket(TLS_Data_Reader& reader,
+                               uint16_t extension_size) : m_ticket(reader.get_elem<uint8_t, std::vector<uint8_t>>(extension_size))
+   {}
+
+SRTP_Protection_Profiles::SRTP_Protection_Profiles(TLS_Data_Reader& reader,
+                                                   uint16_t extension_size) : m_pp(reader.get_range<uint16_t>(2, 0, 65535))
+   {
+   const std::vector<uint8_t> mki = reader.get_range<uint8_t>(1, 0, 255);
+
+   if(m_pp.size() * 2 + mki.size() + 3 != extension_size)
+      throw Decoding_Error("Bad encoding for SRTP protection extension");
+
+   if(!mki.empty())
+      throw Decoding_Error("Unhandled non-empty MKI for SRTP protection extension");
+   }
+
+std::vector<uint8_t> SRTP_Protection_Profiles::serialize(Connection_Side /*whoami*/) const
+   {
+   std::vector<uint8_t> buf;
+
+   const uint16_t pp_len = static_cast<uint16_t>(m_pp.size() * 2);
+   buf.push_back(get_byte(0, pp_len));
+   buf.push_back(get_byte(1, pp_len));
+
+   for(uint16_t pp : m_pp)
+      {
+      buf.push_back(get_byte(0, pp));
+      buf.push_back(get_byte(1, pp));
+      }
+
+   buf.push_back(0); // srtp_mki, always empty here
+
+   return buf;
+   }
+
+Extended_Master_Secret::Extended_Master_Secret(TLS_Data_Reader&,
+                                               uint16_t extension_size)
+   {
+   if(extension_size != 0)
+      throw Decoding_Error("Invalid extended_master_secret extension");
+   }
+
+std::vector<uint8_t> Extended_Master_Secret::serialize(Connection_Side /*whoami*/) const
+   {
+   return std::vector<uint8_t>();
+   }
+
+Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&,
+                                   uint16_t extension_size)
+   {
+   if(extension_size != 0)
+      throw Decoding_Error("Invalid encrypt_then_mac extension");
+   }
+
+std::vector<uint8_t> Encrypt_then_MAC::serialize(Connection_Side /*whoami*/) const
+   {
+   return std::vector<uint8_t>();
+   }
+
+std::vector<uint8_t> Certificate_Status_Request::serialize(Connection_Side whoami) const
+   {
+   std::vector<uint8_t> buf;
+
+   if(whoami == Connection_Side::SERVER)
+      return buf; // server reply is empty
+
+   /*
+   opaque ResponderID<1..2^16-1>;
+   opaque Extensions<0..2^16-1>;
+
+   CertificateStatusType status_type = ocsp(1)
+   ResponderID responder_id_list<0..2^16-1>
+   Extensions  request_extensions;
+   */
+
+   buf.push_back(1); // CertificateStatusType ocsp
+
+   buf.push_back(0);
+   buf.push_back(0);
+   buf.push_back(0);
+   buf.push_back(0);
+
+   return buf;
+   }
+
+Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader,
+                                                       uint16_t extension_size,
+                                                       Connection_Side from)
+   {
+   if(from == Connection_Side::SERVER)
+      {
+      if(extension_size != 0)
+         throw Decoding_Error("Server sent non-empty Certificate_Status_Request extension");
+      }
+   else if(extension_size > 0)
+      {
+      const uint8_t type = reader.get_byte();
+      if(type == 1)
+         {
+         const size_t len_resp_id_list = reader.get_uint16_t();
+         m_ocsp_names = reader.get_fixed<uint8_t>(len_resp_id_list);
+         const size_t len_requ_ext = reader.get_uint16_t();
+         m_extension_bytes = reader.get_fixed<uint8_t>(len_requ_ext);
+         }
+      else
+         {
+         reader.discard_next(extension_size - 1);
+         }
+      }
+   }
+
+Certificate_Status_Request::Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids,
+                                                       const std::vector<std::vector<uint8_t>>& ocsp_key_ids) :
+   m_ocsp_names(ocsp_responder_ids),
+   m_ocsp_keys(ocsp_key_ids)
+   {
+   }
+
+std::vector<uint8_t> Supported_Versions::serialize(Connection_Side whoami) const
+   {
+   std::vector<uint8_t> buf;
+
+   if(whoami == Connection_Side::SERVER)
+      {
+      BOTAN_ASSERT_NOMSG(m_versions.size() == 1);
+      buf.push_back(m_versions[0].major_version());
+      buf.push_back(m_versions[0].minor_version());
+      }
+   else
+      {
+      BOTAN_ASSERT_NOMSG(m_versions.size() >= 1);
+      const uint8_t len = static_cast<uint8_t>(m_versions.size() * 2);
+
+      buf.push_back(len);
+
+      for(Protocol_Version version : m_versions)
+         {
+         buf.push_back(get_byte(0, version.major_version()));
+         buf.push_back(get_byte(1, version.minor_version()));
+         }
+      }
+
+   return buf;
+   }
+
+Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& policy)
+   {
+   if(offer.is_datagram_protocol())
+      {
+      if(offer >= Protocol_Version::DTLS_V12 && policy.allow_dtls12())
+         m_versions.push_back(Protocol_Version::DTLS_V12);
+#if defined(BOTAN_HAS_TLS_V10)
+      if(offer >= Protocol_Version::DTLS_V10 && policy.allow_dtls10())
+         m_versions.push_back(Protocol_Version::DTLS_V10);
+#endif
+      }
+   else
+      {
+      if(offer >= Protocol_Version::TLS_V12 && policy.allow_tls12())
+         m_versions.push_back(Protocol_Version::TLS_V12);
+#if defined(BOTAN_HAS_TLS_V10)
+      if(offer >= Protocol_Version::TLS_V11 && policy.allow_tls11())
+         m_versions.push_back(Protocol_Version::TLS_V11);
+      if(offer >= Protocol_Version::TLS_V10 && policy.allow_tls10())
+         m_versions.push_back(Protocol_Version::TLS_V10);
+#endif
+      }
+   }
+
+Supported_Versions::Supported_Versions(TLS_Data_Reader& reader,
+                                       uint16_t extension_size,
+                                       Connection_Side from)
+   {
+   if(from == Connection_Side::SERVER)
+      {
+      if(extension_size != 2)
+         throw Decoding_Error("Server sent invalid supported_versions extension");
+      m_versions.push_back(Protocol_Version(reader.get_uint16_t()));
+      }
+   else
+      {
+      auto versions = reader.get_range<uint16_t>(1, 1, 127);
+
+      for(auto v : versions)
+         m_versions.push_back(Protocol_Version(v));
+
+      if(extension_size != 1+2*versions.size())
+         throw Decoding_Error("Client sent invalid supported_versions extension");
+      }
+   }
+
+bool Supported_Versions::supports(Protocol_Version version) const
+   {
+   for(auto v : m_versions)
+      if(version == v)
+         return true;
+   return false;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_extensions.h b/third_party/botan/src/lib/tls/tls_extensions.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_extensions.h
@@ -0,0 +1,551 @@
+/*
+* TLS Extensions
+* (C) 2011,2012,2016,2018,2019 Jack Lloyd
+* (C) 2016 Juraj Somorovsky
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_EXTENSIONS_H_
+#define BOTAN_TLS_EXTENSIONS_H_
+
+#include <botan/tls_algos.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <botan/secmem.h>
+#include <botan/x509_dn.h>
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+
+namespace Botan {
+
+namespace TLS {
+
+class Policy;
+
+class TLS_Data_Reader;
+
+// This will become an enum class in a future major release
+enum Handshake_Extension_Type {
+   TLSEXT_SERVER_NAME_INDICATION = 0,
+   TLSEXT_CERT_STATUS_REQUEST    = 5,
+
+   TLSEXT_CERTIFICATE_TYPES      = 9,
+   TLSEXT_SUPPORTED_GROUPS       = 10,
+   TLSEXT_EC_POINT_FORMATS       = 11,
+   TLSEXT_SRP_IDENTIFIER         = 12,
+   TLSEXT_SIGNATURE_ALGORITHMS   = 13,
+   TLSEXT_USE_SRTP               = 14,
+   TLSEXT_ALPN                   = 16,
+
+   TLSEXT_ENCRYPT_THEN_MAC       = 22,
+   TLSEXT_EXTENDED_MASTER_SECRET = 23,
+
+   TLSEXT_SESSION_TICKET         = 35,
+
+   TLSEXT_SUPPORTED_VERSIONS     = 43,
+
+   TLSEXT_SAFE_RENEGOTIATION     = 65281,
+};
+
+/**
+* Base class representing a TLS extension of some kind
+*/
+class BOTAN_UNSTABLE_API Extension
+   {
+   public:
+      /**
+      * @return code number of the extension
+      */
+      virtual Handshake_Extension_Type type() const = 0;
+
+      /**
+      * @return serialized binary for the extension
+      */
+      virtual std::vector<uint8_t> serialize(Connection_Side whoami) const = 0;
+
+      /**
+      * @return if we should encode this extension or not
+      */
+      virtual bool empty() const = 0;
+
+      virtual ~Extension() = default;
+   };
+
+/**
+* Server Name Indicator extension (RFC 3546)
+*/
+class BOTAN_UNSTABLE_API Server_Name_Indicator final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SERVER_NAME_INDICATION; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      explicit Server_Name_Indicator(const std::string& host_name) :
+         m_sni_host_name(host_name) {}
+
+      Server_Name_Indicator(TLS_Data_Reader& reader,
+                            uint16_t extension_size);
+
+      std::string host_name() const { return m_sni_host_name; }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_sni_host_name.empty(); }
+   private:
+      std::string m_sni_host_name;
+   };
+
+#if defined(BOTAN_HAS_SRP6)
+/**
+* SRP identifier extension (RFC 5054)
+*/
+class BOTAN_UNSTABLE_API SRP_Identifier final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SRP_IDENTIFIER; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      explicit SRP_Identifier(const std::string& identifier) :
+         m_srp_identifier(identifier) {}
+
+      SRP_Identifier(TLS_Data_Reader& reader,
+                     uint16_t extension_size);
+
+      std::string identifier() const { return m_srp_identifier; }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_srp_identifier.empty(); }
+   private:
+      std::string m_srp_identifier;
+   };
+#endif
+
+/**
+* Renegotiation Indication Extension (RFC 5746)
+*/
+class BOTAN_UNSTABLE_API Renegotiation_Extension final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SAFE_RENEGOTIATION; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      Renegotiation_Extension() = default;
+
+      explicit Renegotiation_Extension(const std::vector<uint8_t>& bits) :
+         m_reneg_data(bits) {}
+
+      Renegotiation_Extension(TLS_Data_Reader& reader,
+                             uint16_t extension_size);
+
+      const std::vector<uint8_t>& renegotiation_info() const
+         { return m_reneg_data; }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return false; } // always send this
+   private:
+      std::vector<uint8_t> m_reneg_data;
+   };
+
+/**
+* ALPN (RFC 7301)
+*/
+class BOTAN_UNSTABLE_API Application_Layer_Protocol_Notification final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type() { return TLSEXT_ALPN; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      const std::vector<std::string>& protocols() const { return m_protocols; }
+
+      const std::string& single_protocol() const;
+
+      /**
+      * Single protocol, used by server
+      */
+      explicit Application_Layer_Protocol_Notification(const std::string& protocol) :
+         m_protocols(1, protocol) {}
+
+      /**
+      * List of protocols, used by client
+      */
+      explicit Application_Layer_Protocol_Notification(const std::vector<std::string>& protocols) :
+         m_protocols(protocols) {}
+
+      Application_Layer_Protocol_Notification(TLS_Data_Reader& reader,
+                                              uint16_t extension_size);
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_protocols.empty(); }
+   private:
+      std::vector<std::string> m_protocols;
+   };
+
+/**
+* Session Ticket Extension (RFC 5077)
+*/
+class BOTAN_UNSTABLE_API Session_Ticket final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SESSION_TICKET; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      /**
+      * @return contents of the session ticket
+      */
+      const std::vector<uint8_t>& contents() const { return m_ticket; }
+
+      /**
+      * Create empty extension, used by both client and server
+      */
+      Session_Ticket() = default;
+
+      /**
+      * Extension with ticket, used by client
+      */
+      explicit Session_Ticket(const std::vector<uint8_t>& session_ticket) :
+         m_ticket(session_ticket) {}
+
+      /**
+      * Deserialize a session ticket
+      */
+      Session_Ticket(TLS_Data_Reader& reader, uint16_t extension_size);
+
+      std::vector<uint8_t> serialize(Connection_Side) const override { return m_ticket; }
+
+      bool empty() const override { return false; }
+   private:
+      std::vector<uint8_t> m_ticket;
+   };
+
+
+/**
+* Supported Groups Extension (RFC 7919)
+*/
+class BOTAN_UNSTABLE_API Supported_Groups final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SUPPORTED_GROUPS; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<Group_Params> ec_groups() const;
+      std::vector<Group_Params> dh_groups() const;
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      explicit Supported_Groups(const std::vector<Group_Params>& groups);
+
+      Supported_Groups(TLS_Data_Reader& reader,
+                       uint16_t extension_size);
+
+      bool empty() const override { return m_groups.empty(); }
+   private:
+      std::vector<Group_Params> m_groups;
+   };
+
+// previously Supported Elliptic Curves Extension (RFC 4492)
+//using Supported_Elliptic_Curves = Supported_Groups;
+
+/**
+* Supported Point Formats Extension (RFC 4492)
+*/
+class BOTAN_UNSTABLE_API Supported_Point_Formats final : public Extension
+   {
+   public:
+      enum ECPointFormat : uint8_t {
+         UNCOMPRESSED = 0,
+         ANSIX962_COMPRESSED_PRIME = 1,
+         ANSIX962_COMPRESSED_CHAR2 = 2, // don't support these curves
+      };
+
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_EC_POINT_FORMATS; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      explicit Supported_Point_Formats(bool prefer_compressed) :
+         m_prefers_compressed(prefer_compressed) {}
+
+      Supported_Point_Formats(TLS_Data_Reader& reader,
+                              uint16_t extension_size);
+
+      bool empty() const override { return false; }
+
+      bool prefers_compressed() { return m_prefers_compressed; }
+
+   private:
+      bool m_prefers_compressed = false;
+   };
+
+/**
+* Signature Algorithms Extension for TLS 1.2 (RFC 5246)
+*/
+class BOTAN_UNSTABLE_API Signature_Algorithms final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SIGNATURE_ALGORITHMS; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      const std::vector<Signature_Scheme>& supported_schemes() const { return m_schemes; }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_schemes.empty(); }
+
+      explicit Signature_Algorithms(const std::vector<Signature_Scheme>& schemes) :
+         m_schemes(schemes) {}
+
+      Signature_Algorithms(TLS_Data_Reader& reader,
+                           uint16_t extension_size);
+   private:
+      std::vector<Signature_Scheme> m_schemes;
+   };
+
+/**
+* Used to indicate SRTP algorithms for DTLS (RFC 5764)
+*/
+class BOTAN_UNSTABLE_API SRTP_Protection_Profiles final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_USE_SRTP; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      const std::vector<uint16_t>& profiles() const { return m_pp; }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_pp.empty(); }
+
+      explicit SRTP_Protection_Profiles(const std::vector<uint16_t>& pp) : m_pp(pp) {}
+
+      explicit SRTP_Protection_Profiles(uint16_t pp) : m_pp(1, pp) {}
+
+      SRTP_Protection_Profiles(TLS_Data_Reader& reader, uint16_t extension_size);
+   private:
+      std::vector<uint16_t> m_pp;
+   };
+
+/**
+* Extended Master Secret Extension (RFC 7627)
+*/
+class BOTAN_UNSTABLE_API Extended_Master_Secret final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_EXTENDED_MASTER_SECRET; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return false; }
+
+      Extended_Master_Secret() = default;
+
+      Extended_Master_Secret(TLS_Data_Reader& reader, uint16_t extension_size);
+   };
+
+/**
+* Encrypt-then-MAC Extension (RFC 7366)
+*/
+class BOTAN_UNSTABLE_API Encrypt_then_MAC final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_ENCRYPT_THEN_MAC; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return false; }
+
+      Encrypt_then_MAC() = default;
+
+      Encrypt_then_MAC(TLS_Data_Reader& reader, uint16_t extension_size);
+   };
+
+/**
+* Certificate Status Request (RFC 6066)
+*/
+class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_CERT_STATUS_REQUEST; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return false; }
+
+      const std::vector<uint8_t>& get_responder_id_list() const
+         {
+         return m_ocsp_names;
+         }
+
+      const std::vector<uint8_t>& get_request_extensions() const
+         {
+         return m_extension_bytes;
+         }
+
+      // Server generated version: empty
+      Certificate_Status_Request() {}
+
+      // Client version, both lists can be empty
+      Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids,
+                                 const std::vector<std::vector<uint8_t>>& ocsp_key_ids);
+
+      Certificate_Status_Request(TLS_Data_Reader& reader,
+                                 uint16_t extension_size,
+                                 Connection_Side side);
+   private:
+      std::vector<uint8_t> m_ocsp_names;
+      std::vector<std::vector<uint8_t>> m_ocsp_keys; // is this field really needed
+      std::vector<uint8_t> m_extension_bytes;
+   };
+
+/**
+* Supported Versions from RFC 8446
+*/
+class BOTAN_UNSTABLE_API Supported_Versions final : public Extension
+   {
+   public:
+      static Handshake_Extension_Type static_type()
+         { return TLSEXT_SUPPORTED_VERSIONS; }
+
+      Handshake_Extension_Type type() const override { return static_type(); }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+      bool empty() const override { return m_versions.empty(); }
+
+      Supported_Versions(Protocol_Version version, const Policy& policy);
+
+      Supported_Versions(Protocol_Version version)
+         {
+         m_versions.push_back(version);
+         }
+
+      Supported_Versions(TLS_Data_Reader& reader,
+                         uint16_t extension_size,
+                         Connection_Side from);
+
+      bool supports(Protocol_Version version) const;
+
+      const std::vector<Protocol_Version> versions() const { return m_versions; }
+   private:
+      std::vector<Protocol_Version> m_versions;
+   };
+
+/**
+* Unknown extensions are deserialized as this type
+*/
+class BOTAN_UNSTABLE_API Unknown_Extension final : public Extension
+   {
+   public:
+      Unknown_Extension(Handshake_Extension_Type type,
+                        TLS_Data_Reader& reader,
+                        uint16_t extension_size);
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const override; // always fails
+
+      const std::vector<uint8_t>& value() { return m_value; }
+
+      bool empty() const override { return false; }
+
+      Handshake_Extension_Type type() const override { return m_type; }
+
+   private:
+      Handshake_Extension_Type m_type;
+      std::vector<uint8_t> m_value;
+   };
+
+/**
+* Represents a block of extensions in a hello message
+*/
+class BOTAN_UNSTABLE_API Extensions final
+   {
+   public:
+      std::set<Handshake_Extension_Type> extension_types() const;
+
+      template<typename T>
+      T* get() const
+         {
+         return dynamic_cast<T*>(get(T::static_type()));
+         }
+
+      template<typename T>
+      bool has() const
+         {
+         return get<T>() != nullptr;
+         }
+
+      void add(Extension* extn)
+         {
+         m_extensions[extn->type()].reset(extn);
+         }
+
+      Extension* get(Handshake_Extension_Type type) const
+         {
+         auto i = m_extensions.find(type);
+
+         if(i != m_extensions.end())
+            return i->second.get();
+         return nullptr;
+         }
+
+      std::vector<uint8_t> serialize(Connection_Side whoami) const;
+
+      void deserialize(TLS_Data_Reader& reader, Connection_Side from);
+
+      /**
+      * Remvoe an extension from this extensions object, if it exists.
+      * Returns true if the extension existed (and thus is now removed),
+      * otherwise false (the extension wasn't set in the first place).
+      */
+      bool remove_extension(Handshake_Extension_Type typ);
+
+      Extensions() = default;
+
+      Extensions(TLS_Data_Reader& reader, Connection_Side side)
+         {
+         deserialize(reader, side);
+         }
+
+   private:
+      Extensions(const Extensions&) = delete;
+      Extensions& operator=(const Extensions&) = delete;
+
+      std::map<Handshake_Extension_Type, std::unique_ptr<Extension>> m_extensions;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_handshake_hash.cpp b/third_party/botan/src/lib/tls/tls_handshake_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_hash.cpp
@@ -0,0 +1,34 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Return a TLS Handshake Hash
+*/
+secure_vector<uint8_t> Handshake_Hash::final(Protocol_Version version,
+                                          const std::string& mac_algo) const
+   {
+   std::string hash_algo = mac_algo;
+   if(!version.supports_ciphersuite_specific_prf())
+      hash_algo = "Parallel(MD5,SHA-160)";
+   else if(mac_algo == "MD5" || mac_algo == "SHA-1")
+      hash_algo = "SHA-256";
+
+   std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
+   hash->update(m_data);
+   return hash->final();
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_handshake_hash.h b/third_party/botan/src/lib/tls/tls_handshake_hash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_hash.h
@@ -0,0 +1,44 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_HASH_H_
+#define BOTAN_TLS_HANDSHAKE_HASH_H_
+
+#include <botan/secmem.h>
+#include <botan/tls_version.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Handshake Hash
+*/
+class Handshake_Hash final
+   {
+   public:
+      void update(const uint8_t in[], size_t length)
+         { m_data += std::make_pair(in, length); }
+
+      void update(const std::vector<uint8_t>& in)
+         { m_data += in; }
+
+      secure_vector<uint8_t> final(Protocol_Version version,
+                                const std::string& mac_algo) const;
+
+      const std::vector<uint8_t>& get_contents() const { return m_data; }
+
+      void reset() { m_data.clear(); }
+   private:
+      std::vector<uint8_t> m_data;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_handshake_io.cpp b/third_party/botan/src/lib/tls/tls_handshake_io.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_io.cpp
@@ -0,0 +1,480 @@
+/*
+* TLS Handshake IO
+* (C) 2012,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_record.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/tls_messages.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+inline size_t load_be24(const uint8_t q[3])
+   {
+   return make_uint32(0,
+                      q[0],
+                      q[1],
+                      q[2]);
+   }
+
+void store_be24(uint8_t out[3], size_t val)
+   {
+   out[0] = get_byte(1, static_cast<uint32_t>(val));
+   out[1] = get_byte(2, static_cast<uint32_t>(val));
+   out[2] = get_byte(3, static_cast<uint32_t>(val));
+   }
+
+uint64_t steady_clock_ms()
+   {
+   return std::chrono::duration_cast<std::chrono::milliseconds>(
+      std::chrono::steady_clock::now().time_since_epoch()).count();
+   }
+
+}
+
+Protocol_Version Stream_Handshake_IO::initial_record_version() const
+   {
+   return Protocol_Version::TLS_V10;
+   }
+
+void Stream_Handshake_IO::add_record(const uint8_t record[],
+                                     size_t record_len,
+                                     Record_Type record_type, uint64_t)
+   {
+   if(record_type == HANDSHAKE)
+      {
+      m_queue.insert(m_queue.end(), record, record + record_len);
+      }
+   else if(record_type == CHANGE_CIPHER_SPEC)
+      {
+      if(record_len != 1 || record[0] != 1)
+         throw Decoding_Error("Invalid ChangeCipherSpec");
+
+      // Pretend it's a regular handshake message of zero length
+      const uint8_t ccs_hs[] = { HANDSHAKE_CCS, 0, 0, 0 };
+      m_queue.insert(m_queue.end(), ccs_hs, ccs_hs + sizeof(ccs_hs));
+      }
+   else
+      throw Decoding_Error("Unknown message type " + std::to_string(record_type) + " in handshake processing");
+   }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Stream_Handshake_IO::get_next_record(bool)
+   {
+   if(m_queue.size() >= 4)
+      {
+      const size_t length = 4 + make_uint32(0, m_queue[1], m_queue[2], m_queue[3]);
+
+      if(m_queue.size() >= length)
+         {
+         Handshake_Type type = static_cast<Handshake_Type>(m_queue[0]);
+
+         if(type == HANDSHAKE_NONE)
+            throw Decoding_Error("Invalid handshake message type");
+
+         std::vector<uint8_t> contents(m_queue.begin() + 4,
+                                       m_queue.begin() + length);
+
+         m_queue.erase(m_queue.begin(), m_queue.begin() + length);
+
+         return std::make_pair(type, contents);
+         }
+      }
+
+   return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+   }
+
+std::vector<uint8_t>
+Stream_Handshake_IO::format(const std::vector<uint8_t>& msg,
+                            Handshake_Type type) const
+   {
+   std::vector<uint8_t> send_buf(4 + msg.size());
+
+   const size_t buf_size = msg.size();
+
+   send_buf[0] = static_cast<uint8_t>(type);
+
+   store_be24(&send_buf[1], buf_size);
+
+   if (msg.size() > 0)
+      {
+      copy_mem(&send_buf[4], msg.data(), msg.size());
+      }
+
+   return send_buf;
+   }
+
+std::vector<uint8_t> Stream_Handshake_IO::send_under_epoch(const Handshake_Message& /*msg*/, uint16_t /*epoch*/)
+   {
+   throw Invalid_State("Not possible to send under arbitrary epoch with stream based TLS");
+   }
+
+std::vector<uint8_t> Stream_Handshake_IO::send(const Handshake_Message& msg)
+   {
+   const std::vector<uint8_t> msg_bits = msg.serialize();
+
+   if(msg.type() == HANDSHAKE_CCS)
+      {
+      m_send_hs(CHANGE_CIPHER_SPEC, msg_bits);
+      return std::vector<uint8_t>(); // not included in handshake hashes
+      }
+
+   const std::vector<uint8_t> buf = format(msg_bits, msg.type());
+   m_send_hs(HANDSHAKE, buf);
+   return buf;
+   }
+
+Protocol_Version Datagram_Handshake_IO::initial_record_version() const
+   {
+   return Protocol_Version::DTLS_V10;
+   }
+
+void Datagram_Handshake_IO::retransmit_last_flight()
+   {
+   const size_t flight_idx = (m_flights.size() == 1) ? 0 : (m_flights.size() - 2);
+   retransmit_flight(flight_idx);
+   }
+
+void Datagram_Handshake_IO::retransmit_flight(size_t flight_idx)
+   {
+   const std::vector<uint16_t>& flight = m_flights.at(flight_idx);
+
+   BOTAN_ASSERT(flight.size() > 0, "Nonempty flight to retransmit");
+
+   uint16_t epoch = m_flight_data[flight[0]].epoch;
+
+   for(auto msg_seq : flight)
+      {
+      auto& msg = m_flight_data[msg_seq];
+
+      if(msg.epoch != epoch)
+         {
+         // Epoch gap: insert the CCS
+         std::vector<uint8_t> ccs(1, 1);
+         m_send_hs(epoch, CHANGE_CIPHER_SPEC, ccs);
+         }
+
+      send_message(msg_seq, msg.epoch, msg.msg_type, msg.msg_bits);
+      epoch = msg.epoch;
+      }
+   }
+
+bool Datagram_Handshake_IO::timeout_check()
+   {
+   if(m_last_write == 0 || (m_flights.size() > 1 && !m_flights.rbegin()->empty()))
+      {
+      /*
+      If we haven't written anything yet obviously no timeout.
+      Also no timeout possible if we are mid-flight,
+      */
+      return false;
+      }
+
+   const uint64_t ms_since_write = steady_clock_ms() - m_last_write;
+
+   if(ms_since_write < m_next_timeout)
+      return false;
+
+   retransmit_last_flight();
+
+   m_next_timeout = std::min(2 * m_next_timeout, m_max_timeout);
+   return true;
+   }
+
+void Datagram_Handshake_IO::add_record(const uint8_t record[],
+                                       size_t record_len,
+                                       Record_Type record_type,
+                                       uint64_t record_sequence)
+   {
+   const uint16_t epoch = static_cast<uint16_t>(record_sequence >> 48);
+
+   if(record_type == CHANGE_CIPHER_SPEC)
+      {
+      if(record_len != 1 || record[0] != 1)
+         throw Decoding_Error("Invalid ChangeCipherSpec");
+
+      // TODO: check this is otherwise empty
+      m_ccs_epochs.insert(epoch);
+      return;
+      }
+
+   const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
+
+   while(record_len)
+      {
+      if(record_len < DTLS_HANDSHAKE_HEADER_LEN)
+         return; // completely bogus? at least degenerate/weird
+
+      const uint8_t msg_type = record[0];
+      const size_t msg_len = load_be24(&record[1]);
+      const uint16_t message_seq = load_be<uint16_t>(&record[4], 0);
+      const size_t fragment_offset = load_be24(&record[6]);
+      const size_t fragment_length = load_be24(&record[9]);
+
+      const size_t total_size = DTLS_HANDSHAKE_HEADER_LEN + fragment_length;
+
+      if(record_len < total_size)
+         throw Decoding_Error("Bad lengths in DTLS header");
+
+      if(message_seq >= m_in_message_seq)
+         {
+         m_messages[message_seq].add_fragment(&record[DTLS_HANDSHAKE_HEADER_LEN],
+                                              fragment_length,
+                                              fragment_offset,
+                                              epoch,
+                                              msg_type,
+                                              msg_len);
+         }
+      else
+         {
+         // TODO: detect retransmitted flight
+         }
+
+      record += total_size;
+      record_len -= total_size;
+      }
+   }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Datagram_Handshake_IO::get_next_record(bool expecting_ccs)
+   {
+   // Expecting a message means the last flight is concluded
+   if(!m_flights.rbegin()->empty())
+      m_flights.push_back(std::vector<uint16_t>());
+
+   if(expecting_ccs)
+      {
+      if(!m_messages.empty())
+         {
+         const uint16_t current_epoch = m_messages.begin()->second.epoch();
+
+         if(m_ccs_epochs.count(current_epoch))
+            return std::make_pair(HANDSHAKE_CCS, std::vector<uint8_t>());
+         }
+      return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+      }
+
+   auto i = m_messages.find(m_in_message_seq);
+
+   if(i == m_messages.end() || !i->second.complete())
+      {
+      return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+      }
+
+   m_in_message_seq += 1;
+
+   return i->second.message();
+   }
+
+void Datagram_Handshake_IO::Handshake_Reassembly::add_fragment(
+   const uint8_t fragment[],
+   size_t fragment_length,
+   size_t fragment_offset,
+   uint16_t epoch,
+   uint8_t msg_type,
+   size_t msg_length)
+   {
+   if(complete())
+      return; // already have entire message, ignore this
+
+   if(m_msg_type == HANDSHAKE_NONE)
+      {
+      m_epoch = epoch;
+      m_msg_type = msg_type;
+      m_msg_length = msg_length;
+      }
+
+   if(msg_type != m_msg_type || msg_length != m_msg_length || epoch != m_epoch)
+      throw Decoding_Error("Inconsistent values in fragmented DTLS handshake header");
+
+   if(fragment_offset > m_msg_length)
+      throw Decoding_Error("Fragment offset past end of message");
+
+   if(fragment_offset + fragment_length > m_msg_length)
+      throw Decoding_Error("Fragment overlaps past end of message");
+
+   if(fragment_offset == 0 && fragment_length == m_msg_length)
+      {
+      m_fragments.clear();
+      m_message.assign(fragment, fragment+fragment_length);
+      }
+   else
+      {
+      /*
+      * FIXME. This is a pretty lame way to do defragmentation, huge
+      * overhead with a tree node per byte.
+      *
+      * Also should confirm that all overlaps have no changes,
+      * otherwise we expose ourselves to the classic fingerprinting
+      * and IDS evasion attacks on IP fragmentation.
+      */
+      for(size_t i = 0; i != fragment_length; ++i)
+         m_fragments[fragment_offset+i] = fragment[i];
+
+      if(m_fragments.size() == m_msg_length)
+         {
+         m_message.resize(m_msg_length);
+         for(size_t i = 0; i != m_msg_length; ++i)
+            m_message[i] = m_fragments[i];
+         m_fragments.clear();
+         }
+      }
+   }
+
+bool Datagram_Handshake_IO::Handshake_Reassembly::complete() const
+   {
+   return (m_msg_type != HANDSHAKE_NONE && m_message.size() == m_msg_length);
+   }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Datagram_Handshake_IO::Handshake_Reassembly::message() const
+   {
+   if(!complete())
+      throw Internal_Error("Datagram_Handshake_IO - message not complete");
+
+   return std::make_pair(static_cast<Handshake_Type>(m_msg_type), m_message);
+   }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format_fragment(const uint8_t fragment[],
+                                       size_t frag_len,
+                                       uint16_t frag_offset,
+                                       uint16_t msg_len,
+                                       Handshake_Type type,
+                                       uint16_t msg_sequence) const
+   {
+   std::vector<uint8_t> send_buf(12 + frag_len);
+
+   send_buf[0] = static_cast<uint8_t>(type);
+
+   store_be24(&send_buf[1], msg_len);
+
+   store_be(msg_sequence, &send_buf[4]);
+
+   store_be24(&send_buf[6], frag_offset);
+   store_be24(&send_buf[9], frag_len);
+
+   if (frag_len > 0)
+      {
+      copy_mem(&send_buf[12], fragment, frag_len);
+      }
+
+   return send_buf;
+   }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format_w_seq(const std::vector<uint8_t>& msg,
+                                    Handshake_Type type,
+                                    uint16_t msg_sequence) const
+   {
+   return format_fragment(msg.data(), msg.size(), 0, static_cast<uint16_t>(msg.size()), type, msg_sequence);
+   }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format(const std::vector<uint8_t>& msg,
+                              Handshake_Type type) const
+   {
+   return format_w_seq(msg, type, m_in_message_seq - 1);
+   }
+
+std::vector<uint8_t> Datagram_Handshake_IO::send(const Handshake_Message& msg)
+   {
+   return this->send_under_epoch(msg, m_seqs.current_write_epoch());
+   }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::send_under_epoch(const Handshake_Message& msg, uint16_t epoch)
+   {
+   const std::vector<uint8_t> msg_bits = msg.serialize();
+   const Handshake_Type msg_type = msg.type();
+
+   if(msg_type == HANDSHAKE_CCS)
+      {
+      m_send_hs(epoch, CHANGE_CIPHER_SPEC, msg_bits);
+      return std::vector<uint8_t>(); // not included in handshake hashes
+      }
+   else if(msg_type == HELLO_VERIFY_REQUEST)
+      {
+      // This message is not included in the handshake hashes
+      send_message(m_out_message_seq, epoch, msg_type, msg_bits);
+      m_out_message_seq += 1;
+      return std::vector<uint8_t>();
+      }
+
+   // Note: not saving CCS, instead we know it was there due to change in epoch
+   m_flights.rbegin()->push_back(m_out_message_seq);
+   m_flight_data[m_out_message_seq] = Message_Info(epoch, msg_type, msg_bits);
+
+   m_out_message_seq += 1;
+   m_last_write = steady_clock_ms();
+   m_next_timeout = m_initial_timeout;
+
+   return send_message(m_out_message_seq - 1, epoch, msg_type, msg_bits);
+   }
+
+std::vector<uint8_t> Datagram_Handshake_IO::send_message(uint16_t msg_seq,
+                                                      uint16_t epoch,
+                                                      Handshake_Type msg_type,
+                                                      const std::vector<uint8_t>& msg_bits)
+   {
+   const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
+
+   const std::vector<uint8_t> no_fragment =
+      format_w_seq(msg_bits, msg_type, msg_seq);
+
+   if(no_fragment.size() + DTLS_HEADER_SIZE <= m_mtu)
+      {
+      m_send_hs(epoch, HANDSHAKE, no_fragment);
+      }
+   else
+      {
+      size_t frag_offset = 0;
+
+      /**
+      * Largest possible overhead is for SHA-384 CBC ciphers, with 16 byte IV,
+      * 16+ for padding and 48 bytes for MAC. 128 is probably a strict
+      * over-estimate here. When CBC ciphers are removed this can be reduced
+      * since AEAD modes have no padding, at most 16 byte mac, and smaller
+      * per-record nonce.
+      */
+      const size_t ciphersuite_overhead = (epoch > 0) ? 128 : 0;
+      const size_t header_overhead = DTLS_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_LEN;
+
+      if(m_mtu <= (header_overhead + ciphersuite_overhead))
+         throw Invalid_Argument("DTLS MTU is too small to send headers");
+
+      const size_t max_rec_size = m_mtu - (header_overhead + ciphersuite_overhead);
+
+      while(frag_offset != msg_bits.size())
+         {
+         const size_t frag_len = std::min<size_t>(msg_bits.size() - frag_offset, max_rec_size);
+
+         const std::vector<uint8_t> frag =
+            format_fragment(&msg_bits[frag_offset],
+                            frag_len,
+                            static_cast<uint16_t>(frag_offset),
+                            static_cast<uint16_t>(msg_bits.size()),
+                            msg_type,
+                            msg_seq);
+
+         m_send_hs(epoch, HANDSHAKE, frag);
+
+         frag_offset += frag_len;
+         }
+      }
+
+   return no_fragment;
+   }
+
+}
+}
diff --git a/third_party/botan/src/lib/tls/tls_handshake_io.h b/third_party/botan/src/lib/tls/tls_handshake_io.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_io.h
@@ -0,0 +1,218 @@
+/*
+* TLS Handshake Serialization
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_IO_H_
+#define BOTAN_TLS_HANDSHAKE_IO_H_
+
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <functional>
+#include <vector>
+#include <deque>
+#include <map>
+#include <set>
+#include <utility>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_Message;
+
+/**
+* Handshake IO Interface
+*/
+class Handshake_IO
+   {
+   public:
+      virtual Protocol_Version initial_record_version() const = 0;
+
+      virtual std::vector<uint8_t> send(const Handshake_Message& msg) = 0;
+
+      virtual std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) = 0;
+
+      virtual bool timeout_check() = 0;
+
+      virtual std::vector<uint8_t> format(
+         const std::vector<uint8_t>& handshake_msg,
+         Handshake_Type handshake_type) const = 0;
+
+      virtual void add_record(const uint8_t record[],
+                              size_t record_len,
+                              Record_Type type,
+                              uint64_t sequence_number) = 0;
+
+      /**
+      * Returns (HANDSHAKE_NONE, std::vector<>()) if no message currently available
+      */
+      virtual std::pair<Handshake_Type, std::vector<uint8_t>>
+         get_next_record(bool expecting_ccs) = 0;
+
+      Handshake_IO() = default;
+
+      Handshake_IO(const Handshake_IO&) = delete;
+
+      Handshake_IO& operator=(const Handshake_IO&) = delete;
+
+      virtual ~Handshake_IO() = default;
+   };
+
+/**
+* Handshake IO for stream-based handshakes
+*/
+class Stream_Handshake_IO final : public Handshake_IO
+   {
+   public:
+      typedef std::function<void (uint8_t, const std::vector<uint8_t>&)> writer_fn;
+
+      explicit Stream_Handshake_IO(writer_fn writer) : m_send_hs(writer) {}
+
+      Protocol_Version initial_record_version() const override;
+
+      bool timeout_check() override { return false; }
+
+      std::vector<uint8_t> send(const Handshake_Message& msg) override;
+
+      std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
+
+      std::vector<uint8_t> format(
+         const std::vector<uint8_t>& handshake_msg,
+         Handshake_Type handshake_type) const override;
+
+      void add_record(const uint8_t record[],
+                      size_t record_len,
+                      Record_Type type,
+                      uint64_t sequence_number) override;
+
+      std::pair<Handshake_Type, std::vector<uint8_t>>
+         get_next_record(bool expecting_ccs) override;
+   private:
+      std::deque<uint8_t> m_queue;
+      writer_fn m_send_hs;
+   };
+
+/**
+* Handshake IO for datagram-based handshakes
+*/
+class Datagram_Handshake_IO final : public Handshake_IO
+   {
+   public:
+      typedef std::function<void (uint16_t, uint8_t, const std::vector<uint8_t>&)> writer_fn;
+
+      Datagram_Handshake_IO(writer_fn writer,
+                            class Connection_Sequence_Numbers& seq,
+                            uint16_t mtu, uint64_t initial_timeout_ms, uint64_t max_timeout_ms) :
+         m_seqs(seq),
+         m_flights(1),
+         m_initial_timeout(initial_timeout_ms),
+         m_max_timeout(max_timeout_ms),
+         m_send_hs(writer),
+         m_mtu(mtu)
+         {}
+
+      Protocol_Version initial_record_version() const override;
+
+      bool timeout_check() override;
+
+      std::vector<uint8_t> send(const Handshake_Message& msg) override;
+
+      std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
+
+      std::vector<uint8_t> format(
+         const std::vector<uint8_t>& handshake_msg,
+         Handshake_Type handshake_type) const override;
+
+      void add_record(const uint8_t record[],
+                      size_t record_len,
+                      Record_Type type,
+                      uint64_t sequence_number) override;
+
+      std::pair<Handshake_Type, std::vector<uint8_t>>
+         get_next_record(bool expecting_ccs) override;
+   private:
+      void retransmit_flight(size_t flight);
+      void retransmit_last_flight();
+
+      std::vector<uint8_t> format_fragment(
+         const uint8_t fragment[],
+         size_t fragment_len,
+         uint16_t frag_offset,
+         uint16_t msg_len,
+         Handshake_Type type,
+         uint16_t msg_sequence) const;
+
+      std::vector<uint8_t> format_w_seq(
+         const std::vector<uint8_t>& handshake_msg,
+         Handshake_Type handshake_type,
+         uint16_t msg_sequence) const;
+
+      std::vector<uint8_t> send_message(uint16_t msg_seq, uint16_t epoch,
+                                     Handshake_Type msg_type,
+                                     const std::vector<uint8_t>& msg);
+
+      class Handshake_Reassembly final
+         {
+         public:
+            void add_fragment(const uint8_t fragment[],
+                              size_t fragment_length,
+                              size_t fragment_offset,
+                              uint16_t epoch,
+                              uint8_t msg_type,
+                              size_t msg_length);
+
+            bool complete() const;
+
+            uint16_t epoch() const { return m_epoch; }
+
+            std::pair<Handshake_Type, std::vector<uint8_t>> message() const;
+         private:
+            uint8_t m_msg_type = HANDSHAKE_NONE;
+            size_t m_msg_length = 0;
+            uint16_t m_epoch = 0;
+
+            // vector<bool> m_seen;
+            // vector<uint8_t> m_fragments
+            std::map<size_t, uint8_t> m_fragments;
+            std::vector<uint8_t> m_message;
+         };
+
+      struct Message_Info final
+         {
+         Message_Info(uint16_t e, Handshake_Type mt, const std::vector<uint8_t>& msg) :
+            epoch(e), msg_type(mt), msg_bits(msg) {}
+
+         Message_Info() : epoch(0xFFFF), msg_type(HANDSHAKE_NONE) {}
+
+         uint16_t epoch;
+         Handshake_Type msg_type;
+         std::vector<uint8_t> msg_bits;
+         };
+
+      class Connection_Sequence_Numbers& m_seqs;
+      std::map<uint16_t, Handshake_Reassembly> m_messages;
+      std::set<uint16_t> m_ccs_epochs;
+      std::vector<std::vector<uint16_t>> m_flights;
+      std::map<uint16_t, Message_Info> m_flight_data;
+
+      uint64_t m_initial_timeout = 0;
+      uint64_t m_max_timeout = 0;
+
+      uint64_t m_last_write = 0;
+      uint64_t m_next_timeout = 0;
+
+      uint16_t m_in_message_seq = 0;
+      uint16_t m_out_message_seq = 0;
+
+      writer_fn m_send_hs;
+      uint16_t m_mtu;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_handshake_msg.h b/third_party/botan/src/lib/tls/tls_handshake_msg.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_msg.h
@@ -0,0 +1,51 @@
+/*
+* TLS Handshake Message
+* (C) 2012 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_MSG_H_
+#define BOTAN_TLS_HANDSHAKE_MSG_H_
+
+#include <botan/tls_magic.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_IO;
+class Handshake_Hash;
+
+/**
+* TLS Handshake Message Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) Handshake_Message
+   {
+   public:
+      /**
+      * @return string representation of this message type
+      */
+      std::string type_string() const;
+
+      /**
+      * @return the message type
+      */
+      virtual Handshake_Type type() const = 0;
+
+      /**
+      * @return DER representation of this message
+      */
+      virtual std::vector<uint8_t> serialize() const = 0;
+
+      virtual ~Handshake_Message() = default;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_handshake_state.cpp b/third_party/botan/src/lib/tls/tls_handshake_state.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_state.cpp
@@ -0,0 +1,580 @@
+/*
+* TLS Handshaking
+* (C) 2004-2006,2011,2012,2015,2016 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_record.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string Handshake_Message::type_string() const
+   {
+   return handshake_type_to_string(type());
+   }
+
+const char* handshake_type_to_string(Handshake_Type type)
+   {
+   switch(type)
+      {
+      case HELLO_VERIFY_REQUEST:
+         return "hello_verify_request";
+
+      case HELLO_REQUEST:
+         return "hello_request";
+
+      case CLIENT_HELLO:
+         return "client_hello";
+
+      case SERVER_HELLO:
+         return "server_hello";
+
+      case CERTIFICATE:
+         return "certificate";
+
+      case CERTIFICATE_URL:
+         return "certificate_url";
+
+      case CERTIFICATE_STATUS:
+         return "certificate_status";
+
+      case SERVER_KEX:
+         return "server_key_exchange";
+
+      case CERTIFICATE_REQUEST:
+         return "certificate_request";
+
+      case SERVER_HELLO_DONE:
+         return "server_hello_done";
+
+      case CERTIFICATE_VERIFY:
+         return "certificate_verify";
+
+      case CLIENT_KEX:
+         return "client_key_exchange";
+
+      case NEW_SESSION_TICKET:
+         return "new_session_ticket";
+
+      case HANDSHAKE_CCS:
+         return "change_cipher_spec";
+
+      case FINISHED:
+         return "finished";
+
+      case HANDSHAKE_NONE:
+         return "invalid";
+      }
+
+   throw TLS_Exception(Alert::UNEXPECTED_MESSAGE,
+                       "Unknown TLS handshake message type " + std::to_string(type));
+   }
+
+namespace {
+
+uint32_t bitmask_for_handshake_type(Handshake_Type type)
+   {
+   switch(type)
+      {
+      case HELLO_VERIFY_REQUEST:
+         return (1 << 0);
+
+      case HELLO_REQUEST:
+         return (1 << 1);
+
+      case CLIENT_HELLO:
+         return (1 << 2);
+
+      case SERVER_HELLO:
+         return (1 << 3);
+
+      case CERTIFICATE:
+         return (1 << 4);
+
+      case CERTIFICATE_URL:
+         return (1 << 5);
+
+      case CERTIFICATE_STATUS:
+         return (1 << 6);
+
+      case SERVER_KEX:
+         return (1 << 7);
+
+      case CERTIFICATE_REQUEST:
+         return (1 << 8);
+
+      case SERVER_HELLO_DONE:
+         return (1 << 9);
+
+      case CERTIFICATE_VERIFY:
+         return (1 << 10);
+
+      case CLIENT_KEX:
+         return (1 << 11);
+
+      case NEW_SESSION_TICKET:
+         return (1 << 12);
+
+      case HANDSHAKE_CCS:
+         return (1 << 13);
+
+      case FINISHED:
+         return (1 << 14);
+
+      // allow explicitly disabling new handshakes
+      case HANDSHAKE_NONE:
+         return 0;
+      }
+
+   throw TLS_Exception(Alert::UNEXPECTED_MESSAGE,
+                       "Unknown TLS handshake message type " + std::to_string(type));
+   }
+
+std::string handshake_mask_to_string(uint32_t mask, char combiner)
+   {
+   const Handshake_Type types[] = {
+      HELLO_VERIFY_REQUEST,
+      HELLO_REQUEST,
+      CLIENT_HELLO,
+      SERVER_HELLO,
+      CERTIFICATE,
+      CERTIFICATE_URL,
+      CERTIFICATE_STATUS,
+      SERVER_KEX,
+      CERTIFICATE_REQUEST,
+      SERVER_HELLO_DONE,
+      CERTIFICATE_VERIFY,
+      CLIENT_KEX,
+      NEW_SESSION_TICKET,
+      HANDSHAKE_CCS,
+      FINISHED
+   };
+
+   std::ostringstream o;
+   bool empty = true;
+
+   for(auto&& t : types)
+      {
+      if(mask & bitmask_for_handshake_type(t))
+         {
+         if(!empty)
+            o << combiner;
+         o << handshake_type_to_string(t);
+         empty = false;
+         }
+      }
+
+   return o.str();
+   }
+
+}
+
+/*
+* Initialize the SSL/TLS Handshake State
+*/
+Handshake_State::Handshake_State(Handshake_IO* io, Callbacks& cb) :
+   m_callbacks(cb),
+   m_handshake_io(io),
+   m_version(m_handshake_io->initial_record_version())
+   {
+   }
+
+void Handshake_State::note_message(const Handshake_Message& msg)
+   {
+   m_callbacks.tls_inspect_handshake_msg(msg);
+   }
+
+void Handshake_State::hello_verify_request(const Hello_Verify_Request& hello_verify)
+   {
+   note_message(hello_verify);
+
+   m_client_hello->update_hello_cookie(hello_verify);
+   hash().reset();
+   hash().update(handshake_io().send(*m_client_hello));
+   note_message(*m_client_hello);
+   }
+
+void Handshake_State::client_hello(Client_Hello* client_hello)
+   {
+   if(client_hello == nullptr)
+      {
+      m_client_hello.reset();
+      hash().reset();
+      }
+   else
+      {
+      m_client_hello.reset(client_hello);
+      note_message(*m_client_hello);
+      }
+   }
+
+void Handshake_State::server_hello(Server_Hello* server_hello)
+   {
+   m_server_hello.reset(server_hello);
+   m_ciphersuite = Ciphersuite::by_id(m_server_hello->ciphersuite());
+   note_message(*m_server_hello);
+   }
+
+void Handshake_State::server_certs(Certificate* server_certs)
+   {
+   m_server_certs.reset(server_certs);
+   note_message(*m_server_certs);
+   }
+
+void Handshake_State::server_cert_status(Certificate_Status* server_cert_status)
+   {
+   m_server_cert_status.reset(server_cert_status);
+   note_message(*m_server_cert_status);
+   }
+
+void Handshake_State::server_kex(Server_Key_Exchange* server_kex)
+   {
+   m_server_kex.reset(server_kex);
+   note_message(*m_server_kex);
+   }
+
+void Handshake_State::cert_req(Certificate_Req* cert_req)
+   {
+   m_cert_req.reset(cert_req);
+   note_message(*m_cert_req);
+   }
+
+void Handshake_State::server_hello_done(Server_Hello_Done* server_hello_done)
+   {
+   m_server_hello_done.reset(server_hello_done);
+   note_message(*m_server_hello_done);
+   }
+
+void Handshake_State::client_certs(Certificate* client_certs)
+   {
+   m_client_certs.reset(client_certs);
+   note_message(*m_client_certs);
+   }
+
+void Handshake_State::client_kex(Client_Key_Exchange* client_kex)
+   {
+   m_client_kex.reset(client_kex);
+   note_message(*m_client_kex);
+   }
+
+void Handshake_State::client_verify(Certificate_Verify* client_verify)
+   {
+   m_client_verify.reset(client_verify);
+   note_message(*m_client_verify);
+   }
+
+void Handshake_State::new_session_ticket(New_Session_Ticket* new_session_ticket)
+   {
+   m_new_session_ticket.reset(new_session_ticket);
+   note_message(*m_new_session_ticket);
+   }
+
+void Handshake_State::server_finished(Finished* server_finished)
+   {
+   m_server_finished.reset(server_finished);
+   note_message(*m_server_finished);
+   }
+
+void Handshake_State::client_finished(Finished* client_finished)
+   {
+   m_client_finished.reset(client_finished);
+   note_message(*m_client_finished);
+   }
+
+void Handshake_State::set_version(const Protocol_Version& version)
+   {
+   m_version = version;
+   }
+
+void Handshake_State::compute_session_keys()
+   {
+   m_session_keys = Session_Keys(this, client_kex()->pre_master_secret(), false);
+   }
+
+void Handshake_State::compute_session_keys(const secure_vector<uint8_t>& resume_master_secret)
+   {
+   m_session_keys = Session_Keys(this, resume_master_secret, true);
+   }
+
+void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg)
+   {
+   const uint32_t mask = bitmask_for_handshake_type(handshake_msg);
+
+   m_hand_received_mask |= mask;
+
+   const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap?
+
+   if(!ok)
+      {
+      const uint32_t seen_so_far = m_hand_received_mask & ~mask;
+
+      std::ostringstream msg;
+
+      msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(handshake_msg);
+
+      if(m_hand_expecting_mask == 0)
+         msg << " not expecting messages";
+      else
+         msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|');
+
+      if(seen_so_far != 0)
+         msg << " seen " << handshake_mask_to_string(seen_so_far, '+');
+
+      throw Unexpected_Message(msg.str());
+      }
+
+   /* We don't know what to expect next, so force a call to
+      set_expected_next; if it doesn't happen, the next transition
+      check will always fail which is what we want.
+   */
+   m_hand_expecting_mask = 0;
+   }
+
+void Handshake_State::set_expected_next(Handshake_Type handshake_msg)
+   {
+   m_hand_expecting_mask |= bitmask_for_handshake_type(handshake_msg);
+   }
+
+bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const
+   {
+   const uint32_t mask = bitmask_for_handshake_type(handshake_msg);
+
+   return (m_hand_received_mask & mask) != 0;
+   }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Handshake_State::get_next_handshake_msg()
+   {
+   const bool expecting_ccs =
+      (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0;
+
+   return m_handshake_io->get_next_record(expecting_ccs);
+   }
+
+std::string Handshake_State::srp_identifier() const
+   {
+#if defined(BOTAN_HAS_SRP6)
+   // Authenticated via the successful key exchange
+   if(ciphersuite().valid() && ciphersuite().kex_method() == Kex_Algo::SRP_SHA)
+      return client_hello()->srp_identifier();
+#endif
+
+   return "";
+   }
+
+
+std::vector<uint8_t> Handshake_State::session_ticket() const
+   {
+   if(new_session_ticket() && !new_session_ticket()->ticket().empty())
+      return new_session_ticket()->ticket();
+
+   return client_hello()->session_ticket();
+   }
+
+KDF* Handshake_State::protocol_specific_prf() const
+   {
+   if(version().supports_ciphersuite_specific_prf())
+      {
+      const std::string prf_algo = ciphersuite().prf_algo();
+
+      if(prf_algo == "MD5" || prf_algo == "SHA-1")
+         return get_kdf("TLS-12-PRF(SHA-256)");
+
+      return get_kdf("TLS-12-PRF(" + prf_algo + ")");
+      }
+
+   // Old PRF used in TLS v1.0, v1.1 and DTLS v1.0
+   return get_kdf("TLS-PRF");
+   }
+
+std::pair<std::string, Signature_Format>
+Handshake_State::choose_sig_format(const Private_Key& key,
+                                   Signature_Scheme& chosen_scheme,
+                                   bool for_client_auth,
+                                   const Policy& policy) const
+   {
+   const std::string sig_algo = key.algo_name();
+
+   if(this->version().supports_negotiable_signature_algorithms())
+      {
+      const std::vector<Signature_Scheme> allowed = policy.allowed_signature_schemes();
+
+      std::vector<Signature_Scheme> requested =
+         (for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes();
+
+      if(requested.empty())
+         {
+         // Implicit SHA-1
+         requested.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+         requested.push_back(Signature_Scheme::ECDSA_SHA1);
+         requested.push_back(Signature_Scheme::DSA_SHA1);
+         }
+
+      for(Signature_Scheme scheme : allowed)
+         {
+         if(signature_scheme_is_known(scheme) == false)
+            {
+            continue;
+            }
+
+         if(signature_algorithm_of_scheme(scheme) == sig_algo)
+            {
+            if(std::find(requested.begin(), requested.end(), scheme) != requested.end())
+               {
+               chosen_scheme = scheme;
+               break;
+               }
+            }
+         }
+
+      const std::string hash = hash_function_of_scheme(chosen_scheme);
+
+      if(!policy.allowed_signature_hash(hash))
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Policy refuses to accept signing with any hash supported by peer");
+         }
+
+      if(sig_algo == "RSA")
+         {
+         return std::make_pair(padding_string_for_scheme(chosen_scheme), IEEE_1363);
+         }
+      else if(sig_algo == "DSA" || sig_algo == "ECDSA")
+         {
+         return std::make_pair(padding_string_for_scheme(chosen_scheme), DER_SEQUENCE);
+         }
+      }
+   else
+      {
+      if(sig_algo == "RSA")
+         {
+         const std::string padding = "PKCS1v15(Parallel(MD5,SHA-160))";
+         return std::make_pair(padding, IEEE_1363);
+         }
+      else if(sig_algo == "DSA" || sig_algo == "ECDSA")
+         {
+         const std::string padding = "EMSA1(SHA-1)";
+         return std::make_pair(padding, DER_SEQUENCE);
+         }
+      }
+
+   throw Invalid_Argument(sig_algo + " is invalid/unknown for TLS signatures");
+   }
+
+namespace {
+
+bool supported_algos_include(
+   const std::vector<Signature_Scheme>& schemes,
+   const std::string& key_type,
+   const std::string& hash_type)
+   {
+   for(Signature_Scheme scheme : schemes)
+      {
+      if(signature_scheme_is_known(scheme) &&
+         hash_function_of_scheme(scheme) == hash_type &&
+         signature_algorithm_of_scheme(scheme) == key_type)
+         {
+         return true;
+         }
+      }
+
+   return false;
+   }
+
+}
+
+std::pair<std::string, Signature_Format>
+Handshake_State::parse_sig_format(const Public_Key& key,
+                                  Signature_Scheme scheme,
+                                  bool for_client_auth,
+                                  const Policy& policy) const
+   {
+   const std::string key_type = key.algo_name();
+
+   if(!policy.allowed_signature_method(key_type))
+      {
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                          "Rejecting " + key_type + " signature");
+      }
+
+   if(this->version().supports_negotiable_signature_algorithms() == false)
+      {
+      if(scheme != Signature_Scheme::NONE)
+         throw Decoding_Error("Counterparty sent hash/sig IDs with old version");
+
+      /*
+      There is no check on the acceptability of a v1.0/v1.1 hash type,
+      since it's implicit with use of the protocol
+      */
+
+      if(key_type == "RSA")
+         {
+         const std::string padding = "PKCS1v15(Parallel(MD5,SHA-160))";
+         return std::make_pair(padding, IEEE_1363);
+         }
+      else if(key_type == "DSA" || key_type == "ECDSA")
+         {
+         const std::string padding = "EMSA1(SHA-1)";
+         return std::make_pair(padding, DER_SEQUENCE);
+         }
+      else
+         throw Invalid_Argument(key_type + " is invalid/unknown for TLS signatures");
+      }
+
+   if(scheme == Signature_Scheme::NONE)
+      throw Decoding_Error("Counterparty did not send hash/sig IDS");
+
+   if(key_type != signature_algorithm_of_scheme(scheme))
+      throw Decoding_Error("Counterparty sent inconsistent key and sig types");
+
+   if(for_client_auth && !cert_req())
+      {
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                          "No certificate verify set");
+      }
+
+   /*
+   Confirm the signature type we just received against the
+   supported_algos list that we sent; it better be there.
+   */
+
+   const std::vector<Signature_Scheme> supported_algos =
+      for_client_auth ? cert_req()->signature_schemes() :
+      client_hello()->signature_schemes();
+
+   if(!signature_scheme_is_known(scheme))
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                          "Peer sent unknown signature scheme");
+
+   const std::string hash_algo = hash_function_of_scheme(scheme);
+
+   if(!supported_algos_include(supported_algos, key_type, hash_algo))
+      {
+      throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+                          "TLS signature extension did not allow for " +
+                          key_type + "/" + hash_algo + " signature");
+      }
+
+   if(key_type == "RSA")
+      {
+      return std::make_pair(padding_string_for_scheme(scheme), IEEE_1363);
+      }
+   else if(key_type == "DSA" || key_type == "ECDSA")
+      {
+      return std::make_pair(padding_string_for_scheme(scheme), DER_SEQUENCE);
+      }
+
+   throw Invalid_Argument(key_type + " is invalid/unknown for TLS signatures");
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_handshake_state.h b/third_party/botan/src/lib/tls/tls_handshake_state.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_handshake_state.h
@@ -0,0 +1,206 @@
+/*
+* TLS Handshake State
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_STATE_H_
+#define BOTAN_TLS_HANDSHAKE_STATE_H_
+
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_handshake_msg.h>
+#include <botan/tls_callbacks.h>
+#include <botan/pk_keys.h>
+#include <botan/pubkey.h>
+#include <functional>
+
+namespace Botan {
+
+class KDF;
+
+namespace TLS {
+
+class Callbacks;
+class Policy;
+
+class Hello_Verify_Request;
+class Client_Hello;
+class Server_Hello;
+class Certificate;
+class Certificate_Status;
+class Server_Key_Exchange;
+class Certificate_Req;
+class Server_Hello_Done;
+class Certificate;
+class Client_Key_Exchange;
+class Certificate_Verify;
+class New_Session_Ticket;
+class Finished;
+
+/**
+* SSL/TLS Handshake State
+*/
+class Handshake_State
+   {
+   public:
+      Handshake_State(Handshake_IO* io, Callbacks& callbacks);
+
+      virtual ~Handshake_State() = default;
+
+      Handshake_State(const Handshake_State&) = delete;
+      Handshake_State& operator=(const Handshake_State&) = delete;
+
+      Handshake_IO& handshake_io() { return *m_handshake_io; }
+
+      /**
+      * Return true iff we have received a particular message already
+      * @param msg_type the message type
+      */
+      bool received_handshake_msg(Handshake_Type msg_type) const;
+
+      /**
+      * Confirm that we were expecting this message type
+      * @param msg_type the message type
+      */
+      void confirm_transition_to(Handshake_Type msg_type);
+
+      /**
+      * Record that we are expecting a particular message type next
+      * @param msg_type the message type
+      */
+      void set_expected_next(Handshake_Type msg_type);
+
+      std::pair<Handshake_Type, std::vector<uint8_t>>
+         get_next_handshake_msg();
+
+      std::vector<uint8_t> session_ticket() const;
+
+      std::pair<std::string, Signature_Format>
+         parse_sig_format(const Public_Key& key,
+                          Signature_Scheme scheme,
+                          bool for_client_auth,
+                          const Policy& policy) const;
+
+      std::pair<std::string, Signature_Format>
+         choose_sig_format(const Private_Key& key,
+                           Signature_Scheme& scheme,
+                           bool for_client_auth,
+                           const Policy& policy) const;
+
+      std::string srp_identifier() const;
+
+      KDF* protocol_specific_prf() const;
+
+      Protocol_Version version() const { return m_version; }
+
+      void set_version(const Protocol_Version& version);
+
+      void hello_verify_request(const Hello_Verify_Request& hello_verify);
+
+      void client_hello(Client_Hello* client_hello);
+      void server_hello(Server_Hello* server_hello);
+      void server_certs(Certificate* server_certs);
+      void server_cert_status(Certificate_Status* server_cert_status);
+      void server_kex(Server_Key_Exchange* server_kex);
+      void cert_req(Certificate_Req* cert_req);
+      void server_hello_done(Server_Hello_Done* server_hello_done);
+      void client_certs(Certificate* client_certs);
+      void client_kex(Client_Key_Exchange* client_kex);
+      void client_verify(Certificate_Verify* client_verify);
+      void new_session_ticket(New_Session_Ticket* new_session_ticket);
+      void server_finished(Finished* server_finished);
+      void client_finished(Finished* client_finished);
+
+      const Client_Hello* client_hello() const
+         { return m_client_hello.get(); }
+
+      const Server_Hello* server_hello() const
+         { return m_server_hello.get(); }
+
+      const Certificate* server_certs() const
+         { return m_server_certs.get(); }
+
+      const Server_Key_Exchange* server_kex() const
+         { return m_server_kex.get(); }
+
+      const Certificate_Req* cert_req() const
+         { return m_cert_req.get(); }
+
+      const Server_Hello_Done* server_hello_done() const
+         { return m_server_hello_done.get(); }
+
+      const Certificate* client_certs() const
+         { return m_client_certs.get(); }
+
+      const Client_Key_Exchange* client_kex() const
+         { return m_client_kex.get(); }
+
+      const Certificate_Verify* client_verify() const
+         { return m_client_verify.get(); }
+
+      const Certificate_Status* server_cert_status() const
+         { return m_server_cert_status.get(); }
+
+      const New_Session_Ticket* new_session_ticket() const
+         { return m_new_session_ticket.get(); }
+
+      const Finished* server_finished() const
+         { return m_server_finished.get(); }
+
+      const Finished* client_finished() const
+         { return m_client_finished.get(); }
+
+      const Ciphersuite& ciphersuite() const { return m_ciphersuite; }
+
+      const Session_Keys& session_keys() const { return m_session_keys; }
+
+      Callbacks& callbacks() const { return m_callbacks; }
+
+      void compute_session_keys();
+
+      void compute_session_keys(const secure_vector<uint8_t>& resume_master_secret);
+
+      Handshake_Hash& hash() { return m_handshake_hash; }
+
+      const Handshake_Hash& hash() const { return m_handshake_hash; }
+
+      void note_message(const Handshake_Message& msg);
+   private:
+
+      Callbacks& m_callbacks;
+
+      std::unique_ptr<Handshake_IO> m_handshake_io;
+
+      uint32_t m_hand_expecting_mask = 0;
+      uint32_t m_hand_received_mask = 0;
+      Protocol_Version m_version;
+      Ciphersuite m_ciphersuite;
+      Session_Keys m_session_keys;
+      Handshake_Hash m_handshake_hash;
+
+      std::unique_ptr<Client_Hello> m_client_hello;
+      std::unique_ptr<Server_Hello> m_server_hello;
+      std::unique_ptr<Certificate> m_server_certs;
+      std::unique_ptr<Certificate_Status> m_server_cert_status;
+      std::unique_ptr<Server_Key_Exchange> m_server_kex;
+      std::unique_ptr<Certificate_Req> m_cert_req;
+      std::unique_ptr<Server_Hello_Done> m_server_hello_done;
+      std::unique_ptr<Certificate> m_client_certs;
+      std::unique_ptr<Client_Key_Exchange> m_client_kex;
+      std::unique_ptr<Certificate_Verify> m_client_verify;
+      std::unique_ptr<New_Session_Ticket> m_new_session_ticket;
+      std::unique_ptr<Finished> m_server_finished;
+      std::unique_ptr<Finished> m_client_finished;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_magic.h b/third_party/botan/src/lib/tls/tls_magic.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_magic.h
@@ -0,0 +1,72 @@
+/*
+* SSL/TLS Protocol Constants
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PROTOCOL_MAGIC_H_
+#define BOTAN_TLS_PROTOCOL_MAGIC_H_
+
+#include <botan/types.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(tls_magic.h)
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Protocol Constants for SSL/TLS
+*/
+enum Size_Limits {
+   TLS_HEADER_SIZE    = 5,
+   DTLS_HEADER_SIZE   = TLS_HEADER_SIZE + 8,
+
+   MAX_PLAINTEXT_SIZE = 16*1024,
+   MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024,
+   MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024,
+};
+
+// This will become an enum class in a future major release
+enum Connection_Side { CLIENT = 1, SERVER = 2 };
+
+// This will become an enum class in a future major release
+enum Record_Type {
+   CHANGE_CIPHER_SPEC = 20,
+   ALERT              = 21,
+   HANDSHAKE          = 22,
+   APPLICATION_DATA   = 23,
+
+   NO_RECORD          = 256
+};
+
+// This will become an enum class in a future major release
+enum Handshake_Type {
+   HELLO_REQUEST        = 0,
+   CLIENT_HELLO         = 1,
+   SERVER_HELLO         = 2,
+   HELLO_VERIFY_REQUEST = 3,
+   NEW_SESSION_TICKET   = 4, // RFC 5077
+   CERTIFICATE          = 11,
+   SERVER_KEX           = 12,
+   CERTIFICATE_REQUEST  = 13,
+   SERVER_HELLO_DONE    = 14,
+   CERTIFICATE_VERIFY   = 15,
+   CLIENT_KEX           = 16,
+   FINISHED             = 20,
+
+   CERTIFICATE_URL      = 21,
+   CERTIFICATE_STATUS   = 22,
+
+   HANDSHAKE_CCS        = 254, // Not a wire value
+   HANDSHAKE_NONE       = 255  // Null value
+};
+
+const char* handshake_type_to_string(Handshake_Type t);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_messages.h b/third_party/botan/src/lib/tls/tls_messages.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_messages.h
@@ -0,0 +1,653 @@
+/*
+* TLS Messages
+* (C) 2004-2011,2015 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_MESSAGES_H_
+#define BOTAN_TLS_MESSAGES_H_
+
+#include <botan/tls_extensions.h>
+#include <botan/tls_handshake_msg.h>
+#include <botan/tls_session.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/pk_keys.h>
+#include <botan/x509cert.h>
+#include <botan/ocsp.h>
+#include <vector>
+#include <string>
+#include <set>
+
+#if defined(BOTAN_HAS_CECPQ1)
+  #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+  #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+class Public_Key;
+class Credentials_Manager;
+
+namespace TLS {
+
+class Session;
+class Handshake_IO;
+class Handshake_State;
+class Callbacks;
+
+std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng,
+                                       const Policy& policy);
+
+/**
+* DTLS Hello Verify Request
+*/
+class BOTAN_UNSTABLE_API Hello_Verify_Request final : public Handshake_Message
+   {
+   public:
+      std::vector<uint8_t> serialize() const override;
+      Handshake_Type type() const override { return HELLO_VERIFY_REQUEST; }
+
+      const std::vector<uint8_t>& cookie() const { return m_cookie; }
+
+      explicit Hello_Verify_Request(const std::vector<uint8_t>& buf);
+
+      Hello_Verify_Request(const std::vector<uint8_t>& client_hello_bits,
+                           const std::string& client_identity,
+                           const SymmetricKey& secret_key);
+   private:
+      std::vector<uint8_t> m_cookie;
+   };
+
+/**
+* Client Hello Message
+*/
+class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message
+   {
+   public:
+      class Settings final
+         {
+         public:
+            Settings(const Protocol_Version version,
+                     const std::string& hostname = "",
+                     const std::string& srp_identifier = "") :
+               m_new_session_version(version),
+               m_hostname(hostname),
+               m_srp_identifier(srp_identifier) {}
+
+            const Protocol_Version protocol_version() const { return m_new_session_version; }
+            const std::string& hostname() const { return m_hostname; }
+            const std::string& srp_identifier() const { return m_srp_identifier; }
+
+         private:
+            const Protocol_Version m_new_session_version;
+            const std::string m_hostname;
+            const std::string m_srp_identifier;
+         };
+
+      Handshake_Type type() const override { return CLIENT_HELLO; }
+
+      Protocol_Version version() const { return m_version; }
+
+      std::vector<Protocol_Version> supported_versions() const;
+
+      const std::vector<uint8_t>& random() const { return m_random; }
+
+      const std::vector<uint8_t>& session_id() const { return m_session_id; }
+
+      const std::vector<uint8_t>& compression_methods() const { return m_comp_methods; }
+
+      const std::vector<uint16_t>& ciphersuites() const { return m_suites; }
+
+      bool offered_suite(uint16_t ciphersuite) const;
+
+      bool sent_fallback_scsv() const;
+
+      std::vector<Signature_Scheme> signature_schemes() const;
+
+      std::vector<Group_Params> supported_ecc_curves() const;
+
+      std::vector<Group_Params> supported_dh_groups() const;
+
+      bool prefers_compressed_ec_points() const;
+
+      std::string sni_hostname() const;
+
+#if defined(BOTAN_HAS_SRP6)
+      std::string srp_identifier() const;
+#endif
+
+      bool secure_renegotiation() const;
+
+      std::vector<uint8_t> renegotiation_info() const;
+
+      bool supports_session_ticket() const;
+
+      std::vector<uint8_t> session_ticket() const;
+
+      bool supports_alpn() const;
+
+      bool supports_extended_master_secret() const;
+
+      bool supports_cert_status_message() const;
+
+      bool supports_encrypt_then_mac() const;
+
+      bool sent_signature_algorithms() const;
+
+      std::vector<std::string> next_protocols() const;
+
+      std::vector<uint16_t> srtp_profiles() const;
+
+      void update_hello_cookie(const Hello_Verify_Request& hello_verify);
+
+      const std::vector<uint8_t>& cookie() const { return m_hello_cookie; }
+
+      std::vector<uint8_t> cookie_input_data() const;
+
+      std::set<Handshake_Extension_Type> extension_types() const
+         { return m_extensions.extension_types(); }
+
+      const Extensions& extensions() const { return m_extensions; }
+
+      Client_Hello(Handshake_IO& io,
+                   Handshake_Hash& hash,
+                   const Policy& policy,
+                   Callbacks& cb,
+                   RandomNumberGenerator& rng,
+                   const std::vector<uint8_t>& reneg_info,
+                   const Client_Hello::Settings& client_settings,
+                   const std::vector<std::string>& next_protocols);
+
+      Client_Hello(Handshake_IO& io,
+                   Handshake_Hash& hash,
+                   const Policy& policy,
+                   Callbacks& cb,
+                   RandomNumberGenerator& rng,
+                   const std::vector<uint8_t>& reneg_info,
+                   const Session& resumed_session,
+                   const std::vector<std::string>& next_protocols);
+
+      explicit Client_Hello(const std::vector<uint8_t>& buf);
+
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      Protocol_Version m_version;
+      std::vector<uint8_t> m_session_id;
+      std::vector<uint8_t> m_random;
+      std::vector<uint16_t> m_suites;
+      std::vector<uint8_t> m_comp_methods;
+      std::vector<uint8_t> m_hello_cookie; // DTLS only
+
+      Extensions m_extensions;
+   };
+
+/**
+* Server Hello Message
+*/
+class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message
+   {
+   public:
+      class Settings final
+         {
+         public:
+            Settings(const std::vector<uint8_t> new_session_id,
+                     Protocol_Version new_session_version,
+                     uint16_t ciphersuite,
+                     bool offer_session_ticket) :
+               m_new_session_id(new_session_id),
+               m_new_session_version(new_session_version),
+               m_ciphersuite(ciphersuite),
+               m_offer_session_ticket(offer_session_ticket) {}
+
+            const std::vector<uint8_t>& session_id() const { return m_new_session_id; }
+            Protocol_Version protocol_version() const { return m_new_session_version; }
+            uint16_t ciphersuite() const { return m_ciphersuite; }
+            bool offer_session_ticket() const { return m_offer_session_ticket; }
+
+         private:
+            const std::vector<uint8_t> m_new_session_id;
+            Protocol_Version m_new_session_version;
+            uint16_t m_ciphersuite;
+            bool m_offer_session_ticket;
+         };
+
+
+      Handshake_Type type() const override { return SERVER_HELLO; }
+
+      Protocol_Version version() const { return m_version; }
+
+      const std::vector<uint8_t>& random() const { return m_random; }
+
+      const std::vector<uint8_t>& session_id() const { return m_session_id; }
+
+      uint16_t ciphersuite() const { return m_ciphersuite; }
+
+      uint8_t compression_method() const { return m_comp_method; }
+
+      bool secure_renegotiation() const
+         {
+         return m_extensions.has<Renegotiation_Extension>();
+         }
+
+      std::vector<uint8_t> renegotiation_info() const
+         {
+         if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+            return reneg->renegotiation_info();
+         return std::vector<uint8_t>();
+         }
+
+      bool supports_extended_master_secret() const
+         {
+         return m_extensions.has<Extended_Master_Secret>();
+         }
+
+      bool supports_encrypt_then_mac() const
+         {
+         return m_extensions.has<Encrypt_then_MAC>();
+         }
+
+      bool supports_certificate_status_message() const
+         {
+         return m_extensions.has<Certificate_Status_Request>();
+         }
+
+      bool supports_session_ticket() const
+         {
+         return m_extensions.has<Session_Ticket>();
+         }
+
+      uint16_t srtp_profile() const
+         {
+         if(auto srtp = m_extensions.get<SRTP_Protection_Profiles>())
+            {
+            auto prof = srtp->profiles();
+            if(prof.size() != 1 || prof[0] == 0)
+               throw Decoding_Error("Server sent malformed DTLS-SRTP extension");
+            return prof[0];
+            }
+
+         return 0;
+         }
+
+      std::string next_protocol() const
+         {
+         if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>())
+            return alpn->single_protocol();
+         return "";
+         }
+
+      std::set<Handshake_Extension_Type> extension_types() const
+         { return m_extensions.extension_types(); }
+
+      const Extensions& extensions() const { return m_extensions; }
+
+      bool prefers_compressed_ec_points() const
+         {
+         if(auto ecc_formats = m_extensions.get<Supported_Point_Formats>())
+            {
+            return ecc_formats->prefers_compressed();
+            }
+         return false;
+         }
+
+      bool random_signals_downgrade() const;
+
+      Server_Hello(Handshake_IO& io,
+                   Handshake_Hash& hash,
+                   const Policy& policy,
+                   Callbacks& cb,
+                   RandomNumberGenerator& rng,
+                   const std::vector<uint8_t>& secure_reneg_info,
+                   const Client_Hello& client_hello,
+                   const Server_Hello::Settings& settings,
+                   const std::string next_protocol);
+
+      Server_Hello(Handshake_IO& io,
+                   Handshake_Hash& hash,
+                   const Policy& policy,
+                   Callbacks& cb,
+                   RandomNumberGenerator& rng,
+                   const std::vector<uint8_t>& secure_reneg_info,
+                   const Client_Hello& client_hello,
+                   Session& resumed_session,
+                   bool offer_session_ticket,
+                   const std::string& next_protocol);
+
+      explicit Server_Hello(const std::vector<uint8_t>& buf);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      Protocol_Version m_version;
+      std::vector<uint8_t> m_session_id, m_random;
+      uint16_t m_ciphersuite;
+      uint8_t m_comp_method;
+
+      Extensions m_extensions;
+   };
+
+/**
+* Client Key Exchange Message
+*/
+class BOTAN_UNSTABLE_API Client_Key_Exchange final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return CLIENT_KEX; }
+
+      const secure_vector<uint8_t>& pre_master_secret() const
+         { return m_pre_master; }
+
+      Client_Key_Exchange(Handshake_IO& io,
+                          Handshake_State& state,
+                          const Policy& policy,
+                          Credentials_Manager& creds,
+                          const Public_Key* server_public_key,
+                          const std::string& hostname,
+                          RandomNumberGenerator& rng);
+
+      Client_Key_Exchange(const std::vector<uint8_t>& buf,
+                          const Handshake_State& state,
+                          const Private_Key* server_rsa_kex_key,
+                          Credentials_Manager& creds,
+                          const Policy& policy,
+                          RandomNumberGenerator& rng);
+
+   private:
+      std::vector<uint8_t> serialize() const override
+         { return m_key_material; }
+
+      std::vector<uint8_t> m_key_material;
+      secure_vector<uint8_t> m_pre_master;
+   };
+
+/**
+* Certificate Message
+*/
+class BOTAN_UNSTABLE_API Certificate final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return CERTIFICATE; }
+      const std::vector<X509_Certificate>& cert_chain() const { return m_certs; }
+
+      size_t count() const { return m_certs.size(); }
+      bool empty() const { return m_certs.empty(); }
+
+      Certificate(Handshake_IO& io,
+                  Handshake_Hash& hash,
+                  const std::vector<X509_Certificate>& certs);
+
+      explicit Certificate(const std::vector<uint8_t>& buf, const Policy &policy);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      std::vector<X509_Certificate> m_certs;
+   };
+
+/**
+* Certificate Status (RFC 6066)
+*/
+class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return CERTIFICATE_STATUS; }
+
+      //std::shared_ptr<const OCSP::Response> response() const { return m_response; }
+
+      const std::vector<uint8_t>& response() const { return m_response; }
+
+      Certificate_Status(const std::vector<uint8_t>& buf);
+
+      Certificate_Status(Handshake_IO& io,
+                         Handshake_Hash& hash,
+                         std::shared_ptr<const OCSP::Response> response);
+
+      /*
+       * Create a Certificate_Status message using an already DER encoded OCSP response.
+       */
+      Certificate_Status(Handshake_IO& io,
+                         Handshake_Hash& hash,
+                         std::vector<uint8_t> const& raw_response_bytes );
+
+   private:
+      std::vector<uint8_t> serialize() const override;
+      std::vector<uint8_t> m_response;
+   };
+
+/**
+* Certificate Request Message
+*/
+class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return CERTIFICATE_REQUEST; }
+
+      const std::vector<std::string>& acceptable_cert_types() const
+         { return m_cert_key_types; }
+
+      const std::vector<X509_DN>& acceptable_CAs() const { return m_names; }
+
+      const std::vector<Signature_Scheme>& signature_schemes() const
+         {
+         return m_schemes;
+         }
+
+      Certificate_Req(Handshake_IO& io,
+                      Handshake_Hash& hash,
+                      const Policy& policy,
+                      const std::vector<X509_DN>& allowed_cas,
+                      Protocol_Version version);
+
+      Certificate_Req(const std::vector<uint8_t>& buf,
+                      Protocol_Version version);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      std::vector<X509_DN> m_names;
+      std::vector<std::string> m_cert_key_types;
+
+      std::vector<Signature_Scheme> m_schemes;
+   };
+
+/**
+* Certificate Verify Message
+*/
+class BOTAN_UNSTABLE_API Certificate_Verify final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return CERTIFICATE_VERIFY; }
+
+      /**
+      * Check the signature on a certificate verify message
+      * @param cert the purported certificate
+      * @param state the handshake state
+      * @param policy the TLS policy
+      */
+      bool verify(const X509_Certificate& cert,
+                  const Handshake_State& state,
+                  const Policy& policy) const;
+
+      Certificate_Verify(Handshake_IO& io,
+                         Handshake_State& state,
+                         const Policy& policy,
+                         RandomNumberGenerator& rng,
+                         const Private_Key* key);
+
+      Certificate_Verify(const std::vector<uint8_t>& buf,
+                         Protocol_Version version);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      std::vector<uint8_t> m_signature;
+      Signature_Scheme m_scheme = Signature_Scheme::NONE;
+   };
+
+/**
+* Finished Message
+*/
+class BOTAN_UNSTABLE_API Finished final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return FINISHED; }
+
+      std::vector<uint8_t> verify_data() const
+         { return m_verification_data; }
+
+      bool verify(const Handshake_State& state,
+                  Connection_Side side) const;
+
+      Finished(Handshake_IO& io,
+               Handshake_State& state,
+               Connection_Side side);
+
+      explicit Finished(const std::vector<uint8_t>& buf);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      std::vector<uint8_t> m_verification_data;
+   };
+
+/**
+* Hello Request Message
+*/
+class BOTAN_UNSTABLE_API Hello_Request final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return HELLO_REQUEST; }
+
+      explicit Hello_Request(Handshake_IO& io);
+      explicit Hello_Request(const std::vector<uint8_t>& buf);
+   private:
+      std::vector<uint8_t> serialize() const override;
+   };
+
+/**
+* Server Key Exchange Message
+*/
+class BOTAN_UNSTABLE_API Server_Key_Exchange final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return SERVER_KEX; }
+
+      const std::vector<uint8_t>& params() const { return m_params; }
+
+      bool verify(const Public_Key& server_key,
+                  const Handshake_State& state,
+                  const Policy& policy) const;
+
+      // Only valid for certain kex types
+      const Private_Key& server_kex_key() const;
+
+#if defined(BOTAN_HAS_SRP6)
+      // Only valid for SRP negotiation
+      SRP6_Server_Session& server_srp_params() const
+         {
+         BOTAN_ASSERT_NONNULL(m_srp_params);
+         return *m_srp_params;
+         }
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+      // Only valid for CECPQ1 negotiation
+      const CECPQ1_key& cecpq1_key() const
+         {
+         BOTAN_ASSERT_NONNULL(m_cecpq1_key);
+         return *m_cecpq1_key;
+         }
+#endif
+
+      Server_Key_Exchange(Handshake_IO& io,
+                          Handshake_State& state,
+                          const Policy& policy,
+                          Credentials_Manager& creds,
+                          RandomNumberGenerator& rng,
+                          const Private_Key* signing_key = nullptr);
+
+      Server_Key_Exchange(const std::vector<uint8_t>& buf,
+                          Kex_Algo kex_alg,
+                          Auth_Method sig_alg,
+                          Protocol_Version version);
+
+      ~Server_Key_Exchange() = default;
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+#if defined(BOTAN_HAS_SRP6)
+      std::unique_ptr<SRP6_Server_Session> m_srp_params;
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+      std::unique_ptr<CECPQ1_key> m_cecpq1_key;
+#endif
+
+      std::unique_ptr<Private_Key> m_kex_key;
+
+      std::vector<uint8_t> m_params;
+
+      std::vector<uint8_t> m_signature;
+      Signature_Scheme m_scheme = Signature_Scheme::NONE;
+   };
+
+/**
+* Server Hello Done Message
+*/
+class BOTAN_UNSTABLE_API Server_Hello_Done final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return SERVER_HELLO_DONE; }
+
+      Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash);
+      explicit Server_Hello_Done(const std::vector<uint8_t>& buf);
+   private:
+      std::vector<uint8_t> serialize() const override;
+   };
+
+/**
+* New Session Ticket Message
+*/
+class BOTAN_UNSTABLE_API New_Session_Ticket final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return NEW_SESSION_TICKET; }
+
+      uint32_t ticket_lifetime_hint() const { return m_ticket_lifetime_hint; }
+      const std::vector<uint8_t>& ticket() const { return m_ticket; }
+
+      New_Session_Ticket(Handshake_IO& io,
+                         Handshake_Hash& hash,
+                         const std::vector<uint8_t>& ticket,
+                         uint32_t lifetime);
+
+      New_Session_Ticket(Handshake_IO& io,
+                         Handshake_Hash& hash);
+
+      explicit New_Session_Ticket(const std::vector<uint8_t>& buf);
+   private:
+      std::vector<uint8_t> serialize() const override;
+
+      uint32_t m_ticket_lifetime_hint = 0;
+      std::vector<uint8_t> m_ticket;
+   };
+
+/**
+* Change Cipher Spec
+*/
+class BOTAN_UNSTABLE_API Change_Cipher_Spec final : public Handshake_Message
+   {
+   public:
+      Handshake_Type type() const override { return HANDSHAKE_CCS; }
+
+      std::vector<uint8_t> serialize() const override
+         { return std::vector<uint8_t>(1, 1); }
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_policy.cpp b/third_party/botan/src/lib/tls/tls_policy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_policy.cpp
@@ -0,0 +1,616 @@
+/*
+* Policies for TLS
+* (C) 2004-2010,2012,2015,2016 Jack Lloyd
+*     2016 Christian Mainka
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_policy.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_exceptn.h>
+#include <botan/internal/stl_util.h>
+#include <botan/pk_keys.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::vector<Signature_Scheme> Policy::allowed_signature_schemes() const
+   {
+   std::vector<Signature_Scheme> schemes;
+
+   for(Signature_Scheme scheme : all_signature_schemes())
+      {
+      if(signature_scheme_is_known(scheme) == false)
+         continue;
+      const bool sig_allowed = allowed_signature_method(signature_algorithm_of_scheme(scheme));
+      const bool hash_allowed = allowed_signature_hash(hash_function_of_scheme(scheme));
+
+      if(sig_allowed && hash_allowed)
+         {
+         schemes.push_back(scheme);
+         }
+      }
+
+   return schemes;
+   }
+
+std::vector<std::string> Policy::allowed_ciphers() const
+   {
+   return {
+      //"AES-256/OCB(12)",
+      //"AES-128/OCB(12)",
+      "ChaCha20Poly1305",
+      "AES-256/GCM",
+      "AES-128/GCM",
+      //"AES-256/CCM",
+      //"AES-128/CCM",
+      //"AES-256/CCM(8)",
+      //"AES-128/CCM(8)",
+      //"Camellia-256/GCM",
+      //"Camellia-128/GCM",
+      //"ARIA-256/GCM",
+      //"ARIA-128/GCM",
+      //"AES-256",
+      //"AES-128",
+      //"Camellia-256",
+      //"Camellia-128",
+      //"SEED",
+      //"3DES",
+      };
+   }
+
+std::vector<std::string> Policy::allowed_signature_hashes() const
+   {
+   return {
+      "SHA-512",
+      "SHA-384",
+      "SHA-256",
+      //"SHA-1",
+      };
+   }
+
+std::vector<std::string> Policy::allowed_macs() const
+   {
+   /*
+   SHA-256 is preferred because the Lucky13 countermeasure works
+   somewhat better for SHA-256 vs SHA-384:
+   https://github.com/randombit/botan/pull/675
+   */
+   return {
+      "AEAD",
+      "SHA-256",
+      "SHA-384",
+      "SHA-1",
+      };
+   }
+
+std::vector<std::string> Policy::allowed_key_exchange_methods() const
+   {
+   return {
+      //"SRP_SHA",
+      //"ECDHE_PSK",
+      //"DHE_PSK",
+      //"PSK",
+      "CECPQ1",
+      "ECDH",
+      "DH",
+      //"RSA",
+      };
+   }
+
+std::vector<std::string> Policy::allowed_signature_methods() const
+   {
+   return {
+      "ECDSA",
+      "RSA",
+      //"DSA",
+      //"IMPLICIT",
+      //"ANONYMOUS" (anon)
+      };
+   }
+
+bool Policy::allowed_signature_method(const std::string& sig_method) const
+   {
+   return value_exists(allowed_signature_methods(), sig_method);
+   }
+
+bool Policy::allowed_signature_hash(const std::string& sig_hash) const
+   {
+   return value_exists(allowed_signature_hashes(), sig_hash);
+   }
+
+bool Policy::use_ecc_point_compression() const
+   {
+   return false;
+   }
+
+Group_Params Policy::choose_key_exchange_group(const std::vector<Group_Params>& peer_groups) const
+   {
+   if(peer_groups.empty())
+      return Group_Params::NONE;
+
+   const std::vector<Group_Params> our_groups = key_exchange_groups();
+
+   for(auto g : our_groups)
+      {
+      if(value_exists(peer_groups, g))
+         return g;
+      }
+
+   return Group_Params::NONE;
+   }
+
+Group_Params Policy::default_dh_group() const
+   {
+   /*
+   * Return the first listed or just default to 2048
+   */
+   for(auto g : key_exchange_groups())
+      {
+      if(group_param_is_dh(g))
+         return g;
+      }
+
+   return Group_Params::FFDHE_2048;
+   }
+
+std::vector<Group_Params> Policy::key_exchange_groups() const
+   {
+   // Default list is ordered by performance
+   return {
+
+#if defined(BOTAN_HAS_CURVE_25519)
+      Group_Params::X25519,
+#endif
+
+      Group_Params::SECP256R1,
+      Group_Params::BRAINPOOL256R1,
+      Group_Params::SECP384R1,
+      Group_Params::BRAINPOOL384R1,
+      Group_Params::SECP521R1,
+      Group_Params::BRAINPOOL512R1,
+
+      Group_Params::FFDHE_2048,
+      Group_Params::FFDHE_3072,
+      Group_Params::FFDHE_4096,
+      Group_Params::FFDHE_6144,
+      Group_Params::FFDHE_8192,
+      };
+   }
+
+size_t Policy::minimum_dh_group_size() const
+   {
+   return 2048;
+   }
+
+size_t Policy::minimum_ecdsa_group_size() const
+   {
+   // Here we are at the mercy of whatever the CA signed, but most certs should be 256 bit by now
+   return 256;
+   }
+
+size_t Policy::minimum_ecdh_group_size() const
+   {
+   // x25519 is smallest curve currently supported for TLS key exchange
+   return 255;
+   }
+
+size_t Policy::minimum_signature_strength() const
+   {
+   return 110;
+   }
+
+bool Policy::require_cert_revocation_info() const
+   {
+   return true;
+   }
+
+size_t Policy::minimum_rsa_bits() const
+   {
+   /* Default assumption is all end-entity certificates should
+      be at least 2048 bits these days.
+
+      If you are connecting to arbitrary servers on the Internet
+      (ie as a web browser or SMTP client) you'll probably have to reduce this
+      to 1024 bits, or perhaps even lower.
+   */
+   return 2048;
+   }
+
+size_t Policy::minimum_dsa_group_size() const
+   {
+   // FIPS 186-3
+   return 2048;
+   }
+
+void Policy::check_peer_key_acceptable(const Public_Key& public_key) const
+   {
+   const std::string algo_name = public_key.algo_name();
+
+   const size_t keylength = public_key.key_length();
+   size_t expected_keylength = 0;
+
+   if(algo_name == "RSA")
+      {
+      expected_keylength = minimum_rsa_bits();
+      }
+   else if(algo_name == "DH")
+      {
+      expected_keylength = minimum_dh_group_size();
+      }
+   else if(algo_name == "DSA")
+      {
+      expected_keylength = minimum_dsa_group_size();
+      }
+   else if(algo_name == "ECDH" || algo_name == "Curve25519")
+      {
+      expected_keylength = minimum_ecdh_group_size();
+      }
+   else if(algo_name == "ECDSA")
+      {
+      expected_keylength = minimum_ecdsa_group_size();
+      }
+   // else some other algo, so leave expected_keylength as zero and the check is a no-op
+
+   if(keylength < expected_keylength)
+      throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+                          "Peer sent " +
+                           std::to_string(keylength) + " bit " + algo_name + " key"
+                           ", policy requires at least " +
+                           std::to_string(expected_keylength));
+   }
+
+uint32_t Policy::session_ticket_lifetime() const
+   {
+   return 86400; // ~1 day
+   }
+
+bool Policy::send_fallback_scsv(Protocol_Version version) const
+   {
+   return version != latest_supported_version(version.is_datagram_protocol());
+   }
+
+bool Policy::acceptable_protocol_version(Protocol_Version version) const
+   {
+   if(version == Protocol_Version::TLS_V12 && allow_tls12())
+      return true;
+
+   if(version == Protocol_Version::DTLS_V12 && allow_dtls12())
+      return true;
+
+#if defined(BOTAN_HAS_TLS_V10)
+
+   if(version == Protocol_Version::TLS_V11 && allow_tls11())
+      return true;
+   if(version == Protocol_Version::TLS_V10 && allow_tls10())
+      return true;
+   if(version == Protocol_Version::DTLS_V10 && allow_dtls10())
+      return true;
+
+#endif
+
+   return false;
+   }
+
+Protocol_Version Policy::latest_supported_version(bool datagram) const
+   {
+   if(datagram)
+      {
+      if(acceptable_protocol_version(Protocol_Version::DTLS_V12))
+         return Protocol_Version::DTLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+      if(acceptable_protocol_version(Protocol_Version::DTLS_V10))
+         return Protocol_Version::DTLS_V10;
+#endif
+      throw Invalid_State("Policy forbids all available DTLS version");
+      }
+   else
+      {
+      if(acceptable_protocol_version(Protocol_Version::TLS_V12))
+         return Protocol_Version::TLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+      if(acceptable_protocol_version(Protocol_Version::TLS_V11))
+         return Protocol_Version::TLS_V11;
+      if(acceptable_protocol_version(Protocol_Version::TLS_V10))
+         return Protocol_Version::TLS_V10;
+#endif
+      throw Invalid_State("Policy forbids all available TLS version");
+      }
+   }
+
+bool Policy::acceptable_ciphersuite(const Ciphersuite& ciphersuite) const
+   {
+   return value_exists(allowed_ciphers(), ciphersuite.cipher_algo()) &&
+          value_exists(allowed_macs(), ciphersuite.mac_algo());
+   }
+
+bool Policy::allow_client_initiated_renegotiation() const { return false; }
+bool Policy::allow_server_initiated_renegotiation() const { return false; }
+bool Policy::allow_insecure_renegotiation() const { return false; }
+bool Policy::allow_tls10()  const { return false; }
+bool Policy::allow_tls11()  const { return false; }
+bool Policy::allow_tls12()  const { return true; }
+bool Policy::allow_dtls10() const { return false; }
+bool Policy::allow_dtls12() const { return true; }
+bool Policy::include_time_in_hello_random() const { return true; }
+bool Policy::hide_unknown_users() const { return false; }
+bool Policy::server_uses_own_ciphersuite_preferences() const { return true; }
+bool Policy::negotiate_encrypt_then_mac() const { return true; }
+bool Policy::support_cert_status_message() const { return true; }
+bool Policy::allow_resumption_for_renegotiation() const { return true; }
+bool Policy::only_resume_with_exact_version() const { return true; }
+bool Policy::require_client_certificate_authentication() const { return false; }
+bool Policy::request_client_certificate_authentication() const { return require_client_certificate_authentication(); }
+bool Policy::abort_connection_on_undesired_renegotiation() const { return false; }
+bool Policy::allow_dtls_epoch0_restart() const { return false; }
+
+size_t Policy::maximum_certificate_chain_size() const { return 0; }
+
+// 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1
+size_t Policy::dtls_initial_timeout() const { return 1*1000; }
+size_t Policy::dtls_maximum_timeout() const { return 60*1000; }
+
+size_t Policy::dtls_default_mtu() const
+   {
+   // default MTU is IPv6 min MTU minus UDP/IP headers
+   return 1280 - 40 - 8;
+   }
+
+std::vector<uint16_t> Policy::srtp_profiles() const
+   {
+   return std::vector<uint16_t>();
+   }
+
+namespace {
+
+class Ciphersuite_Preference_Ordering final
+   {
+   public:
+      Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers,
+                                      const std::vector<std::string>& macs,
+                                      const std::vector<std::string>& kex,
+                                      const std::vector<std::string>& sigs) :
+         m_ciphers(ciphers), m_macs(macs), m_kex(kex), m_sigs(sigs) {}
+
+      bool operator()(const Ciphersuite& a, const Ciphersuite& b) const
+         {
+         if(a.kex_method() != b.kex_method())
+            {
+            for(size_t i = 0; i != m_kex.size(); ++i)
+               {
+               if(a.kex_algo() == m_kex[i])
+                  return true;
+               if(b.kex_algo() == m_kex[i])
+                  return false;
+               }
+            }
+
+         if(a.cipher_algo() != b.cipher_algo())
+            {
+            for(size_t i = 0; i != m_ciphers.size(); ++i)
+               {
+               if(a.cipher_algo() == m_ciphers[i])
+                  return true;
+               if(b.cipher_algo() == m_ciphers[i])
+                  return false;
+               }
+            }
+
+         if(a.cipher_keylen() != b.cipher_keylen())
+            {
+            if(a.cipher_keylen() < b.cipher_keylen())
+               return false;
+            if(a.cipher_keylen() > b.cipher_keylen())
+               return true;
+            }
+
+         if(a.auth_method() != b.auth_method())
+            {
+            for(size_t i = 0; i != m_sigs.size(); ++i)
+               {
+               if(a.sig_algo() == m_sigs[i])
+                  return true;
+               if(b.sig_algo() == m_sigs[i])
+                  return false;
+               }
+            }
+
+         if(a.mac_algo() != b.mac_algo())
+            {
+            for(size_t i = 0; i != m_macs.size(); ++i)
+               {
+               if(a.mac_algo() == m_macs[i])
+                  return true;
+               if(b.mac_algo() == m_macs[i])
+                  return false;
+               }
+            }
+
+         return false; // equal (?!?)
+         }
+   private:
+      std::vector<std::string> m_ciphers, m_macs, m_kex, m_sigs;
+   };
+
+}
+
+std::vector<uint16_t> Policy::ciphersuite_list(Protocol_Version version,
+                                               bool have_srp) const
+   {
+   const std::vector<std::string> ciphers = allowed_ciphers();
+   const std::vector<std::string> macs = allowed_macs();
+   const std::vector<std::string> kex = allowed_key_exchange_methods();
+   const std::vector<std::string> sigs = allowed_signature_methods();
+
+   std::vector<Ciphersuite> ciphersuites;
+
+   for(auto&& suite : Ciphersuite::all_known_ciphersuites())
+      {
+      // Can we use it?
+      if(!suite.valid())
+         continue;
+
+      // Can we use it in this version?
+      if(!suite.usable_in_version(version))
+         continue;
+
+      // Is it acceptable to the policy?
+      if(!this->acceptable_ciphersuite(suite))
+         continue;
+
+      // Are we doing SRP?
+      if(!have_srp && suite.kex_method() == Kex_Algo::SRP_SHA)
+         continue;
+
+      if(!value_exists(kex, suite.kex_algo()))
+         continue; // unsupported key exchange
+
+      if(!value_exists(ciphers, suite.cipher_algo()))
+         continue; // unsupported cipher
+
+      if(!value_exists(macs, suite.mac_algo()))
+         continue; // unsupported MAC algo
+
+      if(!value_exists(sigs, suite.sig_algo()))
+         {
+         // allow if it's an empty sig algo and we want to use PSK
+         if(suite.auth_method() != Auth_Method::IMPLICIT || !suite.psk_ciphersuite())
+            continue;
+         }
+
+      /*
+      CECPQ1 always uses x25519 for ECDH, so treat the applications
+      removal of x25519 from the ECC curve list as equivalent to
+      saying they do not trust CECPQ1
+      */
+      if(suite.kex_method() == Kex_Algo::CECPQ1)
+         {
+         if(value_exists(key_exchange_groups(), Group_Params::X25519) == false)
+            continue;
+         }
+
+      // OK, consider it
+      ciphersuites.push_back(suite);
+      }
+
+   if(ciphersuites.empty())
+      {
+      throw Invalid_State("Policy does not allow any available cipher suite");
+      }
+
+   Ciphersuite_Preference_Ordering order(ciphers, macs, kex, sigs);
+   std::sort(ciphersuites.begin(), ciphersuites.end(), order);
+
+   std::vector<uint16_t> ciphersuite_codes;
+   for(auto i : ciphersuites)
+      ciphersuite_codes.push_back(i.ciphersuite_code());
+   return ciphersuite_codes;
+   }
+
+namespace {
+
+void print_vec(std::ostream& o,
+               const char* key,
+               const std::vector<std::string>& v)
+   {
+   o << key << " = ";
+   for(size_t i = 0; i != v.size(); ++i)
+      {
+      o << v[i];
+      if(i != v.size() - 1)
+         o << ' ';
+      }
+   o << '\n';
+   }
+
+void print_vec(std::ostream& o,
+               const char* key,
+               const std::vector<Group_Params>& v)
+   {
+   o << key << " = ";
+   for(size_t i = 0; i != v.size(); ++i)
+      {
+      o << group_param_to_string(v[i]);
+      if(i != v.size() - 1)
+         o << ' ';
+      }
+   o << '\n';
+   }
+
+void print_bool(std::ostream& o,
+                const char* key, bool b)
+   {
+   o << key << " = " << (b ? "true" : "false") << '\n';
+   }
+
+}
+
+void Policy::print(std::ostream& o) const
+   {
+   print_bool(o, "allow_tls10", allow_tls10());
+   print_bool(o, "allow_tls11", allow_tls11());
+   print_bool(o, "allow_tls12", allow_tls12());
+   print_bool(o, "allow_dtls10", allow_dtls10());
+   print_bool(o, "allow_dtls12", allow_dtls12());
+   print_vec(o, "ciphers", allowed_ciphers());
+   print_vec(o, "macs", allowed_macs());
+   print_vec(o, "signature_hashes", allowed_signature_hashes());
+   print_vec(o, "signature_methods", allowed_signature_methods());
+   print_vec(o, "key_exchange_methods", allowed_key_exchange_methods());
+   print_vec(o, "key_exchange_groups", key_exchange_groups());
+
+   print_bool(o, "allow_insecure_renegotiation", allow_insecure_renegotiation());
+   print_bool(o, "include_time_in_hello_random", include_time_in_hello_random());
+   print_bool(o, "allow_server_initiated_renegotiation", allow_server_initiated_renegotiation());
+   print_bool(o, "hide_unknown_users", hide_unknown_users());
+   print_bool(o, "server_uses_own_ciphersuite_preferences", server_uses_own_ciphersuite_preferences());
+   print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac());
+   print_bool(o, "support_cert_status_message", support_cert_status_message());
+   o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n';
+   o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n';
+   o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n';
+   o << "minimum_rsa_bits = " << minimum_rsa_bits() << '\n';
+   o << "minimum_signature_strength = " << minimum_signature_strength() << '\n';
+   }
+
+std::string Policy::to_string() const
+   {
+   std::ostringstream oss;
+   this->print(oss);
+   return oss.str();
+   }
+
+std::vector<std::string> Strict_Policy::allowed_ciphers() const
+   {
+   return { "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM" };
+   }
+
+std::vector<std::string> Strict_Policy::allowed_signature_hashes() const
+   {
+   return { "SHA-512", "SHA-384"};
+   }
+
+std::vector<std::string> Strict_Policy::allowed_macs() const
+   {
+   return { "AEAD" };
+   }
+
+std::vector<std::string> Strict_Policy::allowed_key_exchange_methods() const
+   {
+   return { "CECPQ1", "ECDH" };
+   }
+
+bool Strict_Policy::allow_tls10()  const { return false; }
+bool Strict_Policy::allow_tls11()  const { return false; }
+bool Strict_Policy::allow_tls12()  const { return true;  }
+bool Strict_Policy::allow_dtls10() const { return false; }
+bool Strict_Policy::allow_dtls12() const { return true;  }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_policy.h b/third_party/botan/src/lib/tls/tls_policy.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_policy.h
@@ -0,0 +1,616 @@
+/*
+* Hooks for application level policies on TLS connections
+* (C) 2004-2006,2013 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_POLICY_H_
+#define BOTAN_TLS_POLICY_H_
+
+#include <botan/tls_version.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_ciphersuite.h>
+#include <vector>
+#include <map>
+
+namespace Botan {
+
+class Public_Key;
+
+namespace TLS {
+
+/**
+* TLS Policy Base Class
+* Inherit and overload as desired to suit local policy concerns
+*/
+class BOTAN_PUBLIC_API(2,0) Policy
+   {
+   public:
+
+      /**
+      * Returns a list of ciphers we are willing to negotiate, in
+      * order of preference.
+      */
+      virtual std::vector<std::string> allowed_ciphers() const;
+
+      /**
+      * Returns a list of hash algorithms we are willing to use for
+      * signatures, in order of preference.
+      */
+      virtual std::vector<std::string> allowed_signature_hashes() const;
+
+      /**
+      * Returns a list of MAC algorithms we are willing to use.
+      */
+      virtual std::vector<std::string> allowed_macs() const;
+
+      /**
+      * Returns a list of key exchange algorithms we are willing to
+      * use, in order of preference. Allowed values: DH, empty string
+      * (representing RSA using server certificate key)
+      */
+      virtual std::vector<std::string> allowed_key_exchange_methods() const;
+
+      /**
+      * Returns a list of signature algorithms we are willing to
+      * use, in order of preference. Allowed values RSA and DSA.
+      */
+      virtual std::vector<std::string> allowed_signature_methods() const;
+
+      virtual std::vector<Signature_Scheme> allowed_signature_schemes() const;
+
+      /**
+      * The minimum signature strength we will accept
+      * Returning 80 allows RSA 1024 and SHA-1. Values larger than 80 disable SHA-1 support.
+      * Returning 110 allows RSA 2048.
+      * Return 128 to force ECC (P-256) or large (~3000 bit) RSA keys.
+      * Default is 110
+      */
+      virtual size_t minimum_signature_strength() const;
+
+      /**
+      * Return if cert revocation info (CRL/OCSP) is required
+      * If true, validation will fail unless a valid CRL or OCSP response
+      * was examined.
+      */
+      virtual bool require_cert_revocation_info() const;
+
+      bool allowed_signature_method(const std::string& sig_method) const;
+      bool allowed_signature_hash(const std::string& hash) const;
+
+      /**
+      * Return list of ECC curves and FFDHE groups we are willing to
+      * use in order of preference.
+      */
+      virtual std::vector<Group_Params> key_exchange_groups() const;
+
+      /**
+      * Request that ECC curve points are sent compressed
+      */
+      virtual bool use_ecc_point_compression() const;
+
+      /**
+      * Select a key exchange group to use, from the list of groups sent by the
+      * peer. If none are acceptable, return Group_Params::NONE
+      */
+      virtual Group_Params choose_key_exchange_group(const std::vector<Group_Params>& peer_groups) const;
+
+      /**
+      * Allow renegotiation even if the counterparty doesn't
+      * support the secure renegotiation extension.
+      *
+      * @warning Changing this to true exposes you to injected
+      * plaintext attacks. Read RFC 5746 for background.
+      */
+      virtual bool allow_insecure_renegotiation() const;
+
+      /**
+      * The protocol dictates that the first 32 bits of the random
+      * field are the current time in seconds. However this allows
+      * client fingerprinting attacks. Set to false to disable, in
+      * which case random bytes will be used instead.
+      */
+      virtual bool include_time_in_hello_random() const;
+
+      /**
+      * Consulted by server side. If true, allows clients to initiate a new handshake
+      */
+      virtual bool allow_client_initiated_renegotiation() const;
+
+      /**
+      * Consulted by client side. If true, allows servers to initiate a new handshake
+      */
+      virtual bool allow_server_initiated_renegotiation() const;
+
+      /**
+      * If true, a request to renegotiate will close the connection with
+      * a fatal alert. Otherwise, a warning alert is sent.
+      */
+      virtual bool abort_connection_on_undesired_renegotiation() const;
+
+      virtual bool only_resume_with_exact_version() const;
+
+      /**
+      * Allow TLS v1.0
+      */
+      virtual bool allow_tls10() const;
+
+      /**
+      * Allow TLS v1.1
+      */
+      virtual bool allow_tls11() const;
+
+      /**
+      * Allow TLS v1.2
+      */
+      virtual bool allow_tls12() const;
+
+      /**
+      * Allow DTLS v1.0
+      */
+      virtual bool allow_dtls10() const;
+
+      /**
+      * Allow DTLS v1.2
+      */
+      virtual bool allow_dtls12() const;
+
+      virtual Group_Params default_dh_group() const;
+
+      /**
+      * Return the minimum DH group size we're willing to use
+      * Default is currently 1024 (insecure), should be 2048
+      */
+      virtual size_t minimum_dh_group_size() const;
+
+      /**
+      * For ECDSA authenticated ciphersuites, the smallest key size the
+      * client will accept.
+      * This policy is currently only enforced on the server by the client.
+      */
+      virtual size_t minimum_ecdsa_group_size() const;
+
+      /**
+      * Return the minimum ECDH group size we're willing to use
+      * for key exchange
+      *
+      * Default 255, allowing x25519 and larger
+      * x25519 is the smallest curve we will negotiate
+      * P-521 is the largest
+      */
+      virtual size_t minimum_ecdh_group_size() const;
+
+      /**
+      * Return the minimum bit size we're willing to accept for RSA
+      * key exchange or server signatures.
+      *
+      * It does not place any requirements on the size of any RSA signature(s)
+      * which were used to check the server certificate. This is only
+      * concerned with the server's public key.
+      *
+      * Default is 2048 which is smallest RSA key size still secure
+      * for medium term security.
+      */
+      virtual size_t minimum_rsa_bits() const;
+
+      /**
+      * Minimum DSA group size, default 2048 bits
+      */
+      virtual size_t minimum_dsa_group_size() const;
+
+      /**
+      * Throw an exception if you don't like the peer's key.
+      * Default impl checks the key size against minimum_rsa_bits, minimum_ecdsa_group_size,
+      * or minimum_ecdh_group_size depending on the key's type.
+      * Override if you'd like to perform some other kind of test on
+      * (or logging of) the peer's keys.
+      */
+      virtual void check_peer_key_acceptable(const Public_Key& public_key) const;
+
+      /**
+      * If this function returns false, unknown SRP/PSK identifiers
+      * will be rejected with an unknown_psk_identifier alert as soon
+      * as the non-existence is identified. Otherwise, a false
+      * identifier value will be used and the protocol allowed to
+      * proceed, causing the handshake to eventually fail without
+      * revealing that the username does not exist on this system.
+      */
+      virtual bool hide_unknown_users() const;
+
+      /**
+      * Return the allowed lifetime of a session ticket. If 0, session
+      * tickets do not expire until the session ticket key rolls over.
+      * Expired session tickets cannot be used to resume a session.
+      */
+      virtual uint32_t session_ticket_lifetime() const;
+
+      /**
+      * If this returns a non-empty vector, and DTLS is negotiated,
+      * then we will also attempt to negotiate the SRTP extension from
+      * RFC 5764 using the returned values as the profile ids.
+      */
+      virtual std::vector<uint16_t> srtp_profiles() const;
+
+      /**
+      * @return true if and only if we are willing to accept this version
+      * Default accepts TLS v1.0 and later or DTLS v1.2 or later.
+      */
+      virtual bool acceptable_protocol_version(Protocol_Version version) const;
+
+      /**
+      * Returns the more recent protocol version we are willing to
+      * use, for either TLS or DTLS depending on datagram param.
+      * Shouldn't ever need to override this unless you want to allow
+      * a user to disable use of TLS v1.2 (which is *not recommended*)
+      */
+      virtual Protocol_Version latest_supported_version(bool datagram) const;
+
+      /**
+      * When offering this version, should we send a fallback SCSV?
+      * Default returns true iff version is not the latest version the
+      * policy allows, exists to allow override in case of interop problems.
+      */
+      virtual bool send_fallback_scsv(Protocol_Version version) const;
+
+      /**
+      * Allows policy to reject any ciphersuites which are undesirable
+      * for whatever reason without having to reimplement ciphersuite_list
+      */
+      virtual bool acceptable_ciphersuite(const Ciphersuite& suite) const;
+
+      /**
+      * @return true if servers should choose the ciphersuite matching
+      *         their highest preference, rather than the clients.
+      *         Has no effect on client side.
+      */
+      virtual bool server_uses_own_ciphersuite_preferences() const;
+
+      /**
+      * Indicates whether the encrypt-then-MAC extension should be negotiated
+      * (RFC 7366)
+      */
+      virtual bool negotiate_encrypt_then_mac() const;
+
+      /**
+      * Indicates whether certificate status messages should be supported
+      */
+      virtual bool support_cert_status_message() const;
+
+      /**
+      * Indicate if client certificate authentication is required.
+      * If true, then a cert will be requested and if the client does
+      * not send a certificate the connection will be closed.
+      */
+      virtual bool require_client_certificate_authentication() const;
+
+      /**
+      * Indicate if client certificate authentication is requested.
+      * If true, then a cert will be requested.
+      */
+      virtual bool request_client_certificate_authentication() const;
+
+      /**
+      * If true, then allow a DTLS client to restart a connection to the
+      * same server association as described in section 4.2.8 of the DTLS RFC
+      */
+      virtual bool allow_dtls_epoch0_restart() const;
+
+      /**
+      * Return allowed ciphersuites, in order of preference
+      */
+      virtual std::vector<uint16_t> ciphersuite_list(Protocol_Version version,
+                                                     bool have_srp) const;
+
+      /**
+      * @return the default MTU for DTLS
+      */
+      virtual size_t dtls_default_mtu() const;
+
+      /**
+      * @return the initial timeout for DTLS
+      */
+      virtual size_t dtls_initial_timeout() const;
+
+      /**
+      * @return the maximum timeout for DTLS
+      */
+      virtual size_t dtls_maximum_timeout() const;
+
+      /**
+      * @return the maximum size of the certificate chain, in bytes.
+      * Return 0 to disable this and accept any size.
+      */
+      virtual size_t maximum_certificate_chain_size() const;
+
+      virtual bool allow_resumption_for_renegotiation() const;
+
+      /**
+      * Convert this policy to a printable format.
+      * @param o stream to be printed to
+      */
+      virtual void print(std::ostream& o) const;
+
+      /**
+      * Convert this policy to a printable format.
+      * Same as calling `print` on a ostringstream and reading o.str()
+      */
+      std::string to_string() const;
+
+      virtual ~Policy() = default;
+   };
+
+typedef Policy Default_Policy;
+
+/**
+* NSA Suite B 128-bit security level (RFC 6460)
+*
+* @warning As of August 2015 NSA indicated only the 192-bit Suite B
+* should be used for all classification levels.
+*/
+class BOTAN_PUBLIC_API(2,0) NSA_Suite_B_128 : public Policy
+   {
+   public:
+      std::vector<std::string> allowed_ciphers() const override
+         { return std::vector<std::string>({"AES-128/GCM"}); }
+
+      std::vector<std::string> allowed_signature_hashes() const override
+         { return std::vector<std::string>({"SHA-256"}); }
+
+      std::vector<std::string> allowed_macs() const override
+         { return std::vector<std::string>({"AEAD"}); }
+
+      std::vector<std::string> allowed_key_exchange_methods() const override
+         { return std::vector<std::string>({"ECDH"}); }
+
+      std::vector<std::string> allowed_signature_methods() const override
+         { return std::vector<std::string>({"ECDSA"}); }
+
+      std::vector<Group_Params> key_exchange_groups() const override
+         { return {Group_Params::SECP256R1}; }
+
+      size_t minimum_signature_strength() const override { return 128; }
+
+      bool allow_tls10()  const override { return false; }
+      bool allow_tls11()  const override { return false; }
+      bool allow_tls12()  const override { return true;  }
+      bool allow_dtls10() const override { return false; }
+      bool allow_dtls12() const override { return false; }
+   };
+
+/**
+* NSA Suite B 192-bit security level (RFC 6460)
+*/
+class BOTAN_PUBLIC_API(2,7) NSA_Suite_B_192 : public Policy
+   {
+   public:
+      std::vector<std::string> allowed_ciphers() const override
+         { return std::vector<std::string>({"AES-256/GCM"}); }
+
+      std::vector<std::string> allowed_signature_hashes() const override
+         { return std::vector<std::string>({"SHA-384"}); }
+
+      std::vector<std::string> allowed_macs() const override
+         { return std::vector<std::string>({"AEAD"}); }
+
+      std::vector<std::string> allowed_key_exchange_methods() const override
+         { return std::vector<std::string>({"ECDH"}); }
+
+      std::vector<std::string> allowed_signature_methods() const override
+         { return std::vector<std::string>({"ECDSA"}); }
+
+      std::vector<Group_Params> key_exchange_groups() const override
+         { return {Group_Params::SECP384R1}; }
+
+      size_t minimum_signature_strength() const override { return 192; }
+
+      bool allow_tls10()  const override { return false; }
+      bool allow_tls11()  const override { return false; }
+      bool allow_tls12()  const override { return true;  }
+      bool allow_dtls10() const override { return false; }
+      bool allow_dtls12() const override { return false; }
+   };
+
+/**
+* BSI TR-02102-2 Policy
+*/
+class BOTAN_PUBLIC_API(2,0) BSI_TR_02102_2 : public Policy
+   {
+   public:
+      std::vector<std::string> allowed_ciphers() const override
+         {
+         return std::vector<std::string>({"AES-256/GCM", "AES-128/GCM", "AES-256/CCM", "AES-128/CCM", "AES-256", "AES-128"});
+         }
+
+      std::vector<std::string> allowed_signature_hashes() const override
+         {
+         return std::vector<std::string>({"SHA-512", "SHA-384", "SHA-256"});
+         }
+
+      std::vector<std::string> allowed_macs() const override
+         {
+         return std::vector<std::string>({"AEAD", "SHA-384", "SHA-256"});
+         }
+
+      std::vector<std::string> allowed_key_exchange_methods() const override
+         {
+         return std::vector<std::string>({"ECDH", "DH", "ECDHE_PSK", "DHE_PSK"});
+         }
+
+      std::vector<std::string> allowed_signature_methods() const override
+         {
+         return std::vector<std::string>({"ECDSA", "RSA", "DSA"});
+         }
+
+      std::vector<Group_Params> key_exchange_groups() const override
+         {
+         return std::vector<Group_Params>({
+            Group_Params::BRAINPOOL512R1,
+            Group_Params::BRAINPOOL384R1,
+            Group_Params::BRAINPOOL256R1,
+            Group_Params::SECP384R1,
+            Group_Params::SECP256R1,
+            Group_Params::FFDHE_4096,
+            Group_Params::FFDHE_3072,
+            Group_Params::FFDHE_2048
+            });
+         }
+
+      bool allow_insecure_renegotiation() const override { return false; }
+      bool allow_server_initiated_renegotiation() const override { return true; }
+      bool server_uses_own_ciphersuite_preferences() const override { return true; }
+      bool negotiate_encrypt_then_mac() const override { return true; }
+
+      size_t minimum_rsa_bits() const override { return 2000; }
+      size_t minimum_dh_group_size() const override { return 2000; }
+      size_t minimum_dsa_group_size() const override { return 2000; }
+
+      size_t minimum_ecdh_group_size() const override { return 250; }
+      size_t minimum_ecdsa_group_size() const override { return 250; }
+
+      bool allow_tls10()  const override { return false; }
+      bool allow_tls11()  const override { return false; }
+      bool allow_tls12()  const override { return true;  }
+      bool allow_dtls10() const override { return false; }
+      bool allow_dtls12() const override { return false; }
+   };
+
+/**
+* Policy for DTLS. We require DTLS v1.2 and an AEAD mode.
+*/
+class BOTAN_PUBLIC_API(2,0) Datagram_Policy : public Policy
+   {
+   public:
+      std::vector<std::string> allowed_macs() const override
+         { return std::vector<std::string>({"AEAD"}); }
+
+      bool allow_tls10()  const override { return false; }
+      bool allow_tls11()  const override { return false; }
+      bool allow_tls12()  const override { return false; }
+      bool allow_dtls10() const override { return false; }
+      bool allow_dtls12() const override { return true;  }
+   };
+
+/*
+* This policy requires a secure version of TLS and disables all insecure
+* algorithms. It is compatible with other botan TLSes (including those using the
+* default policy) and with many other recent implementations. It is a great idea
+* to use if you control both sides of the protocol and don't have to worry
+* about ancient and/or bizarre TLS implementations.
+*/
+class BOTAN_PUBLIC_API(2,0) Strict_Policy : public Policy
+   {
+   public:
+      std::vector<std::string> allowed_ciphers() const override;
+
+      std::vector<std::string> allowed_signature_hashes() const override;
+
+      std::vector<std::string> allowed_macs() const override;
+
+      std::vector<std::string> allowed_key_exchange_methods() const override;
+
+      bool allow_tls10()  const override;
+      bool allow_tls11()  const override;
+      bool allow_tls12()  const override;
+      bool allow_dtls10() const override;
+      bool allow_dtls12() const override;
+   };
+
+class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy
+   {
+   public:
+
+      std::vector<std::string> allowed_ciphers() const override;
+
+      std::vector<std::string> allowed_signature_hashes() const override;
+
+      std::vector<std::string> allowed_macs() const override;
+
+      std::vector<std::string> allowed_key_exchange_methods() const override;
+
+      std::vector<std::string> allowed_signature_methods() const override;
+
+      std::vector<Group_Params> key_exchange_groups() const override;
+
+      bool use_ecc_point_compression() const override;
+
+      bool allow_tls10() const override;
+
+      bool allow_tls11() const override;
+
+      bool allow_tls12() const override;
+
+      bool allow_dtls10() const override;
+
+      bool allow_dtls12() const override;
+
+      bool allow_insecure_renegotiation() const override;
+
+      bool include_time_in_hello_random() const override;
+
+      bool allow_client_initiated_renegotiation() const override;
+      bool allow_server_initiated_renegotiation() const override;
+
+      bool server_uses_own_ciphersuite_preferences() const override;
+
+      bool negotiate_encrypt_then_mac() const override;
+
+      bool support_cert_status_message() const override;
+
+      bool require_client_certificate_authentication() const override;
+
+      size_t minimum_ecdh_group_size() const override;
+
+      size_t minimum_ecdsa_group_size() const override;
+
+      size_t minimum_dh_group_size() const override;
+
+      size_t minimum_rsa_bits() const override;
+
+      size_t minimum_signature_strength() const override;
+
+      size_t dtls_default_mtu() const override;
+
+      size_t dtls_initial_timeout() const override;
+
+      size_t dtls_maximum_timeout() const override;
+
+      bool require_cert_revocation_info() const override;
+
+      bool hide_unknown_users() const override;
+
+      uint32_t session_ticket_lifetime() const override;
+
+      bool send_fallback_scsv(Protocol_Version version) const override;
+
+      std::vector<uint16_t> srtp_profiles() const override;
+
+      void set(const std::string& k, const std::string& v);
+
+      explicit Text_Policy(const std::string& s);
+
+      explicit Text_Policy(std::istream& in);
+
+   protected:
+
+      std::vector<std::string> get_list(const std::string& key,
+                                        const std::vector<std::string>& def) const;
+
+      size_t get_len(const std::string& key, size_t def) const;
+
+      bool get_bool(const std::string& key, bool def) const;
+
+      std::string get_str(const std::string& key, const std::string& def = "") const;
+
+      bool set_value(const std::string& key, const std::string& val, bool overwrite);
+
+   private:
+      std::map<std::string, std::string> m_kv;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_reader.h b/third_party/botan/src/lib/tls/tls_reader.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_reader.h
@@ -0,0 +1,231 @@
+/*
+* TLS Data Reader
+* (C) 2010-2011,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_READER_H_
+#define BOTAN_TLS_READER_H_
+
+#include <botan/exceptn.h>
+#include <botan/secmem.h>
+#include <botan/loadstor.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Helper class for decoding TLS protocol messages
+*/
+class TLS_Data_Reader final
+   {
+   public:
+      TLS_Data_Reader(const char* type, const std::vector<uint8_t>& buf_in) :
+         m_typename(type), m_buf(buf_in), m_offset(0) {}
+
+      void assert_done() const
+         {
+         if(has_remaining())
+            throw decode_error("Extra bytes at end of message");
+         }
+
+      size_t read_so_far() const { return m_offset; }
+
+      size_t remaining_bytes() const { return m_buf.size() - m_offset; }
+
+      bool has_remaining() const { return (remaining_bytes() > 0); }
+
+      std::vector<uint8_t> get_remaining()
+         {
+         return std::vector<uint8_t>(m_buf.begin() + m_offset, m_buf.end());
+         }
+
+      void discard_next(size_t bytes)
+         {
+         assert_at_least(bytes);
+         m_offset += bytes;
+         }
+
+      uint32_t get_uint32_t()
+         {
+         assert_at_least(4);
+         uint32_t result = make_uint32(m_buf[m_offset  ], m_buf[m_offset+1],
+                                     m_buf[m_offset+2], m_buf[m_offset+3]);
+         m_offset += 4;
+         return result;
+         }
+
+      uint16_t get_uint16_t()
+         {
+         assert_at_least(2);
+         uint16_t result = make_uint16(m_buf[m_offset], m_buf[m_offset+1]);
+         m_offset += 2;
+         return result;
+         }
+
+      uint8_t get_byte()
+         {
+         assert_at_least(1);
+         uint8_t result = m_buf[m_offset];
+         m_offset += 1;
+         return result;
+         }
+
+      template<typename T, typename Container>
+      Container get_elem(size_t num_elems)
+         {
+         assert_at_least(num_elems * sizeof(T));
+
+         Container result(num_elems);
+
+         for(size_t i = 0; i != num_elems; ++i)
+            result[i] = load_be<T>(&m_buf[m_offset], i);
+
+         m_offset += num_elems * sizeof(T);
+
+         return result;
+         }
+
+      template<typename T>
+      std::vector<T> get_range(size_t len_bytes,
+                               size_t min_elems,
+                               size_t max_elems)
+         {
+         const size_t num_elems =
+            get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+         return get_elem<T, std::vector<T>>(num_elems);
+         }
+
+      template<typename T>
+      std::vector<T> get_range_vector(size_t len_bytes,
+                                      size_t min_elems,
+                                      size_t max_elems)
+         {
+         const size_t num_elems =
+            get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+         return get_elem<T, std::vector<T>>(num_elems);
+         }
+
+      std::string get_string(size_t len_bytes,
+                             size_t min_bytes,
+                             size_t max_bytes)
+         {
+         std::vector<uint8_t> v =
+            get_range_vector<uint8_t>(len_bytes, min_bytes, max_bytes);
+
+         return std::string(cast_uint8_ptr_to_char(v.data()), v.size());
+         }
+
+      template<typename T>
+      std::vector<T> get_fixed(size_t size)
+         {
+         return get_elem<T, std::vector<T>>(size);
+         }
+
+   private:
+      size_t get_length_field(size_t len_bytes)
+         {
+         assert_at_least(len_bytes);
+
+         if(len_bytes == 1)
+            return get_byte();
+         else if(len_bytes == 2)
+            return get_uint16_t();
+
+         throw decode_error("Bad length size");
+         }
+
+      size_t get_num_elems(size_t len_bytes,
+                           size_t T_size,
+                           size_t min_elems,
+                           size_t max_elems)
+         {
+         const size_t byte_length = get_length_field(len_bytes);
+
+         if(byte_length % T_size != 0)
+            throw decode_error("Size isn't multiple of T");
+
+         const size_t num_elems = byte_length / T_size;
+
+         if(num_elems < min_elems || num_elems > max_elems)
+            throw decode_error("Length field outside parameters");
+
+         return num_elems;
+         }
+
+      void assert_at_least(size_t n) const
+         {
+         if(m_buf.size() - m_offset < n)
+            throw decode_error("Expected " + std::to_string(n) +
+                               " bytes remaining, only " +
+                               std::to_string(m_buf.size()-m_offset) +
+                               " left");
+         }
+
+      Decoding_Error decode_error(const std::string& why) const
+         {
+         return Decoding_Error("Invalid " + std::string(m_typename) + ": " + why);
+         }
+
+      const char* m_typename;
+      const std::vector<uint8_t>& m_buf;
+      size_t m_offset;
+   };
+
+/**
+* Helper function for encoding length-tagged vectors
+*/
+template<typename T, typename Alloc>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& buf,
+                             const T* vals,
+                             size_t vals_size,
+                             size_t tag_size)
+   {
+   const size_t T_size = sizeof(T);
+   const size_t val_bytes = T_size * vals_size;
+
+   if(tag_size != 1 && tag_size != 2)
+      throw Invalid_Argument("append_tls_length_value: invalid tag size");
+
+   if((tag_size == 1 && val_bytes > 255) ||
+      (tag_size == 2 && val_bytes > 65535))
+      throw Invalid_Argument("append_tls_length_value: value too large");
+
+   for(size_t i = 0; i != tag_size; ++i)
+      buf.push_back(get_byte(sizeof(val_bytes)-tag_size+i, val_bytes));
+
+   for(size_t i = 0; i != vals_size; ++i)
+      for(size_t j = 0; j != T_size; ++j)
+         buf.push_back(get_byte(j, vals[i]));
+   }
+
+template<typename T, typename Alloc, typename Alloc2>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& buf,
+                             const std::vector<T, Alloc2>& vals,
+                             size_t tag_size)
+   {
+   append_tls_length_value(buf, vals.data(), vals.size(), tag_size);
+   }
+
+template<typename Alloc>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& buf,
+                             const std::string& str,
+                             size_t tag_size)
+   {
+   append_tls_length_value(buf,
+                           cast_char_ptr_to_uint8(str.data()),
+                           str.size(),
+                           tag_size);
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_record.cpp b/third_party/botan/src/lib/tls/tls_record.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_record.cpp
@@ -0,0 +1,532 @@
+/*
+* TLS Record Handling
+* (C) 2012,2013,2014,2015,2016,2019 Jack Lloyd
+*     2016 Juraj Somorovsky
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_record.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_CBC)
+  #include <botan/internal/tls_cbc.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version,
+                                                 Connection_Side side,
+                                                 bool our_side,
+                                                 const Ciphersuite& suite,
+                                                 const Session_Keys& keys,
+                                                 bool uses_encrypt_then_mac) :
+   m_start_time(std::chrono::system_clock::now())
+   {
+   m_nonce_format = suite.nonce_format();
+   m_nonce_bytes_from_record = suite.nonce_bytes_from_record(version);
+   m_nonce_bytes_from_handshake = suite.nonce_bytes_from_handshake();
+
+   const secure_vector<uint8_t>& aead_key = keys.aead_key(side);
+   m_nonce = keys.nonce(side);
+
+   BOTAN_ASSERT_NOMSG(m_nonce.size() == m_nonce_bytes_from_handshake);
+
+   if(nonce_format() == Nonce_Format::CBC_MODE)
+      {
+#if defined(BOTAN_HAS_TLS_CBC)
+      // legacy CBC+HMAC mode
+      auto mac = MessageAuthenticationCode::create_or_throw("HMAC(" + suite.mac_algo() + ")");
+      auto cipher = BlockCipher::create_or_throw(suite.cipher_algo());
+
+      if(our_side)
+         {
+         m_aead.reset(new TLS_CBC_HMAC_AEAD_Encryption(
+                         std::move(cipher),
+                         std::move(mac),
+                         suite.cipher_keylen(),
+                         suite.mac_keylen(),
+                         version,
+                         uses_encrypt_then_mac));
+         }
+      else
+         {
+         m_aead.reset(new TLS_CBC_HMAC_AEAD_Decryption(
+                         std::move(cipher),
+                         std::move(mac),
+                         suite.cipher_keylen(),
+                         suite.mac_keylen(),
+                         version,
+                         uses_encrypt_then_mac));
+         }
+
+#else
+      BOTAN_UNUSED(uses_encrypt_then_mac);
+      throw Internal_Error("Negotiated disabled TLS CBC+HMAC ciphersuite");
+#endif
+      }
+   else
+      {
+      m_aead = AEAD_Mode::create_or_throw(suite.cipher_algo(), our_side ? ENCRYPTION : DECRYPTION);
+      }
+
+   m_aead->set_key(aead_key);
+   }
+
+std::vector<uint8_t> Connection_Cipher_State::aead_nonce(uint64_t seq, RandomNumberGenerator& rng)
+   {
+   switch(m_nonce_format)
+      {
+      case Nonce_Format::CBC_MODE:
+         {
+         if(m_nonce.size())
+            {
+            std::vector<uint8_t> nonce;
+            nonce.swap(m_nonce);
+            return nonce;
+            }
+         std::vector<uint8_t> nonce(nonce_bytes_from_record());
+         rng.randomize(nonce.data(), nonce.size());
+         return nonce;
+         }
+      case Nonce_Format::AEAD_XOR_12:
+         {
+         std::vector<uint8_t> nonce(12);
+         store_be(seq, nonce.data() + 4);
+         xor_buf(nonce, m_nonce.data(), m_nonce.size());
+         return nonce;
+         }
+      case Nonce_Format::AEAD_IMPLICIT_4:
+         {
+         BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
+         std::vector<uint8_t> nonce(12);
+         copy_mem(&nonce[0], m_nonce.data(), 4);
+         store_be(seq, &nonce[nonce_bytes_from_handshake()]);
+         return nonce;
+         }
+      }
+
+   throw Invalid_State("Unknown nonce format specified");
+   }
+
+std::vector<uint8_t>
+Connection_Cipher_State::aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq)
+   {
+   switch(m_nonce_format)
+      {
+      case Nonce_Format::CBC_MODE:
+         {
+         if(nonce_bytes_from_record() == 0 && m_nonce.size())
+            {
+            std::vector<uint8_t> nonce;
+            nonce.swap(m_nonce);
+            return nonce;
+            }
+         if(record_len < nonce_bytes_from_record())
+            throw Decoding_Error("Invalid CBC packet too short to be valid");
+         std::vector<uint8_t> nonce(record, record + nonce_bytes_from_record());
+         return nonce;
+         }
+      case Nonce_Format::AEAD_XOR_12:
+         {
+         std::vector<uint8_t> nonce(12);
+         store_be(seq, nonce.data() + 4);
+         xor_buf(nonce, m_nonce.data(), m_nonce.size());
+         return nonce;
+         }
+      case Nonce_Format::AEAD_IMPLICIT_4:
+         {
+         BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
+         if(record_len < nonce_bytes_from_record())
+            throw Decoding_Error("Invalid AEAD packet too short to be valid");
+         std::vector<uint8_t> nonce(12);
+         copy_mem(&nonce[0], m_nonce.data(), 4);
+         copy_mem(&nonce[nonce_bytes_from_handshake()], record, nonce_bytes_from_record());
+         return nonce;
+         }
+      }
+
+   throw Invalid_State("Unknown nonce format specified");
+   }
+
+std::vector<uint8_t>
+Connection_Cipher_State::format_ad(uint64_t msg_sequence,
+                                   uint8_t msg_type,
+                                   Protocol_Version version,
+                                   uint16_t msg_length)
+   {
+   std::vector<uint8_t> ad(13);
+
+   store_be(msg_sequence, &ad[0]);
+   ad[8] = msg_type;
+   ad[9] = version.major_version();
+   ad[10] = version.minor_version();
+   ad[11] = get_byte(0, msg_length);
+   ad[12] = get_byte(1, msg_length);
+
+   return ad;
+   }
+
+namespace {
+
+inline void append_u16_len(secure_vector<uint8_t>& output, size_t len_field)
+   {
+   const uint16_t len16 = static_cast<uint16_t>(len_field);
+   BOTAN_ASSERT_EQUAL(len_field, len16, "No truncation");
+   output.push_back(get_byte(0, len16));
+   output.push_back(get_byte(1, len16));
+   }
+
+void write_record_header(secure_vector<uint8_t>& output,
+                         uint8_t record_type,
+                         Protocol_Version version,
+                         uint64_t record_sequence)
+   {
+   output.clear();
+
+   output.push_back(record_type);
+   output.push_back(version.major_version());
+   output.push_back(version.minor_version());
+
+   if(version.is_datagram_protocol())
+      {
+      for(size_t i = 0; i != 8; ++i)
+         output.push_back(get_byte(i, record_sequence));
+      }
+   }
+
+}
+
+void write_unencrypted_record(secure_vector<uint8_t>& output,
+                              uint8_t record_type,
+                              Protocol_Version version,
+                              uint64_t record_sequence,
+                              const uint8_t* message,
+                              size_t message_len)
+   {
+   if(record_type == APPLICATION_DATA)
+      throw Internal_Error("Writing an unencrypted TLS application data record");
+   write_record_header(output, record_type, version, record_sequence);
+   append_u16_len(output, message_len);
+   output.insert(output.end(), message, message + message_len);
+   }
+
+void write_record(secure_vector<uint8_t>& output,
+                  uint8_t record_type,
+                  Protocol_Version version,
+                  uint64_t record_sequence,
+                  const uint8_t* message,
+                  size_t message_len,
+                  Connection_Cipher_State& cs,
+                  RandomNumberGenerator& rng)
+   {
+   write_record_header(output, record_type, version, record_sequence);
+
+   AEAD_Mode& aead = cs.aead();
+   std::vector<uint8_t> aad = cs.format_ad(record_sequence, record_type, version, static_cast<uint16_t>(message_len));
+
+   const size_t ctext_size = aead.output_length(message_len);
+
+   const size_t rec_size = ctext_size + cs.nonce_bytes_from_record();
+
+   aead.set_ad(aad);
+
+   const std::vector<uint8_t> nonce = cs.aead_nonce(record_sequence, rng);
+
+   append_u16_len(output, rec_size);
+
+   if(cs.nonce_bytes_from_record() > 0)
+      {
+      if(cs.nonce_format() == Nonce_Format::CBC_MODE)
+         output += nonce;
+      else
+         output += std::make_pair(&nonce[cs.nonce_bytes_from_handshake()], cs.nonce_bytes_from_record());
+      }
+
+   const size_t header_size = output.size();
+   output += std::make_pair(message, message_len);
+
+   aead.start(nonce);
+   aead.finish(output, header_size);
+
+   BOTAN_ASSERT(output.size() < MAX_CIPHERTEXT_SIZE,
+                "Produced ciphertext larger than protocol allows");
+   }
+
+namespace {
+
+size_t fill_buffer_to(secure_vector<uint8_t>& readbuf,
+                      const uint8_t*& input,
+                      size_t& input_size,
+                      size_t& input_consumed,
+                      size_t desired)
+   {
+   if(readbuf.size() >= desired)
+      return 0; // already have it
+
+   const size_t taken = std::min(input_size, desired - readbuf.size());
+
+   readbuf.insert(readbuf.end(), input, input + taken);
+   input_consumed += taken;
+   input_size -= taken;
+   input += taken;
+
+   return (desired - readbuf.size()); // how many bytes do we still need?
+   }
+
+void decrypt_record(secure_vector<uint8_t>& output,
+                    uint8_t record_contents[], size_t record_len,
+                    uint64_t record_sequence,
+                    Protocol_Version record_version,
+                    Record_Type record_type,
+                    Connection_Cipher_State& cs)
+   {
+   AEAD_Mode& aead = cs.aead();
+
+   const std::vector<uint8_t> nonce = cs.aead_nonce(record_contents, record_len, record_sequence);
+   const uint8_t* msg = &record_contents[cs.nonce_bytes_from_record()];
+   const size_t msg_length = record_len - cs.nonce_bytes_from_record();
+
+   /*
+   * This early rejection is based just on public information (length of the
+   * encrypted packet) and so does not leak any information. We used to use
+   * decode_error here which really is more appropriate, but that confuses some
+   * tools which are attempting automated detection of padding oracles,
+   * including older versions of TLS-Attacker.
+   */
+   if(msg_length < aead.minimum_final_size())
+      throw TLS_Exception(Alert::BAD_RECORD_MAC, "AEAD packet is shorter than the tag");
+
+   const size_t ptext_size = aead.output_length(msg_length);
+
+   aead.set_associated_data_vec(
+      cs.format_ad(record_sequence,
+                   static_cast<uint8_t>(record_type),
+                   record_version,
+                   static_cast<uint16_t>(ptext_size))
+      );
+
+   aead.start(nonce);
+
+   output.assign(msg, msg + msg_length);
+   aead.finish(output, 0);
+   }
+
+Record_Header read_tls_record(secure_vector<uint8_t>& readbuf,
+                              const uint8_t input[],
+                              size_t input_len,
+                              size_t& consumed,
+                              secure_vector<uint8_t>& recbuf,
+                              Connection_Sequence_Numbers* sequence_numbers,
+                              get_cipherstate_fn get_cipherstate)
+   {
+   if(readbuf.size() < TLS_HEADER_SIZE) // header incomplete?
+      {
+      if(size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE))
+         {
+         return Record_Header(needed);
+         }
+
+      BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header");
+      }
+
+   const Protocol_Version version(readbuf[1], readbuf[2]);
+
+   if(version.is_datagram_protocol())
+      throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                          "Expected TLS but got a record with DTLS version");
+
+   const size_t record_size = make_uint16(readbuf[TLS_HEADER_SIZE-2],
+                                          readbuf[TLS_HEADER_SIZE-1]);
+
+   if(record_size > MAX_CIPHERTEXT_SIZE)
+      throw TLS_Exception(Alert::RECORD_OVERFLOW,
+                          "Received a record that exceeds maximum size");
+
+   if(record_size == 0)
+      throw TLS_Exception(Alert::DECODE_ERROR,
+                          "Received a completely empty record");
+
+   if(size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE + record_size))
+      {
+      return Record_Header(needed);
+      }
+
+   BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size,
+                      readbuf.size(),
+                      "Have the full record");
+
+   const Record_Type type = static_cast<Record_Type>(readbuf[0]);
+
+   uint16_t epoch = 0;
+
+   uint64_t sequence = 0;
+   if(sequence_numbers)
+      {
+      sequence = sequence_numbers->next_read_sequence();
+      epoch = sequence_numbers->current_read_epoch();
+      }
+   else
+      {
+      // server initial handshake case
+      epoch = 0;
+      }
+
+   if(epoch == 0) // Unencrypted initial handshake
+      {
+      recbuf.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size);
+      readbuf.clear();
+      return Record_Header(sequence, version, type);
+      }
+
+   // Otherwise, decrypt, check MAC, return plaintext
+   auto cs = get_cipherstate(epoch);
+
+   BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
+
+   decrypt_record(recbuf,
+                  &readbuf[TLS_HEADER_SIZE],
+                  record_size,
+                  sequence,
+                  version,
+                  type,
+                  *cs);
+
+   if(sequence_numbers)
+      sequence_numbers->read_accept(sequence);
+
+   readbuf.clear();
+   return Record_Header(sequence, version, type);
+   }
+
+Record_Header read_dtls_record(secure_vector<uint8_t>& readbuf,
+                               const uint8_t input[],
+                               size_t input_len,
+                               size_t& consumed,
+                               secure_vector<uint8_t>& recbuf,
+                               Connection_Sequence_Numbers* sequence_numbers,
+                               get_cipherstate_fn get_cipherstate,
+                               bool allow_epoch0_restart)
+   {
+   if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete?
+      {
+      if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE))
+         {
+         readbuf.clear();
+         return Record_Header(0);
+         }
+
+      BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header");
+      }
+
+   const Protocol_Version version(readbuf[1], readbuf[2]);
+
+   if(version.is_datagram_protocol() == false)
+      {
+      readbuf.clear();
+      return Record_Header(0);
+      }
+
+   const size_t record_size = make_uint16(readbuf[DTLS_HEADER_SIZE-2],
+                                          readbuf[DTLS_HEADER_SIZE-1]);
+
+   if(record_size > MAX_CIPHERTEXT_SIZE)
+      {
+      // Too large to be valid, ignore it
+      readbuf.clear();
+      return Record_Header(0);
+      }
+
+   if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE + record_size))
+      {
+      // Truncated packet?
+      readbuf.clear();
+      return Record_Header(0);
+      }
+
+   BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(),
+                      "Have the full record");
+
+   const Record_Type type = static_cast<Record_Type>(readbuf[0]);
+
+   const uint64_t sequence = load_be<uint64_t>(&readbuf[3], 0);
+   const uint16_t epoch = (sequence >> 48);
+
+   const bool already_seen = sequence_numbers && sequence_numbers->already_seen(sequence);
+
+   if(already_seen && !(epoch == 0 && allow_epoch0_restart))
+      {
+      readbuf.clear();
+      return Record_Header(0);
+      }
+
+   if(epoch == 0) // Unencrypted initial handshake
+      {
+      recbuf.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size);
+      readbuf.clear();
+      if(sequence_numbers)
+         sequence_numbers->read_accept(sequence);
+      return Record_Header(sequence, version, type);
+      }
+
+   try
+      {
+      // Otherwise, decrypt, check MAC, return plaintext
+      auto cs = get_cipherstate(epoch);
+
+      BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
+
+      decrypt_record(recbuf,
+                     &readbuf[DTLS_HEADER_SIZE],
+                     record_size,
+                     sequence,
+                     version,
+                     type,
+                     *cs);
+      }
+   catch(std::exception&)
+      {
+      readbuf.clear();
+      return Record_Header(0);
+      }
+
+   if(sequence_numbers)
+      sequence_numbers->read_accept(sequence);
+
+   readbuf.clear();
+   return Record_Header(sequence, version, type);
+   }
+
+}
+
+Record_Header read_record(bool is_datagram,
+                          secure_vector<uint8_t>& readbuf,
+                          const uint8_t input[],
+                          size_t input_len,
+                          size_t& consumed,
+                          secure_vector<uint8_t>& recbuf,
+                          Connection_Sequence_Numbers* sequence_numbers,
+                          get_cipherstate_fn get_cipherstate,
+                          bool allow_epoch0_restart)
+   {
+   if(is_datagram)
+      return read_dtls_record(readbuf, input, input_len, consumed,
+                              recbuf, sequence_numbers, get_cipherstate, allow_epoch0_restart);
+   else
+      return read_tls_record(readbuf, input, input_len, consumed,
+                             recbuf, sequence_numbers, get_cipherstate);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_record.h b/third_party/botan/src/lib/tls/tls_record.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_record.h
@@ -0,0 +1,188 @@
+/*
+* TLS Record Handling
+* (C) 2004-2012 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_RECORDS_H_
+#define BOTAN_TLS_RECORDS_H_
+
+#include <botan/tls_algos.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <botan/aead.h>
+#include <vector>
+#include <chrono>
+#include <functional>
+
+namespace Botan {
+
+namespace TLS {
+
+class Ciphersuite;
+class Session_Keys;
+
+class Connection_Sequence_Numbers;
+
+/**
+* TLS Cipher State
+*/
+class Connection_Cipher_State final
+   {
+   public:
+      /**
+      * Initialize a new cipher state
+      */
+      Connection_Cipher_State(Protocol_Version version,
+                              Connection_Side which_side,
+                              bool is_our_side,
+                              const Ciphersuite& suite,
+                              const Session_Keys& keys,
+                              bool uses_encrypt_then_mac);
+
+      AEAD_Mode& aead()
+         {
+         BOTAN_ASSERT_NONNULL(m_aead.get());
+         return *m_aead.get();
+         }
+
+      std::vector<uint8_t> aead_nonce(uint64_t seq, RandomNumberGenerator& rng);
+
+      std::vector<uint8_t> aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq);
+
+      std::vector<uint8_t> format_ad(uint64_t seq, uint8_t type,
+                                  Protocol_Version version,
+                                  uint16_t ptext_length);
+
+      size_t nonce_bytes_from_handshake() const { return m_nonce_bytes_from_handshake; }
+      size_t nonce_bytes_from_record() const { return m_nonce_bytes_from_record; }
+
+      Nonce_Format nonce_format() const { return m_nonce_format; }
+
+      std::chrono::seconds age() const
+         {
+         return std::chrono::duration_cast<std::chrono::seconds>(
+            std::chrono::system_clock::now() - m_start_time);
+         }
+
+   private:
+      std::chrono::system_clock::time_point m_start_time;
+      std::unique_ptr<AEAD_Mode> m_aead;
+
+      std::vector<uint8_t> m_nonce;
+      Nonce_Format m_nonce_format;
+      size_t m_nonce_bytes_from_handshake;
+      size_t m_nonce_bytes_from_record;
+   };
+
+class Record_Header final
+   {
+   public:
+      Record_Header(uint64_t sequence,
+                    Protocol_Version version,
+                    Record_Type type) :
+         m_needed(0),
+         m_sequence(sequence),
+         m_version(version),
+         m_type(type)
+         {}
+
+      Record_Header(size_t needed) :
+         m_needed(needed),
+         m_sequence(0),
+         m_version(Protocol_Version()),
+         m_type(NO_RECORD)
+         {}
+
+      size_t needed() const { return m_needed; }
+
+      Protocol_Version version() const
+         {
+         BOTAN_ASSERT_NOMSG(m_needed == 0);
+         return m_version;
+         }
+
+      uint64_t sequence() const
+         {
+         BOTAN_ASSERT_NOMSG(m_needed == 0);
+         return m_sequence;
+         }
+
+      uint16_t epoch() const
+         {
+         return static_cast<uint16_t>(sequence() >> 48);
+         }
+
+      Record_Type type() const
+         {
+         BOTAN_ASSERT_NOMSG(m_needed == 0);
+         return m_type;
+         }
+
+   private:
+      size_t m_needed;
+      uint64_t m_sequence;
+      Protocol_Version m_version;
+      Record_Type m_type;
+   };
+
+/**
+* Create an initial (unencrypted) TLS handshake record
+* @param write_buffer the output record is placed here
+* @param record_type the record layer type
+* @param record_version the record layer version
+* @param record_sequence the record layer sequence number
+* @param message the record contents
+* @param message_len is size of message
+*/
+void write_unencrypted_record(secure_vector<uint8_t>& output,
+                              uint8_t record_type,
+                              Protocol_Version version,
+                              uint64_t record_sequence,
+                              const uint8_t* message,
+                              size_t message_len);
+
+/**
+* Create a TLS record
+* @param write_buffer the output record is placed here
+* @param record_type the record layer type
+* @param record_version the record layer version
+* @param record_sequence the record layer sequence number
+* @param message the record contents
+* @param message_len is size of message
+* @param cipherstate is the writing cipher state
+* @param rng is a random number generator
+*/
+void write_record(secure_vector<uint8_t>& write_buffer,
+                  uint8_t record_type,
+                  Protocol_Version record_version,
+                  uint64_t record_sequence,
+                  const uint8_t* message,
+                  size_t message_len,
+                  Connection_Cipher_State& cipherstate,
+                  RandomNumberGenerator& rng);
+
+// epoch -> cipher state
+typedef std::function<std::shared_ptr<Connection_Cipher_State> (uint16_t)> get_cipherstate_fn;
+
+/**
+* Decode a TLS record
+* @return zero if full message, else number of bytes still needed
+*/
+Record_Header read_record(bool is_datagram,
+                          secure_vector<uint8_t>& read_buffer,
+                          const uint8_t input[],
+                          size_t input_len,
+                          size_t& consumed,
+                          secure_vector<uint8_t>& record_buf,
+                          Connection_Sequence_Numbers* sequence_numbers,
+                          get_cipherstate_fn get_cipherstate,
+                          bool allow_epoch0_restart);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_seq_numbers.h b/third_party/botan/src/lib/tls/tls_seq_numbers.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_seq_numbers.h
@@ -0,0 +1,174 @@
+/*
+* TLS Sequence Number Handling
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SEQ_NUMBERS_H_
+#define BOTAN_TLS_SEQ_NUMBERS_H_
+
+#include <botan/types.h>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+class Connection_Sequence_Numbers
+   {
+   public:
+      virtual ~Connection_Sequence_Numbers() = default;
+
+      virtual void new_read_cipher_state() = 0;
+      virtual void new_write_cipher_state() = 0;
+
+      virtual uint16_t current_read_epoch() const = 0;
+      virtual uint16_t current_write_epoch() const = 0;
+
+      virtual uint64_t next_write_sequence(uint16_t) = 0;
+      virtual uint64_t next_read_sequence() = 0;
+
+      virtual bool already_seen(uint64_t seq) const = 0;
+      virtual void read_accept(uint64_t seq) = 0;
+
+      virtual void reset() = 0;
+   };
+
+class Stream_Sequence_Numbers final : public Connection_Sequence_Numbers
+   {
+   public:
+      Stream_Sequence_Numbers() { Stream_Sequence_Numbers::reset(); }
+
+      void reset() override
+         {
+         m_write_seq_no = 0;
+         m_read_seq_no = 0;
+         m_read_epoch = 0;
+         m_write_epoch = 0;
+         }
+
+      void new_read_cipher_state() override { m_read_seq_no = 0; m_read_epoch++; }
+      void new_write_cipher_state() override { m_write_seq_no = 0; m_write_epoch++; }
+
+      uint16_t current_read_epoch() const override { return m_read_epoch; }
+      uint16_t current_write_epoch() const override { return m_write_epoch; }
+
+      uint64_t next_write_sequence(uint16_t) override { return m_write_seq_no++; }
+      uint64_t next_read_sequence() override { return m_read_seq_no; }
+
+      bool already_seen(uint64_t) const override { return false; }
+      void read_accept(uint64_t) override { m_read_seq_no++; }
+
+   private:
+      uint64_t m_write_seq_no;
+      uint64_t m_read_seq_no;
+      uint16_t m_read_epoch;
+      uint16_t m_write_epoch;
+   };
+
+class Datagram_Sequence_Numbers final : public Connection_Sequence_Numbers
+   {
+   public:
+      Datagram_Sequence_Numbers() { Datagram_Sequence_Numbers::reset(); }
+
+      void reset() override
+         {
+         m_write_seqs.clear();
+         m_write_seqs[0] = 0;
+         m_write_epoch = 0;
+         m_read_epoch = 0;
+         m_window_highest = 0;
+         m_window_bits = 0;
+         }
+
+      void new_read_cipher_state() override { m_read_epoch++; }
+
+      void new_write_cipher_state() override
+         {
+         m_write_epoch++;
+         m_write_seqs[m_write_epoch] = 0;
+         }
+
+      uint16_t current_read_epoch() const override { return m_read_epoch; }
+      uint16_t current_write_epoch() const override { return m_write_epoch; }
+
+      uint64_t next_write_sequence(uint16_t epoch) override
+         {
+         auto i = m_write_seqs.find(epoch);
+         BOTAN_ASSERT(i != m_write_seqs.end(), "Found epoch");
+         return (static_cast<uint64_t>(epoch) << 48) | i->second++;
+         }
+
+      uint64_t next_read_sequence() override
+         {
+         throw Invalid_State("DTLS uses explicit sequence numbers");
+         }
+
+      bool already_seen(uint64_t sequence) const override
+         {
+         const size_t window_size = sizeof(m_window_bits) * 8;
+
+         if(sequence > m_window_highest)
+            {
+            return false;
+            }
+
+         const uint64_t offset = m_window_highest - sequence;
+
+         if(offset >= window_size)
+            {
+            return true; // really old?
+            }
+
+         return (((m_window_bits >> offset) & 1) == 1);
+         }
+
+      void read_accept(uint64_t sequence) override
+         {
+         const size_t window_size = sizeof(m_window_bits) * 8;
+
+         if(sequence > m_window_highest)
+            {
+            // We've received a later sequence which advances our window
+            const uint64_t offset = sequence - m_window_highest;
+            m_window_highest += offset;
+
+            if(offset >= window_size)
+               m_window_bits = 0;
+            else
+               m_window_bits <<= offset;
+
+            m_window_bits |= 0x01;
+            }
+         else
+            {
+            const uint64_t offset = m_window_highest - sequence;
+
+            if(offset < window_size)
+               {
+               // We've received an old sequence but still within our window
+               m_window_bits |= (static_cast<uint64_t>(1) << offset);
+               }
+            else
+               {
+               // This occurs only if we have reset state (DTLS reconnection case)
+               m_window_highest = sequence;
+               m_window_bits = 0;
+               }
+            }
+         }
+
+   private:
+      std::map<uint16_t, uint64_t> m_write_seqs;
+      uint16_t m_write_epoch = 0;
+      uint16_t m_read_epoch = 0;
+      uint64_t m_window_highest = 0;
+      uint64_t m_window_bits = 0;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_server.cpp b/third_party/botan/src/lib/tls/tls_server.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_server.cpp
@@ -0,0 +1,1025 @@
+/*
+* TLS Server
+* (C) 2004-2011,2012,2016 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_server.h>
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/stl_util.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State final : public Handshake_State
+   {
+   public:
+      Server_Handshake_State(Handshake_IO* io, Callbacks& cb)
+         : Handshake_State(io, cb) {}
+
+      Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; }
+      void set_server_rsa_kex_key(Private_Key* key)
+         { m_server_rsa_kex_key = key; }
+
+      bool allow_session_resumption() const
+         { return m_allow_session_resumption; }
+      void set_allow_session_resumption(bool allow_session_resumption)
+         { m_allow_session_resumption = allow_session_resumption; }
+
+      const std::vector<X509_Certificate>& resume_peer_certs() const
+         { return m_resume_peer_certs; }
+
+      void set_resume_certs(const std::vector<X509_Certificate>& certs)
+         { m_resume_peer_certs = certs; }
+
+      void mark_as_resumption() { m_is_a_resumption = true; }
+
+      bool is_a_resumption() const { return m_is_a_resumption; }
+
+   private:
+      // Used by the server only, in case of RSA key exchange. Not owned
+      Private_Key* m_server_rsa_kex_key = nullptr;
+
+      /*
+      * Used by the server to know if resumption should be allowed on
+      * a server-initiated renegotiation
+      */
+      bool m_allow_session_resumption = true;
+
+      bool m_is_a_resumption = false;
+
+      std::vector<X509_Certificate> m_resume_peer_certs;
+   };
+
+namespace {
+
+bool check_for_resume(Session& session_info,
+                      Session_Manager& session_manager,
+                      Credentials_Manager& credentials,
+                      const Client_Hello* client_hello,
+                      std::chrono::seconds session_ticket_lifetime)
+   {
+   const std::vector<uint8_t>& client_session_id = client_hello->session_id();
+   const std::vector<uint8_t>& session_ticket = client_hello->session_ticket();
+
+   if(session_ticket.empty())
+      {
+      if(client_session_id.empty()) // not resuming
+         return false;
+
+      // not found
+      if(!session_manager.load_from_session_id(client_session_id, session_info))
+         return false;
+      }
+   else
+      {
+      // If a session ticket was sent, ignore client session ID
+      try
+         {
+         session_info = Session::decrypt(
+            session_ticket,
+            credentials.psk("tls-server", "session-ticket", ""));
+
+         if(session_ticket_lifetime != std::chrono::seconds(0) &&
+            session_info.session_age() > session_ticket_lifetime)
+            return false; // ticket has expired
+         }
+      catch(...)
+         {
+         return false;
+         }
+      }
+
+   // wrong version
+   if(client_hello->version() != session_info.version())
+      return false;
+
+   // client didn't send original ciphersuite
+   if(!value_exists(client_hello->ciphersuites(),
+                    session_info.ciphersuite_code()))
+      return false;
+
+#if defined(BOTAN_HAS_SRP6)
+   // client sent a different SRP identity
+   if(client_hello->srp_identifier() != "")
+      {
+      if(client_hello->srp_identifier() != session_info.srp_identifier())
+         return false;
+      }
+#endif
+
+   // client sent a different SNI hostname
+   if(client_hello->sni_hostname() != "")
+      {
+      if(client_hello->sni_hostname() != session_info.server_info().hostname())
+         return false;
+      }
+
+   // Checking extended_master_secret on resume (RFC 7627 section 5.3)
+   if(client_hello->supports_extended_master_secret() != session_info.supports_extended_master_secret())
+      {
+      if(!session_info.supports_extended_master_secret())
+         {
+         return false; // force new handshake with extended master secret
+         }
+      else
+         {
+         /*
+         Client previously negotiated session with extended master secret,
+         but has now attempted to resume without the extension: abort
+         */
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Client resumed extended ms session without sending extension");
+         }
+      }
+
+   // Checking encrypt_then_mac on resume (RFC 7366 section 3.1)
+   if(!client_hello->supports_encrypt_then_mac() && session_info.supports_encrypt_then_mac())
+      {
+      /*
+      Client previously negotiated session with Encrypt-then-MAC,
+      but has now attempted to resume without the extension: abort
+      */
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                             "Client resumed Encrypt-then-MAC session without sending extension");
+      }
+
+   return true;
+   }
+
+/*
+* Choose which ciphersuite to use
+*/
+uint16_t choose_ciphersuite(
+   const Policy& policy,
+   Protocol_Version version,
+   Credentials_Manager& creds,
+   const std::map<std::string, std::vector<X509_Certificate>>& cert_chains,
+   const Client_Hello& client_hello)
+   {
+   const bool our_choice = policy.server_uses_own_ciphersuite_preferences();
+   const bool have_srp = creds.attempt_srp("tls-server", client_hello.sni_hostname());
+   const std::vector<uint16_t> client_suites = client_hello.ciphersuites();
+   const std::vector<uint16_t> server_suites = policy.ciphersuite_list(version, have_srp);
+
+   if(server_suites.empty())
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                          "Policy forbids us from negotiating any ciphersuite");
+
+   const bool have_shared_ecc_curve =
+      (policy.choose_key_exchange_group(client_hello.supported_ecc_curves()) != Group_Params::NONE);
+
+   /*
+   Walk down one list in preference order
+   */
+   std::vector<uint16_t> pref_list = server_suites;
+   std::vector<uint16_t> other_list = client_suites;
+
+   if(!our_choice)
+      std::swap(pref_list, other_list);
+
+   for(auto suite_id : pref_list)
+      {
+      if(!value_exists(other_list, suite_id))
+         continue;
+
+      const Ciphersuite suite = Ciphersuite::by_id(suite_id);
+
+      if(suite.valid() == false)
+         {
+         continue;
+         }
+
+      if(have_shared_ecc_curve == false && suite.ecc_ciphersuite())
+         {
+         continue;
+         }
+
+      // For non-anon ciphersuites
+      if(suite.signature_used())
+         {
+         const std::string sig_algo = suite.sig_algo();
+
+         // Do we have any certificates for this sig?
+         if(cert_chains.count(sig_algo) == 0)
+            {
+            continue;
+            }
+
+         if(version.supports_negotiable_signature_algorithms())
+            {
+            const std::vector<Signature_Scheme> allowed =
+               policy.allowed_signature_schemes();
+
+            std::vector<Signature_Scheme> client_sig_methods =
+               client_hello.signature_schemes();
+
+            if(client_sig_methods.empty())
+               {
+               // If empty, then implicit SHA-1 (TLS v1.2 rules)
+               client_sig_methods.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+               client_sig_methods.push_back(Signature_Scheme::ECDSA_SHA1);
+               client_sig_methods.push_back(Signature_Scheme::DSA_SHA1);
+               }
+
+            bool we_support_some_hash_by_client = false;
+
+            for(Signature_Scheme scheme : client_sig_methods)
+               {
+               if(signature_scheme_is_known(scheme) == false)
+                  continue;
+
+               if(signature_algorithm_of_scheme(scheme) == suite.sig_algo() &&
+                  policy.allowed_signature_hash(hash_function_of_scheme(scheme)))
+                  {
+                  we_support_some_hash_by_client = true;
+                  }
+               }
+
+            if(we_support_some_hash_by_client == false)
+               {
+               throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                                   "Policy does not accept any hash function supported by client");
+               }
+            }
+         }
+
+#if defined(BOTAN_HAS_SRP6)
+      /*
+      The client may offer SRP cipher suites in the hello message but
+      omit the SRP extension.  If the server would like to select an
+      SRP cipher suite in this case, the server SHOULD return a fatal
+      "unknown_psk_identity" alert immediately after processing the
+      client hello message.
+       - RFC 5054 section 2.5.1.2
+      */
+      if(suite.kex_method() == Kex_Algo::SRP_SHA && client_hello.srp_identifier() == "")
+         throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+                             "Client wanted SRP but did not send username");
+#endif
+
+      return suite_id;
+      }
+
+   throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+                       "Can't agree on a ciphersuite with client");
+   }
+
+std::map<std::string, std::vector<X509_Certificate>>
+get_server_certs(const std::string& hostname,
+                 Credentials_Manager& creds)
+   {
+   const char* cert_types[] = { "RSA", "ECDSA", "DSA", nullptr };
+
+   std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+   for(size_t i = 0; cert_types[i]; ++i)
+      {
+      const std::vector<X509_Certificate> certs =
+         creds.cert_chain_single_type(cert_types[i], "tls-server", hostname);
+
+      if(!certs.empty())
+         cert_chains[cert_types[i]] = certs;
+      }
+
+   return cert_chains;
+   }
+
+}
+
+/*
+* TLS Server Constructor
+*/
+Server::Server(Callbacks& callbacks,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               bool is_datagram,
+               size_t io_buf_sz) :
+   Channel(callbacks, session_manager, rng, policy,
+           true, is_datagram, io_buf_sz),
+   m_creds(creds)
+   {
+   }
+
+Server::Server(output_fn output,
+               data_cb got_data_cb,
+               alert_cb recv_alert_cb,
+               handshake_cb hs_cb,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               next_protocol_fn next_proto,
+               bool is_datagram,
+               size_t io_buf_sz) :
+   Channel(output, got_data_cb, recv_alert_cb, hs_cb,
+           Channel::handshake_msg_cb(), session_manager,
+           rng, policy, true, is_datagram, io_buf_sz),
+   m_creds(creds),
+   m_choose_next_protocol(next_proto)
+   {
+   }
+
+Server::Server(output_fn output,
+               data_cb got_data_cb,
+               alert_cb recv_alert_cb,
+               handshake_cb hs_cb,
+               handshake_msg_cb hs_msg_cb,
+               Session_Manager& session_manager,
+               Credentials_Manager& creds,
+               const Policy& policy,
+               RandomNumberGenerator& rng,
+               next_protocol_fn next_proto,
+               bool is_datagram) :
+   Channel(output, got_data_cb, recv_alert_cb, hs_cb, hs_msg_cb,
+           session_manager, rng, policy, true, is_datagram),
+   m_creds(creds),
+   m_choose_next_protocol(next_proto)
+   {
+   }
+
+Handshake_State* Server::new_handshake_state(Handshake_IO* io)
+   {
+   std::unique_ptr<Handshake_State> state(new Server_Handshake_State(io, callbacks()));
+
+   state->set_expected_next(CLIENT_HELLO);
+   return state.release();
+   }
+
+std::vector<X509_Certificate>
+Server::get_peer_cert_chain(const Handshake_State& state_base) const
+   {
+   const Server_Handshake_State& state = dynamic_cast<const Server_Handshake_State&>(state_base);
+   if(state.resume_peer_certs().size() > 0)
+      return state.resume_peer_certs();
+
+   if(state.client_certs())
+      return state.client_certs()->cert_chain();
+   return std::vector<X509_Certificate>();
+   }
+
+/*
+* Send a hello request to the client
+*/
+void Server::initiate_handshake(Handshake_State& state,
+                                bool force_full_renegotiation)
+   {
+   dynamic_cast<Server_Handshake_State&>(state).
+       set_allow_session_resumption(!force_full_renegotiation);
+
+   Hello_Request hello_req(state.handshake_io());
+   }
+
+namespace {
+
+Protocol_Version select_version(const Botan::TLS::Policy& policy,
+                                Protocol_Version client_offer,
+                                Protocol_Version active_version,
+                                bool is_fallback,
+                                const std::vector<Protocol_Version>& supported_versions)
+   {
+   const bool is_datagram = client_offer.is_datagram_protocol();
+   const bool initial_handshake = (active_version.valid() == false);
+
+   const Protocol_Version latest_supported = policy.latest_supported_version(is_datagram);
+
+   if(is_fallback)
+      {
+      if(latest_supported > client_offer)
+         throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
+                              "Client signalled fallback SCSV, possible attack");
+      }
+
+   if(supported_versions.size() > 0)
+      {
+      if(is_datagram)
+         {
+         if(policy.allow_dtls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V12)))
+            return Protocol_Version::DTLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+         if(policy.allow_dtls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V10)))
+            return Protocol_Version::DTLS_V10;
+#endif
+         throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared DTLS version");
+         }
+      else
+         {
+         if(policy.allow_tls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V12)))
+            return Protocol_Version::TLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+         if(policy.allow_tls11() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V11)))
+            return Protocol_Version::TLS_V11;
+         if(policy.allow_tls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V10)))
+            return Protocol_Version::TLS_V10;
+#endif
+         throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared TLS version");
+         }
+      }
+
+   const bool client_offer_acceptable =
+      client_offer.known_version() && policy.acceptable_protocol_version(client_offer);
+
+   if(!initial_handshake)
+      {
+      /*
+      * If this is a renegotiation, and the client has offered a
+      * later version than what it initially negotiated, negotiate
+      * the old version. This matches OpenSSL's behavior. If the
+      * client is offering a version earlier than what it initially
+      * negotiated, reject as a probable attack.
+      */
+      if(active_version > client_offer)
+         {
+         throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                              "Client negotiated " +
+                              active_version.to_string() +
+                              " then renegotiated with " +
+                              client_offer.to_string());
+         }
+      else
+         {
+         return active_version;
+         }
+      }
+   else if(client_offer_acceptable)
+      {
+      return client_offer;
+      }
+   else if(!client_offer.known_version() || client_offer > latest_supported)
+      {
+      /*
+      The client offered some version newer than the latest we
+      support.  Offer them the best we know.
+      */
+      return latest_supported;
+      }
+   else
+      {
+      throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                           "Client version " + client_offer.to_string() +
+                           " is unacceptable by policy");
+      }
+   }
+
+}
+
+/*
+* Process a CLIENT HELLO Message
+*/
+void Server::process_client_hello_msg(const Handshake_State* active_state,
+                                      Server_Handshake_State& pending_state,
+                                      const std::vector<uint8_t>& contents,
+                                      bool epoch0_restart)
+   {
+   BOTAN_ASSERT_IMPLICATION(epoch0_restart, active_state != nullptr, "Can't restart with a dead connection");
+
+   const bool initial_handshake = epoch0_restart || !active_state;
+
+   if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false)
+      {
+      if(policy().abort_connection_on_undesired_renegotiation())
+         throw TLS_Exception(Alert::NO_RENEGOTIATION, "Server policy prohibits renegotiation");
+      else
+         send_warning_alert(Alert::NO_RENEGOTIATION);
+      return;
+      }
+
+   if(!policy().allow_insecure_renegotiation() &&
+      !(initial_handshake || secure_renegotiation_supported()))
+      {
+      send_warning_alert(Alert::NO_RENEGOTIATION);
+      return;
+      }
+
+   pending_state.client_hello(new Client_Hello(contents));
+   const Protocol_Version client_offer = pending_state.client_hello()->version();
+   const bool datagram = client_offer.is_datagram_protocol();
+
+   if(datagram)
+      {
+      if(client_offer.major_version() == 0xFF)
+         throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered DTLS version with major version 0xFF");
+      }
+   else
+      {
+      if(client_offer.major_version() < 3)
+         throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered TLS version with major version under 3");
+      if(client_offer.major_version() == 3 && client_offer.minor_version() == 0)
+         throw TLS_Exception(Alert::PROTOCOL_VERSION, "SSLv3 is not supported");
+      }
+
+   /*
+   * BoGo test suite expects that we will send the hello verify with a record
+   * version matching the version that is eventually negotiated. This is wrong
+   * but harmless, so go with it. Also doing the version negotiation step first
+   * allows to immediately close the connection with an alert if the client has
+   * offered a version that we are not going to negotiate anyway, instead of
+   * making them first do the cookie exchange and then telling them no.
+   *
+   * There is no issue with amplification here, since the alert is just 2 bytes.
+   */
+   const Protocol_Version negotiated_version =
+      select_version(policy(), client_offer,
+                     active_state ? active_state->version() : Protocol_Version(),
+                     pending_state.client_hello()->sent_fallback_scsv(),
+                     pending_state.client_hello()->supported_versions());
+
+   pending_state.set_version(negotiated_version);
+
+   const auto compression_methods = pending_state.client_hello()->compression_methods();
+   if(!value_exists(compression_methods, uint8_t(0)))
+      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Client did not offer NULL compression");
+
+   if(initial_handshake && datagram)
+      {
+      SymmetricKey cookie_secret;
+
+      try
+         {
+         cookie_secret = m_creds.psk("tls-server", "dtls-cookie-secret", "");
+         }
+      catch(...) {}
+
+      if(cookie_secret.size() > 0)
+         {
+         const std::string client_identity = callbacks().tls_peer_network_identity();
+         Hello_Verify_Request verify(pending_state.client_hello()->cookie_input_data(), client_identity, cookie_secret);
+
+         if(pending_state.client_hello()->cookie() != verify.cookie())
+            {
+            if(epoch0_restart)
+               pending_state.handshake_io().send_under_epoch(verify, 0);
+            else
+               pending_state.handshake_io().send(verify);
+
+            pending_state.client_hello(nullptr);
+            pending_state.set_expected_next(CLIENT_HELLO);
+            return;
+            }
+         }
+      else if(epoch0_restart)
+         {
+         throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Reuse of DTLS association requires DTLS cookie secret be set");
+         }
+      }
+
+   if(epoch0_restart)
+      {
+      // If we reached here then we were able to verify the cookie
+      reset_active_association_state();
+      }
+
+   secure_renegotiation_check(pending_state.client_hello());
+
+   callbacks().tls_examine_extensions(pending_state.client_hello()->extensions(), CLIENT);
+
+   Session session_info;
+   const bool resuming =
+      pending_state.allow_session_resumption() &&
+      check_for_resume(session_info,
+                       session_manager(),
+                       m_creds,
+                       pending_state.client_hello(),
+                       std::chrono::seconds(policy().session_ticket_lifetime()));
+
+   bool have_session_ticket_key = false;
+
+   try
+      {
+      have_session_ticket_key =
+         m_creds.psk("tls-server", "session-ticket", "").length() > 0;
+      }
+   catch(...) {}
+
+   m_next_protocol = "";
+   if(pending_state.client_hello()->supports_alpn())
+      {
+      m_next_protocol = callbacks().tls_server_choose_app_protocol(pending_state.client_hello()->next_protocols());
+
+      // if the callback return was empty, fall back to the (deprecated) std::function
+      if(m_next_protocol.empty() && m_choose_next_protocol)
+         {
+         m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols());
+         }
+      }
+
+   if(resuming)
+      {
+      this->session_resume(pending_state, have_session_ticket_key, session_info);
+      }
+   else // new session
+      {
+      this->session_create(pending_state, have_session_ticket_key);
+      }
+   }
+
+void Server::process_certificate_msg(Server_Handshake_State& pending_state,
+                                     const std::vector<uint8_t>& contents)
+   {
+   pending_state.client_certs(new Certificate(contents, policy()));
+
+   // CERTIFICATE_REQUIRED would make more sense but BoGo expects handshake failure alert
+   if(pending_state.client_certs()->empty() && policy().require_client_certificate_authentication())
+      throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Policy requires client send a certificate, but it did not");
+
+   pending_state.set_expected_next(CLIENT_KEX);
+   }
+
+void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+                                             const std::vector<uint8_t>& contents)
+   {
+   if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty())
+      pending_state.set_expected_next(CERTIFICATE_VERIFY);
+   else
+      pending_state.set_expected_next(HANDSHAKE_CCS);
+
+   pending_state.client_kex(new Client_Key_Exchange(contents, pending_state,
+                                                    pending_state.server_rsa_kex_key(),
+                                                    m_creds, policy(), rng()));
+
+   pending_state.compute_session_keys();
+   }
+
+void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state)
+   {
+   pending_state.set_expected_next(FINISHED);
+   change_cipher_spec_reader(SERVER);
+   }
+
+void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state,
+                                            Handshake_Type type,
+                                            const std::vector<uint8_t>& contents)
+   {
+   pending_state.client_verify(new Certificate_Verify(contents, pending_state.version()));
+
+   const std::vector<X509_Certificate>& client_certs =
+      pending_state.client_certs()->cert_chain();
+
+   const bool sig_valid =
+      pending_state.client_verify()->verify(client_certs[0], pending_state, policy());
+
+   pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+   /*
+   * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
+   * "A handshake cryptographic operation failed, including being
+   * unable to correctly verify a signature, ..."
+   */
+   if(!sig_valid)
+      throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed");
+
+   try
+      {
+      const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+      auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+      callbacks().tls_verify_cert_chain(client_certs,
+                                        {}, // ocsp
+                                        trusted_CAs,
+                                        Usage_Type::TLS_CLIENT_AUTH,
+                                        sni_hostname,
+                                        policy());
+      }
+   catch(std::exception& e)
+      {
+      throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+      }
+
+   pending_state.set_expected_next(HANDSHAKE_CCS);
+   }
+
+void Server::process_finished_msg(Server_Handshake_State& pending_state,
+                                  Handshake_Type type,
+                                  const std::vector<uint8_t>& contents)
+   {
+   pending_state.set_expected_next(HANDSHAKE_NONE);
+
+   pending_state.client_finished(new Finished(contents));
+
+   if(!pending_state.client_finished()->verify(pending_state, CLIENT))
+      throw TLS_Exception(Alert::DECRYPT_ERROR,
+                          "Finished message didn't verify");
+
+   if(!pending_state.server_finished())
+      {
+      // already sent finished if resuming, so this is a new session
+
+      pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+      Session session_info(
+         pending_state.server_hello()->session_id(),
+         pending_state.session_keys().master_secret(),
+         pending_state.server_hello()->version(),
+         pending_state.server_hello()->ciphersuite(),
+         SERVER,
+         pending_state.server_hello()->supports_extended_master_secret(),
+         pending_state.server_hello()->supports_encrypt_then_mac(),
+         get_peer_cert_chain(pending_state),
+         std::vector<uint8_t>(),
+         Server_Information(pending_state.client_hello()->sni_hostname()),
+         pending_state.srp_identifier(),
+         pending_state.server_hello()->srtp_profile());
+
+      if(save_session(session_info))
+         {
+         if(pending_state.server_hello()->supports_session_ticket())
+            {
+            try
+               {
+               const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+               pending_state.new_session_ticket(
+                  new New_Session_Ticket(pending_state.handshake_io(),
+                                         pending_state.hash(),
+                                         session_info.encrypt(ticket_key, rng()),
+                                         policy().session_ticket_lifetime()));
+               }
+            catch(...) {}
+            }
+         else
+            session_manager().save(session_info);
+         }
+
+      if(!pending_state.new_session_ticket() &&
+         pending_state.server_hello()->supports_session_ticket())
+         {
+         pending_state.new_session_ticket(
+            new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+         }
+
+      pending_state.handshake_io().send(Change_Cipher_Spec());
+
+      change_cipher_spec_writer(SERVER);
+
+      pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+      }
+
+   activate_session();
+   }
+
+/*
+* Process a handshake message
+*/
+void Server::process_handshake_msg(const Handshake_State* active_state,
+                                   Handshake_State& state_base,
+                                   Handshake_Type type,
+                                   const std::vector<uint8_t>& contents,
+                                   bool epoch0_restart)
+   {
+   Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base);
+   state.confirm_transition_to(type);
+
+   /*
+   * The change cipher spec message isn't technically a handshake
+   * message so it's not included in the hash. The finished and
+   * certificate verify messages are verified based on the current
+   * state of the hash *before* this message so we delay adding them
+   * to the hash computation until we've processed them below.
+   */
+   if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
+      {
+      state.hash().update(state.handshake_io().format(contents, type));
+      }
+
+   switch(type)
+      {
+      case CLIENT_HELLO:
+         return this->process_client_hello_msg(active_state, state, contents, epoch0_restart);
+
+      case CERTIFICATE:
+         return this->process_certificate_msg(state, contents);
+
+      case CLIENT_KEX:
+         return this->process_client_key_exchange_msg(state, contents);
+
+      case CERTIFICATE_VERIFY:
+         return this->process_certificate_verify_msg(state, type, contents);
+
+      case HANDSHAKE_CCS:
+         return this->process_change_cipher_spec_msg(state);
+
+      case FINISHED:
+         return this->process_finished_msg(state, type, contents);
+
+      default:
+         throw Unexpected_Message("Unknown handshake message received");
+      }
+   }
+
+void Server::session_resume(Server_Handshake_State& pending_state,
+                            bool have_session_ticket_key,
+                            Session& session_info)
+   {
+   // Only offer a resuming client a new ticket if they didn't send one this time,
+   // ie, resumed via server-side resumption. TODO: also send one if expiring soon?
+
+   const bool offer_new_session_ticket =
+      (pending_state.client_hello()->supports_session_ticket() &&
+       pending_state.client_hello()->session_ticket().empty() &&
+       have_session_ticket_key);
+
+   pending_state.server_hello(new Server_Hello(
+                                 pending_state.handshake_io(),
+                                 pending_state.hash(),
+                                 policy(),
+                                 callbacks(),
+                                 rng(),
+                                 secure_renegotiation_data_for_server_hello(),
+                                 *pending_state.client_hello(),
+                                 session_info,
+                                 offer_new_session_ticket,
+                                 m_next_protocol));
+
+   secure_renegotiation_check(pending_state.server_hello());
+
+   pending_state.mark_as_resumption();
+   pending_state.compute_session_keys(session_info.master_secret());
+   pending_state.set_resume_certs(session_info.peer_certs());
+
+   if(!save_session(session_info))
+      {
+      session_manager().remove_entry(session_info.session_id());
+
+      if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket
+         {
+         pending_state.new_session_ticket(
+            new New_Session_Ticket(pending_state.handshake_io(),
+                                   pending_state.hash()));
+         }
+      }
+
+   if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket())
+      {
+      try
+         {
+         const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+         pending_state.new_session_ticket(
+            new New_Session_Ticket(pending_state.handshake_io(),
+                                   pending_state.hash(),
+                                   session_info.encrypt(ticket_key, rng()),
+                                   policy().session_ticket_lifetime()));
+         }
+      catch(...) {}
+
+      if(!pending_state.new_session_ticket())
+         {
+         pending_state.new_session_ticket(
+            new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+         }
+      }
+
+   pending_state.handshake_io().send(Change_Cipher_Spec());
+
+   change_cipher_spec_writer(SERVER);
+
+   pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+   pending_state.set_expected_next(HANDSHAKE_CCS);
+   }
+
+void Server::session_create(Server_Handshake_State& pending_state,
+                            bool have_session_ticket_key)
+   {
+   std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+   const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+
+   cert_chains = get_server_certs(sni_hostname, m_creds);
+
+   if(sni_hostname != "" && cert_chains.empty())
+      {
+      cert_chains = get_server_certs("", m_creds);
+
+      /*
+      * Only send the unrecognized_name alert if we couldn't
+      * find any certs for the requested name but did find at
+      * least one cert to use in general. That avoids sending an
+      * unrecognized_name when a server is configured for purely
+      * anonymous/PSK operation.
+      */
+      if(!cert_chains.empty())
+         send_warning_alert(Alert::UNRECOGNIZED_NAME);
+      }
+
+   const uint16_t ciphersuite = choose_ciphersuite(policy(), pending_state.version(),
+                                                   m_creds, cert_chains,
+                                                   *pending_state.client_hello());
+
+   Server_Hello::Settings srv_settings(
+      make_hello_random(rng(), policy()), // new session ID
+      pending_state.version(),
+      ciphersuite,
+      have_session_ticket_key);
+
+   pending_state.server_hello(new Server_Hello(
+                                 pending_state.handshake_io(),
+                                 pending_state.hash(),
+                                 policy(),
+                                 callbacks(),
+                                 rng(),
+                                 secure_renegotiation_data_for_server_hello(),
+                                 *pending_state.client_hello(),
+                                 srv_settings,
+                                 m_next_protocol));
+
+   secure_renegotiation_check(pending_state.server_hello());
+
+   const Ciphersuite& pending_suite = pending_state.ciphersuite();
+
+   Private_Key* private_key = nullptr;
+
+   if(pending_suite.signature_used() || pending_suite.kex_method() == Kex_Algo::STATIC_RSA)
+      {
+      const std::string algo_used =
+         pending_suite.signature_used() ? pending_suite.sig_algo() : "RSA";
+
+      BOTAN_ASSERT(!cert_chains[algo_used].empty(),
+                     "Attempting to send empty certificate chain");
+
+      pending_state.server_certs(new Certificate(pending_state.handshake_io(),
+                                                 pending_state.hash(),
+                                                 cert_chains[algo_used]));
+
+      if(pending_state.client_hello()->supports_cert_status_message() && pending_state.is_a_resumption() == false)
+         {
+         auto csr = pending_state.client_hello()->extensions().get<Certificate_Status_Request>();
+         // csr is non-null if client_hello()->supports_cert_status_message()
+         BOTAN_ASSERT_NOMSG(csr != nullptr);
+         const auto resp_bytes = callbacks().tls_provide_cert_status(cert_chains[algo_used], *csr);
+         if(resp_bytes.size() > 0)
+            {
+            pending_state.server_cert_status(new Certificate_Status(
+                                                pending_state.handshake_io(),
+                                                pending_state.hash(),
+                                                resp_bytes
+                                                ));
+            }
+         }
+
+      private_key = m_creds.private_key_for(
+         pending_state.server_certs()->cert_chain()[0],
+         "tls-server",
+         sni_hostname);
+
+      if(!private_key)
+         throw Internal_Error("No private key located for associated server cert");
+      }
+
+   if(pending_suite.kex_method() == Kex_Algo::STATIC_RSA)
+      {
+      pending_state.set_server_rsa_kex_key(private_key);
+      }
+   else
+      {
+      pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(),
+                                                       pending_state, policy(),
+                                                       m_creds, rng(), private_key));
+      }
+
+   auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+   std::vector<X509_DN> client_auth_CAs;
+
+   for(auto store : trusted_CAs)
+      {
+      auto subjects = store->all_subjects();
+      client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+      }
+
+   const bool request_cert =
+      (client_auth_CAs.empty() == false) ||
+      policy().request_client_certificate_authentication();
+
+   if(request_cert && pending_state.ciphersuite().signature_used())
+      {
+      pending_state.cert_req(
+         new Certificate_Req(pending_state.handshake_io(),
+                             pending_state.hash(),
+                             policy(),
+                             client_auth_CAs,
+                             pending_state.version()));
+
+      /*
+      SSLv3 allowed clients to skip the Certificate message entirely
+      if they wanted. In TLS v1.0 and later clients must send a
+      (possibly empty) Certificate message
+      */
+      pending_state.set_expected_next(CERTIFICATE);
+      }
+   else
+      {
+      pending_state.set_expected_next(CLIENT_KEX);
+      }
+
+   pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash()));
+   }
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_server.h b/third_party/botan/src/lib/tls/tls_server.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_server.h
@@ -0,0 +1,169 @@
+/*
+* TLS Server
+* (C) 2004-2011 Jack Lloyd
+*     2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SERVER_H_
+#define BOTAN_TLS_SERVER_H_
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/credentials_manager.h>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State;
+
+/**
+* TLS Server
+*/
+class BOTAN_PUBLIC_API(2,0) Server final : public Channel
+   {
+   public:
+      typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn;
+
+      /**
+      * Server initialization
+      *
+      * @param callbacks contains a set of callback function references
+      *        required by the TLS client.
+      *
+      * @param session_manager manages session state
+      *
+      * @param creds manages application/user credentials
+      *
+      * @param policy specifies other connection policy information
+      *
+      * @param rng a random number generator
+      *
+      * @param is_datagram set to true if this server should expect DTLS
+      *        connections. Otherwise TLS connections are expected.
+      *
+      * @param reserved_io_buffer_size This many bytes of memory will
+      *        be preallocated for the read and write buffers. Smaller
+      *        values just mean reallocations and copies are more likely.
+      */
+      Server(Callbacks& callbacks,
+             Session_Manager& session_manager,
+             Credentials_Manager& creds,
+             const Policy& policy,
+             RandomNumberGenerator& rng,
+             bool is_datagram = false,
+             size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
+         );
+
+      /**
+       * DEPRECATED. This constructor is only provided for backward
+       * compatibility and should not be used in new implementations.
+       * It will be removed in a future release.
+       */
+      BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
+      Server(output_fn output,
+             data_cb data_cb,
+             alert_cb recv_alert_cb,
+             handshake_cb hs_cb,
+             Session_Manager& session_manager,
+             Credentials_Manager& creds,
+             const Policy& policy,
+             RandomNumberGenerator& rng,
+             next_protocol_fn next_proto = next_protocol_fn(),
+             bool is_datagram = false,
+             size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
+         );
+
+      /**
+       * DEPRECATED. This constructor is only provided for backward
+       * compatibility and should not be used in new implementations.
+       * It will be removed in a future release.
+       */
+      BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
+      Server(output_fn output,
+             data_cb data_cb,
+             alert_cb recv_alert_cb,
+             handshake_cb hs_cb,
+             handshake_msg_cb hs_msg_cb,
+             Session_Manager& session_manager,
+             Credentials_Manager& creds,
+             const Policy& policy,
+             RandomNumberGenerator& rng,
+             next_protocol_fn next_proto = next_protocol_fn(),
+             bool is_datagram = false
+         );
+
+      /**
+      * Return the protocol notification set by the client (using the
+      * ALPN extension) for this connection, if any. This value is not
+      * tied to the session and a later renegotiation of the same
+      * session can choose a new protocol.
+      */
+      std::string next_protocol() const { return m_next_protocol; }
+
+      /**
+      * Return the protocol notification set by the client (using the
+      * ALPN extension) for this connection, if any. This value is not
+      * tied to the session and a later renegotiation of the same
+      * session can choose a new protocol.
+      */
+      std::string application_protocol() const override { return m_next_protocol; }
+
+   private:
+      std::vector<X509_Certificate>
+         get_peer_cert_chain(const Handshake_State& state) const override;
+
+      void initiate_handshake(Handshake_State& state,
+                              bool force_full_renegotiation) override;
+
+      void process_handshake_msg(const Handshake_State* active_state,
+                                 Handshake_State& pending_state,
+                                 Handshake_Type type,
+                                 const std::vector<uint8_t>& contents,
+                                 bool epoch0_restart) override;
+
+      void process_client_hello_msg(const Handshake_State* active_state,
+                                    Server_Handshake_State& pending_state,
+                                    const std::vector<uint8_t>& contents,
+                                    bool epoch0_restart);
+
+      void process_certificate_msg(Server_Handshake_State& pending_state,
+                                   const std::vector<uint8_t>& contents);
+
+      void process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+                                           const std::vector<uint8_t>& contents);
+
+      void process_change_cipher_spec_msg(Server_Handshake_State& pending_state);
+
+      void process_certificate_verify_msg(Server_Handshake_State& pending_state,
+                                          Handshake_Type type,
+                                          const std::vector<uint8_t>& contents);
+
+      void process_finished_msg(Server_Handshake_State& pending_state,
+                                Handshake_Type type,
+                                const std::vector<uint8_t>& contents);
+
+      void session_resume(Server_Handshake_State& pending_state,
+                          bool have_session_ticket_key,
+                          Session& session_info);
+
+      void session_create(Server_Handshake_State& pending_state,
+                          bool have_session_ticket_key);
+
+      Handshake_State* new_handshake_state(Handshake_IO* io) override;
+
+      Credentials_Manager& m_creds;
+      std::string m_next_protocol;
+
+      // Set by deprecated constructor, Server calls both this fn and Callbacks version
+      next_protocol_fn m_choose_next_protocol;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_server_info.h b/third_party/botan/src/lib/tls/tls_server_info.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_server_info.h
@@ -0,0 +1,104 @@
+/*
+* TLS Server Information
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SERVER_INFO_H_
+#define BOTAN_TLS_SERVER_INFO_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Represents information known about a TLS server.
+*/
+class BOTAN_PUBLIC_API(2,0) Server_Information final
+   {
+   public:
+      /**
+      * An empty server info - nothing known
+      */
+      Server_Information() : m_hostname(""), m_service(""), m_port(0) {}
+
+      /**
+      * @param hostname the host's DNS name, if known
+      * @param port specifies the protocol port of the server (eg for
+      *        TCP/UDP). Zero represents unknown.
+      */
+      Server_Information(const std::string& hostname,
+                        uint16_t port = 0) :
+         m_hostname(hostname), m_service(""), m_port(port) {}
+
+      /**
+      * @param hostname the host's DNS name, if known
+      * @param service is a text string of the service type
+      *        (eg "https", "tor", or "git")
+      * @param port specifies the protocol port of the server (eg for
+      *        TCP/UDP). Zero represents unknown.
+      */
+      Server_Information(const std::string& hostname,
+                        const std::string& service,
+                        uint16_t port = 0) :
+         m_hostname(hostname), m_service(service), m_port(port) {}
+
+      /**
+      * @return the host's DNS name, if known
+      */
+      std::string hostname() const { return m_hostname; }
+
+      /**
+      * @return text string of the service type, e.g.,
+      * "https", "tor", or "git"
+      */
+      std::string service() const { return m_service; }
+
+      /**
+      * @return the protocol port of the server, or zero if unknown
+      */
+      uint16_t port() const { return m_port; }
+
+      /**
+      * @return whether the hostname is known
+      */
+      bool empty() const { return m_hostname.empty(); }
+
+   private:
+      std::string m_hostname, m_service;
+      uint16_t m_port;
+   };
+
+inline bool operator==(const Server_Information& a, const Server_Information& b)
+   {
+   return (a.hostname() == b.hostname()) &&
+          (a.service() == b.service()) &&
+          (a.port() == b.port());
+
+   }
+
+inline bool operator!=(const Server_Information& a, const Server_Information& b)
+   {
+   return !(a == b);
+   }
+
+inline bool operator<(const Server_Information& a, const Server_Information& b)
+   {
+   if(a.hostname() != b.hostname())
+      return (a.hostname() < b.hostname());
+   if(a.service() != b.service())
+      return (a.service() < b.service());
+   if(a.port() != b.port())
+      return (a.port() < b.port());
+   return false; // equal
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_session.cpp b/third_party/botan/src/lib/tls/tls_session.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session.cpp
@@ -0,0 +1,299 @@
+/*
+* TLS Session State
+* (C) 2011-2012,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session.h>
+#include <botan/loadstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_str.h>
+#include <botan/pem.h>
+#include <botan/aead.h>
+#include <botan/mac.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Session::Session(const std::vector<uint8_t>& session_identifier,
+                 const secure_vector<uint8_t>& master_secret,
+                 Protocol_Version version,
+                 uint16_t ciphersuite,
+                 Connection_Side side,
+                 bool extended_master_secret,
+                 bool encrypt_then_mac,
+                 const std::vector<X509_Certificate>& certs,
+                 const std::vector<uint8_t>& ticket,
+                 const Server_Information& server_info,
+                 const std::string& srp_identifier,
+                 uint16_t srtp_profile) :
+   m_start_time(std::chrono::system_clock::now()),
+   m_identifier(session_identifier),
+   m_session_ticket(ticket),
+   m_master_secret(master_secret),
+   m_version(version),
+   m_ciphersuite(ciphersuite),
+   m_connection_side(side),
+   m_srtp_profile(srtp_profile),
+   m_extended_master_secret(extended_master_secret),
+   m_encrypt_then_mac(encrypt_then_mac),
+   m_peer_certs(certs),
+   m_server_info(server_info),
+   m_srp_identifier(srp_identifier)
+   {
+   }
+
+Session::Session(const std::string& pem)
+   {
+   secure_vector<uint8_t> der = PEM_Code::decode_check_label(pem, "TLS SESSION");
+
+   *this = Session(der.data(), der.size());
+   }
+
+Session::Session(const uint8_t ber[], size_t ber_len)
+   {
+   uint8_t side_code = 0;
+
+   ASN1_String server_hostname;
+   ASN1_String server_service;
+   size_t server_port;
+
+   ASN1_String srp_identifier_str;
+
+   uint8_t major_version = 0, minor_version = 0;
+   std::vector<uint8_t> peer_cert_bits;
+
+   size_t start_time = 0;
+   size_t srtp_profile = 0;
+   size_t fragment_size = 0;
+   size_t compression_method = 0;
+
+   BER_Decoder(ber, ber_len)
+      .start_cons(SEQUENCE)
+        .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION),
+                          "Unknown version in serialized TLS session")
+        .decode_integer_type(start_time)
+        .decode_integer_type(major_version)
+        .decode_integer_type(minor_version)
+        .decode(m_identifier, OCTET_STRING)
+        .decode(m_session_ticket, OCTET_STRING)
+        .decode_integer_type(m_ciphersuite)
+        .decode_integer_type(compression_method)
+        .decode_integer_type(side_code)
+        .decode_integer_type(fragment_size)
+        .decode(m_extended_master_secret)
+        .decode(m_encrypt_then_mac)
+        .decode(m_master_secret, OCTET_STRING)
+        .decode(peer_cert_bits, OCTET_STRING)
+        .decode(server_hostname)
+        .decode(server_service)
+        .decode(server_port)
+        .decode(srp_identifier_str)
+        .decode(srtp_profile)
+      .end_cons()
+      .verify_end();
+
+   /*
+   * Compression is not supported and must be zero
+   */
+   if(compression_method != 0)
+      {
+      throw Decoding_Error("Serialized TLS session contains non-null compression method");
+      }
+
+   /*
+   Fragment size is not supported anymore, but the field is still
+   set in the session object.
+   */
+   if(fragment_size != 0)
+      {
+      throw Decoding_Error("Serialized TLS session used maximum fragment length which is "
+                           " no longer supported");
+      }
+
+   m_version = Protocol_Version(major_version, minor_version);
+   m_start_time = std::chrono::system_clock::from_time_t(start_time);
+   m_connection_side = static_cast<Connection_Side>(side_code);
+   m_srtp_profile = static_cast<uint16_t>(srtp_profile);
+
+   m_server_info = Server_Information(server_hostname.value(),
+                                      server_service.value(),
+                                      static_cast<uint16_t>(server_port));
+
+   m_srp_identifier = srp_identifier_str.value();
+
+   if(!peer_cert_bits.empty())
+      {
+      DataSource_Memory certs(peer_cert_bits.data(), peer_cert_bits.size());
+
+      while(!certs.end_of_data())
+         m_peer_certs.push_back(X509_Certificate(certs));
+      }
+   }
+
+secure_vector<uint8_t> Session::DER_encode() const
+   {
+   std::vector<uint8_t> peer_cert_bits;
+   for(size_t i = 0; i != m_peer_certs.size(); ++i)
+      peer_cert_bits += m_peer_certs[i].BER_encode();
+
+   return DER_Encoder()
+      .start_cons(SEQUENCE)
+         .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
+         .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
+         .encode(static_cast<size_t>(m_version.major_version()))
+         .encode(static_cast<size_t>(m_version.minor_version()))
+         .encode(m_identifier, OCTET_STRING)
+         .encode(m_session_ticket, OCTET_STRING)
+         .encode(static_cast<size_t>(m_ciphersuite))
+         .encode(static_cast<size_t>(/*old compression method*/0))
+         .encode(static_cast<size_t>(m_connection_side))
+         .encode(static_cast<size_t>(/*old fragment size*/0))
+         .encode(m_extended_master_secret)
+         .encode(m_encrypt_then_mac)
+         .encode(m_master_secret, OCTET_STRING)
+         .encode(peer_cert_bits, OCTET_STRING)
+         .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING))
+         .encode(ASN1_String(m_server_info.service(), UTF8_STRING))
+         .encode(static_cast<size_t>(m_server_info.port()))
+         .encode(ASN1_String(m_srp_identifier, UTF8_STRING))
+         .encode(static_cast<size_t>(m_srtp_profile))
+      .end_cons()
+   .get_contents();
+   }
+
+std::string Session::PEM_encode() const
+   {
+   return PEM_Code::encode(this->DER_encode(), "TLS SESSION");
+   }
+
+std::chrono::seconds Session::session_age() const
+   {
+   return std::chrono::duration_cast<std::chrono::seconds>(
+      std::chrono::system_clock::now() - m_start_time);
+   }
+
+namespace {
+
+// The output length of the HMAC must be a valid keylength for the AEAD
+const char* TLS_SESSION_CRYPT_HMAC = "HMAC(SHA-512-256)";
+// SIV would be better, but we can't assume it is available
+const char* TLS_SESSION_CRYPT_AEAD = "AES-256/GCM";
+const char* TLS_SESSION_CRYPT_KEY_NAME = "BOTAN TLS SESSION KEY NAME";
+const uint64_t TLS_SESSION_CRYPT_MAGIC = 0x068B5A9D396C0000;
+const size_t TLS_SESSION_CRYPT_MAGIC_LEN = 8;
+const size_t TLS_SESSION_CRYPT_KEY_NAME_LEN = 4;
+const size_t TLS_SESSION_CRYPT_AEAD_NONCE_LEN = 12;
+const size_t TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN = 16;
+const size_t TLS_SESSION_CRYPT_AEAD_TAG_SIZE = 16;
+
+const size_t TLS_SESSION_CRYPT_HDR_LEN =
+   TLS_SESSION_CRYPT_MAGIC_LEN +
+   TLS_SESSION_CRYPT_KEY_NAME_LEN +
+   TLS_SESSION_CRYPT_AEAD_NONCE_LEN +
+   TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+
+const size_t TLS_SESSION_CRYPT_OVERHEAD =
+   TLS_SESSION_CRYPT_HDR_LEN + TLS_SESSION_CRYPT_AEAD_TAG_SIZE;
+
+}
+
+std::vector<uint8_t>
+Session::encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const
+   {
+   auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+   hmac->set_key(key);
+
+   // First derive the "key name"
+   std::vector<uint8_t> key_name(hmac->output_length());
+   hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+   hmac->final(key_name.data());
+   key_name.resize(TLS_SESSION_CRYPT_KEY_NAME_LEN);
+
+   std::vector<uint8_t> aead_nonce;
+   std::vector<uint8_t> key_seed;
+
+   rng.random_vec(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+   rng.random_vec(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+
+   hmac->update(key_seed);
+   const secure_vector<uint8_t> aead_key = hmac->final();
+
+   secure_vector<uint8_t> bits = this->DER_encode();
+
+   // create the header
+   std::vector<uint8_t> buf;
+   buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size());
+   buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN);
+   store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]);
+   buf += key_name;
+   buf += key_seed;
+   buf += aead_nonce;
+
+   std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, ENCRYPTION);
+   BOTAN_ASSERT_NOMSG(aead->valid_nonce_length(TLS_SESSION_CRYPT_AEAD_NONCE_LEN));
+   BOTAN_ASSERT_NOMSG(aead->tag_size() == TLS_SESSION_CRYPT_AEAD_TAG_SIZE);
+   aead->set_key(aead_key);
+   aead->set_associated_data_vec(buf);
+   aead->start(aead_nonce);
+   aead->finish(bits, 0);
+
+   // append the ciphertext
+   buf += bits;
+   return buf;
+   }
+
+Session Session::decrypt(const uint8_t in[], size_t in_len, const SymmetricKey& key)
+   {
+   try
+      {
+      const size_t min_session_size = 48 + 4; // serious under-estimate
+      if(in_len < TLS_SESSION_CRYPT_OVERHEAD + min_session_size)
+         throw Decoding_Error("Encrypted session too short to be valid");
+
+      const uint8_t* magic = &in[0];
+      const uint8_t* key_name = magic + TLS_SESSION_CRYPT_MAGIC_LEN;
+      const uint8_t* key_seed = key_name + TLS_SESSION_CRYPT_KEY_NAME_LEN;
+      const uint8_t* aead_nonce = key_seed + TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+      const uint8_t* ctext = aead_nonce + TLS_SESSION_CRYPT_AEAD_NONCE_LEN;
+      const size_t ctext_len = in_len - TLS_SESSION_CRYPT_HDR_LEN; // includes the tag
+
+      if(load_be<uint64_t>(magic, 0) != TLS_SESSION_CRYPT_MAGIC)
+         throw Decoding_Error("Missing expected magic numbers");
+
+      auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+      hmac->set_key(key);
+
+      // First derive and check the "key name"
+      std::vector<uint8_t> cmp_key_name(hmac->output_length());
+      hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+      hmac->final(cmp_key_name.data());
+
+      if(same_mem(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN) == false)
+         throw Decoding_Error("Wrong key name for encrypted session");
+
+      hmac->update(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+      const secure_vector<uint8_t> aead_key = hmac->final();
+
+      auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, DECRYPTION);
+      aead->set_key(aead_key);
+      aead->set_associated_data(in, TLS_SESSION_CRYPT_HDR_LEN);
+      aead->start(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+      secure_vector<uint8_t> buf(ctext, ctext + ctext_len);
+      aead->finish(buf, 0);
+      return Session(buf.data(), buf.size());
+      }
+   catch(std::exception& e)
+      {
+      throw Decoding_Error("Failed to decrypt serialized TLS session: " +
+                           std::string(e.what()));
+      }
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_session.h b/third_party/botan/src/lib/tls/tls_session.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session.h
@@ -0,0 +1,210 @@
+/*
+* TLS Session
+* (C) 2011-2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_STATE_H_
+#define BOTAN_TLS_SESSION_STATE_H_
+
+#include <botan/x509cert.h>
+#include <botan/tls_version.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_server_info.h>
+#include <botan/secmem.h>
+#include <botan/symkey.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Class representing a TLS session state
+*/
+class BOTAN_PUBLIC_API(2,0) Session final
+   {
+   public:
+
+      /**
+      * Uninitialized session
+      */
+      Session() :
+         m_start_time(std::chrono::system_clock::time_point::min()),
+         m_version(),
+         m_ciphersuite(0),
+         m_connection_side(static_cast<Connection_Side>(0)),
+         m_srtp_profile(0),
+         m_extended_master_secret(false),
+         m_encrypt_then_mac(false)
+            {}
+
+      /**
+      * New session (sets session start time)
+      */
+      Session(const std::vector<uint8_t>& session_id,
+              const secure_vector<uint8_t>& master_secret,
+              Protocol_Version version,
+              uint16_t ciphersuite,
+              Connection_Side side,
+              bool supports_extended_master_secret,
+              bool supports_encrypt_then_mac,
+              const std::vector<X509_Certificate>& peer_certs,
+              const std::vector<uint8_t>& session_ticket,
+              const Server_Information& server_info,
+              const std::string& srp_identifier,
+              uint16_t srtp_profile);
+
+      /**
+      * Load a session from DER representation (created by DER_encode)
+      * @param ber DER representation buffer
+      * @param ber_len size of buffer in bytes
+      */
+      Session(const uint8_t ber[], size_t ber_len);
+
+      /**
+      * Load a session from PEM representation (created by PEM_encode)
+      * @param pem PEM representation
+      */
+      explicit Session(const std::string& pem);
+
+      /**
+      * Encode this session data for storage
+      * @warning if the master secret is compromised so is the
+      * session traffic
+      */
+      secure_vector<uint8_t> DER_encode() const;
+
+      /**
+      * Encrypt a session (useful for serialization or session tickets)
+      */
+      std::vector<uint8_t> encrypt(const SymmetricKey& key,
+                                RandomNumberGenerator& rng) const;
+
+
+      /**
+      * Decrypt a session created by encrypt
+      * @param ctext the ciphertext returned by encrypt
+      * @param ctext_size the size of ctext in bytes
+      * @param key the same key used by the encrypting side
+      */
+      static Session decrypt(const uint8_t ctext[],
+                             size_t ctext_size,
+                             const SymmetricKey& key);
+
+      /**
+      * Decrypt a session created by encrypt
+      * @param ctext the ciphertext returned by encrypt
+      * @param key the same key used by the encrypting side
+      */
+      static inline Session decrypt(const std::vector<uint8_t>& ctext,
+                                    const SymmetricKey& key)
+         {
+         return Session::decrypt(ctext.data(), ctext.size(), key);
+         }
+
+      /**
+      * Encode this session data for storage
+      * @warning if the master secret is compromised so is the
+      * session traffic
+      */
+      std::string PEM_encode() const;
+
+      /**
+      * Get the version of the saved session
+      */
+      Protocol_Version version() const { return m_version; }
+
+      /**
+      * Get the ciphersuite code of the saved session
+      */
+      uint16_t ciphersuite_code() const { return m_ciphersuite; }
+
+      /**
+      * Get the ciphersuite info of the saved session
+      */
+      Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); }
+
+      /**
+      * Get which side of the connection the resumed session we are/were
+      * acting as.
+      */
+      Connection_Side side() const { return m_connection_side; }
+
+      /**
+      * Get the SRP identity (if sent by the client in the initial handshake)
+      */
+      const std::string& srp_identifier() const { return m_srp_identifier; }
+
+      /**
+      * Get the saved master secret
+      */
+      const secure_vector<uint8_t>& master_secret() const { return m_master_secret; }
+
+      /**
+      * Get the session identifier
+      */
+      const std::vector<uint8_t>& session_id() const { return m_identifier; }
+
+      /**
+      * Get the negotiated DTLS-SRTP algorithm (RFC 5764)
+      */
+      uint16_t dtls_srtp_profile() const { return m_srtp_profile; }
+
+      bool supports_extended_master_secret() const { return m_extended_master_secret; }
+
+      bool supports_encrypt_then_mac() const { return m_encrypt_then_mac; }
+
+      /**
+      * Return the certificate chain of the peer (possibly empty)
+      */
+      const std::vector<X509_Certificate>& peer_certs() const { return m_peer_certs; }
+
+      /**
+      * Get the wall clock time this session began
+      */
+      std::chrono::system_clock::time_point start_time() const { return m_start_time; }
+
+      /**
+      * Return how long this session has existed (in seconds)
+      */
+      std::chrono::seconds session_age() const;
+
+      /**
+      * Return the session ticket the server gave us
+      */
+      const std::vector<uint8_t>& session_ticket() const { return m_session_ticket; }
+
+      /**
+      * @return information about the TLS server
+      */
+      const Server_Information& server_info() const { return m_server_info; }
+
+   private:
+      enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160812 };
+
+      std::chrono::system_clock::time_point m_start_time;
+
+      std::vector<uint8_t> m_identifier;
+      std::vector<uint8_t> m_session_ticket; // only used by client side
+      secure_vector<uint8_t> m_master_secret;
+
+      Protocol_Version m_version;
+      uint16_t m_ciphersuite;
+      Connection_Side m_connection_side;
+      uint16_t m_srtp_profile;
+      bool m_extended_master_secret;
+      bool m_encrypt_then_mac;
+
+      std::vector<X509_Certificate> m_peer_certs;
+      Server_Information m_server_info; // optional
+      std::string m_srp_identifier; // optional
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_session_key.cpp b/third_party/botan/src/lib/tls/tls_session_key.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session_key.cpp
@@ -0,0 +1,101 @@
+/*
+* TLS Session Key
+* (C) 2004-2006,2011,2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Session_Keys Constructor
+*/
+Session_Keys::Session_Keys(const Handshake_State* state,
+                           const secure_vector<uint8_t>& pre_master_secret,
+                           bool resuming)
+   {
+   const size_t cipher_keylen = state->ciphersuite().cipher_keylen();
+   const size_t mac_keylen = state->ciphersuite().mac_keylen();
+   const size_t cipher_nonce_bytes = state->ciphersuite().nonce_bytes_from_handshake();
+
+   const bool extended_master_secret = state->server_hello()->supports_extended_master_secret();
+
+   const size_t prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_nonce_bytes);
+
+   const uint8_t MASTER_SECRET_MAGIC[] = {
+      0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+
+   const uint8_t EXT_MASTER_SECRET_MAGIC[] = {
+      0x65, 0x78, 0x74, 0x65, 0x6E, 0x64, 0x65, 0x64, 0x20,
+      0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+
+   const uint8_t KEY_GEN_MAGIC[] = {
+      0x6B, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6E, 0x73, 0x69, 0x6F, 0x6E };
+
+   std::unique_ptr<KDF> prf(state->protocol_specific_prf());
+
+   if(resuming)
+      {
+      // This is actually the master secret saved as part of the session
+      m_master_sec = pre_master_secret;
+      }
+   else
+      {
+      std::vector<uint8_t> salt;
+      std::vector<uint8_t> label;
+      if(extended_master_secret)
+         {
+         label.assign(EXT_MASTER_SECRET_MAGIC, EXT_MASTER_SECRET_MAGIC + sizeof(EXT_MASTER_SECRET_MAGIC));
+         salt += state->hash().final(state->version(),
+                                     state->ciphersuite().prf_algo());
+         }
+      else
+         {
+         label.assign(MASTER_SECRET_MAGIC, MASTER_SECRET_MAGIC + sizeof(MASTER_SECRET_MAGIC));
+         salt += state->client_hello()->random();
+         salt += state->server_hello()->random();
+         }
+
+      m_master_sec = prf->derive_key(48, pre_master_secret, salt, label);
+      }
+
+   std::vector<uint8_t> salt;
+   std::vector<uint8_t> label;
+   label.assign(KEY_GEN_MAGIC, KEY_GEN_MAGIC + sizeof(KEY_GEN_MAGIC));
+   salt += state->server_hello()->random();
+   salt += state->client_hello()->random();
+
+   const secure_vector<uint8_t> prf_output = prf->derive_key(
+      prf_gen,
+      m_master_sec.data(), m_master_sec.size(),
+      salt.data(), salt.size(),
+      label.data(), label.size());
+
+   const uint8_t* key_data = prf_output.data();
+
+   m_c_aead.resize(mac_keylen + cipher_keylen);
+   m_s_aead.resize(mac_keylen + cipher_keylen);
+
+   copy_mem(&m_c_aead[0], key_data, mac_keylen);
+   copy_mem(&m_s_aead[0], key_data + mac_keylen, mac_keylen);
+
+   copy_mem(&m_c_aead[mac_keylen], key_data + 2*mac_keylen, cipher_keylen);
+   copy_mem(&m_s_aead[mac_keylen], key_data + 2*mac_keylen + cipher_keylen, cipher_keylen);
+
+   m_c_nonce.resize(cipher_nonce_bytes);
+   m_s_nonce.resize(cipher_nonce_bytes);
+
+   copy_mem(&m_c_nonce[0], key_data + 2*(mac_keylen + cipher_keylen), cipher_nonce_bytes);
+   copy_mem(&m_s_nonce[0], key_data + 2*(mac_keylen + cipher_keylen) + cipher_nonce_bytes, cipher_nonce_bytes);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_session_key.h b/third_party/botan/src/lib/tls/tls_session_key.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session_key.h
@@ -0,0 +1,82 @@
+/*
+* TLS Session Key
+* (C) 2004-2006,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_KEYS_H_
+#define BOTAN_TLS_SESSION_KEYS_H_
+
+#include <botan/secmem.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_State;
+
+/**
+* TLS Session Keys
+*/
+class Session_Keys final
+   {
+   public:
+      /**
+      * @return client AEAD key
+      */
+      const secure_vector<uint8_t>& client_aead_key() const { return m_c_aead; }
+
+      /**
+      * @return server AEAD key
+      */
+      const secure_vector<uint8_t>& server_aead_key() const { return m_s_aead; }
+
+      /**
+      * @return client nonce
+      */
+      const std::vector<uint8_t>& client_nonce() const { return m_c_nonce; }
+
+      /**
+      * @return server nonce
+      */
+      const std::vector<uint8_t>& server_nonce() const { return m_s_nonce; }
+
+      /**
+      * @return TLS master secret
+      */
+      const secure_vector<uint8_t>& master_secret() const { return m_master_sec; }
+
+      const secure_vector<uint8_t>& aead_key(Connection_Side side) const
+         {
+         return (side == Connection_Side::CLIENT) ? client_aead_key() : server_aead_key();
+         }
+
+      const std::vector<uint8_t>& nonce(Connection_Side side) const
+         {
+         return (side == Connection_Side::CLIENT) ? client_nonce() : server_nonce();
+         }
+
+      Session_Keys() = default;
+
+      /**
+      * @param state state the handshake state
+      * @param pre_master_secret the pre-master secret
+      * @param resuming whether this TLS session is resumed
+      */
+      Session_Keys(const Handshake_State* state,
+                   const secure_vector<uint8_t>& pre_master_secret,
+                   bool resuming);
+
+   private:
+      secure_vector<uint8_t> m_master_sec;
+      secure_vector<uint8_t> m_c_aead, m_s_aead;
+      std::vector<uint8_t> m_c_nonce, m_s_nonce;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_session_manager.h b/third_party/botan/src/lib/tls/tls_session_manager.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session_manager.h
@@ -0,0 +1,160 @@
+/*
+* TLS Session Manager
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_MANAGER_H_
+#define BOTAN_TLS_SESSION_MANAGER_H_
+
+#include <botan/tls_session.h>
+#include <botan/mutex.h>
+#include <chrono>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Session_Manager is an interface to systems which can save
+* session parameters for supporting session resumption.
+*
+* Saving sessions is done on a best-effort basis; an implementation is
+* allowed to drop sessions due to space constraints.
+*
+* Implementations should strive to be thread safe
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager
+   {
+   public:
+      /**
+      * Try to load a saved session (using session ID)
+      * @param session_id the session identifier we are trying to resume
+      * @param session will be set to the saved session data (if found),
+               or not modified if not found
+      * @return true if session was modified
+      */
+      virtual bool load_from_session_id(const std::vector<uint8_t>& session_id,
+                                        Session& session) = 0;
+
+      /**
+      * Try to load a saved session (using info about server)
+      * @param info the information about the server
+      * @param session will be set to the saved session data (if found),
+               or not modified if not found
+      * @return true if session was modified
+      */
+      virtual bool load_from_server_info(const Server_Information& info,
+                                         Session& session) = 0;
+
+      /**
+      * Remove this session id from the cache, if it exists
+      */
+      virtual void remove_entry(const std::vector<uint8_t>& session_id) = 0;
+
+      /**
+      * Remove all sessions from the cache, return number of sessions deleted
+      */
+      virtual size_t remove_all() = 0;
+
+      /**
+      * Save a session on a best effort basis; the manager may not in
+      * fact be able to save the session for whatever reason; this is
+      * not an error. Caller cannot assume that calling save followed
+      * immediately by load_from_* will result in a successful lookup.
+      *
+      * @param session to save
+      */
+      virtual void save(const Session& session) = 0;
+
+      /**
+      * Return the allowed lifetime of a session; beyond this time,
+      * sessions are not resumed. Returns 0 if unknown/no explicit
+      * expiration policy.
+      */
+      virtual std::chrono::seconds session_lifetime() const = 0;
+
+      virtual ~Session_Manager() = default;
+   };
+
+/**
+* An implementation of Session_Manager that does not save sessions at
+* all, preventing session resumption.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_Noop final : public Session_Manager
+   {
+   public:
+      bool load_from_session_id(const std::vector<uint8_t>&, Session&) override
+         { return false; }
+
+      bool load_from_server_info(const Server_Information&, Session&) override
+         { return false; }
+
+      void remove_entry(const std::vector<uint8_t>&) override {}
+
+      size_t remove_all() override { return 0; }
+
+      void save(const Session&) override {}
+
+      std::chrono::seconds session_lifetime() const override
+         { return std::chrono::seconds(0); }
+   };
+
+/**
+* An implementation of Session_Manager that saves values in memory.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_In_Memory final : public Session_Manager
+   {
+   public:
+      /**
+      * @param rng a RNG used for generating session key and for
+      *        session encryption
+      * @param max_sessions a hint on the maximum number of sessions
+      *        to keep in memory at any one time. (If zero, don't cap)
+      * @param session_lifetime sessions are expired after this many
+      *        seconds have elapsed from initial handshake.
+      */
+      Session_Manager_In_Memory(RandomNumberGenerator& rng,
+                                size_t max_sessions = 1000,
+                                std::chrono::seconds session_lifetime =
+                                   std::chrono::seconds(7200));
+
+      bool load_from_session_id(const std::vector<uint8_t>& session_id,
+                                Session& session) override;
+
+      bool load_from_server_info(const Server_Information& info,
+                                 Session& session) override;
+
+      void remove_entry(const std::vector<uint8_t>& session_id) override;
+
+      size_t remove_all() override;
+
+      void save(const Session& session_data) override;
+
+      std::chrono::seconds session_lifetime() const override
+         { return m_session_lifetime; }
+
+   private:
+      bool load_from_session_str(const std::string& session_str,
+                                 Session& session);
+
+      mutex_type m_mutex;
+
+      size_t m_max_sessions;
+
+      std::chrono::seconds m_session_lifetime;
+
+      RandomNumberGenerator& m_rng;
+      secure_vector<uint8_t> m_session_key;
+
+      std::map<std::string, std::vector<uint8_t>> m_sessions; // hex(session_id) -> session
+      std::map<Server_Information, std::string> m_info_sessions;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp b/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp
@@ -0,0 +1,132 @@
+/*
+* TLS Session Management
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_In_Memory::Session_Manager_In_Memory(
+   RandomNumberGenerator& rng,
+   size_t max_sessions,
+   std::chrono::seconds session_lifetime) :
+   m_max_sessions(max_sessions),
+   m_session_lifetime(session_lifetime),
+   m_rng(rng),
+   m_session_key(m_rng.random_vec(32))
+   {}
+
+bool Session_Manager_In_Memory::load_from_session_str(
+   const std::string& session_str, Session& session)
+   {
+   // assert(lock is held)
+
+   auto i = m_sessions.find(session_str);
+
+   if(i == m_sessions.end())
+      return false;
+
+   try
+      {
+      session = Session::decrypt(i->second, m_session_key);
+      }
+   catch(...)
+      {
+      return false;
+      }
+
+   // if session has expired, remove it
+   const auto now = std::chrono::system_clock::now();
+
+   if(session.start_time() + session_lifetime() < now)
+      {
+      m_sessions.erase(i);
+      return false;
+      }
+
+   return true;
+   }
+
+bool Session_Manager_In_Memory::load_from_session_id(
+   const std::vector<uint8_t>& session_id, Session& session)
+   {
+   lock_guard_type<mutex_type> lock(m_mutex);
+
+   return load_from_session_str(hex_encode(session_id), session);
+   }
+
+bool Session_Manager_In_Memory::load_from_server_info(
+   const Server_Information& info, Session& session)
+   {
+   lock_guard_type<mutex_type> lock(m_mutex);
+
+   auto i = m_info_sessions.find(info);
+
+   if(i == m_info_sessions.end())
+      return false;
+
+   if(load_from_session_str(i->second, session))
+      return true;
+
+   /*
+   * It existed at one point but was removed from the sessions map,
+   * remove m_info_sessions entry as well
+   */
+   m_info_sessions.erase(i);
+
+   return false;
+   }
+
+void Session_Manager_In_Memory::remove_entry(
+   const std::vector<uint8_t>& session_id)
+   {
+   lock_guard_type<mutex_type> lock(m_mutex);
+
+   auto i = m_sessions.find(hex_encode(session_id));
+
+   if(i != m_sessions.end())
+      m_sessions.erase(i);
+   }
+
+size_t Session_Manager_In_Memory::remove_all()
+   {
+   const size_t removed = m_sessions.size();
+   m_info_sessions.clear();
+   m_sessions.clear();
+   m_rng.random_vec(m_session_key, 32);
+   return removed;
+   }
+
+void Session_Manager_In_Memory::save(const Session& session)
+   {
+   lock_guard_type<mutex_type> lock(m_mutex);
+
+   if(m_max_sessions != 0)
+      {
+      /*
+      We generate new session IDs with the first 4 bytes being a
+      timestamp, so this actually removes the oldest sessions first.
+      */
+      while(m_sessions.size() >= m_max_sessions)
+         m_sessions.erase(m_sessions.begin());
+      }
+
+   const std::string session_id_str = hex_encode(session.session_id());
+
+   m_sessions[session_id_str] = session.encrypt(m_session_key, m_rng);
+
+   if(session.side() == CLIENT && !session.server_info().empty())
+      m_info_sessions[session.server_info()] = session_id_str;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_suite_info.cpp b/third_party/botan/src/lib/tls/tls_suite_info.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_suite_info.cpp
@@ -0,0 +1,212 @@
+/*
+* TLS cipher suite information
+*
+* This file was automatically generated from the IANA assignments
+* (tls-parameters.txt hash fe1ef8f3492b0708f3b14c9e8f8de55188c1b3c0)
+* by ./src/scripts/tls_suite_info.py on 2019-05-24
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_ciphersuite.h>
+
+namespace Botan {
+
+namespace TLS {
+
+//static
+const std::vector<Ciphersuite>& Ciphersuite::all_known_ciphersuites()
+   {
+   // Note that this list of ciphersuites is ordered by id!
+   static const std::vector<Ciphersuite> g_ciphersuite_list = {
+      Ciphersuite(0x000A, "RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0013, "DHE_DSS_WITH_3DES_EDE_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0016, "DHE_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x001B, "DH_anon_WITH_3DES_EDE_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x002F, "RSA_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0032, "DHE_DSS_WITH_AES_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0033, "DHE_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0034, "DH_anon_WITH_AES_128_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0035, "RSA_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0038, "DHE_DSS_WITH_AES_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0039, "DHE_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x003A, "DH_anon_WITH_AES_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x003C, "RSA_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x003D, "RSA_WITH_AES_256_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0040, "DHE_DSS_WITH_AES_128_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0041, "RSA_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0044, "DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0045, "DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0046, "DH_anon_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0067, "DHE_RSA_WITH_AES_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x006A, "DHE_DSS_WITH_AES_256_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x006B, "DHE_RSA_WITH_AES_256_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x006C, "DH_anon_WITH_AES_128_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x006D, "DH_anon_WITH_AES_256_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0084, "RSA_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0087, "DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0088, "DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0089, "DH_anon_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x008B, "PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x008C, "PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x008D, "PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x008F, "DHE_PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0090, "DHE_PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0091, "DHE_PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0096, "RSA_WITH_SEED_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x0099, "DHE_DSS_WITH_SEED_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x009A, "DHE_RSA_WITH_SEED_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x009B, "DH_anon_WITH_SEED_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x009C, "RSA_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x009D, "RSA_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x009E, "DHE_RSA_WITH_AES_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x009F, "DHE_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A2, "DHE_DSS_WITH_AES_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A3, "DHE_DSS_WITH_AES_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A6, "DH_anon_WITH_AES_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A7, "DH_anon_WITH_AES_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A8, "PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00A9, "PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00AA, "DHE_PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00AB, "DHE_PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x00AE, "PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00AF, "PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00B2, "DHE_PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00B3, "DHE_PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00BA, "RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00BD, "DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00BE, "DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00BF, "DH_anon_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00C0, "RSA_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00C3, "DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00C4, "DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x00C5, "DH_anon_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0x16B7, "CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0x16B8, "CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0x16B9, "CECPQ1_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::CECPQ1, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0x16BA, "CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC008, "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC009, "ECDHE_ECDSA_WITH_AES_128_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC00A, "ECDHE_ECDSA_WITH_AES_256_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC012, "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC013, "ECDHE_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC014, "ECDHE_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC017, "ECDH_anon_WITH_3DES_EDE_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC018, "ECDH_anon_WITH_AES_128_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC019, "ECDH_anon_WITH_AES_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01A, "SRP_SHA_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01B, "SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01C, "SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01D, "SRP_SHA_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01E, "SRP_SHA_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC01F, "SRP_SHA_DSS_WITH_AES_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC020, "SRP_SHA_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC021, "SRP_SHA_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC022, "SRP_SHA_DSS_WITH_AES_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC023, "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC024, "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC027, "ECDHE_RSA_WITH_AES_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC028, "ECDHE_RSA_WITH_AES_256_CBC_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC02B, "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC02C, "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC02F, "ECDHE_RSA_WITH_AES_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC030, "ECDHE_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC034, "ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC035, "ECDHE_PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC036, "ECDHE_PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC037, "ECDHE_PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC038, "ECDHE_PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC050, "RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC051, "RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC052, "DHE_RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC053, "DHE_RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC056, "DHE_DSS_WITH_ARIA_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC057, "DHE_DSS_WITH_ARIA_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC05A, "DH_anon_WITH_ARIA_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC05B, "DH_anon_WITH_ARIA_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC05C, "ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC05D, "ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC060, "ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC061, "ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC06A, "PSK_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC06B, "PSK_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC06C, "DHE_PSK_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC06D, "DHE_PSK_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC072, "ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC073, "ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC076, "ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC077, "ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC07A, "RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC07B, "RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC07C, "DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC07D, "DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC080, "DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC081, "DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC084, "DH_anon_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC085, "DH_anon_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC086, "ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC087, "ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC08A, "ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC08B, "ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC08E, "PSK_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC08F, "PSK_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC090, "DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC091, "DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC094, "PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC095, "PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC096, "DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC097, "DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC09A, "ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC09B, "ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+      Ciphersuite(0xC09C, "RSA_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC09D, "RSA_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC09E, "DHE_RSA_WITH_AES_128_CCM", Auth_Method::RSA, Kex_Algo::DH, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC09F, "DHE_RSA_WITH_AES_256_CCM", Auth_Method::RSA, Kex_Algo::DH, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A0, "RSA_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A1, "RSA_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A2, "DHE_RSA_WITH_AES_128_CCM_8", Auth_Method::RSA, Kex_Algo::DH, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A3, "DHE_RSA_WITH_AES_256_CCM_8", Auth_Method::RSA, Kex_Algo::DH, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A4, "PSK_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A5, "PSK_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A6, "DHE_PSK_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A7, "DHE_PSK_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A8, "PSK_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0A9, "PSK_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AA, "PSK_DHE_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AB, "PSK_DHE_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AC, "ECDHE_ECDSA_WITH_AES_128_CCM", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AD, "ECDHE_ECDSA_WITH_AES_256_CCM", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AE, "ECDHE_ECDSA_WITH_AES_128_CCM_8", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xC0AF, "ECDHE_ECDSA_WITH_AES_256_CCM_8", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xCCA8, "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xCCA9, "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xCCAA, "DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::DH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xCCAB, "PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xCCAC, "ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xCCAD, "DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xD001, "ECDHE_PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xD002, "ECDHE_PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xD003, "ECDHE_PSK_WITH_AES_128_CCM_8_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xD005, "ECDHE_PSK_WITH_AES_128_CCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+      Ciphersuite(0xFFC0, "DHE_RSA_WITH_AES_128_OCB_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC1, "DHE_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC2, "ECDHE_RSA_WITH_AES_128_OCB_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC3, "ECDHE_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC4, "ECDHE_ECDSA_WITH_AES_128_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC5, "ECDHE_ECDSA_WITH_AES_256_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC6, "PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC7, "PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC8, "DHE_PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFC9, "DHE_PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFCA, "ECDHE_PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFCB, "ECDHE_PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFCC, "CECPQ1_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::CECPQ1, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      Ciphersuite(0xFFCD, "CECPQ1_ECDSA_WITH_AES_256_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+      };
+
+   return g_ciphersuite_list;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_text_policy.cpp b/third_party/botan/src/lib/tls/tls_text_policy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_text_policy.cpp
@@ -0,0 +1,319 @@
+/*
+* Text-Based TLS Policy
+* (C) 2016,2017 Jack Lloyd
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_policy.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::vector<std::string> Text_Policy::allowed_ciphers() const
+   {
+   return get_list("ciphers", Policy::allowed_ciphers());
+   }
+
+std::vector<std::string> Text_Policy::allowed_signature_hashes() const
+   {
+   return get_list("signature_hashes", Policy::allowed_signature_hashes());
+   }
+
+std::vector<std::string> Text_Policy::allowed_macs() const
+   {
+   return get_list("macs", Policy::allowed_macs());
+   }
+
+std::vector<std::string> Text_Policy::allowed_key_exchange_methods() const
+   {
+   return get_list("key_exchange_methods", Policy::allowed_key_exchange_methods());
+   }
+
+std::vector<std::string> Text_Policy::allowed_signature_methods() const
+   {
+   return get_list("signature_methods", Policy::allowed_signature_methods());
+   }
+
+bool Text_Policy::use_ecc_point_compression() const
+   {
+   return get_bool("use_ecc_point_compression", Policy::use_ecc_point_compression());
+   }
+
+bool Text_Policy::allow_tls10() const
+   {
+   return get_bool("allow_tls10", Policy::allow_tls10());
+   }
+
+bool Text_Policy::allow_tls11() const
+   {
+   return get_bool("allow_tls11", Policy::allow_tls11());
+   }
+
+bool Text_Policy::allow_tls12() const
+   {
+   return get_bool("allow_tls12", Policy::allow_tls12());
+   }
+
+bool Text_Policy::allow_dtls10() const
+   {
+   return get_bool("allow_dtls10", Policy::allow_dtls10());
+   }
+
+bool Text_Policy::allow_dtls12() const
+   {
+   return get_bool("allow_dtls12", Policy::allow_dtls12());
+   }
+
+bool Text_Policy::allow_insecure_renegotiation() const
+   {
+   return get_bool("allow_insecure_renegotiation", Policy::allow_insecure_renegotiation());
+   }
+
+bool Text_Policy::include_time_in_hello_random() const
+   {
+   return get_bool("include_time_in_hello_random", Policy::include_time_in_hello_random());
+   }
+
+bool Text_Policy::require_client_certificate_authentication() const
+   {
+   return get_bool("require_client_certificate_authentication", Policy::require_client_certificate_authentication());
+   }
+
+bool Text_Policy::allow_client_initiated_renegotiation() const
+   {
+   return get_bool("allow_client_initiated_renegotiation", Policy::allow_client_initiated_renegotiation());
+   }
+
+bool Text_Policy::allow_server_initiated_renegotiation() const
+   {
+   return get_bool("allow_server_initiated_renegotiation", Policy::allow_server_initiated_renegotiation());
+   }
+
+bool Text_Policy::server_uses_own_ciphersuite_preferences() const
+   {
+   return get_bool("server_uses_own_ciphersuite_preferences", Policy::server_uses_own_ciphersuite_preferences());
+   }
+
+bool Text_Policy::negotiate_encrypt_then_mac() const
+   {
+   return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac());
+   }
+
+bool Text_Policy::support_cert_status_message() const
+   {
+   return get_bool("support_cert_status_message", Policy::support_cert_status_message());
+   }
+
+std::vector<Group_Params> Text_Policy::key_exchange_groups() const
+   {
+   std::string group_str = get_str("key_exchange_groups");
+
+   if(group_str.empty())
+      {
+      // fall back to previously used name
+      group_str = get_str("groups");
+      }
+
+   if(group_str.empty())
+      {
+      return Policy::key_exchange_groups();
+      }
+
+   std::vector<Group_Params> groups;
+   for(std::string group_name : split_on(group_str, ' '))
+      {
+      Group_Params group_id = group_param_from_string(group_name);
+
+      if(group_id == Group_Params::NONE)
+         {
+         try
+            {
+            size_t consumed = 0;
+            unsigned long ll_id = std::stoul(group_name, &consumed, 0);
+            if(consumed != group_name.size())
+               continue; // some other cruft
+
+            const uint16_t id = static_cast<uint16_t>(ll_id);
+
+            if(id != ll_id)
+               continue; // integer too large
+
+            group_id = static_cast<Group_Params>(id);
+            }
+         catch(...)
+            {
+            continue;
+            }
+         }
+
+      if(group_id != Group_Params::NONE)
+         groups.push_back(group_id);
+      }
+
+   return groups;
+   }
+
+size_t Text_Policy::minimum_ecdh_group_size() const
+   {
+   return get_len("minimum_ecdh_group_size", Policy::minimum_ecdh_group_size());
+   }
+
+size_t Text_Policy::minimum_ecdsa_group_size() const
+   {
+   return get_len("minimum_ecdsa_group_size", Policy::minimum_ecdsa_group_size());
+   }
+
+size_t Text_Policy::minimum_dh_group_size() const
+   {
+   return get_len("minimum_dh_group_size", Policy::minimum_dh_group_size());
+   }
+
+size_t Text_Policy::minimum_rsa_bits() const
+   {
+   return get_len("minimum_rsa_bits", Policy::minimum_rsa_bits());
+   }
+
+size_t Text_Policy::minimum_signature_strength() const
+   {
+   return get_len("minimum_signature_strength", Policy::minimum_signature_strength());
+   }
+
+size_t Text_Policy::dtls_default_mtu() const
+   {
+   return get_len("dtls_default_mtu", Policy::dtls_default_mtu());
+   }
+
+size_t Text_Policy::dtls_initial_timeout() const
+   {
+   return get_len("dtls_initial_timeout", Policy::dtls_initial_timeout());
+   }
+
+size_t Text_Policy::dtls_maximum_timeout() const
+   {
+   return get_len("dtls_maximum_timeout", Policy::dtls_maximum_timeout());
+   }
+
+bool Text_Policy::require_cert_revocation_info() const
+   {
+   return get_bool("require_cert_revocation_info", Policy::require_cert_revocation_info());
+   }
+
+bool Text_Policy::hide_unknown_users() const
+   {
+   return get_bool("hide_unknown_users", Policy::hide_unknown_users());
+   }
+
+uint32_t Text_Policy::session_ticket_lifetime() const
+   {
+   return static_cast<uint32_t>(get_len("session_ticket_lifetime", Policy::session_ticket_lifetime()));
+   }
+
+bool Text_Policy::send_fallback_scsv(Protocol_Version version) const
+   {
+   return get_bool("send_fallback_scsv", false) ? Policy::send_fallback_scsv(version) : false;
+   }
+
+std::vector<uint16_t> Text_Policy::srtp_profiles() const
+   {
+   std::vector<uint16_t> r;
+   for(std::string p : get_list("srtp_profiles", std::vector<std::string>()))
+      {
+      r.push_back(to_uint16(p));
+      }
+   return r;
+   }
+
+void Text_Policy::set(const std::string& k, const std::string& v)
+   {
+   m_kv[k] = v;
+   }
+
+Text_Policy::Text_Policy(const std::string& s)
+   {
+   std::istringstream iss(s);
+   m_kv = read_cfg(iss);
+   }
+
+Text_Policy::Text_Policy(std::istream& in) : m_kv(read_cfg(in))
+   {}
+
+std::vector<std::string>
+Text_Policy::get_list(const std::string& key,
+                      const std::vector<std::string>& def) const
+   {
+   const std::string v = get_str(key);
+
+   if(v.empty())
+      {
+      return def;
+      }
+
+   return split_on(v, ' ');
+   }
+
+size_t Text_Policy::get_len(const std::string& key, size_t def) const
+   {
+   const std::string v = get_str(key);
+
+   if(v.empty())
+      {
+      return def;
+      }
+
+   return to_u32bit(v);
+   }
+
+bool Text_Policy::get_bool(const std::string& key, bool def) const
+   {
+   const std::string v = get_str(key);
+
+   if(v.empty())
+      {
+      return def;
+      }
+
+   if(v == "true" || v == "True")
+      {
+      return true;
+      }
+   else if(v == "false" || v == "False")
+      {
+      return false;
+      }
+   else
+      {
+      throw Decoding_Error("Invalid boolean '" + v + "'");
+      }
+   }
+
+std::string Text_Policy::get_str(const std::string& key, const std::string& def) const
+   {
+   auto i = m_kv.find(key);
+   if(i == m_kv.end())
+      {
+      return def;
+      }
+
+   return i->second;
+   }
+
+bool Text_Policy::set_value(const std::string& key, const std::string& val, bool overwrite)
+   {
+   auto i = m_kv.find(key);
+
+   if(overwrite == false && i != m_kv.end())
+      return false;
+
+   m_kv.insert(i, std::make_pair(key, val));
+   return true;
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_version.cpp b/third_party/botan/src/lib/tls/tls_version.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_version.cpp
@@ -0,0 +1,88 @@
+/*
+* TLS Protocol Version Management
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_version.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string Protocol_Version::to_string() const
+   {
+   const uint8_t maj = major_version();
+   const uint8_t min = minor_version();
+
+   if(maj == 3 && min == 0)
+      return "SSL v3";
+
+   if(maj == 3 && min >= 1) // TLS v1.x
+      return "TLS v1." + std::to_string(min-1);
+
+   if(maj == 254) // DTLS 1.x
+      return "DTLS v1." + std::to_string(255 - min);
+
+   // Some very new or very old protocol (or bogus data)
+   return "Unknown " + std::to_string(maj) + "." + std::to_string(min);
+   }
+
+bool Protocol_Version::is_datagram_protocol() const
+   {
+   return major_version() > 250;
+   }
+
+bool Protocol_Version::operator>(const Protocol_Version& other) const
+   {
+   if(this->is_datagram_protocol() != other.is_datagram_protocol())
+      throw TLS_Exception(Alert::PROTOCOL_VERSION,
+                          "Version comparing " + to_string() +
+                          " with " + other.to_string());
+
+   if(this->is_datagram_protocol())
+      return m_version < other.m_version; // goes backwards
+
+   return m_version > other.m_version;
+   }
+
+bool Protocol_Version::known_version() const
+   {
+   return (m_version == Protocol_Version::TLS_V10 ||
+           m_version == Protocol_Version::TLS_V11 ||
+           m_version == Protocol_Version::TLS_V12 ||
+           m_version == Protocol_Version::DTLS_V10 ||
+           m_version == Protocol_Version::DTLS_V12);
+   }
+
+bool Protocol_Version::supports_negotiable_signature_algorithms() const
+   {
+   return (m_version != Protocol_Version::TLS_V10 &&
+           m_version != Protocol_Version::TLS_V11 &&
+           m_version != Protocol_Version::DTLS_V10);
+   }
+
+bool Protocol_Version::supports_explicit_cbc_ivs() const
+   {
+   return (m_version != Protocol_Version::TLS_V10);
+   }
+
+bool Protocol_Version::supports_ciphersuite_specific_prf() const
+   {
+   return (m_version != Protocol_Version::TLS_V10 &&
+           m_version != Protocol_Version::TLS_V11 &&
+           m_version != Protocol_Version::DTLS_V10);
+   }
+
+bool Protocol_Version::supports_aead_modes() const
+   {
+   return (m_version != Protocol_Version::TLS_V10 &&
+           m_version != Protocol_Version::TLS_V11 &&
+           m_version != Protocol_Version::DTLS_V10);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/tls/tls_version.h b/third_party/botan/src/lib/tls/tls_version.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/tls/tls_version.h
@@ -0,0 +1,156 @@
+/*
+* TLS Protocol Version Management
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PROTOCOL_VERSION_H_
+#define BOTAN_TLS_PROTOCOL_VERSION_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Protocol Version
+*/
+class BOTAN_PUBLIC_API(2,0) Protocol_Version final
+   {
+   public:
+      enum Version_Code {
+         TLS_V10            = 0x0301,
+         TLS_V11            = 0x0302,
+         TLS_V12            = 0x0303,
+
+         DTLS_V10           = 0xFEFF,
+         DTLS_V12           = 0xFEFD
+      };
+
+      /**
+      * @return latest known TLS version
+      */
+      static Protocol_Version latest_tls_version()
+         {
+         return Protocol_Version(TLS_V12);
+         }
+
+      /**
+      * @return latest known DTLS version
+      */
+      static Protocol_Version latest_dtls_version()
+         {
+         return Protocol_Version(DTLS_V12);
+         }
+
+      Protocol_Version() : m_version(0) {}
+
+      explicit Protocol_Version(uint16_t code) : m_version(code) {}
+
+      /**
+      * @param named_version a specific named version of the protocol
+      */
+      Protocol_Version(Version_Code named_version) :
+         Protocol_Version(static_cast<uint16_t>(named_version)) {}
+
+      /**
+      * @param major the major version
+      * @param minor the minor version
+      */
+      Protocol_Version(uint8_t major, uint8_t minor) :
+         Protocol_Version(static_cast<uint16_t>((static_cast<uint16_t>(major) << 8) | minor)) {}
+
+      /**
+      * @return true if this is a valid protocol version
+      */
+      bool valid() const { return (m_version != 0); }
+
+      /**
+      * @return true if this is a protocol version we know about
+      */
+      bool known_version() const;
+
+      /**
+      * @return major version of the protocol version
+      */
+      uint8_t major_version() const { return static_cast<uint8_t>(m_version >> 8); }
+
+      /**
+      * @return minor version of the protocol version
+      */
+      uint8_t minor_version() const { return static_cast<uint8_t>(m_version & 0xFF); }
+
+      /**
+      * @return the version code
+      */
+      uint16_t version_code() const { return m_version; }
+
+      /**
+      * @return human-readable description of this version
+      */
+      std::string to_string() const;
+
+      /**
+      * @return true iff this is a DTLS version
+      */
+      bool is_datagram_protocol() const;
+
+      /**
+      * @return true if this version supports negotiable signature algorithms
+      */
+      bool supports_negotiable_signature_algorithms() const;
+
+      /**
+      * @return true if this version uses explicit IVs for block ciphers
+      */
+      bool supports_explicit_cbc_ivs() const;
+
+      /**
+      * @return true if this version uses a ciphersuite specific PRF
+      */
+      bool supports_ciphersuite_specific_prf() const;
+
+      bool supports_aead_modes() const;
+
+      /**
+      * @return if this version is equal to other
+      */
+      bool operator==(const Protocol_Version& other) const
+         {
+         return (m_version == other.m_version);
+         }
+
+      /**
+      * @return if this version is not equal to other
+      */
+      bool operator!=(const Protocol_Version& other) const
+         {
+         return (m_version != other.m_version);
+         }
+
+      /**
+      * @return if this version is later than other
+      */
+      bool operator>(const Protocol_Version& other) const;
+
+      /**
+      * @return if this version is later than or equal to other
+      */
+      bool operator>=(const Protocol_Version& other) const
+         {
+         return (*this == other || *this > other);
+         }
+
+   private:
+      uint16_t m_version;
+   };
+
+}
+
+}
+
+#endif
+
diff --git a/third_party/botan/src/lib/utils/assert.cpp b/third_party/botan/src/lib/utils/assert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/assert.cpp
@@ -0,0 +1,54 @@
+/*
+* Runtime assertion checking
+* (C) 2010,2012,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+#include <sstream>
+
+namespace Botan {
+
+void throw_invalid_argument(const char* message,
+                            const char* func,
+                            const char* file)
+   {
+   std::ostringstream format;
+   format << message << " in " << func << ":" << file;
+   throw Invalid_Argument(format.str());
+   }
+
+void throw_invalid_state(const char* expr,
+                         const char* func,
+                         const char* file)
+   {
+   std::ostringstream format;
+   format << "Invalid state: " << expr << " was false in " << func << ":" << file;
+   throw Invalid_State(format.str());
+   }
+
+void assertion_failure(const char* expr_str,
+                       const char* assertion_made,
+                       const char* func,
+                       const char* file,
+                       int line)
+   {
+   std::ostringstream format;
+
+   format << "False assertion ";
+
+   if(assertion_made && assertion_made[0] != 0)
+      format << "'" << assertion_made << "' (expression " << expr_str << ") ";
+   else
+      format << expr_str << " ";
+
+   if(func)
+      format << "in " << func << " ";
+
+   format << "@" << file << ":" << line;
+
+   throw Internal_Error(format.str());
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/assert.h b/third_party/botan/src/lib/utils/assert.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/assert.h
@@ -0,0 +1,157 @@
+/*
+* Runtime assertion checking
+* (C) 2010,2018 Jack Lloyd
+*     2017 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASSERTION_CHECKING_H_
+#define BOTAN_ASSERTION_CHECKING_H_
+
+#include <botan/build.h>
+#include <botan/compiler.h>
+
+namespace Botan {
+
+/**
+* Called when an assertion fails
+* Throws an Exception object
+*/
+BOTAN_NORETURN void BOTAN_PUBLIC_API(2,0)
+   assertion_failure(const char* expr_str,
+                     const char* assertion_made,
+                     const char* func,
+                     const char* file,
+                     int line);
+
+/**
+* Called when an invalid argument is used
+* Throws Invalid_Argument
+*/
+BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_argument(const char* message,
+                                                              const char* func,
+                                                              const char* file);
+
+
+#define BOTAN_ARG_CHECK(expr, msg)                                      \
+   do { if(!(expr)) Botan::throw_invalid_argument(msg, __func__, __FILE__); } while(0)
+
+/**
+* Called when an invalid state is encountered
+* Throws Invalid_State
+*/
+BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_state(const char* message,
+                                                           const char* func,
+                                                           const char* file);
+
+
+#define BOTAN_STATE_CHECK(expr)                                     \
+   do { if(!(expr)) Botan::throw_invalid_state(#expr, __func__, __FILE__); } while(0)
+
+/**
+* Make an assertion
+*/
+#define BOTAN_ASSERT(expr, assertion_made)                \
+   do {                                                   \
+      if(!(expr))                                         \
+         Botan::assertion_failure(#expr,                  \
+                                  assertion_made,         \
+                                  __func__,               \
+                                  __FILE__,               \
+                                  __LINE__);              \
+   } while(0)
+
+/**
+* Make an assertion
+*/
+#define BOTAN_ASSERT_NOMSG(expr)                          \
+   do {                                                   \
+      if(!(expr))                                         \
+         Botan::assertion_failure(#expr,                  \
+                                  "",                     \
+                                  __func__,               \
+                                  __FILE__,               \
+                                  __LINE__);              \
+   } while(0)
+
+/**
+* Assert that value1 == value2
+*/
+#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)   \
+   do {                                                    \
+     if((expr1) != (expr2))                                \
+       Botan::assertion_failure(#expr1 " == " #expr2,      \
+                                assertion_made,            \
+                                __func__,                  \
+                                __FILE__,                  \
+                                __LINE__);                 \
+   } while(0)
+
+/**
+* Assert that expr1 (if true) implies expr2 is also true
+*/
+#define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg)        \
+   do {                                                    \
+     if((expr1) && !(expr2))                               \
+       Botan::assertion_failure(#expr1 " implies " #expr2, \
+                                msg,                       \
+                                __func__,                  \
+                                __FILE__,                  \
+                                __LINE__);                 \
+   } while(0)
+
+/**
+* Assert that a pointer is not null
+*/
+#define BOTAN_ASSERT_NONNULL(ptr)                          \
+   do {                                                    \
+     if((ptr) == nullptr)                                  \
+         Botan::assertion_failure(#ptr " is not null",     \
+                                  "",                      \
+                                  __func__,                \
+                                  __FILE__,                \
+                                  __LINE__);               \
+   } while(0)
+
+#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)
+
+#define BOTAN_DEBUG_ASSERT(expr) BOTAN_ASSERT_NOMSG(expr)
+
+#else
+
+#define BOTAN_DEBUG_ASSERT(expr) do {} while(0)
+
+#endif
+
+/**
+* Mark variable as unused. Takes between 1 and 9 arguments and marks all as unused,
+* e.g. BOTAN_UNUSED(a); or BOTAN_UNUSED(x, y, z);
+*/
+#define _BOTAN_UNUSED_IMPL1(a)                         static_cast<void>(a)
+#define _BOTAN_UNUSED_IMPL2(a, b)                      static_cast<void>(a); _BOTAN_UNUSED_IMPL1(b)
+#define _BOTAN_UNUSED_IMPL3(a, b, c)                   static_cast<void>(a); _BOTAN_UNUSED_IMPL2(b, c)
+#define _BOTAN_UNUSED_IMPL4(a, b, c, d)                static_cast<void>(a); _BOTAN_UNUSED_IMPL3(b, c, d)
+#define _BOTAN_UNUSED_IMPL5(a, b, c, d, e)             static_cast<void>(a); _BOTAN_UNUSED_IMPL4(b, c, d, e)
+#define _BOTAN_UNUSED_IMPL6(a, b, c, d, e, f)          static_cast<void>(a); _BOTAN_UNUSED_IMPL5(b, c, d, e, f)
+#define _BOTAN_UNUSED_IMPL7(a, b, c, d, e, f, g)       static_cast<void>(a); _BOTAN_UNUSED_IMPL6(b, c, d, e, f, g)
+#define _BOTAN_UNUSED_IMPL8(a, b, c, d, e, f, g, h)    static_cast<void>(a); _BOTAN_UNUSED_IMPL7(b, c, d, e, f, g, h)
+#define _BOTAN_UNUSED_IMPL9(a, b, c, d, e, f, g, h, i) static_cast<void>(a); _BOTAN_UNUSED_IMPL8(b, c, d, e, f, g, h, i)
+#define _BOTAN_UNUSED_GET_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, IMPL_NAME, ...) IMPL_NAME
+
+#define BOTAN_UNUSED(...) _BOTAN_UNUSED_GET_IMPL(__VA_ARGS__,                      \
+                                                 _BOTAN_UNUSED_IMPL9,              \
+                                                 _BOTAN_UNUSED_IMPL8,              \
+                                                 _BOTAN_UNUSED_IMPL7,              \
+                                                 _BOTAN_UNUSED_IMPL6,              \
+                                                 _BOTAN_UNUSED_IMPL5,              \
+                                                 _BOTAN_UNUSED_IMPL4,              \
+                                                 _BOTAN_UNUSED_IMPL3,              \
+                                                 _BOTAN_UNUSED_IMPL2,              \
+                                                 _BOTAN_UNUSED_IMPL1,              \
+                                                 unused dummy rest value           \
+                          ) /* we got an one of _BOTAN_UNUSED_IMPL*, now call it */ (__VA_ARGS__)
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/bit_ops.h b/third_party/botan/src/lib/utils/bit_ops.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/bit_ops.h
@@ -0,0 +1,171 @@
+/*
+* Bit/Word Operations
+* (C) 1999-2008 Jack Lloyd
+* (C) Copyright Projet SECRET, INRIA, Rocquencourt
+* (C) Bhaskar Biswas and  Nicolas Sendrier
+* (C) 2014 cryptosource GmbH
+* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BIT_OPS_H_
+#define BOTAN_BIT_OPS_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* If top bit of arg is set, return ~0. Otherwise return 0.
+*/
+template<typename T>
+inline T expand_top_bit(T a)
+   {
+   return static_cast<T>(0) - (a >> (sizeof(T)*8-1));
+   }
+
+/**
+* If arg is zero, return ~0. Otherwise return 0
+*/
+template<typename T>
+inline T ct_is_zero(T x)
+   {
+   return expand_top_bit<T>(~x & (x - 1));
+   }
+
+/**
+* Power of 2 test. T should be an unsigned integer type
+* @param arg an integer value
+* @return true iff arg is 2^n for some n > 0
+*/
+template<typename T>
+inline constexpr bool is_power_of_2(T arg)
+   {
+   return (arg != 0) && (arg != 1) && ((arg & static_cast<T>(arg-1)) == 0);
+   }
+
+/**
+* Return the index of the highest set bit
+* T is an unsigned integer type
+* @param n an integer value
+* @return index of the highest set bit in n
+*/
+template<typename T>
+inline size_t high_bit(T n)
+   {
+   size_t hb = 0;
+
+   for(size_t s = 8*sizeof(T) / 2; s > 0; s /= 2)
+      {
+      const size_t z = s * ((~ct_is_zero(n >> s)) & 1);
+      hb += z;
+      n >>= z;
+      }
+
+   hb += n;
+
+   return hb;
+   }
+
+/**
+* Return the number of significant bytes in n
+* @param n an integer value
+* @return number of significant bytes in n
+*/
+template<typename T>
+inline size_t significant_bytes(T n)
+   {
+   size_t b = 0;
+
+   for(size_t s = 8*sizeof(n) / 2; s >= 8; s /= 2)
+      {
+      const size_t z = s * (~ct_is_zero(n >> s) & 1);
+      b += z/8;
+      n >>= z;
+      }
+
+   b += (n != 0);
+
+   return b;
+   }
+
+/**
+* Count the trailing zero bits in n
+* @param n an integer value
+* @return maximum x st 2^x divides n
+*/
+template<typename T>
+inline size_t ctz(T n)
+   {
+   /*
+   * If n == 0 then this function will compute 8*sizeof(T)-1, so
+   * initialize lb to 1 if n == 0 to produce the expected result.
+   */
+   size_t lb = ct_is_zero(n) & 1;
+
+   for(size_t s = 8*sizeof(T) / 2; s > 0; s /= 2)
+      {
+      const T mask = (static_cast<T>(1) << s) - 1;
+      const size_t z = s * (ct_is_zero(n & mask) & 1);
+      lb += z;
+      n >>= z;
+      }
+
+   return lb;
+   }
+
+template<typename T>
+uint8_t ceil_log2(T x)
+   {
+   static_assert(sizeof(T) < 32, "Abnormally large scalar");
+
+   if(x >> (sizeof(T)*8-1))
+      return sizeof(T)*8;
+
+   uint8_t result = 0;
+   T compare = 1;
+
+   while(compare < x)
+      {
+      compare <<= 1;
+      result++;
+      }
+
+   return result;
+   }
+
+// Potentially variable time ctz used for OCB
+inline size_t var_ctz32(uint32_t n)
+   {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+   if(n == 0)
+      return 32;
+   return __builtin_ctz(n);
+#else
+   return ctz<uint32_t>(n);
+#endif
+   }
+
+template<typename T>
+inline T bit_permute_step(T x, T mask, size_t shift)
+   {
+   /*
+   See https://reflectionsonsecurity.wordpress.com/2014/05/11/efficient-bit-permutation-using-delta-swaps/
+   and http://programming.sirrida.de/bit_perm.html
+   */
+   const T swap = ((x >> shift) ^ x) & mask;
+   return (x ^ swap) ^ (swap << shift);
+   }
+
+template<typename T>
+inline void swap_bits(T& x, T& y, T mask, size_t shift)
+   {
+   const T swap = ((x >> shift) ^ y) & mask;
+   x ^= swap << shift;
+   y ^= swap;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/bswap.h b/third_party/botan/src/lib/utils/bswap.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/bswap.h
@@ -0,0 +1,108 @@
+/*
+* Byte Swapping Operations
+* (C) 1999-2011,2018 Jack Lloyd
+* (C) 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BYTE_SWAP_H_
+#define BOTAN_BYTE_SWAP_H_
+
+#include <botan/types.h>
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+  #include <stdlib.h>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(bswap.h)
+
+namespace Botan {
+
+/**
+* Swap a 16 bit integer
+*/
+inline uint16_t reverse_bytes(uint16_t val)
+   {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+   return __builtin_bswap16(val);
+#else
+   return static_cast<uint16_t>((val << 8) | (val >> 8));
+#endif
+   }
+
+/**
+* Swap a 32 bit integer
+*/
+inline uint32_t reverse_bytes(uint32_t val)
+   {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+   return __builtin_bswap32(val);
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+   return _byteswap_ulong(val);
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+   // GCC-style inline assembly for x86 or x86-64
+   asm("bswapl %0" : "=r" (val) : "0" (val));
+   return val;
+
+#else
+   // Generic implementation
+   uint16_t hi = static_cast<uint16_t>(val >> 16);
+   uint16_t lo = static_cast<uint16_t>(val);
+
+   hi = reverse_bytes(hi);
+   lo = reverse_bytes(lo);
+
+   return (static_cast<uint32_t>(lo) << 16) | hi;
+#endif
+   }
+
+/**
+* Swap a 64 bit integer
+*/
+inline uint64_t reverse_bytes(uint64_t val)
+   {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+   return __builtin_bswap64(val);
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+   return _byteswap_uint64(val);
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_X86_64)
+   // GCC-style inline assembly for x86-64
+   asm("bswapq %0" : "=r" (val) : "0" (val));
+   return val;
+
+#else
+   /* Generic implementation. Defined in terms of 32-bit bswap so any
+    * optimizations in that version can help.
+    */
+
+   uint32_t hi = static_cast<uint32_t>(val >> 32);
+   uint32_t lo = static_cast<uint32_t>(val);
+
+   hi = reverse_bytes(hi);
+   lo = reverse_bytes(lo);
+
+   return (static_cast<uint64_t>(lo) << 32) | hi;
+#endif
+   }
+
+/**
+* Swap 4 Ts in an array
+*/
+template<typename T>
+inline void bswap_4(T x[4])
+   {
+   x[0] = reverse_bytes(x[0]);
+   x[1] = reverse_bytes(x[1]);
+   x[2] = reverse_bytes(x[2]);
+   x[3] = reverse_bytes(x[3]);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/calendar.cpp b/third_party/botan/src/lib/utils/calendar.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/calendar.cpp
@@ -0,0 +1,124 @@
+/*
+* Calendar Functions
+* (C) 1999-2010,2017 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/calendar.h>
+#include <botan/exceptn.h>
+#include <ctime>
+#include <sstream>
+#include <iomanip>
+#include <stdlib.h>
+
+namespace Botan {
+
+namespace {
+
+std::tm do_gmtime(std::time_t time_val)
+   {
+   std::tm tm;
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32)
+   ::gmtime_s(&tm, &time_val); // Windows
+#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   ::gmtime_r(&time_val, &tm); // Unix/SUSv2
+#else
+   std::tm* tm_p = std::gmtime(&time_val);
+   if (tm_p == nullptr)
+      throw Encoding_Error("time_t_to_tm could not convert");
+   tm = *tm_p;
+#endif
+
+   return tm;
+   }
+
+/*
+Portable replacement for timegm, _mkgmtime, etc
+
+Algorithm due to Howard Hinnant
+
+See https://howardhinnant.github.io/date_algorithms.html#days_from_civil
+for details and explaination. The code is slightly simplified by our assumption
+that the date is at least 1970, which is sufficient for our purposes.
+*/
+size_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day)
+   {
+   if(month <= 2)
+      year -= 1;
+   const uint32_t era = year / 400;
+   const uint32_t yoe = year - era * 400;      // [0, 399]
+   const uint32_t doy = (153*(month + (month > 2 ? -3 : 9)) + 2)/5 + day-1;  // [0, 365]
+   const uint32_t doe = yoe * 365 + yoe/4 - yoe/100 + doy;         // [0, 146096]
+   return era * 146097 + doe - 719468;
+   }
+
+}
+
+std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
+   {
+   if(get_year() < 1970)
+      throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970");
+
+   // 32 bit time_t ends at January 19, 2038
+   // https://msdn.microsoft.com/en-us/library/2093ets1.aspx
+   // Throw after 2037 if 32 bit time_t is used
+
+   BOTAN_IF_CONSTEXPR(sizeof(std::time_t) == 4)
+      {
+      if(get_year() > 2037)
+         {
+         throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037 on this system");
+         }
+      }
+
+   // This upper bound is completely arbitrary
+   if(get_year() >= 2400)
+      {
+      throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2400");
+      }
+
+   const uint64_t seconds_64 = (days_since_epoch(get_year(), get_month(), get_day()) * 86400) +
+                                (get_hour() * 60 * 60) + (get_minutes() * 60) + get_seconds();
+
+   const time_t seconds_time_t = static_cast<time_t>(seconds_64);
+
+   if(seconds_64 - seconds_time_t != 0)
+      {
+      throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow");
+      }
+
+   return std::chrono::system_clock::from_time_t(seconds_time_t);
+   }
+
+std::string calendar_point::to_string() const
+   {
+   // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss>
+   std::stringstream output;
+   output << std::setfill('0')
+          << std::setw(4) << get_year() << "-"
+          << std::setw(2) << get_month() << "-"
+          << std::setw(2) << get_day() << "T"
+          << std::setw(2) << get_hour() << ":"
+          << std::setw(2) << get_minutes() << ":"
+          << std::setw(2) << get_seconds();
+   return output.str();
+   }
+
+
+calendar_point calendar_value(
+   const std::chrono::system_clock::time_point& time_point)
+   {
+   std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point));
+
+   return calendar_point(tm.tm_year + 1900,
+                         tm.tm_mon + 1,
+                         tm.tm_mday,
+                         tm.tm_hour,
+                         tm.tm_min,
+                         tm.tm_sec);
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/calendar.h b/third_party/botan/src/lib/utils/calendar.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/calendar.h
@@ -0,0 +1,91 @@
+/*
+* Calendar Functions
+* (C) 1999-2009,2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CALENDAR_H_
+#define BOTAN_CALENDAR_H_
+
+#include <botan/types.h>
+#include <chrono>
+#include <string>
+
+namespace Botan {
+
+/**
+* Struct representing a particular date and time
+*/
+class BOTAN_PUBLIC_API(2,0) calendar_point
+   {
+   public:
+
+      /** The year */
+      uint32_t get_year() const { return year; }
+
+      /** The month, 1 through 12 for Jan to Dec */
+      uint32_t get_month() const { return month; }
+
+      /** The day of the month, 1 through 31 (or 28 or 30 based on month */
+      uint32_t get_day() const { return day; }
+
+      /** Hour in 24-hour form, 0 to 23 */
+      uint32_t get_hour() const { return hour; }
+
+      /** Minutes in the hour, 0 to 60 */
+      uint32_t get_minutes() const { return minutes; }
+
+      /** Seconds in the minute, 0 to 60, but might be slightly
+      larger to deal with leap seconds on some systems
+      */
+      uint32_t get_seconds() const { return seconds; }
+
+      /**
+      * Initialize a calendar_point
+      * @param y the year
+      * @param mon the month
+      * @param d the day
+      * @param h the hour
+      * @param min the minute
+      * @param sec the second
+      */
+      calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec) :
+         year(y), month(mon), day(d), hour(h), minutes(min), seconds(sec) {}
+
+      /**
+      * Returns an STL timepoint object
+      */
+      std::chrono::system_clock::time_point to_std_timepoint() const;
+
+      /**
+      * Returns a human readable string of the struct's components.
+      * Formatting might change over time. Currently it is RFC339 'iso-date-time'.
+      */
+      std::string to_string() const;
+
+   BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+      /*
+      The member variables are public for historical reasons. Use the get_xxx() functions
+      defined above. These members will be made private in a future major release.
+      */
+      uint32_t year;
+      uint32_t month;
+      uint32_t day;
+      uint32_t hour;
+      uint32_t minutes;
+      uint32_t seconds;
+   };
+
+/**
+* Convert a time_point to a calendar_point
+* @param time_point a time point from the system clock
+* @return calendar_point object representing this time point
+*/
+BOTAN_PUBLIC_API(2,0) calendar_point calendar_value(
+   const std::chrono::system_clock::time_point& time_point);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/charset.cpp b/third_party/botan/src/lib/utils/charset.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/charset.cpp
@@ -0,0 +1,283 @@
+/*
+* Character Set Handling
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/charset.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <cctype>
+
+namespace Botan {
+
+namespace {
+
+void append_utf8_for(std::string& s, uint32_t c)
+   {
+   if(c >= 0xD800 && c < 0xE000)
+      throw Decoding_Error("Invalid Unicode character");
+
+   if(c <= 0x7F)
+      {
+      const uint8_t b0 = static_cast<uint8_t>(c);
+      s.push_back(static_cast<char>(b0));
+      }
+   else if(c <= 0x7FF)
+      {
+      const uint8_t b0 = 0xC0 | static_cast<uint8_t>(c >> 6);
+      const uint8_t b1 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+      s.push_back(static_cast<char>(b0));
+      s.push_back(static_cast<char>(b1));
+      }
+   else if(c <= 0xFFFF)
+      {
+      const uint8_t b0 = 0xE0 | static_cast<uint8_t>(c >> 12);
+      const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
+      const uint8_t b2 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+      s.push_back(static_cast<char>(b0));
+      s.push_back(static_cast<char>(b1));
+      s.push_back(static_cast<char>(b2));
+      }
+   else if(c <= 0x10FFFF)
+      {
+      const uint8_t b0 = 0xF0 | static_cast<uint8_t>(c >> 18);
+      const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 12) & 0x3F);
+      const uint8_t b2 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
+      const uint8_t b3 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+      s.push_back(static_cast<char>(b0));
+      s.push_back(static_cast<char>(b1));
+      s.push_back(static_cast<char>(b2));
+      s.push_back(static_cast<char>(b3));
+      }
+   else
+      throw Decoding_Error("Invalid Unicode character");
+
+   }
+
+}
+
+std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len)
+   {
+   if(len % 2 != 0)
+      throw Decoding_Error("Invalid length for UCS-2 string");
+
+   const size_t chars = len / 2;
+
+   std::string s;
+   for(size_t i = 0; i != chars; ++i)
+      {
+      const uint16_t c = load_be<uint16_t>(ucs2, i);
+      append_utf8_for(s, c);
+      }
+
+   return s;
+   }
+
+std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len)
+   {
+   if(len % 4 != 0)
+      throw Decoding_Error("Invalid length for UCS-4 string");
+
+   const size_t chars = len / 4;
+
+   std::string s;
+   for(size_t i = 0; i != chars; ++i)
+      {
+      const uint32_t c = load_be<uint32_t>(ucs4, i);
+      append_utf8_for(s, c);
+      }
+
+   return s;
+   }
+
+/*
+* Convert from UTF-8 to ISO 8859-1
+*/
+std::string utf8_to_latin1(const std::string& utf8)
+   {
+   std::string iso8859;
+
+   size_t position = 0;
+   while(position != utf8.size())
+      {
+      const uint8_t c1 = static_cast<uint8_t>(utf8[position++]);
+
+      if(c1 <= 0x7F)
+         {
+         iso8859 += static_cast<char>(c1);
+         }
+      else if(c1 >= 0xC0 && c1 <= 0xC7)
+         {
+         if(position == utf8.size())
+            throw Decoding_Error("UTF-8: sequence truncated");
+
+         const uint8_t c2 = static_cast<uint8_t>(utf8[position++]);
+         const uint8_t iso_char = ((c1 & 0x07) << 6) | (c2 & 0x3F);
+
+         if(iso_char <= 0x7F)
+            throw Decoding_Error("UTF-8: sequence longer than needed");
+
+         iso8859 += static_cast<char>(iso_char);
+         }
+      else
+         throw Decoding_Error("UTF-8: Unicode chars not in Latin1 used");
+      }
+
+   return iso8859;
+   }
+
+namespace Charset {
+
+namespace {
+
+/*
+* Convert from UCS-2 to ISO 8859-1
+*/
+std::string ucs2_to_latin1(const std::string& ucs2)
+   {
+   if(ucs2.size() % 2 == 1)
+      throw Decoding_Error("UCS-2 string has an odd number of bytes");
+
+   std::string latin1;
+
+   for(size_t i = 0; i != ucs2.size(); i += 2)
+      {
+      const uint8_t c1 = ucs2[i];
+      const uint8_t c2 = ucs2[i+1];
+
+      if(c1 != 0)
+         throw Decoding_Error("UCS-2 has non-Latin1 characters");
+
+      latin1 += static_cast<char>(c2);
+      }
+
+   return latin1;
+   }
+
+/*
+* Convert from ISO 8859-1 to UTF-8
+*/
+std::string latin1_to_utf8(const std::string& iso8859)
+   {
+   std::string utf8;
+   for(size_t i = 0; i != iso8859.size(); ++i)
+      {
+      const uint8_t c = static_cast<uint8_t>(iso8859[i]);
+
+      if(c <= 0x7F)
+         utf8 += static_cast<char>(c);
+      else
+         {
+         utf8 += static_cast<char>((0xC0 | (c >> 6)));
+         utf8 += static_cast<char>((0x80 | (c & 0x3F)));
+         }
+      }
+   return utf8;
+   }
+
+}
+
+/*
+* Perform character set transcoding
+*/
+std::string transcode(const std::string& str,
+                      Character_Set to, Character_Set from)
+   {
+   if(to == LOCAL_CHARSET)
+      to = LATIN1_CHARSET;
+   if(from == LOCAL_CHARSET)
+      from = LATIN1_CHARSET;
+
+   if(to == from)
+      return str;
+
+   if(from == LATIN1_CHARSET && to == UTF8_CHARSET)
+      return latin1_to_utf8(str);
+   if(from == UTF8_CHARSET && to == LATIN1_CHARSET)
+      return utf8_to_latin1(str);
+   if(from == UCS2_CHARSET && to == LATIN1_CHARSET)
+      return ucs2_to_latin1(str);
+
+   throw Invalid_Argument("Unknown transcoding operation from " +
+                          std::to_string(from) + " to " + std::to_string(to));
+   }
+
+/*
+* Check if a character represents a digit
+*/
+bool is_digit(char c)
+   {
+   if(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' ||
+      c == '5' || c == '6' || c == '7' || c == '8' || c == '9')
+      return true;
+   return false;
+   }
+
+/*
+* Check if a character represents whitespace
+*/
+bool is_space(char c)
+   {
+   if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
+      return true;
+   return false;
+   }
+
+/*
+* Convert a character to a digit
+*/
+uint8_t char2digit(char c)
+   {
+   switch(c)
+      {
+      case '0': return 0;
+      case '1': return 1;
+      case '2': return 2;
+      case '3': return 3;
+      case '4': return 4;
+      case '5': return 5;
+      case '6': return 6;
+      case '7': return 7;
+      case '8': return 8;
+      case '9': return 9;
+      }
+
+   throw Invalid_Argument("char2digit: Input is not a digit character");
+   }
+
+/*
+* Convert a digit to a character
+*/
+char digit2char(uint8_t b)
+   {
+   switch(b)
+      {
+      case 0: return '0';
+      case 1: return '1';
+      case 2: return '2';
+      case 3: return '3';
+      case 4: return '4';
+      case 5: return '5';
+      case 6: return '6';
+      case 7: return '7';
+      case 8: return '8';
+      case 9: return '9';
+      }
+
+   throw Invalid_Argument("digit2char: Input is not a digit");
+   }
+
+/*
+* Case-insensitive character comparison
+*/
+bool caseless_cmp(char a, char b)
+   {
+   return (std::tolower(static_cast<unsigned char>(a)) ==
+           std::tolower(static_cast<unsigned char>(b)));
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/utils/charset.h b/third_party/botan/src/lib/utils/charset.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/charset.h
@@ -0,0 +1,80 @@
+/*
+* Character Set Handling
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHARSET_H_
+#define BOTAN_CHARSET_H_
+
+#include <botan/types.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(charset.h)
+
+namespace Botan {
+
+/**
+* Convert a sequence of UCS-2 (big endian) characters to a UTF-8 string
+* This is used for ASN.1 BMPString type
+* @param ucs2 the sequence of UCS-2 characters
+* @param len length of ucs2 in bytes, must be a multiple of 2
+*/
+std::string BOTAN_UNSTABLE_API ucs2_to_utf8(const uint8_t ucs2[], size_t len);
+
+/**
+* Convert a sequence of UCS-4 (big endian) characters to a UTF-8 string
+* This is used for ASN.1 UniversalString type
+* @param ucs4 the sequence of UCS-4 characters
+* @param len length of ucs4 in bytes, must be a multiple of 4
+*/
+std::string BOTAN_UNSTABLE_API ucs4_to_utf8(const uint8_t ucs4[], size_t len);
+
+/**
+* Convert a UTF-8 string to Latin-1
+* If a character outside the Latin-1 range is encountered, an exception is thrown.
+*/
+std::string BOTAN_UNSTABLE_API utf8_to_latin1(const std::string& utf8);
+
+/**
+* The different charsets (nominally) supported by Botan.
+*/
+enum Character_Set {
+   LOCAL_CHARSET,
+   UCS2_CHARSET,
+   UTF8_CHARSET,
+   LATIN1_CHARSET
+};
+
+namespace Charset {
+
+/*
+* Character set conversion - avoid this.
+* For specific conversions, use the functions above like
+* ucs2_to_utf8 and utf8_to_latin1
+*
+* If you need something more complex than that, use a real library
+* such as iconv, Boost.Locale, or ICU
+*/
+std::string BOTAN_PUBLIC_API(2,0)
+   BOTAN_DEPRECATED("Avoid. See comment in header.")
+   transcode(const std::string& str,
+             Character_Set to,
+             Character_Set from);
+
+/*
+* Simple character classifier functions
+*/
+bool BOTAN_PUBLIC_API(2,0) is_digit(char c);
+bool BOTAN_PUBLIC_API(2,0) is_space(char c);
+bool BOTAN_PUBLIC_API(2,0) caseless_cmp(char x, char y);
+
+uint8_t BOTAN_PUBLIC_API(2,0) char2digit(char c);
+char BOTAN_PUBLIC_API(2,0) digit2char(uint8_t b);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/codec_base.h b/third_party/botan/src/lib/utils/codec_base.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/codec_base.h
@@ -0,0 +1,220 @@
+/*
+* Base Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE_CODEC_H_
+#define BOTAN_BASE_CODEC_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform encoding using the base provided
+* @param base object giving access to the encodings specifications
+* @param output an array of at least base.encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+template <class Base>
+size_t base_encode(Base&& base,
+                   char output[],
+                   const uint8_t input[],
+                   size_t input_length,
+                   size_t& input_consumed,
+                   bool final_inputs)
+   {
+   input_consumed = 0;
+
+   const size_t encoding_bytes_in = base.encoding_bytes_in();
+   const size_t encoding_bytes_out = base.encoding_bytes_out();
+
+   size_t input_remaining = input_length;
+   size_t output_produced = 0;
+
+   while(input_remaining >= encoding_bytes_in)
+      {
+      base.encode(output + output_produced, input + input_consumed);
+
+      input_consumed += encoding_bytes_in;
+      output_produced += encoding_bytes_out;
+      input_remaining -= encoding_bytes_in;
+      }
+
+   if(final_inputs && input_remaining)
+      {
+      std::vector<uint8_t> remainder(encoding_bytes_in, 0);
+      for(size_t i = 0; i != input_remaining; ++i)
+         { remainder[i] = input[input_consumed + i]; }
+
+      base.encode(output + output_produced, remainder.data());
+
+      const size_t bits_consumed = base.bits_consumed();
+      const size_t remaining_bits_before_padding = base.remaining_bits_before_padding();
+
+      size_t empty_bits = 8 * (encoding_bytes_in - input_remaining);
+      size_t index = output_produced + encoding_bytes_out - 1;
+      while(empty_bits >= remaining_bits_before_padding)
+         {
+         output[index--] = '=';
+         empty_bits -= bits_consumed;
+         }
+
+      input_consumed += input_remaining;
+      output_produced += encoding_bytes_out;
+      }
+
+   return output_produced;
+   }
+
+
+template <typename Base>
+std::string base_encode_to_string(Base&& base, const uint8_t input[], size_t input_length)
+   {
+   const size_t output_length = base.encode_max_output(input_length);
+   std::string output(output_length, 0);
+
+   size_t consumed = 0;
+   size_t produced = 0;
+
+   if(output_length > 0)
+      {
+      produced = base_encode(base, &output.front(),
+                                   input, input_length,
+                                   consumed, true);
+      }
+
+   BOTAN_ASSERT_EQUAL(consumed, input_length, "Consumed the entire input");
+   BOTAN_ASSERT_EQUAL(produced, output.size(), "Produced expected size");
+
+   return output;
+   }
+
+/**
+* Perform decoding using the base provided
+* @param base object giving access to the encodings specifications
+* @param output an array of at least Base::decode_max_output bytes
+* @param input some base input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+*        bytes of input were actually consumed. If less than
+*        input_length, then the range input[consumed:length]
+*        should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+         padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+                   exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+template <typename Base>
+size_t base_decode(Base&& base,
+                   uint8_t output[],
+                   const char input[],
+                   size_t input_length,
+                   size_t& input_consumed,
+                   bool final_inputs,
+                   bool ignore_ws = true)
+   {
+   const size_t decoding_bytes_in = base.decoding_bytes_in();
+   const size_t decoding_bytes_out = base.decoding_bytes_out();
+
+   uint8_t* out_ptr = output;
+   std::vector<uint8_t> decode_buf(decoding_bytes_in, 0);
+   size_t decode_buf_pos = 0;
+   size_t final_truncate = 0;
+
+   clear_mem(output, base.decode_max_output(input_length));
+
+   for(size_t i = 0; i != input_length; ++i)
+      {
+      const uint8_t bin = base.lookup_binary_value(input[i]);
+
+      if(base.check_bad_char(bin, input[i], ignore_ws)) // May throw Invalid_Argument
+         {
+         decode_buf[decode_buf_pos] = bin;
+         ++decode_buf_pos;
+         }
+
+      /*
+      * If we're at the end of the input, pad with 0s and truncate
+      */
+      if(final_inputs && (i == input_length - 1))
+         {
+         if(decode_buf_pos)
+            {
+            for(size_t j = decode_buf_pos; j < decoding_bytes_in; ++j)
+               { decode_buf[j] = 0; }
+
+            final_truncate = decoding_bytes_in - decode_buf_pos;
+            decode_buf_pos = decoding_bytes_in;
+            }
+         }
+
+      if(decode_buf_pos == decoding_bytes_in)
+         {
+         base.decode(out_ptr, decode_buf.data());
+
+         out_ptr += decoding_bytes_out;
+         decode_buf_pos = 0;
+         input_consumed = i+1;
+         }
+      }
+
+   while(input_consumed < input_length &&
+         base.lookup_binary_value(input[input_consumed]) == 0x80)
+      {
+      ++input_consumed;
+      }
+
+   size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate);
+
+   return written;
+   }
+
+template<typename Base>
+size_t base_decode_full(Base&& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws)
+   {
+   size_t consumed = 0;
+   const size_t written = base_decode(base, output, input, input_length, consumed, true, ignore_ws);
+
+   if(consumed != input_length)
+      {
+      throw Invalid_Argument(base.name() + " decoding failed, input did not have full bytes");
+      }
+
+   return written;
+   }
+
+template<typename Vector, typename Base>
+Vector base_decode_to_vec(Base&& base,
+                          const char input[],
+                          size_t input_length,
+                          bool ignore_ws)
+   {
+   const size_t output_length = base.decode_max_output(input_length);
+   Vector bin(output_length);
+
+   const size_t written =
+      base_decode_full(base, bin.data(), input, input_length, ignore_ws);
+
+   bin.resize(written);
+   return bin;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/compiler.h b/third_party/botan/src/lib/utils/compiler.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/compiler.h
@@ -0,0 +1,234 @@
+/*
+* Define useful compiler-specific macros
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/* This header is included in both C++ and C (via ffi.h) and should only
+   contain macro definitions. Avoid C++ style // comments in this file.
+*/
+
+#ifndef BOTAN_UTIL_COMPILER_FLAGS_H_
+#define BOTAN_UTIL_COMPILER_FLAGS_H_
+
+/* Should we use GCC-style inline assembler? */
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || \
+   defined(BOTAN_BUILD_COMPILER_IS_CLANG) || \
+   defined(BOTAN_BUILD_COMPILER_IS_XLC) || \
+   defined(BOTAN_BUILD_COMPILER_IS_SUN_STUDIO)
+
+  #define BOTAN_USE_GCC_INLINE_ASM
+#endif
+
+/**
+* Used to annotate API exports which are public and supported.
+* These APIs will not be broken/removed unless strictly required for
+* functionality or security, and only in new major versions.
+* @param maj The major version this public API was released in
+* @param min The minor version this public API was released in
+*/
+#define BOTAN_PUBLIC_API(maj,min) BOTAN_DLL
+
+/**
+* Used to annotate API exports which are public, but are now deprecated
+* and which will be removed in a future major release.
+*/
+#define BOTAN_DEPRECATED_API(msg) BOTAN_DLL BOTAN_DEPRECATED(msg)
+
+/**
+* Used to annotate API exports which are public and can be used by
+* applications if needed, but which are intentionally not documented,
+* and which may change incompatibly in a future major version.
+*/
+#define BOTAN_UNSTABLE_API BOTAN_DLL
+
+/**
+* Used to annotate API exports which are exported but only for the
+* purposes of testing. They should not be used by applications and
+* may be removed or changed without notice.
+*/
+#define BOTAN_TEST_API BOTAN_DLL
+
+/*
+* Define BOTAN_GCC_VERSION
+*/
+#if defined(__GNUC__) && !defined(__clang__)
+  #define BOTAN_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
+#else
+  #define BOTAN_GCC_VERSION 0
+#endif
+
+/*
+* Define BOTAN_CLANG_VERSION
+*/
+#if defined(__clang__)
+  #define BOTAN_CLANG_VERSION (__clang_major__ * 10 + __clang_minor__)
+#else
+  #define BOTAN_CLANG_VERSION 0
+#endif
+
+/*
+* Define BOTAN_FUNC_ISA
+*/
+#if (defined(__GNUC__) && !defined(__clang__)) || (BOTAN_CLANG_VERSION > 38)
+  #define BOTAN_FUNC_ISA(isa) __attribute__ ((target(isa)))
+#else
+  #define BOTAN_FUNC_ISA(isa)
+#endif
+
+/*
+* Define BOTAN_WARN_UNUSED_RESULT
+*/
+#if defined(__GNUC__) || defined(__clang__)
+  #define BOTAN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
+#else
+  #define BOTAN_WARN_UNUSED_RESULT
+#endif
+
+/*
+* Define BOTAN_MALLOC_FN
+*/
+#if defined(__ibmxl__)
+  /* XLC pretends to be both Clang and GCC, but is neither */
+  #define BOTAN_MALLOC_FN __attribute__ ((malloc))
+#elif defined(__GNUC__)
+  #define BOTAN_MALLOC_FN __attribute__ ((malloc, alloc_size(1,2)))
+#elif defined(_MSC_VER)
+  #define BOTAN_MALLOC_FN __declspec(restrict)
+#else
+  #define BOTAN_MALLOC_FN
+#endif
+
+/*
+* Define BOTAN_DEPRECATED
+*/
+#if !defined(BOTAN_NO_DEPRECATED_WARNINGS)
+
+  #if defined(__clang__)
+    #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
+    #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("message \"this header is deprecated\"")
+
+    #if !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_AMALGAMATION_H_)
+      #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("message \"this header will be made internal in the future\"")
+    #endif
+
+  #elif defined(_MSC_VER)
+    #define BOTAN_DEPRECATED(msg) __declspec(deprecated(msg))
+    #define BOTAN_DEPRECATED_HEADER(hdr) __pragma(message("this header is deprecated"))
+
+    #if !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_AMALGAMATION_H_)
+      #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) __pragma(message("this header will be made internal in the future"))
+    #endif
+
+  #elif defined(__GNUC__)
+    /* msg supported since GCC 4.5, earliest we support is 4.8 */
+    #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
+    #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("GCC warning \"this header is deprecated\"")
+
+    #if !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_AMALGAMATION_H_)
+      #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("GCC warning \"this header will be made internal in the future\"")
+    #endif
+  #endif
+
+#endif
+
+#if !defined(BOTAN_DEPRECATED)
+  #define BOTAN_DEPRECATED(msg)
+#endif
+
+#if !defined(BOTAN_DEPRECATED_HEADER)
+  #define BOTAN_DEPRECATED_HEADER(hdr)
+#endif
+
+#if !defined(BOTAN_FUTURE_INTERNAL_HEADER)
+  #define BOTAN_FUTURE_INTERNAL_HEADER(hdr)
+#endif
+
+/*
+* Define BOTAN_NORETURN
+*/
+#if !defined(BOTAN_NORETURN)
+
+  #if defined (__clang__) || defined (__GNUC__)
+    #define BOTAN_NORETURN __attribute__ ((__noreturn__))
+
+  #elif defined (_MSC_VER)
+    #define BOTAN_NORETURN __declspec(noreturn)
+
+  #else
+    #define BOTAN_NORETURN
+  #endif
+
+#endif
+
+/*
+* Define BOTAN_THREAD_LOCAL
+*/
+#if !defined(BOTAN_THREAD_LOCAL)
+
+  #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_TARGET_OS_HAS_THREAD_LOCAL)
+    #define BOTAN_THREAD_LOCAL thread_local
+  #else
+    #define BOTAN_THREAD_LOCAL /**/
+  #endif
+
+#endif
+
+/*
+* Define BOTAN_IF_CONSTEXPR
+*/
+#if !defined(BOTAN_IF_CONSTEXPR)
+   #if __cplusplus >= 201703
+      #define BOTAN_IF_CONSTEXPR if constexpr
+   #else
+      #define BOTAN_IF_CONSTEXPR if
+   #endif
+#endif
+
+/*
+* Define BOTAN_PARALLEL_FOR
+*/
+#if !defined(BOTAN_PARALLEL_FOR)
+
+#if defined(BOTAN_TARGET_HAS_OPENMP)
+  #define BOTAN_PARALLEL_FOR _Pragma("omp parallel for") for
+#else
+  #define BOTAN_PARALLEL_FOR for
+#endif
+
+#endif
+
+/*
+* Define BOTAN_FORCE_INLINE
+*/
+#if !defined(BOTAN_FORCE_INLINE)
+
+  #if defined (__clang__) || defined (__GNUC__)
+    #define BOTAN_FORCE_INLINE __attribute__ ((__always_inline__)) inline
+
+  #elif defined (_MSC_VER)
+    #define BOTAN_FORCE_INLINE __forceinline
+
+  #else
+    #define BOTAN_FORCE_INLINE inline
+  #endif
+
+#endif
+
+/*
+* Define BOTAN_PARALLEL_SIMD_FOR
+*/
+#if !defined(BOTAN_PARALLEL_SIMD_FOR)
+
+#if defined(BOTAN_TARGET_HAS_OPENMP)
+  #define BOTAN_PARALLEL_SIMD_FOR _Pragma("omp simd") for
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && (BOTAN_GCC_VERSION >= 490)
+  #define BOTAN_PARALLEL_SIMD_FOR _Pragma("GCC ivdep") for
+#else
+  #define BOTAN_PARALLEL_SIMD_FOR for
+#endif
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/utils/cpuid/cpuid.cpp b/third_party/botan/src/lib/utils/cpuid/cpuid.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/cpuid/cpuid.cpp
@@ -0,0 +1,220 @@
+/*
+* Runtime CPU detection
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <ostream>
+
+namespace Botan {
+
+bool CPUID::has_simd_32()
+   {
+#if defined(BOTAN_TARGET_SUPPORTS_SSE2)
+   return CPUID::has_sse2();
+#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
+   return CPUID::has_altivec();
+#elif defined(BOTAN_TARGET_SUPPORTS_NEON)
+   return CPUID::has_neon();
+#else
+   return true;
+#endif
+   }
+
+//static
+std::string CPUID::to_string()
+   {
+   std::vector<std::string> flags;
+
+#define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   CPUID_PRINT(sse2);
+   CPUID_PRINT(ssse3);
+   CPUID_PRINT(sse41);
+   CPUID_PRINT(sse42);
+   CPUID_PRINT(avx2);
+   CPUID_PRINT(avx512f);
+
+   CPUID_PRINT(rdtsc);
+   CPUID_PRINT(bmi1);
+   CPUID_PRINT(bmi2);
+   CPUID_PRINT(adx);
+
+   CPUID_PRINT(aes_ni);
+   CPUID_PRINT(clmul);
+   CPUID_PRINT(rdrand);
+   CPUID_PRINT(rdseed);
+   CPUID_PRINT(intel_sha);
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+   CPUID_PRINT(altivec);
+   CPUID_PRINT(power_crypto);
+   CPUID_PRINT(darn_rng);
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+   CPUID_PRINT(neon);
+   CPUID_PRINT(arm_sve);
+
+   CPUID_PRINT(arm_sha1);
+   CPUID_PRINT(arm_sha2);
+   CPUID_PRINT(arm_aes);
+   CPUID_PRINT(arm_pmull);
+   CPUID_PRINT(arm_sha2_512);
+   CPUID_PRINT(arm_sha3);
+   CPUID_PRINT(arm_sm3);
+   CPUID_PRINT(arm_sm4);
+#endif
+
+#undef CPUID_PRINT
+
+   return string_join(flags, ' ');
+   }
+
+//static
+void CPUID::print(std::ostream& o)
+   {
+   o << "CPUID flags: " << CPUID::to_string() << "\n";
+   }
+
+//static
+void CPUID::initialize()
+   {
+   state() = CPUID_Data();
+   }
+
+CPUID::CPUID_Data::CPUID_Data()
+   {
+   m_cache_line_size = 0;
+   m_processor_features = 0;
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+   m_processor_features = detect_cpu_features(&m_cache_line_size);
+
+#endif
+
+   m_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
+
+   if(m_cache_line_size == 0)
+      m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
+
+   m_endian_status = runtime_check_endian();
+   }
+
+//static
+CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian()
+   {
+   // Check runtime endian
+   const uint32_t endian32 = 0x01234567;
+   const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
+
+   CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown;
+
+   if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
+      {
+      endian = CPUID::Endian_Status::Big;
+      }
+   else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
+      {
+      endian = CPUID::Endian_Status::Little;
+      }
+   else
+      {
+      throw Internal_Error("Unexpected endian at runtime, neither big nor little");
+      }
+
+   // If we were compiled with a known endian, verify it matches at runtime
+#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+   BOTAN_ASSERT(endian == CPUID::Endian_Status::Little, "Build and runtime endian match");
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+   BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match");
+#endif
+
+   return endian;
+   }
+
+std::vector<Botan::CPUID::CPUID_bits>
+CPUID::bit_from_string(const std::string& tok)
+   {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+   if(tok == "sse2" || tok == "simd")
+      return {Botan::CPUID::CPUID_SSE2_BIT};
+   if(tok == "ssse3")
+      return {Botan::CPUID::CPUID_SSSE3_BIT};
+   if(tok == "sse41")
+      return {Botan::CPUID::CPUID_SSE41_BIT};
+   if(tok == "sse42")
+      return {Botan::CPUID::CPUID_SSE42_BIT};
+   // aes_ni is the string printed on the console when running "botan cpuid"
+   if(tok == "aesni" || tok == "aes_ni")
+      return {Botan::CPUID::CPUID_AESNI_BIT};
+   if(tok == "clmul")
+      return {Botan::CPUID::CPUID_CLMUL_BIT};
+   if(tok == "avx2")
+      return {Botan::CPUID::CPUID_AVX2_BIT};
+   if(tok == "avx512f")
+      return {Botan::CPUID::CPUID_AVX512F_BIT};
+   // there were two if statements testing "sha" and "intel_sha" separately; combined
+   if(tok == "sha" || tok=="intel_sha")
+      return {Botan::CPUID::CPUID_SHA_BIT};
+   if(tok == "rdtsc")
+      return {Botan::CPUID::CPUID_RDTSC_BIT};
+   if(tok == "bmi1")
+      return {Botan::CPUID::CPUID_BMI1_BIT};
+   if(tok == "bmi2")
+      return {Botan::CPUID::CPUID_BMI2_BIT};
+   if(tok == "adx")
+      return {Botan::CPUID::CPUID_ADX_BIT};
+   if(tok == "rdrand")
+      return {Botan::CPUID::CPUID_RDRAND_BIT};
+   if(tok == "rdseed")
+      return {Botan::CPUID::CPUID_RDSEED_BIT};
+
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+   if(tok == "altivec" || tok == "simd")
+      return {Botan::CPUID::CPUID_ALTIVEC_BIT};
+   if(tok == "power_crypto")
+      return {Botan::CPUID::CPUID_POWER_CRYPTO_BIT};
+   if(tok == "darn_rng")
+      return {Botan::CPUID::CPUID_DARN_BIT};
+
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+   if(tok == "neon" || tok == "simd")
+      return {Botan::CPUID::CPUID_ARM_NEON_BIT};
+   if(tok == "arm_sve")
+      return {Botan::CPUID::CPUID_ARM_SVE_BIT};
+   if(tok == "armv8sha1" || tok == "arm_sha1")
+      return {Botan::CPUID::CPUID_ARM_SHA1_BIT};
+   if(tok == "armv8sha2" || tok == "arm_sha2")
+      return {Botan::CPUID::CPUID_ARM_SHA2_BIT};
+   if(tok == "armv8aes" || tok == "arm_aes")
+      return {Botan::CPUID::CPUID_ARM_AES_BIT};
+   if(tok == "armv8pmull" || tok == "arm_pmull")
+      return {Botan::CPUID::CPUID_ARM_PMULL_BIT};
+   if(tok == "armv8sha3" || tok == "arm_sha3")
+      return {Botan::CPUID::CPUID_ARM_SHA3_BIT};
+   if(tok == "armv8sha2_512" || tok == "arm_sha2_512")
+      return {Botan::CPUID::CPUID_ARM_SHA2_512_BIT};
+   if(tok == "armv8sm3" || tok == "arm_sm3")
+      return {Botan::CPUID::CPUID_ARM_SM3_BIT};
+   if(tok == "armv8sm4" || tok == "arm_sm4")
+      return {Botan::CPUID::CPUID_ARM_SM4_BIT};
+
+#else
+   BOTAN_UNUSED(tok);
+#endif
+
+   return {};
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/cpuid/cpuid.h b/third_party/botan/src/lib/utils/cpuid/cpuid.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/cpuid/cpuid.h
@@ -0,0 +1,446 @@
+/*
+* Runtime CPU detection
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CPUID_H_
+#define BOTAN_CPUID_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+#include <iosfwd>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h)
+
+namespace Botan {
+
+/**
+* A class handling runtime CPU feature detection. It is limited to
+* just the features necessary to implement CPU specific code in Botan,
+* rather than being a general purpose utility.
+*
+* This class supports:
+*
+*  - x86 features using CPUID. x86 is also the only processor with
+*    accurate cache line detection currently.
+*
+*  - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS
+*
+*  - ARM NEON and crypto extensions detection. On Linux and Android
+*    systems which support getauxval, that is used to access CPU
+*    feature information. Otherwise a relatively portable but
+*    thread-unsafe mechanism involving executing probe functions which
+*    catching SIGILL signal is used.
+*/
+class BOTAN_PUBLIC_API(2,1) CPUID final
+   {
+   public:
+      /**
+      * Probe the CPU and see what extensions are supported
+      */
+      static void initialize();
+
+      static bool has_simd_32();
+
+      /**
+      * Deprecated equivalent to
+      * o << "CPUID flags: " << CPUID::to_string() << "\n";
+      */
+      BOTAN_DEPRECATED("Use CPUID::to_string")
+      static void print(std::ostream& o);
+
+      /**
+      * Return a possibly empty string containing list of known CPU
+      * extensions. Each name will be seperated by a space, and the ordering
+      * will be arbitrary. This list only contains values that are useful to
+      * Botan (for example FMA instructions are not checked).
+      *
+      * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec"
+      */
+      static std::string to_string();
+
+      /**
+      * Return a best guess of the cache line size
+      */
+      static size_t cache_line_size()
+         {
+         return state().cache_line_size();
+         }
+
+      static bool is_little_endian()
+         {
+#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+         return true;
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+         return false;
+#else
+         return state().endian_status() == Endian_Status::Little;
+#endif
+         }
+
+      static bool is_big_endian()
+         {
+#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+         return true;
+#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+         return false;
+#else
+         return state().endian_status() == Endian_Status::Big;
+#endif
+         }
+
+      enum CPUID_bits : uint64_t {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+         // These values have no relation to cpuid bitfields
+
+         // SIMD instruction sets
+         CPUID_SSE2_BIT    = (1ULL << 0),
+         CPUID_SSSE3_BIT   = (1ULL << 1),
+         CPUID_SSE41_BIT   = (1ULL << 2),
+         CPUID_SSE42_BIT   = (1ULL << 3),
+         CPUID_AVX2_BIT    = (1ULL << 4),
+         CPUID_AVX512F_BIT = (1ULL << 5),
+
+         // Misc useful instructions
+         CPUID_RDTSC_BIT   = (1ULL << 10),
+         CPUID_BMI2_BIT    = (1ULL << 11),
+         CPUID_ADX_BIT     = (1ULL << 12),
+         CPUID_BMI1_BIT    = (1ULL << 13),
+
+         // Crypto-specific ISAs
+         CPUID_AESNI_BIT   = (1ULL << 16),
+         CPUID_CLMUL_BIT   = (1ULL << 17),
+         CPUID_RDRAND_BIT  = (1ULL << 18),
+         CPUID_RDSEED_BIT  = (1ULL << 19),
+         CPUID_SHA_BIT     = (1ULL << 20),
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+         CPUID_ALTIVEC_BIT    = (1ULL << 0),
+         CPUID_POWER_CRYPTO_BIT = (1ULL << 1),
+         CPUID_DARN_BIT       = (1ULL << 2),
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+         CPUID_ARM_NEON_BIT      = (1ULL << 0),
+         CPUID_ARM_SVE_BIT       = (1ULL << 1),
+         CPUID_ARM_AES_BIT       = (1ULL << 16),
+         CPUID_ARM_PMULL_BIT     = (1ULL << 17),
+         CPUID_ARM_SHA1_BIT      = (1ULL << 18),
+         CPUID_ARM_SHA2_BIT      = (1ULL << 19),
+         CPUID_ARM_SHA3_BIT      = (1ULL << 20),
+         CPUID_ARM_SHA2_512_BIT  = (1ULL << 21),
+         CPUID_ARM_SM3_BIT       = (1ULL << 22),
+         CPUID_ARM_SM4_BIT       = (1ULL << 23),
+#endif
+
+         CPUID_INITIALIZED_BIT = (1ULL << 63)
+      };
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+      /**
+      * Check if the processor supports AltiVec/VMX
+      */
+      static bool has_altivec()
+         { return has_cpuid_bit(CPUID_ALTIVEC_BIT); }
+
+      /**
+      * Check if the processor supports POWER8 crypto extensions
+      */
+      static bool has_power_crypto()
+         { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); }
+
+      /**
+      * Check if the processor supports POWER9 DARN RNG
+      */
+      static bool has_darn_rng()
+         { return has_cpuid_bit(CPUID_DARN_BIT); }
+
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+      /**
+      * Check if the processor supports NEON SIMD
+      */
+      static bool has_neon()
+         { return has_cpuid_bit(CPUID_ARM_NEON_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SVE
+      */
+      static bool has_arm_sve()
+         { return has_cpuid_bit(CPUID_ARM_SVE_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SHA1
+      */
+      static bool has_arm_sha1()
+         { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SHA2
+      */
+      static bool has_arm_sha2()
+         { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 AES
+      */
+      static bool has_arm_aes()
+         { return has_cpuid_bit(CPUID_ARM_AES_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 PMULL
+      */
+      static bool has_arm_pmull()
+         { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SHA-512
+      */
+      static bool has_arm_sha2_512()
+         { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SHA-3
+      */
+      static bool has_arm_sha3()
+         { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SM3
+      */
+      static bool has_arm_sm3()
+         { return has_cpuid_bit(CPUID_ARM_SM3_BIT); }
+
+      /**
+      * Check if the processor supports ARMv8 SM4
+      */
+      static bool has_arm_sm4()
+         { return has_cpuid_bit(CPUID_ARM_SM4_BIT); }
+
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+      /**
+      * Check if the processor supports RDTSC
+      */
+      static bool has_rdtsc()
+         { return has_cpuid_bit(CPUID_RDTSC_BIT); }
+
+      /**
+      * Check if the processor supports SSE2
+      */
+      static bool has_sse2()
+         { return has_cpuid_bit(CPUID_SSE2_BIT); }
+
+      /**
+      * Check if the processor supports SSSE3
+      */
+      static bool has_ssse3()
+         { return has_cpuid_bit(CPUID_SSSE3_BIT); }
+
+      /**
+      * Check if the processor supports SSE4.1
+      */
+      static bool has_sse41()
+         { return has_cpuid_bit(CPUID_SSE41_BIT); }
+
+      /**
+      * Check if the processor supports SSE4.2
+      */
+      static bool has_sse42()
+         { return has_cpuid_bit(CPUID_SSE42_BIT); }
+
+      /**
+      * Check if the processor supports AVX2
+      */
+      static bool has_avx2()
+         { return has_cpuid_bit(CPUID_AVX2_BIT); }
+
+      /**
+      * Check if the processor supports AVX-512F
+      */
+      static bool has_avx512f()
+         { return has_cpuid_bit(CPUID_AVX512F_BIT); }
+
+      /**
+      * Check if the processor supports BMI1
+      */
+      static bool has_bmi1()
+         { return has_cpuid_bit(CPUID_BMI1_BIT); }
+
+      /**
+      * Check if the processor supports BMI2
+      */
+      static bool has_bmi2()
+         { return has_cpuid_bit(CPUID_BMI2_BIT); }
+
+      /**
+      * Check if the processor supports AES-NI
+      */
+      static bool has_aes_ni()
+         { return has_cpuid_bit(CPUID_AESNI_BIT); }
+
+      /**
+      * Check if the processor supports CLMUL
+      */
+      static bool has_clmul()
+         { return has_cpuid_bit(CPUID_CLMUL_BIT); }
+
+      /**
+      * Check if the processor supports Intel SHA extension
+      */
+      static bool has_intel_sha()
+         { return has_cpuid_bit(CPUID_SHA_BIT); }
+
+      /**
+      * Check if the processor supports ADX extension
+      */
+      static bool has_adx()
+         { return has_cpuid_bit(CPUID_ADX_BIT); }
+
+      /**
+      * Check if the processor supports RDRAND
+      */
+      static bool has_rdrand()
+         { return has_cpuid_bit(CPUID_RDRAND_BIT); }
+
+      /**
+      * Check if the processor supports RDSEED
+      */
+      static bool has_rdseed()
+         { return has_cpuid_bit(CPUID_RDSEED_BIT); }
+#endif
+
+      /**
+      * Check if the processor supports byte-level vector permutes
+      * (SSSE3, NEON, Altivec)
+      */
+      static bool has_vperm()
+         {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+         return has_ssse3();
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+         return has_neon();
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+         return has_altivec();
+#else
+         return false;
+#endif
+         }
+
+      /**
+      * Check if the processor supports hardware AES instructions
+      */
+      static bool has_hw_aes()
+         {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+         return has_aes_ni();
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+         return has_arm_aes();
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+         return has_power_crypto();
+#else
+         return false;
+#endif
+         }
+
+      /**
+      * Check if the processor supports carryless multiply
+      * (CLMUL, PMULL)
+      */
+      static bool has_carryless_multiply()
+         {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+         return has_clmul();
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+         return has_arm_pmull();
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
+         return has_power_crypto();
+#else
+         return false;
+#endif
+         }
+
+      /*
+      * Clear a CPUID bit
+      * Call CPUID::initialize to reset
+      *
+      * This is only exposed for testing, don't use unless you know
+      * what you are doing.
+      */
+      static void clear_cpuid_bit(CPUID_bits bit)
+         {
+         state().clear_cpuid_bit(static_cast<uint64_t>(bit));
+         }
+
+      /*
+      * Don't call this function, use CPUID::has_xxx above
+      * It is only exposed for the tests.
+      */
+      static bool has_cpuid_bit(CPUID_bits elem)
+         {
+         const uint64_t elem64 = static_cast<uint64_t>(elem);
+         return state().has_bit(elem64);
+         }
+
+      static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
+   private:
+      enum class Endian_Status : uint32_t {
+         Unknown = 0x00000000,
+         Big     = 0x01234567,
+         Little  = 0x67452301,
+      };
+
+      struct CPUID_Data
+         {
+         public:
+            CPUID_Data();
+
+            CPUID_Data(const CPUID_Data& other) = default;
+            CPUID_Data& operator=(const CPUID_Data& other) = default;
+
+            void clear_cpuid_bit(uint64_t bit)
+               {
+               m_processor_features &= ~bit;
+               }
+
+            bool has_bit(uint64_t bit) const
+               {
+               return (m_processor_features & bit) == bit;
+               }
+
+            uint64_t processor_features() const { return m_processor_features; }
+            Endian_Status endian_status() const { return m_endian_status; }
+            size_t cache_line_size() const { return m_cache_line_size; }
+
+         private:
+            static Endian_Status runtime_check_endian();
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+            static uint64_t detect_cpu_features(size_t* cache_line_size);
+
+#endif
+            uint64_t m_processor_features;
+            size_t m_cache_line_size;
+            Endian_Status m_endian_status;
+         };
+
+      static CPUID_Data& state()
+         {
+         static CPUID::CPUID_Data g_cpuid;
+         return g_cpuid;
+         }
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp b/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp
@@ -0,0 +1,237 @@
+/*
+* Runtime CPU detection for ARM
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+
+#if defined(BOTAN_TARGET_OS_IS_IOS)
+  #include <sys/types.h>
+  #include <sys/sysctl.h>
+
+#else
+  #include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+  #include <sys/auxv.h>
+#endif
+
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+
+#if defined(BOTAN_TARGET_OS_IS_IOS)
+
+namespace {
+
+uint64_t flags_by_ios_machine_type(const std::string& machine)
+   {
+   /*
+   * This relies on a map of known machine names to features. This
+   * will quickly grow out of date as new products are introduced, but
+   * is apparently the best we can do for iOS.
+   */
+
+   struct version_info {
+      std::string name;
+      size_t min_version_neon;
+      size_t min_version_armv8;
+      };
+
+   static const version_info min_versions[] = {
+      { "iPhone", 2, 6 },
+      { "iPad", 1, 4 },
+      { "iPod", 4, 7 },
+      { "AppleTV", 2, 5 },
+   };
+
+   if(machine.size() < 3)
+      return 0;
+
+   auto comma = machine.find(',');
+
+   // Simulator, or something we don't know about
+   if(comma == std::string::npos)
+      return 0;
+
+   std::string product = machine.substr(0, comma);
+
+   size_t version = 0;
+   size_t place = 1;
+   while(product.size() > 1 && ::isdigit(product.back()))
+      {
+      const size_t digit = product.back() - '0';
+      version += digit * place;
+      place *= 10;
+      product.pop_back();
+      }
+
+   if(version == 0)
+      return 0;
+
+   for(const version_info& info : min_versions)
+      {
+      if(info.name != product)
+         continue;
+
+      if(version >= info.min_version_armv8)
+         {
+         return CPUID::CPUID_ARM_AES_BIT |
+                CPUID::CPUID_ARM_PMULL_BIT |
+                CPUID::CPUID_ARM_SHA1_BIT |
+                CPUID::CPUID_ARM_SHA2_BIT |
+                CPUID::CPUID_ARM_NEON_BIT;
+         }
+
+      if(version >= info.min_version_neon)
+         return CPUID::CPUID_ARM_NEON_BIT;
+      }
+
+   // Some other product we don't know about
+   return 0;
+   }
+
+}
+
+#endif
+
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+   {
+   BOTAN_UNUSED(cache_line_size);
+
+   uint64_t detected_features = 0;
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+   /*
+   * On systems with getauxval these bits should normally be defined
+   * in bits/auxv.h but some buggy? glibc installs seem to miss them.
+   * These following values are all fixed, for the Linux ELF format,
+   * so we just hardcode them in ARM_hwcap_bit enum.
+   */
+
+   enum ARM_hwcap_bit {
+#if defined(BOTAN_TARGET_ARCH_IS_ARM32)
+      NEON_bit  = (1 << 12),
+      AES_bit   = (1 << 0),
+      PMULL_bit = (1 << 1),
+      SHA1_bit  = (1 << 2),
+      SHA2_bit  = (1 << 3),
+
+      ARCH_hwcap_neon   = 16, // AT_HWCAP
+      ARCH_hwcap_crypto = 26, // AT_HWCAP2
+#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
+      NEON_bit  = (1 << 1),
+      AES_bit   = (1 << 3),
+      PMULL_bit = (1 << 4),
+      SHA1_bit  = (1 << 5),
+      SHA2_bit  = (1 << 6),
+      SHA3_bit  = (1 << 17),
+      SM3_bit  = (1 << 18),
+      SM4_bit  = (1 << 19),
+      SHA2_512_bit  = (1 << 21),
+      SVE_bit = (1 << 22),
+
+      ARCH_hwcap_neon   = 16, // AT_HWCAP
+      ARCH_hwcap_crypto = 16, // AT_HWCAP
+#endif
+   };
+
+#if defined(AT_DCACHEBSIZE)
+   // Exists only on Linux
+   const unsigned long dcache_line = OS::get_auxval(AT_DCACHEBSIZE);
+
+   // plausibility check
+   if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
+      *cache_line_size = static_cast<size_t>(dcache_line);
+#endif
+
+   const unsigned long hwcap_neon = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_neon);
+   if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
+      detected_features |= CPUID::CPUID_ARM_NEON_BIT;
+
+   /*
+   On aarch64 this ends up calling getauxval twice with AT_HWCAP
+   It doesn't seem worth optimizing this out, since getauxval is
+   just reading a field in the ELF header.
+   */
+   const unsigned long hwcap_crypto = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_crypto);
+   if(hwcap_crypto & ARM_hwcap_bit::AES_bit)
+      detected_features |= CPUID::CPUID_ARM_AES_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit)
+      detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit)
+      detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
+      detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+
+#if defined(BOTAN_TARGET_ARCH_IS_ARM64)
+   if(hwcap_crypto & ARM_hwcap_bit::SHA3_bit)
+      detected_features |= CPUID::CPUID_ARM_SHA3_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SM3_bit)
+      detected_features |= CPUID::CPUID_ARM_SM3_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SM4_bit)
+      detected_features |= CPUID::CPUID_ARM_SM4_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SHA2_512_bit)
+      detected_features |= CPUID::CPUID_ARM_SHA2_512_BIT;
+   if(hwcap_crypto & ARM_hwcap_bit::SVE_bit)
+      detected_features |= CPUID::CPUID_ARM_SVE_BIT;
+#endif
+
+#elif defined(BOTAN_TARGET_OS_IS_IOS)
+
+   char machine[64] = { 0 };
+   size_t size = sizeof(machine) - 1;
+   ::sysctlbyname("hw.machine", machine, &size, nullptr, 0);
+
+   detected_features = flags_by_ios_machine_type(machine);
+   // No way to detect cache line size on iOS?
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
+
+   /*
+   No getauxval API available, fall back on probe functions. We only
+   bother with Aarch64 here to simplify the code and because going to
+   extreme contortions to support detect NEON on devices that probably
+   don't support it doesn't seem worthwhile.
+
+   NEON registers v0-v7 are caller saved in Aarch64
+   */
+
+   auto neon_probe  = []() noexcept -> int { asm("and v0.16b, v0.16b, v0.16b"); return 1; };
+   auto aes_probe   = []() noexcept -> int { asm(".word 0x4e284800"); return 1; };
+   auto pmull_probe = []() noexcept -> int { asm(".word 0x0ee0e000"); return 1; };
+   auto sha1_probe  = []() noexcept -> int { asm(".word 0x5e280800"); return 1; };
+   auto sha2_probe  = []() noexcept -> int { asm(".word 0x5e282800"); return 1; };
+
+   // Only bother running the crypto detection if we found NEON
+
+   if(OS::run_cpu_instruction_probe(neon_probe) == 1)
+      {
+      detected_features |= CPUID::CPUID_ARM_NEON_BIT;
+
+      if(OS::run_cpu_instruction_probe(aes_probe) == 1)
+         detected_features |= CPUID::CPUID_ARM_AES_BIT;
+      if(OS::run_cpu_instruction_probe(pmull_probe) == 1)
+         detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+      if(OS::run_cpu_instruction_probe(sha1_probe) == 1)
+         detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+      if(OS::run_cpu_instruction_probe(sha2_probe) == 1)
+         detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+      }
+
+#endif
+
+   return detected_features;
+   }
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp b/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp
@@ -0,0 +1,132 @@
+/*
+* Runtime CPU detection for POWER/PowerPC
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+/*
+* On macOS and OpenBSD ppc, use sysctl to detect AltiVec
+*/
+#if defined(BOTAN_TARGET_OS_IS_MACOS)
+  #include <sys/sysctl.h>
+#elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
+  #include <sys/param.h>
+  #include <sys/sysctl.h>
+  #include <machine/cpu.h>
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+/*
+* PowerPC specific block: check for AltiVec using either
+* sysctl or by reading processor version number register.
+*/
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+   {
+   BOTAN_UNUSED(cache_line_size);
+
+#if defined(BOTAN_TARGET_OS_IS_MACOS) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
+   // On macOS and OpenBSD, use sysctl
+
+   int sels[2] = {
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
+      CTL_MACHDEP, CPU_ALTIVEC
+#else
+      CTL_HW, HW_VECTORUNIT
+#endif
+   };
+
+   int vector_type = 0;
+   size_t length = sizeof(vector_type);
+   int error = ::sysctl(sels, 2, &vector_type, &length, NULL, 0);
+
+   if(error == 0 && vector_type > 0)
+      return CPUID::CPUID_ALTIVEC_BIT;
+
+#elif (defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_HAS_ELF_AUX_INFO)) && defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+   enum PPC_hwcap_bit {
+      ALTIVEC_bit  = (1 << 28),
+      CRYPTO_bit   = (1 << 25),
+      DARN_bit     = (1 << 21),
+
+      ARCH_hwcap_altivec = 16, // AT_HWCAP
+      ARCH_hwcap_crypto  = 26, // AT_HWCAP2
+   };
+
+   uint64_t detected_features = 0;
+
+   const unsigned long hwcap_altivec = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_altivec);
+   if(hwcap_altivec & PPC_hwcap_bit::ALTIVEC_bit)
+      detected_features |= CPUID::CPUID_ALTIVEC_BIT;
+
+   const unsigned long hwcap_crypto = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_crypto);
+   if(hwcap_crypto & PPC_hwcap_bit::CRYPTO_bit)
+     detected_features |= CPUID::CPUID_POWER_CRYPTO_BIT;
+   if(hwcap_crypto & PPC_hwcap_bit::DARN_bit)
+     detected_features |= CPUID::CPUID_DARN_BIT;
+
+   return detected_features;
+
+#else
+
+   /*
+   On PowerPC, MSR 287 is PVR, the Processor Version Number
+   Normally it is only accessible to ring 0, but Linux and NetBSD
+   (others, too, maybe?) will trap and emulate it for us.
+   */
+
+   int pvr = OS::run_cpu_instruction_probe([]() noexcept -> int {
+      uint32_t pvr = 0;
+      asm volatile("mfspr %0, 287" : "=r" (pvr));
+      // Top 16 bits suffice to identify the model
+      return static_cast<int>(pvr >> 16);
+      });
+
+   if(pvr > 0)
+      {
+      const uint16_t ALTIVEC_PVR[] = {
+         0x003E, // IBM POWER6
+         0x003F, // IBM POWER7
+         0x004A, // IBM POWER7p
+         0x004B, // IBM POWER8E
+         0x004C, // IBM POWER8 NVL
+         0x004D, // IBM POWER8
+         0x004E, // IBM POWER9
+         0x000C, // G4-7400
+         0x0039, // G5 970
+         0x003C, // G5 970FX
+         0x0044, // G5 970MP
+         0x0070, // Cell PPU
+         0, // end
+      };
+
+      for(size_t i = 0; ALTIVEC_PVR[i]; ++i)
+         {
+         if(pvr == ALTIVEC_PVR[i])
+            return CPUID::CPUID_ALTIVEC_BIT;
+         }
+
+      return 0;
+      }
+
+   // TODO try direct instruction probing
+
+#endif
+
+   return 0;
+   }
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp b/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp
@@ -0,0 +1,178 @@
+/*
+* Runtime CPU detection for x86
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/mem_ops.h>
+#include <botan/loadstor.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+  #include <intrin.h>
+#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
+  #include <ia32intrin.h>
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+  #include <cpuid.h>
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+   {
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+  #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
+  #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
+  #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
+  #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && defined(BOTAN_USE_GCC_INLINE_ASM)
+  #define X86_CPUID(type, out)                                                    \
+     asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+         : "0" (type))
+
+  #define X86_CPUID_SUBLEVEL(type, level, out)                                    \
+     asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+         : "0" (type), "2" (level))
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+  #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
+
+  #define X86_CPUID_SUBLEVEL(type, level, out) \
+     do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
+#else
+  #warning "No way of calling x86 cpuid instruction for this compiler"
+  #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
+  #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
+#endif
+
+   uint64_t features_detected = 0;
+   uint32_t cpuid[4] = { 0 };
+
+   // CPUID 0: vendor identification, max sublevel
+   X86_CPUID(0, cpuid);
+
+   const uint32_t max_supported_sublevel = cpuid[0];
+
+   const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
+   const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
+   const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
+   const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
+
+   if(max_supported_sublevel >= 1)
+      {
+      // CPUID 1: feature bits
+      X86_CPUID(1, cpuid);
+      const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
+
+      enum x86_CPUID_1_bits : uint64_t {
+         RDTSC = (1ULL << 4),
+         SSE2 = (1ULL << 26),
+         CLMUL = (1ULL << 33),
+         SSSE3 = (1ULL << 41),
+         SSE41 = (1ULL << 51),
+         SSE42 = (1ULL << 52),
+         AESNI = (1ULL << 57),
+         RDRAND = (1ULL << 62)
+      };
+
+      if(flags0 & x86_CPUID_1_bits::RDTSC)
+         features_detected |= CPUID::CPUID_RDTSC_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSE2)
+         features_detected |= CPUID::CPUID_SSE2_BIT;
+      if(flags0 & x86_CPUID_1_bits::CLMUL)
+         features_detected |= CPUID::CPUID_CLMUL_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSSE3)
+         features_detected |= CPUID::CPUID_SSSE3_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSE41)
+         features_detected |= CPUID::CPUID_SSE41_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSE42)
+         features_detected |= CPUID::CPUID_SSE42_BIT;
+      if(flags0 & x86_CPUID_1_bits::AESNI)
+         features_detected |= CPUID::CPUID_AESNI_BIT;
+      if(flags0 & x86_CPUID_1_bits::RDRAND)
+         features_detected |= CPUID::CPUID_RDRAND_BIT;
+      }
+
+   if(is_intel)
+      {
+      // Intel cache line size is in cpuid(1) output
+      *cache_line_size = 8 * get_byte(2, cpuid[1]);
+      }
+   else if(is_amd)
+      {
+      // AMD puts it in vendor zone
+      X86_CPUID(0x80000005, cpuid);
+      *cache_line_size = get_byte(3, cpuid[2]);
+      }
+
+   if(max_supported_sublevel >= 7)
+      {
+      clear_mem(cpuid, 4);
+      X86_CPUID_SUBLEVEL(7, 0, cpuid);
+
+      enum x86_CPUID_7_bits : uint64_t {
+         BMI1 = (1ULL << 3),
+         AVX2 = (1ULL << 5),
+         BMI2 = (1ULL << 8),
+         AVX512F = (1ULL << 16),
+         RDSEED = (1ULL << 18),
+         ADX = (1ULL << 19),
+         SHA = (1ULL << 29),
+      };
+      uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
+
+      if(flags7 & x86_CPUID_7_bits::AVX2)
+         features_detected |= CPUID::CPUID_AVX2_BIT;
+      if(flags7 & x86_CPUID_7_bits::BMI1)
+         {
+         features_detected |= CPUID::CPUID_BMI1_BIT;
+         /*
+         We only set the BMI2 bit if BMI1 is also supported, so BMI2
+         code can safely use both extensions. No known processor
+         implements BMI2 but not BMI1.
+         */
+         if(flags7 & x86_CPUID_7_bits::BMI2)
+            features_detected |= CPUID::CPUID_BMI2_BIT;
+         }
+
+      if(flags7 & x86_CPUID_7_bits::AVX512F)
+         features_detected |= CPUID::CPUID_AVX512F_BIT;
+      if(flags7 & x86_CPUID_7_bits::RDSEED)
+         features_detected |= CPUID::CPUID_RDSEED_BIT;
+      if(flags7 & x86_CPUID_7_bits::ADX)
+         features_detected |= CPUID::CPUID_ADX_BIT;
+      if(flags7 & x86_CPUID_7_bits::SHA)
+         features_detected |= CPUID::CPUID_SHA_BIT;
+      }
+
+#undef X86_CPUID
+#undef X86_CPUID_SUBLEVEL
+
+   /*
+   * If we don't have access to CPUID, we can still safely assume that
+   * any x86-64 processor has SSE2 and RDTSC
+   */
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
+   if(features_detected == 0)
+      {
+      features_detected |= CPUID::CPUID_SSE2_BIT;
+      features_detected |= CPUID::CPUID_RDTSC_BIT;
+      }
+#endif
+
+   return features_detected;
+   }
+
+#endif
+
+}
diff --git a/third_party/botan/src/lib/utils/ct_utils.cpp b/third_party/botan/src/lib/utils/ct_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ct_utils.cpp
@@ -0,0 +1,83 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace CT {
+
+secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input,
+                                   const uint8_t input[],
+                                   size_t input_length,
+                                   size_t offset)
+   {
+   if(input_length == 0)
+      return secure_vector<uint8_t>();
+
+   /*
+   * Ensure at runtime that offset <= input_length. This is an invalid input,
+   * but we can't throw without using the poisoned value. Instead, if it happens,
+   * set offset to be equal to the input length (so output_bytes becomes 0 and
+   * the returned vector is empty)
+   */
+   const auto valid_offset = CT::Mask<size_t>::is_lte(offset, input_length);
+   offset = valid_offset.select(offset, input_length);
+
+   const size_t output_bytes = input_length - offset;
+
+   secure_vector<uint8_t> output(input_length);
+
+   /*
+   Move the desired output bytes to the front using a slow (O^n)
+   but constant time loop that does not leak the value of the offset
+   */
+   for(size_t i = 0; i != input_length; ++i)
+      {
+      /*
+      start index from i rather than 0 since we know j must be >= i + offset
+      to have any effect, and starting from i does not reveal information
+      */
+      for(size_t j = i; j != input_length; ++j)
+         {
+         const uint8_t b = input[j];
+         const auto is_eq = CT::Mask<size_t>::is_equal(j, offset + i);
+         output[i] |= is_eq.if_set_return(b);
+         }
+      }
+
+   bad_input.if_set_zero_out(output.data(), output.size());
+
+   CT::unpoison(output.data(), output.size());
+   CT::unpoison(output_bytes);
+
+   /*
+   This is potentially not const time, depending on how std::vector is
+   implemented. But since we are always reducing length, it should
+   just amount to setting the member var holding the length.
+   */
+   output.resize(output_bytes);
+   return output;
+   }
+
+secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length)
+   {
+   size_t leading_zeros = 0;
+
+   auto only_zeros = Mask<uint8_t>::set();
+
+   for(size_t i = 0; i != length; ++i)
+      {
+      only_zeros &= CT::Mask<uint8_t>::is_zero(in[i]);
+      leading_zeros += only_zeros.if_set_return(1);
+      }
+
+   return copy_output(CT::Mask<uint8_t>::cleared(), in, length, leading_zeros);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/utils/ct_utils.h b/third_party/botan/src/lib/utils/ct_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ct_utils.h
@@ -0,0 +1,394 @@
+/*
+* Functions for constant time operations on data and testing of
+* constant time annotations using valgrind.
+*
+* For more information about constant time programming see
+* Wagner, Molnar, et al "The Program Counter Security Model"
+*
+* (C) 2010 Falko Strenzke
+* (C) 2015,2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CT_UTILS_H_
+#define BOTAN_CT_UTILS_H_
+
+#include <botan/secmem.h>
+#include <botan/internal/bit_ops.h>
+#include <type_traits>
+#include <vector>
+
+#if defined(BOTAN_HAS_VALGRIND)
+  #include <valgrind/memcheck.h>
+#endif
+
+namespace Botan {
+
+namespace CT {
+
+/**
+* Use valgrind to mark the contents of memory as being undefined.
+* Valgrind will accept operations which manipulate undefined values,
+* but will warn if an undefined value is used to decided a conditional
+* jump or a load/store address. So if we poison all of our inputs we
+* can confirm that the operations in question are truly const time
+* when compiled by whatever compiler is in use.
+*
+* Even better, the VALGRIND_MAKE_MEM_* macros work even when the
+* program is not run under valgrind (though with a few cycles of
+* overhead, which is unfortunate in final binaries as these
+* annotations tend to be used in fairly important loops).
+*
+* This approach was first used in ctgrind (https://github.com/agl/ctgrind)
+* but calling the valgrind mecheck API directly works just as well and
+* doesn't require a custom patched valgrind.
+*/
+template<typename T>
+inline void poison(const T* p, size_t n)
+   {
+#if defined(BOTAN_HAS_VALGRIND)
+   VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T));
+#else
+   BOTAN_UNUSED(p);
+   BOTAN_UNUSED(n);
+#endif
+   }
+
+template<typename T>
+inline void unpoison(const T* p, size_t n)
+   {
+#if defined(BOTAN_HAS_VALGRIND)
+   VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
+#else
+   BOTAN_UNUSED(p);
+   BOTAN_UNUSED(n);
+#endif
+   }
+
+template<typename T>
+inline void unpoison(T& p)
+   {
+#if defined(BOTAN_HAS_VALGRIND)
+   VALGRIND_MAKE_MEM_DEFINED(&p, sizeof(T));
+#else
+   BOTAN_UNUSED(p);
+#endif
+   }
+
+/**
+* A Mask type used for constant-time operations. A Mask<T> always has value
+* either 0 (all bits cleared) or ~0 (all bits set). All operations in a Mask<T>
+* are intended to compile to code which does not contain conditional jumps.
+* This must be verified with tooling (eg binary disassembly or using valgrind)
+* since you never know what a compiler might do.
+*/
+template<typename T>
+class Mask
+   {
+   public:
+      static_assert(std::is_unsigned<T>::value, "CT::Mask only defined for unsigned integer types");
+
+      Mask(const Mask<T>& other) = default;
+      Mask<T>& operator=(const Mask<T>& other) = default;
+
+      /**
+      * Derive a Mask from a Mask of a larger type
+      */
+      template<typename U>
+      Mask(Mask<U> o) : m_mask(static_cast<T>(o.value()))
+         {
+         static_assert(sizeof(U) > sizeof(T), "sizes ok");
+         }
+
+      /**
+      * Return a Mask<T> with all bits set
+      */
+      static Mask<T> set()
+         {
+         return Mask<T>(static_cast<T>(~0));
+         }
+
+      /**
+      * Return a Mask<T> with all bits cleared
+      */
+      static Mask<T> cleared()
+         {
+         return Mask<T>(0);
+         }
+
+      /**
+      * Return a Mask<T> which is set if v is != 0
+      */
+      static Mask<T> expand(T v)
+         {
+         return ~Mask<T>::is_zero(v);
+         }
+
+      /**
+      * Return a Mask<T> which is set if m is set
+      */
+      template<typename U>
+      static Mask<T> expand(Mask<U> m)
+         {
+         static_assert(sizeof(U) < sizeof(T), "sizes ok");
+         return ~Mask<T>::is_zero(m.value());
+         }
+
+      /**
+      * Return a Mask<T> which is set if v is == 0 or cleared otherwise
+      */
+      static Mask<T> is_zero(T x)
+         {
+         return Mask<T>(ct_is_zero<T>(x));
+         }
+
+      /**
+      * Return a Mask<T> which is set if x == y
+      */
+      static Mask<T> is_equal(T x, T y)
+         {
+         return Mask<T>::is_zero(static_cast<T>(x ^ y));
+         }
+
+      /**
+      * Return a Mask<T> which is set if x < y
+      */
+      static Mask<T> is_lt(T x, T y)
+         {
+         return Mask<T>(expand_top_bit<T>(x^((x^y) | ((x-y)^x))));
+         }
+
+      /**
+      * Return a Mask<T> which is set if x > y
+      */
+      static Mask<T> is_gt(T x, T y)
+         {
+         return Mask<T>::is_lt(y, x);
+         }
+
+      /**
+      * Return a Mask<T> which is set if x <= y
+      */
+      static Mask<T> is_lte(T x, T y)
+         {
+         return ~Mask<T>::is_gt(x, y);
+         }
+
+      /**
+      * Return a Mask<T> which is set if x >= y
+      */
+      static Mask<T> is_gte(T x, T y)
+         {
+         return ~Mask<T>::is_lt(x, y);
+         }
+
+      /**
+      * AND-combine two masks
+      */
+      Mask<T>& operator&=(Mask<T> o)
+         {
+         m_mask &= o.value();
+         return (*this);
+         }
+
+      /**
+      * XOR-combine two masks
+      */
+      Mask<T>& operator^=(Mask<T> o)
+         {
+         m_mask ^= o.value();
+         return (*this);
+         }
+
+      /**
+      * OR-combine two masks
+      */
+      Mask<T>& operator|=(Mask<T> o)
+         {
+         m_mask |= o.value();
+         return (*this);
+         }
+
+      /**
+      * AND-combine two masks
+      */
+      friend Mask<T> operator&(Mask<T> x, Mask<T> y)
+         {
+         return Mask<T>(x.value() & y.value());
+         }
+
+      /**
+      * XOR-combine two masks
+      */
+      friend Mask<T> operator^(Mask<T> x, Mask<T> y)
+         {
+         return Mask<T>(x.value() ^ y.value());
+         }
+
+      /**
+      * OR-combine two masks
+      */
+      friend Mask<T> operator|(Mask<T> x, Mask<T> y)
+         {
+         return Mask<T>(x.value() | y.value());
+         }
+
+      /**
+      * Negate this mask
+      */
+      Mask<T> operator~() const
+         {
+         return Mask<T>(~value());
+         }
+
+      /**
+      * Return x if the mask is set, or otherwise zero
+      */
+      T if_set_return(T x) const
+         {
+         return m_mask & x;
+         }
+
+      /**
+      * Return x if the mask is cleared, or otherwise zero
+      */
+      T if_not_set_return(T x) const
+         {
+         return ~m_mask & x;
+         }
+
+      /**
+      * If this mask is set, return x, otherwise return y
+      */
+      T select(T x, T y) const
+         {
+         // (x & value()) | (y & ~value())
+         return static_cast<T>(y ^ (value() & (x ^ y)));
+         }
+
+      T select_and_unpoison(T x, T y) const
+         {
+         T r = this->select(x, y);
+         CT::unpoison(r);
+         return r;
+         }
+
+      /**
+      * If this mask is set, return x, otherwise return y
+      */
+      Mask<T> select_mask(Mask<T> x, Mask<T> y) const
+         {
+         return Mask<T>(select(x.value(), y.value()));
+         }
+
+      /**
+      * Conditionally set output to x or y, depending on if mask is set or
+      * cleared (resp)
+      */
+      void select_n(T output[], const T x[], const T y[], size_t len) const
+         {
+         for(size_t i = 0; i != len; ++i)
+            output[i] = this->select(x[i], y[i]);
+         }
+
+      /**
+      * If this mask is set, zero out buf, otherwise do nothing
+      */
+      void if_set_zero_out(T buf[], size_t elems)
+         {
+         for(size_t i = 0; i != elems; ++i)
+            {
+            buf[i] = this->if_not_set_return(buf[i]);
+            }
+         }
+
+      /**
+      * Return the value of the mask, unpoisoned
+      */
+      T unpoisoned_value() const
+         {
+         T r = value();
+         CT::unpoison(r);
+         return r;
+         }
+
+      /**
+      * Return true iff this mask is set
+      */
+      bool is_set() const
+         {
+         return unpoisoned_value() != 0;
+         }
+
+      /**
+      * Return the underlying value of the mask
+      */
+      T value() const
+         {
+         return m_mask;
+         }
+
+   private:
+      Mask(T m) : m_mask(m) {}
+
+      T m_mask;
+   };
+
+template<typename T>
+inline Mask<T> conditional_copy_mem(T cnd,
+                                    T* to,
+                                    const T* from0,
+                                    const T* from1,
+                                    size_t elems)
+   {
+   const auto mask = CT::Mask<T>::expand(cnd);
+   mask.select_n(to, from0, from1, elems);
+   return mask;
+   }
+
+template<typename T>
+inline void conditional_swap(bool cnd, T& x, T& y)
+   {
+   const auto swap = CT::Mask<T>::expand(cnd);
+
+   T t0 = swap.select(y, x);
+   T t1 = swap.select(x, y);
+   x = t0;
+   y = t1;
+   }
+
+template<typename T>
+inline void conditional_swap_ptr(bool cnd, T& x, T& y)
+   {
+   uintptr_t xp = reinterpret_cast<uintptr_t>(x);
+   uintptr_t yp = reinterpret_cast<uintptr_t>(y);
+
+   conditional_swap<uintptr_t>(cnd, xp, yp);
+
+   x = reinterpret_cast<T>(xp);
+   y = reinterpret_cast<T>(yp);
+   }
+
+/**
+* If bad_mask is unset, return in[delim_idx:input_length] copied to
+* new buffer. If bad_mask is set, return an all zero vector of
+* unspecified length.
+*/
+secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input,
+                                   const uint8_t input[],
+                                   size_t input_length,
+                                   size_t delim_idx);
+
+secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length);
+
+inline secure_vector<uint8_t> strip_leading_zeros(const secure_vector<uint8_t>& in)
+   {
+   return strip_leading_zeros(in.data(), in.size());
+   }
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/data_src.cpp b/third_party/botan/src/lib/utils/data_src.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/data_src.cpp
@@ -0,0 +1,214 @@
+/*
+* DataSource
+* (C) 1999-2007 Jack Lloyd
+*     2005 Matthew Gregan
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/data_src.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+#include <istream>
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+  #include <fstream>
+#endif
+
+namespace Botan {
+
+/*
+* Read a single byte from the DataSource
+*/
+size_t DataSource::read_byte(uint8_t& out)
+   {
+   return read(&out, 1);
+   }
+
+/*
+* Peek a single byte from the DataSource
+*/
+size_t DataSource::peek_byte(uint8_t& out) const
+   {
+   return peek(&out, 1, 0);
+   }
+
+/*
+* Discard the next N bytes of the data
+*/
+size_t DataSource::discard_next(size_t n)
+   {
+   uint8_t buf[64] = { 0 };
+   size_t discarded = 0;
+
+   while(n)
+      {
+      const size_t got = this->read(buf, std::min(n, sizeof(buf)));
+      discarded += got;
+      n -= got;
+
+      if(got == 0)
+         break;
+      }
+
+   return discarded;
+   }
+
+/*
+* Read from a memory buffer
+*/
+size_t DataSource_Memory::read(uint8_t out[], size_t length)
+   {
+   const size_t got = std::min<size_t>(m_source.size() - m_offset, length);
+   copy_mem(out, m_source.data() + m_offset, got);
+   m_offset += got;
+   return got;
+   }
+
+bool DataSource_Memory::check_available(size_t n)
+   {
+   return (n <= (m_source.size() - m_offset));
+   }
+
+/*
+* Peek into a memory buffer
+*/
+size_t DataSource_Memory::peek(uint8_t out[], size_t length,
+                               size_t peek_offset) const
+   {
+   const size_t bytes_left = m_source.size() - m_offset;
+   if(peek_offset >= bytes_left) return 0;
+
+   const size_t got = std::min(bytes_left - peek_offset, length);
+   copy_mem(out, &m_source[m_offset + peek_offset], got);
+   return got;
+   }
+
+/*
+* Check if the memory buffer is empty
+*/
+bool DataSource_Memory::end_of_data() const
+   {
+   return (m_offset == m_source.size());
+   }
+
+/*
+* DataSource_Memory Constructor
+*/
+DataSource_Memory::DataSource_Memory(const std::string& in) :
+   m_source(cast_char_ptr_to_uint8(in.data()),
+            cast_char_ptr_to_uint8(in.data()) + in.length()),
+   m_offset(0)
+   {
+   }
+
+/*
+* Read from a stream
+*/
+size_t DataSource_Stream::read(uint8_t out[], size_t length)
+   {
+   m_source.read(cast_uint8_ptr_to_char(out), length);
+   if(m_source.bad())
+      throw Stream_IO_Error("DataSource_Stream::read: Source failure");
+
+   const size_t got = static_cast<size_t>(m_source.gcount());
+   m_total_read += got;
+   return got;
+   }
+
+bool DataSource_Stream::check_available(size_t n)
+   {
+   const std::streampos orig_pos = m_source.tellg();
+   m_source.seekg(0, std::ios::end);
+   const size_t avail = static_cast<size_t>(m_source.tellg() - orig_pos);
+   m_source.seekg(orig_pos);
+   return (avail >= n);
+   }
+
+/*
+* Peek into a stream
+*/
+size_t DataSource_Stream::peek(uint8_t out[], size_t length, size_t offset) const
+   {
+   if(end_of_data())
+      throw Invalid_State("DataSource_Stream: Cannot peek when out of data");
+
+   size_t got = 0;
+
+   if(offset)
+      {
+      secure_vector<uint8_t> buf(offset);
+      m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size());
+      if(m_source.bad())
+         throw Stream_IO_Error("DataSource_Stream::peek: Source failure");
+      got = static_cast<size_t>(m_source.gcount());
+      }
+
+   if(got == offset)
+      {
+      m_source.read(cast_uint8_ptr_to_char(out), length);
+      if(m_source.bad())
+         throw Stream_IO_Error("DataSource_Stream::peek: Source failure");
+      got = static_cast<size_t>(m_source.gcount());
+      }
+
+   if(m_source.eof())
+      m_source.clear();
+   m_source.seekg(m_total_read, std::ios::beg);
+
+   return got;
+   }
+
+/*
+* Check if the stream is empty or in error
+*/
+bool DataSource_Stream::end_of_data() const
+   {
+   return (!m_source.good());
+   }
+
+/*
+* Return a human-readable ID for this stream
+*/
+std::string DataSource_Stream::id() const
+   {
+   return m_identifier;
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* DataSource_Stream Constructor
+*/
+DataSource_Stream::DataSource_Stream(const std::string& path,
+                                     bool use_binary) :
+   m_identifier(path),
+   m_source_memory(new std::ifstream(path, use_binary ? std::ios::binary : std::ios::in)),
+   m_source(*m_source_memory),
+   m_total_read(0)
+   {
+   if(!m_source.good())
+      {
+      throw Stream_IO_Error("DataSource: Failure opening file " + path);
+      }
+   }
+
+#endif
+
+/*
+* DataSource_Stream Constructor
+*/
+DataSource_Stream::DataSource_Stream(std::istream& in,
+                                     const std::string& name) :
+   m_identifier(name),
+   m_source(in),
+   m_total_read(0)
+   {
+   }
+
+DataSource_Stream::~DataSource_Stream()
+   {
+   // for ~unique_ptr
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/data_src.h b/third_party/botan/src/lib/utils/data_src.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/data_src.h
@@ -0,0 +1,181 @@
+/*
+* DataSource
+* (C) 1999-2007 Jack Lloyd
+*     2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_SRC_H_
+#define BOTAN_DATA_SRC_H_
+
+#include <botan/secmem.h>
+#include <string>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* This class represents an abstract data source object.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSource
+   {
+   public:
+      /**
+      * Read from the source. Moves the internal offset so that every
+      * call to read will return a new portion of the source.
+      *
+      * @param out the byte array to write the result to
+      * @param length the length of the byte array out
+      * @return length in bytes that was actually read and put
+      * into out
+      */
+      virtual size_t read(uint8_t out[], size_t length) BOTAN_WARN_UNUSED_RESULT = 0;
+
+      virtual bool check_available(size_t n) = 0;
+
+      /**
+      * Read from the source but do not modify the internal
+      * offset. Consecutive calls to peek() will return portions of
+      * the source starting at the same position.
+      *
+      * @param out the byte array to write the output to
+      * @param length the length of the byte array out
+      * @param peek_offset the offset into the stream to read at
+      * @return length in bytes that was actually read and put
+      * into out
+      */
+      virtual size_t peek(uint8_t out[], size_t length, size_t peek_offset) const BOTAN_WARN_UNUSED_RESULT = 0;
+
+      /**
+      * Test whether the source still has data that can be read.
+      * @return true if there is no more data to read, false otherwise
+      */
+      virtual bool end_of_data() const = 0;
+      /**
+      * return the id of this data source
+      * @return std::string representing the id of this data source
+      */
+      virtual std::string id() const { return ""; }
+
+      /**
+      * Read one byte.
+      * @param out the byte to read to
+      * @return length in bytes that was actually read and put
+      * into out
+      */
+      size_t read_byte(uint8_t& out);
+
+      /**
+      * Peek at one byte.
+      * @param out an output byte
+      * @return length in bytes that was actually read and put
+      * into out
+      */
+      size_t peek_byte(uint8_t& out) const;
+
+      /**
+      * Discard the next N bytes of the data
+      * @param N the number of bytes to discard
+      * @return number of bytes actually discarded
+      */
+      size_t discard_next(size_t N);
+
+      /**
+      * @return number of bytes read so far.
+      */
+      virtual size_t get_bytes_read() const = 0;
+
+      DataSource() = default;
+      virtual ~DataSource() = default;
+      DataSource& operator=(const DataSource&) = delete;
+      DataSource(const DataSource&) = delete;
+   };
+
+/**
+* This class represents a Memory-Based DataSource
+*/
+class BOTAN_PUBLIC_API(2,0) DataSource_Memory final : public DataSource
+   {
+   public:
+      size_t read(uint8_t[], size_t) override;
+      size_t peek(uint8_t[], size_t, size_t) const override;
+      bool check_available(size_t n) override;
+      bool end_of_data() const override;
+
+      /**
+      * Construct a memory source that reads from a string
+      * @param in the string to read from
+      */
+      explicit DataSource_Memory(const std::string& in);
+
+      /**
+      * Construct a memory source that reads from a byte array
+      * @param in the byte array to read from
+      * @param length the length of the byte array
+      */
+      DataSource_Memory(const uint8_t in[], size_t length) :
+         m_source(in, in + length), m_offset(0) {}
+
+      /**
+      * Construct a memory source that reads from a secure_vector
+      * @param in the MemoryRegion to read from
+      */
+      explicit DataSource_Memory(const secure_vector<uint8_t>& in) :
+         m_source(in), m_offset(0) {}
+
+      /**
+      * Construct a memory source that reads from a std::vector
+      * @param in the MemoryRegion to read from
+      */
+      explicit DataSource_Memory(const std::vector<uint8_t>& in) :
+         m_source(in.begin(), in.end()), m_offset(0) {}
+
+      size_t get_bytes_read() const override { return m_offset; }
+   private:
+      secure_vector<uint8_t> m_source;
+      size_t m_offset;
+   };
+
+/**
+* This class represents a Stream-Based DataSource.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSource_Stream final : public DataSource
+   {
+   public:
+      size_t read(uint8_t[], size_t) override;
+      size_t peek(uint8_t[], size_t, size_t) const override;
+      bool check_available(size_t n) override;
+      bool end_of_data() const override;
+      std::string id() const override;
+
+      DataSource_Stream(std::istream&,
+                        const std::string& id = "<std::istream>");
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+      /**
+      * Construct a Stream-Based DataSource from filesystem path
+      * @param file the path to the file
+      * @param use_binary whether to treat the file as binary or not
+      */
+      DataSource_Stream(const std::string& file, bool use_binary = false);
+#endif
+
+      DataSource_Stream(const DataSource_Stream&) = delete;
+
+      DataSource_Stream& operator=(const DataSource_Stream&) = delete;
+
+      ~DataSource_Stream();
+
+      size_t get_bytes_read() const override { return m_total_read; }
+   private:
+      const std::string m_identifier;
+
+      std::unique_ptr<std::istream> m_source_memory;
+      std::istream& m_source;
+      size_t m_total_read;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/database.h b/third_party/botan/src/lib/utils/database.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/database.h
@@ -0,0 +1,88 @@
+/*
+* SQL database interface
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SQL_DATABASE_H_
+#define BOTAN_SQL_DATABASE_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <chrono>
+#include <vector>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) SQL_Database
+   {
+   public:
+
+      class BOTAN_PUBLIC_API(2,0) SQL_DB_Error final : public Exception
+         {
+         public:
+            explicit SQL_DB_Error(const std::string& what) :
+               Exception("SQL database", what),
+               m_rc(0)
+               {}
+
+            SQL_DB_Error(const std::string& what, int rc) :
+               Exception("SQL database", what),
+               m_rc(rc)
+               {}
+
+            ErrorType error_type() const noexcept override { return Botan::ErrorType::DatabaseError; }
+
+            int error_code() const noexcept override { return m_rc; }
+         private:
+            int m_rc;
+         };
+
+      class BOTAN_PUBLIC_API(2,0) Statement
+         {
+         public:
+            /* Bind statement parameters */
+            virtual void bind(int column, const std::string& str) = 0;
+
+            virtual void bind(int column, size_t i) = 0;
+
+            virtual void bind(int column, std::chrono::system_clock::time_point time) = 0;
+
+            virtual void bind(int column, const std::vector<uint8_t>& blob) = 0;
+
+            virtual void bind(int column, const uint8_t* data, size_t len) = 0;
+
+            /* Get output */
+            virtual std::pair<const uint8_t*, size_t> get_blob(int column) = 0;
+
+            virtual std::string get_str(int column) = 0;
+
+            virtual size_t get_size_t(int column) = 0;
+
+            /* Run to completion */
+            virtual size_t spin() = 0;
+
+            /* Maybe update */
+            virtual bool step() = 0;
+
+            virtual ~Statement() = default;
+         };
+
+      /*
+      * Create a new statement for execution.
+      * Use ?1, ?2, ?3, etc for parameters to set later with bind
+      */
+      virtual std::shared_ptr<Statement> new_statement(const std::string& base_sql) const = 0;
+
+      virtual size_t row_count(const std::string& table_name) = 0;
+
+      virtual void create_table(const std::string& table_schema) = 0;
+
+      virtual ~SQL_Database() = default;
+};
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/donna128.h b/third_party/botan/src/lib/utils/donna128.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/donna128.h
@@ -0,0 +1,143 @@
+/*
+* A minimal 128-bit integer type for curve25519-donna
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CURVE25519_DONNA128_H_
+#define BOTAN_CURVE25519_DONNA128_H_
+
+#include <botan/mul128.h>
+
+namespace Botan {
+
+class donna128 final
+   {
+   public:
+      donna128(uint64_t ll = 0, uint64_t hh = 0) { l = ll; h = hh; }
+
+      donna128(const donna128&) = default;
+      donna128& operator=(const donna128&) = default;
+
+      friend donna128 operator>>(const donna128& x, size_t shift)
+         {
+         donna128 z = x;
+         if(shift > 0)
+            {
+            const uint64_t carry = z.h << (64 - shift);
+            z.h = (z.h >> shift);
+            z.l = (z.l >> shift) | carry;
+            }
+         return z;
+         }
+
+      friend donna128 operator<<(const donna128& x, size_t shift)
+         {
+         donna128 z = x;
+         if(shift > 0)
+            {
+            const uint64_t carry = z.l >> (64 - shift);
+            z.l = (z.l << shift);
+            z.h = (z.h << shift) | carry;
+            }
+         return z;
+         }
+
+      friend uint64_t operator&(const donna128& x, uint64_t mask)
+         {
+         return x.l & mask;
+         }
+
+      uint64_t operator&=(uint64_t mask)
+         {
+         h = 0;
+         l &= mask;
+         return l;
+         }
+
+      donna128& operator+=(const donna128& x)
+         {
+         l += x.l;
+         h += x.h;
+
+         const uint64_t carry = (l < x.l);
+         h += carry;
+         return *this;
+         }
+
+      donna128& operator+=(uint64_t x)
+         {
+         l += x;
+         const uint64_t carry = (l < x);
+         h += carry;
+         return *this;
+         }
+
+      uint64_t lo() const { return l; }
+      uint64_t hi() const { return h; }
+   private:
+      uint64_t h = 0, l = 0;
+   };
+
+inline donna128 operator*(const donna128& x, uint64_t y)
+   {
+   BOTAN_ARG_CHECK(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply");
+
+   uint64_t lo = 0, hi = 0;
+   mul64x64_128(x.lo(), y, &lo, &hi);
+   return donna128(lo, hi);
+   }
+
+inline donna128 operator*(uint64_t y, const donna128& x)
+   {
+   return x * y;
+   }
+
+inline donna128 operator+(const donna128& x, const donna128& y)
+   {
+   donna128 z = x;
+   z += y;
+   return z;
+   }
+
+inline donna128 operator+(const donna128& x, uint64_t y)
+   {
+   donna128 z = x;
+   z += y;
+   return z;
+   }
+
+inline donna128 operator|(const donna128& x, const donna128& y)
+   {
+   return donna128(x.lo() | y.lo(), x.hi() | y.hi());
+   }
+
+inline uint64_t carry_shift(const donna128& a, size_t shift)
+   {
+   return (a >> shift).lo();
+   }
+
+inline uint64_t combine_lower(const donna128& a, size_t s1,
+                              const donna128& b, size_t s2)
+   {
+   donna128 z = (a >> s1) | (b << s2);
+   return z.lo();
+   }
+
+#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+inline uint64_t carry_shift(const uint128_t a, size_t shift)
+   {
+   return static_cast<uint64_t>(a >> shift);
+   }
+
+inline uint64_t combine_lower(const uint128_t a, size_t s1,
+                              const uint128_t b, size_t s2)
+   {
+   return static_cast<uint64_t>((a >> s1) | (b << s2));
+   }
+#endif
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp b/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp
@@ -0,0 +1,82 @@
+/*
+* Dynamically Loaded Object
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dyn_load.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+  #include <dlfcn.h>
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+  #define NOMINMAX 1
+  #define _WINSOCKAPI_ // stop windows.h including winsock.h
+  #include <windows.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+void raise_runtime_loader_exception(const std::string& lib_name,
+                                    const char* msg)
+   {
+   const std::string ex_msg =
+      "Failed to load " + lib_name + ": " +
+      (msg ? msg : "Unknown error");
+
+   throw System_Error(ex_msg, 0);
+   }
+
+}
+
+Dynamically_Loaded_Library::Dynamically_Loaded_Library(
+   const std::string& library) :
+   m_lib_name(library), m_lib(nullptr)
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   m_lib = ::dlopen(m_lib_name.c_str(), RTLD_LAZY);
+
+   if(!m_lib)
+      raise_runtime_loader_exception(m_lib_name, ::dlerror());
+
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   m_lib = ::LoadLibraryA(m_lib_name.c_str());
+
+   if(!m_lib)
+      raise_runtime_loader_exception(m_lib_name, "LoadLibrary failed");
+#endif
+
+   if(!m_lib)
+      raise_runtime_loader_exception(m_lib_name, "Dynamic load not supported");
+   }
+
+Dynamically_Loaded_Library::~Dynamically_Loaded_Library()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   ::dlclose(m_lib);
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   ::FreeLibrary((HMODULE)m_lib);
+#endif
+   }
+
+void* Dynamically_Loaded_Library::resolve_symbol(const std::string& symbol)
+   {
+   void* addr = nullptr;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   addr = ::dlsym(m_lib, symbol.c_str());
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   addr = reinterpret_cast<void*>(::GetProcAddress((HMODULE)m_lib, symbol.c_str()));
+#endif
+
+   if(!addr)
+      throw Invalid_Argument("Failed to resolve symbol " + symbol +
+                             " in " + m_lib_name);
+
+   return addr;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/dyn_load/dyn_load.h b/third_party/botan/src/lib/utils/dyn_load/dyn_load.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/dyn_load/dyn_load.h
@@ -0,0 +1,68 @@
+/*
+* Dynamically Loaded Object
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DYNAMIC_LOADER_H_
+#define BOTAN_DYNAMIC_LOADER_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Represents a DLL or shared object
+*/
+class BOTAN_PUBLIC_API(2,0) Dynamically_Loaded_Library final
+   {
+   public:
+      /**
+      * Load a DLL (or fail with an exception)
+      * @param lib_name name or path to a library
+      *
+      * If you don't use a full path, the search order will be defined
+      * by whatever the system linker does by default. Always using fully
+      * qualified pathnames can help prevent code injection attacks (eg
+      * via manipulation of LD_LIBRARY_PATH on Linux)
+      */
+      Dynamically_Loaded_Library(const std::string& lib_name);
+
+      /**
+      * Unload the DLL
+      * @warning Any pointers returned by resolve()/resolve_symbol()
+      * should not be used after this destructor runs.
+      */
+      ~Dynamically_Loaded_Library();
+
+      /**
+      * Load a symbol (or fail with an exception)
+      * @param symbol names the symbol to load
+      * @return address of the loaded symbol
+      */
+      void* resolve_symbol(const std::string& symbol);
+
+      /**
+      * Convenience function for casting symbol to the right type
+      * @param symbol names the symbol to load
+      * @return address of the loaded symbol
+      */
+      template<typename T>
+      T resolve(const std::string& symbol)
+         {
+         return reinterpret_cast<T>(resolve_symbol(symbol));
+         }
+
+   private:
+      Dynamically_Loaded_Library(const Dynamically_Loaded_Library&);
+      Dynamically_Loaded_Library& operator=(const Dynamically_Loaded_Library&);
+
+      std::string m_lib_name;
+      void* m_lib;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/exceptn.cpp b/third_party/botan/src/lib/utils/exceptn.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/exceptn.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string to_string(ErrorType type)
+   {
+   switch(type)
+      {
+      case ErrorType::Unknown:
+         return "Unknown";
+      case ErrorType::SystemError:
+         return "SystemError";
+      case ErrorType::NotImplemented:
+         return "NotImplemented";
+      case ErrorType::OutOfMemory:
+         return "OutOfMemory";
+      case ErrorType::InternalError:
+         return "InternalError";
+      case ErrorType::IoError:
+         return "IoError";
+      case ErrorType::InvalidObjectState :
+         return "InvalidObjectState";
+      case ErrorType::KeyNotSet:
+         return "KeyNotSet";
+      case ErrorType::InvalidArgument:
+         return "InvalidArgument";
+      case ErrorType::InvalidKeyLength:
+         return "InvalidKeyLength";
+      case ErrorType::InvalidNonceLength:
+         return "InvalidNonceLength";
+      case ErrorType::LookupError:
+         return "LookupError";
+      case ErrorType::EncodingFailure:
+         return "EncodingFailure";
+      case ErrorType::DecodingFailure:
+         return "DecodingFailure";
+      case ErrorType::TLSError:
+         return "TLSError";
+      case ErrorType::HttpError:
+         return "HttpError";
+      case ErrorType::InvalidTag:
+         return "InvalidTag";
+      case ErrorType::RoughtimeError:
+         return "RoughtimeError";
+      case ErrorType::OpenSSLError :
+         return "OpenSSLError";
+      case ErrorType::CommonCryptoError:
+         return "CommonCryptoError";
+      case ErrorType::Pkcs11Error:
+         return "Pkcs11Error";
+      case ErrorType::TPMError:
+         return "TPMError";
+      case ErrorType::DatabaseError:
+         return "DatabaseError";
+      case ErrorType::ZlibError :
+         return "ZlibError";
+      case ErrorType::Bzip2Error:
+         return "Bzip2Error" ;
+      case ErrorType::LzmaError:
+         return "LzmaError";
+      }
+
+   // No default case in above switch so compiler warns
+   return "Unrecognized Botan error";
+   }
+
+Exception::Exception(const std::string& msg) : m_msg(msg)
+   {}
+
+Exception::Exception(const std::string& msg, const std::exception& e) :
+   m_msg(msg + " failed with " + std::string(e.what()))
+   {}
+
+Exception::Exception(const char* prefix, const std::string& msg) :
+   m_msg(std::string(prefix) + " " + msg)
+   {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg) :
+   Exception(msg)
+   {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg, const std::string& where) :
+   Exception(msg + " in " + where)
+   {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg, const std::exception& e) :
+   Exception(msg, e) {}
+
+Lookup_Error::Lookup_Error(const std::string& type,
+                           const std::string& algo,
+                           const std::string& provider) :
+   Exception("Unavailable " + type + " " + algo +
+             (provider.empty() ? std::string("") : (" for provider " + provider)))
+   {}
+
+Internal_Error::Internal_Error(const std::string& err) :
+   Exception("Internal error: " + err)
+   {}
+
+Invalid_Key_Length::Invalid_Key_Length(const std::string& name, size_t length) :
+   Invalid_Argument(name + " cannot accept a key of length " +
+                    std::to_string(length))
+   {}
+
+Invalid_IV_Length::Invalid_IV_Length(const std::string& mode, size_t bad_len) :
+   Invalid_Argument("IV length " + std::to_string(bad_len) +
+                    " is invalid for " + mode)
+   {}
+
+Key_Not_Set::Key_Not_Set(const std::string& algo) :
+   Invalid_State("Key not set in " + algo)
+   {}
+
+Policy_Violation::Policy_Violation(const std::string& err) :
+   Invalid_State("Policy violation: " + err) {}
+
+PRNG_Unseeded::PRNG_Unseeded(const std::string& algo) :
+   Invalid_State("PRNG not seeded: " + algo)
+   {}
+
+Algorithm_Not_Found::Algorithm_Not_Found(const std::string& name) :
+   Lookup_Error("Could not find any algorithm named \"" + name + "\"")
+   {}
+
+No_Provider_Found::No_Provider_Found(const std::string& name) :
+   Exception("Could not find any provider for algorithm named \"" + name + "\"")
+   {}
+
+Provider_Not_Found::Provider_Not_Found(const std::string& algo, const std::string& provider) :
+   Lookup_Error("Could not find provider '" + provider + "' for " + algo)
+   {}
+
+Invalid_Algorithm_Name::Invalid_Algorithm_Name(const std::string& name):
+   Invalid_Argument("Invalid algorithm name: " + name)
+   {}
+
+Encoding_Error::Encoding_Error(const std::string& name) :
+   Invalid_Argument("Encoding error: " + name)
+   {}
+
+Decoding_Error::Decoding_Error(const std::string& name) :
+   Invalid_Argument(name)
+   {}
+
+Decoding_Error::Decoding_Error(const std::string& msg, const std::exception& e) :
+   Invalid_Argument(msg, e)
+   {}
+
+Decoding_Error::Decoding_Error(const std::string& name, const char* exception_message) :
+   Invalid_Argument(name + " failed with exception " + exception_message) {}
+
+Invalid_Authentication_Tag::Invalid_Authentication_Tag(const std::string& msg) :
+   Exception("Invalid authentication tag: " + msg)
+   {}
+
+Invalid_OID::Invalid_OID(const std::string& oid) :
+   Decoding_Error("Invalid ASN.1 OID: " + oid)
+   {}
+
+Stream_IO_Error::Stream_IO_Error(const std::string& err) :
+   Exception("I/O error: " + err)
+   {}
+
+System_Error::System_Error(const std::string& msg, int err_code) :
+   Exception(msg + " error code " + std::to_string(err_code)),
+   m_error_code(err_code)
+   {}
+
+Self_Test_Failure::Self_Test_Failure(const std::string& err) :
+   Internal_Error("Self test failed: " + err)
+   {}
+
+Not_Implemented::Not_Implemented(const std::string& err) :
+   Exception("Not implemented", err)
+   {}
+
+}
diff --git a/third_party/botan/src/lib/utils/exceptn.h b/third_party/botan/src/lib/utils/exceptn.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/exceptn.h
@@ -0,0 +1,441 @@
+/*
+* Exceptions
+* (C) 1999-2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EXCEPTION_H_
+#define BOTAN_EXCEPTION_H_
+
+#include <botan/types.h>
+#include <exception>
+#include <string>
+
+namespace Botan {
+
+/**
+* Different types of errors that might occur
+*/
+enum class ErrorType {
+   /** Some unknown error */
+   Unknown = 1,
+   /** An error while calling a system interface */
+   SystemError,
+   /** An operation seems valid, but not supported by the current version */
+   NotImplemented,
+   /** Memory allocation failure */
+   OutOfMemory,
+   /** An internal error occurred */
+   InternalError,
+   /** An I/O error occurred */
+   IoError,
+
+   /** Invalid object state */
+   InvalidObjectState = 100,
+   /** A key was not set on an object when this is required */
+   KeyNotSet,
+   /** The application provided an argument which is invalid */
+   InvalidArgument,
+   /** A key with invalid length was provided */
+   InvalidKeyLength,
+   /** A nonce with invalid length was provided */
+   InvalidNonceLength,
+   /** An object type was requested but cannot be found */
+   LookupError,
+   /** Encoding a message or datum failed */
+   EncodingFailure,
+   /** Decoding a message or datum failed */
+   DecodingFailure,
+   /** A TLS error (error_code will be the alert type) */
+   TLSError,
+   /** An error during an HTTP operation */
+   HttpError,
+   /** A message with an invalid authentication tag was detected */
+   InvalidTag,
+   /** An error during Roughtime validation */
+   RoughtimeError,
+
+   /** An error when calling OpenSSL */
+   OpenSSLError = 200,
+   /** An error when interacting with CommonCrypto API */
+   CommonCryptoError,
+   /** An error when interacting with a PKCS11 device */
+   Pkcs11Error,
+   /** An error when interacting with a TPM device */
+   TPMError,
+   /** An error when interacting with a database */
+   DatabaseError,
+
+   /** An error when interacting with zlib */
+   ZlibError = 300,
+   /** An error when interacting with bzip2 */
+   Bzip2Error,
+   /** An error when interacting with lzma */
+   LzmaError,
+
+};
+
+//! \brief Convert an ErrorType to string
+std::string BOTAN_PUBLIC_API(2,11) to_string(ErrorType type);
+
+/**
+* Base class for all exceptions thrown by the library
+*/
+class BOTAN_PUBLIC_API(2,0) Exception : public std::exception
+   {
+   public:
+      /**
+      * Return a descriptive string which is hopefully comprehensible to
+      * a developer. It will likely not be useful for an end user.
+      *
+      * The string has no particular format, and the content of exception
+      * messages may change from release to release. Thus the main use of this
+      * function is for logging or debugging.
+      */
+      const char* what() const noexcept override { return m_msg.c_str(); }
+
+      /**
+      * Return the "type" of error which occurred.
+      */
+      virtual ErrorType error_type() const noexcept { return Botan::ErrorType::Unknown; }
+
+      /**
+      * Return an error code associated with this exception, or otherwise 0.
+      *
+      * The domain of this error varies depending on the source, for example on
+      * POSIX systems it might be errno, while on a Windows system it might be
+      * the result of GetLastError or WSAGetLastError. For error_type() is
+      * OpenSSLError, it will (if nonzero) be an OpenSSL error code from
+      * ERR_get_error.
+      */
+      virtual int error_code() const noexcept { return 0; }
+
+      /**
+      * Avoid throwing base Exception, use a subclass
+      */
+      explicit Exception(const std::string& msg);
+
+      /**
+      * Avoid throwing base Exception, use a subclass
+      */
+      Exception(const char* prefix, const std::string& msg);
+
+      /**
+      * Avoid throwing base Exception, use a subclass
+      */
+      Exception(const std::string& msg, const std::exception& e);
+
+   private:
+      std::string m_msg;
+   };
+
+/**
+* An invalid argument was provided to an API call.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Argument : public Exception
+   {
+   public:
+      explicit Invalid_Argument(const std::string& msg);
+
+      explicit Invalid_Argument(const std::string& msg, const std::string& where);
+
+      Invalid_Argument(const std::string& msg, const std::exception& e);
+
+      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
+   };
+
+/**
+* An invalid key length was used
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Key_Length final : public Invalid_Argument
+   {
+   public:
+      Invalid_Key_Length(const std::string& name, size_t length);
+      ErrorType error_type() const noexcept override { return ErrorType::InvalidKeyLength; }
+   };
+
+/**
+* An invalid nonce length was used
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_IV_Length final : public Invalid_Argument
+   {
+   public:
+      Invalid_IV_Length(const std::string& mode, size_t bad_len);
+      ErrorType error_type() const noexcept override { return ErrorType::InvalidNonceLength; }
+   };
+
+/**
+* Invalid_Algorithm_Name Exception
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Algorithm_Name final : public Invalid_Argument
+   {
+   public:
+      explicit Invalid_Algorithm_Name(const std::string& name);
+   };
+
+/**
+* Encoding_Error Exception
+*
+* This exception derives from Invalid_Argument for historical reasons, and it
+* does not make any real sense for it to do so. In a future major release this
+* exception type will derive directly from Exception instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Encoding_Error final : public Invalid_Argument
+   {
+   public:
+      explicit Encoding_Error(const std::string& name);
+
+      ErrorType error_type() const noexcept override { return ErrorType::EncodingFailure; }
+   };
+
+/**
+* A decoding error occurred.
+*
+* This exception derives from Invalid_Argument for historical reasons, and it
+* does not make any real sense for it to do so. In a future major release this
+* exception type will derive directly from Exception instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Decoding_Error : public Invalid_Argument
+   {
+   public:
+      explicit Decoding_Error(const std::string& name);
+
+      Decoding_Error(const std::string& name, const char* exception_message);
+
+      Decoding_Error(const std::string& msg, const std::exception& e);
+
+      ErrorType error_type() const noexcept override { return ErrorType::DecodingFailure; }
+   };
+
+/**
+* Invalid state was encountered. A request was made on an object while the
+* object was in a state where the operation cannot be performed.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_State : public Exception
+   {
+   public:
+      explicit Invalid_State(const std::string& err) : Exception(err) {}
+
+      ErrorType error_type() const noexcept override { return ErrorType::InvalidObjectState; }
+   };
+
+/**
+* A PRNG was called on to produce output while still unseeded
+*/
+class BOTAN_PUBLIC_API(2,0) PRNG_Unseeded final : public Invalid_State
+   {
+   public:
+      explicit PRNG_Unseeded(const std::string& algo);
+   };
+
+/**
+* The key was not set on an object. This occurs with symmetric objects where
+* an operation which requires the key is called prior to set_key being called.
+*/
+class BOTAN_PUBLIC_API(2,4) Key_Not_Set : public Invalid_State
+   {
+   public:
+      explicit Key_Not_Set(const std::string& algo);
+
+      ErrorType error_type() const noexcept override { return ErrorType::KeyNotSet; }
+   };
+
+/**
+* A request was made for some kind of object which could not be located
+*/
+class BOTAN_PUBLIC_API(2,0) Lookup_Error : public Exception
+   {
+   public:
+      explicit Lookup_Error(const std::string& err) : Exception(err) {}
+
+      Lookup_Error(const std::string& type,
+                   const std::string& algo,
+                   const std::string& provider);
+
+      ErrorType error_type() const noexcept override { return ErrorType::LookupError; }
+   };
+
+/**
+* Algorithm_Not_Found Exception
+*
+* @warning This exception type will be removed in the future. Instead
+* just catch Lookup_Error.
+*/
+class BOTAN_PUBLIC_API(2,0) Algorithm_Not_Found final : public Lookup_Error
+   {
+   public:
+      explicit Algorithm_Not_Found(const std::string& name);
+   };
+
+/**
+* Provider_Not_Found is thrown when a specific provider was requested
+* but that provider is not available.
+*
+* @warning This exception type will be removed in the future. Instead
+* just catch Lookup_Error.
+*/
+class BOTAN_PUBLIC_API(2,0) Provider_Not_Found final : public Lookup_Error
+   {
+   public:
+      Provider_Not_Found(const std::string& algo, const std::string& provider);
+   };
+
+/**
+* An AEAD or MAC check detected a message modification
+*
+* In versions before 2.10, Invalid_Authentication_Tag was named
+* Integrity_Failure, it was renamed to make its usage more clear.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Authentication_Tag final : public Exception
+   {
+   public:
+      explicit Invalid_Authentication_Tag(const std::string& msg);
+
+      ErrorType error_type() const noexcept override { return ErrorType::InvalidTag; }
+   };
+
+/**
+* For compatability with older versions
+*/
+typedef Invalid_Authentication_Tag Integrity_Failure;
+
+/**
+* An error occurred while operating on an IO stream
+*/
+class BOTAN_PUBLIC_API(2,0) Stream_IO_Error final : public Exception
+   {
+   public:
+      explicit Stream_IO_Error(const std::string& err);
+
+      ErrorType error_type() const noexcept override { return ErrorType::IoError; }
+   };
+
+/**
+* System_Error
+*
+* This exception is thrown in the event of an error related to interacting
+* with the operating system.
+*
+* This exception type also (optionally) captures an integer error code eg
+* POSIX errno or Windows GetLastError.
+*/
+class BOTAN_PUBLIC_API(2,9) System_Error : public Exception
+   {
+   public:
+      System_Error(const std::string& msg) : Exception(msg), m_error_code(0) {}
+
+      System_Error(const std::string& msg, int err_code);
+
+      ErrorType error_type() const noexcept override { return ErrorType::SystemError; }
+
+      int error_code() const noexcept override { return m_error_code; }
+
+   private:
+      int m_error_code;
+   };
+
+/**
+* An internal error occurred. If observed, please file a bug.
+*/
+class BOTAN_PUBLIC_API(2,0) Internal_Error : public Exception
+   {
+   public:
+      explicit Internal_Error(const std::string& err);
+
+      ErrorType error_type() const noexcept override { return ErrorType::InternalError; }
+   };
+
+/**
+* Not Implemented Exception
+*
+* This is thrown in the situation where a requested operation is
+* logically valid but is not implemented by this version of the library.
+*/
+class BOTAN_PUBLIC_API(2,0) Not_Implemented final : public Exception
+   {
+   public:
+      explicit Not_Implemented(const std::string& err);
+
+      ErrorType error_type() const noexcept override { return ErrorType::NotImplemented; }
+   };
+
+/*
+   The following exception types are still in use for compatability reasons,
+   but are deprecated and will be removed in a future major release.
+   Instead catch the base class.
+*/
+
+/**
+* An invalid OID string was used.
+*
+* This exception will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_OID final : public Decoding_Error
+   {
+   public:
+      explicit Invalid_OID(const std::string& oid);
+   };
+
+/*
+   The following exception types are deprecated, no longer used,
+   and will be removed in a future major release
+*/
+
+/**
+* Self Test Failure Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Self_Test_Failure final : public Internal_Error
+   {
+   public:
+      BOTAN_DEPRECATED("no longer used") explicit Self_Test_Failure(const std::string& err);
+   };
+
+/**
+* No_Provider_Found Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) No_Provider_Found final : public Exception
+   {
+   public:
+      BOTAN_DEPRECATED("no longer used") explicit No_Provider_Found(const std::string& name);
+   };
+
+/**
+* Policy_Violation Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Policy_Violation final : public Invalid_State
+   {
+   public:
+      BOTAN_DEPRECATED("no longer used") explicit Policy_Violation(const std::string& err);
+   };
+
+/**
+* Unsupported_Argument Exception
+*
+* An argument that is invalid because it is not supported by Botan.
+* It might or might not be valid in another context like a standard.
+*
+* This exception is no longer used, instead Not_Implemented is thrown.
+* It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Unsupported_Argument final : public Invalid_Argument
+   {
+   public:
+      BOTAN_DEPRECATED("no longer used") explicit Unsupported_Argument(const std::string& msg) : Invalid_Argument(msg) {}
+   };
+
+template<typename E, typename... Args>
+inline void do_throw_error(const char* file, int line, const char* func, Args... args)
+   {
+   throw E(file, line, func, args...);
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/filesystem.cpp b/third_party/botan/src/lib/utils/filesystem.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/filesystem.cpp
@@ -0,0 +1,144 @@
+/*
+* (C) 2015,2017,2019 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+#include <botan/internal/filesystem.h>
+#include <algorithm>
+#include <deque>
+#include <memory>
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+  #include <sys/types.h>
+  #include <sys/stat.h>
+  #include <dirent.h>
+  #include <functional>
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+  #define NOMINMAX 1
+  #define _WINSOCKAPI_ // stop windows.h including winsock.h
+  #include <windows.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+
+std::vector<std::string> impl_readdir(const std::string& dir_path)
+   {
+   std::vector<std::string> out;
+   std::deque<std::string> dir_list;
+   dir_list.push_back(dir_path);
+
+   while(!dir_list.empty())
+      {
+      const std::string cur_path = dir_list[0];
+      dir_list.pop_front();
+
+      std::unique_ptr<DIR, std::function<int (DIR*)>> dir(::opendir(cur_path.c_str()), ::closedir);
+
+      if(dir)
+         {
+         while(struct dirent* dirent = ::readdir(dir.get()))
+            {
+            const std::string filename = dirent->d_name;
+            if(filename == "." || filename == "..")
+               continue;
+            const std::string full_path = cur_path + "/" + filename;
+
+            struct stat stat_buf;
+
+            if(::stat(full_path.c_str(), &stat_buf) == -1)
+               continue;
+
+            if(S_ISDIR(stat_buf.st_mode))
+               dir_list.push_back(full_path);
+            else if(S_ISREG(stat_buf.st_mode))
+               out.push_back(full_path);
+            }
+         }
+      }
+
+   return out;
+   }
+
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+
+std::vector<std::string> impl_win32(const std::string& dir_path)
+   {
+   std::vector<std::string> out;
+   std::deque<std::string> dir_list;
+   dir_list.push_back(dir_path);
+
+   while(!dir_list.empty())
+      {
+      const std::string cur_path = dir_list[0];
+      dir_list.pop_front();
+
+      WIN32_FIND_DATAA find_data;
+      HANDLE dir = ::FindFirstFileA((cur_path + "/*").c_str(), &find_data);
+
+      if(dir != INVALID_HANDLE_VALUE)
+         {
+         do
+            {
+            const std::string filename = find_data.cFileName;
+            if(filename == "." || filename == "..")
+               continue;
+            const std::string full_path = cur_path + "/" + filename;
+
+            if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               {
+               dir_list.push_back(full_path);
+               }
+            else
+               {
+               out.push_back(full_path);
+               }
+            }
+         while(::FindNextFileA(dir, &find_data));
+         }
+
+      ::FindClose(dir);
+      }
+
+   return out;
+}
+#endif
+
+}
+
+bool has_filesystem_impl()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   return true;
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   return true;
+#else
+   return false;
+#endif
+   }
+
+std::vector<std::string> get_files_recursive(const std::string& dir)
+   {
+   std::vector<std::string> files;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   files = impl_readdir(dir);
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   files = impl_win32(dir);
+#else
+   BOTAN_UNUSED(dir);
+   throw No_Filesystem_Access();
+#endif
+
+   std::sort(files.begin(), files.end());
+
+   return files;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/filesystem.h b/third_party/botan/src/lib/utils/filesystem.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/filesystem.h
@@ -0,0 +1,33 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_FILESYSTEM_H_
+#define BOTAN_UTIL_FILESYSTEM_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* No_Filesystem_Access Exception
+*/
+class BOTAN_PUBLIC_API(2,0) No_Filesystem_Access final : public Exception
+   {
+   public:
+      No_Filesystem_Access() : Exception("No filesystem access enabled.")
+         {}
+   };
+
+BOTAN_TEST_API bool has_filesystem_impl();
+
+BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& dir);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/ghash/ghash.cpp b/third_party/botan/src/lib/utils/ghash/ghash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ghash/ghash.cpp
@@ -0,0 +1,236 @@
+/*
+* GCM GHASH
+* (C) 2013,2015,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string GHASH::provider() const
+   {
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+   if(CPUID::has_carryless_multiply())
+      return "clmul";
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+   if(CPUID::has_vperm())
+      return "vperm";
+#endif
+
+   return "base";
+   }
+
+void GHASH::ghash_multiply(secure_vector<uint8_t>& x,
+                         const uint8_t input[],
+                         size_t blocks)
+   {
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+   if(CPUID::has_carryless_multiply())
+      {
+      return ghash_multiply_cpu(x.data(), m_H_pow.data(), input, blocks);
+      }
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+   if(CPUID::has_vperm())
+      {
+      return ghash_multiply_vperm(x.data(), m_HM.data(), input, blocks);
+      }
+#endif
+
+   CT::poison(x.data(), x.size());
+
+   const uint64_t ALL_BITS = 0xFFFFFFFFFFFFFFFF;
+
+   uint64_t X[2] = {
+      load_be<uint64_t>(x.data(), 0),
+      load_be<uint64_t>(x.data(), 1)
+   };
+
+   for(size_t b = 0; b != blocks; ++b)
+      {
+      X[0] ^= load_be<uint64_t>(input, 2*b);
+      X[1] ^= load_be<uint64_t>(input, 2*b+1);
+
+      uint64_t Z[2] = { 0, 0 };
+
+      for(size_t i = 0; i != 64; ++i)
+         {
+         const uint64_t X0MASK = (ALL_BITS + (X[0] >> 63)) ^ ALL_BITS;
+         const uint64_t X1MASK = (ALL_BITS + (X[1] >> 63)) ^ ALL_BITS;
+
+         X[0] <<= 1;
+         X[1] <<= 1;
+
+         Z[0] ^= m_HM[4*i  ] & X0MASK;
+         Z[1] ^= m_HM[4*i+1] & X0MASK;
+         Z[0] ^= m_HM[4*i+2] & X1MASK;
+         Z[1] ^= m_HM[4*i+3] & X1MASK;
+         }
+
+      X[0] = Z[0];
+      X[1] = Z[1];
+      }
+
+   store_be<uint64_t>(x.data(), X[0], X[1]);
+   CT::unpoison(x.data(), x.size());
+   }
+
+void GHASH::ghash_update(secure_vector<uint8_t>& ghash,
+                         const uint8_t input[], size_t length)
+   {
+   verify_key_set(!m_HM.empty());
+
+   /*
+   This assumes if less than block size input then we're just on the
+   final block and should pad with zeros
+   */
+
+   const size_t full_blocks = length / GCM_BS;
+   const size_t final_bytes = length - (full_blocks * GCM_BS);
+
+   if(full_blocks > 0)
+      {
+      ghash_multiply(ghash, input, full_blocks);
+      }
+
+   if(final_bytes)
+      {
+      uint8_t last_block[GCM_BS] = { 0 };
+      copy_mem(last_block, input + full_blocks * GCM_BS, final_bytes);
+      ghash_multiply(ghash, last_block, 1);
+      secure_scrub_memory(last_block, final_bytes);
+      }
+   }
+
+void GHASH::key_schedule(const uint8_t key[], size_t length)
+   {
+   m_H.assign(key, key+length);
+   m_H_ad.resize(GCM_BS);
+   m_ad_len = 0;
+   m_text_len = 0;
+
+   uint64_t H0 = load_be<uint64_t>(m_H.data(), 0);
+   uint64_t H1 = load_be<uint64_t>(m_H.data(), 1);
+
+   const uint64_t R = 0xE100000000000000;
+
+   m_HM.resize(256);
+
+   // precompute the multiples of H
+   for(size_t i = 0; i != 2; ++i)
+      {
+      for(size_t j = 0; j != 64; ++j)
+         {
+         /*
+         we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68
+         to make indexing nicer in the multiplication code
+         */
+         m_HM[4*j+2*i] = H0;
+         m_HM[4*j+2*i+1] = H1;
+
+         // GCM's bit ops are reversed so we carry out of the bottom
+         const uint64_t carry = R * (H1 & 1);
+         H1 = (H1 >> 1) | (H0 << 63);
+         H0 = (H0 >> 1) ^ carry;
+         }
+      }
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+   if(CPUID::has_carryless_multiply())
+      {
+      m_H_pow.resize(8);
+      ghash_precompute_cpu(m_H.data(), m_H_pow.data());
+      }
+#endif
+   }
+
+void GHASH::start(const uint8_t nonce[], size_t len)
+   {
+   BOTAN_ARG_CHECK(len == 16, "GHASH requires a 128-bit nonce");
+   m_nonce.assign(nonce, nonce + len);
+   m_ghash = m_H_ad;
+   }
+
+void GHASH::set_associated_data(const uint8_t input[], size_t length)
+   {
+   if(m_ghash.empty() == false)
+      throw Invalid_State("Too late to set AD in GHASH");
+
+   zeroise(m_H_ad);
+
+   ghash_update(m_H_ad, input, length);
+   m_ad_len = length;
+   }
+
+void GHASH::update_associated_data(const uint8_t ad[], size_t length)
+   {
+   verify_key_set(m_ghash.size() == GCM_BS);
+   m_ad_len += length;
+   ghash_update(m_ghash, ad, length);
+   }
+
+void GHASH::update(const uint8_t input[], size_t length)
+   {
+   verify_key_set(m_ghash.size() == GCM_BS);
+   m_text_len += length;
+   ghash_update(m_ghash, input, length);
+   }
+
+void GHASH::add_final_block(secure_vector<uint8_t>& hash,
+                            size_t ad_len, size_t text_len)
+   {
+   /*
+   * stack buffer is fine here since the text len is public
+   * and the length of the AD is probably not sensitive either.
+   */
+   uint8_t final_block[GCM_BS];
+   store_be<uint64_t>(final_block, 8*ad_len, 8*text_len);
+   ghash_update(hash, final_block, GCM_BS);
+   }
+
+void GHASH::final(uint8_t mac[], size_t mac_len)
+   {
+   BOTAN_ARG_CHECK(mac_len > 0 && mac_len <= 16, "GHASH output length");
+   add_final_block(m_ghash, m_ad_len, m_text_len);
+
+   for(size_t i = 0; i != mac_len; ++i)
+      mac[i] = m_ghash[i] ^ m_nonce[i];
+
+   m_ghash.clear();
+   m_text_len = 0;
+   }
+
+void GHASH::nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t nonce_len)
+   {
+   BOTAN_ASSERT(m_ghash.size() == 0, "nonce_hash called during wrong time");
+
+   ghash_update(y0, nonce, nonce_len);
+   add_final_block(y0, 0, nonce_len);
+   }
+
+void GHASH::clear()
+   {
+   zap(m_H);
+   zap(m_HM);
+   reset();
+   }
+
+void GHASH::reset()
+   {
+   zeroise(m_H_ad);
+   m_ghash.clear();
+   m_nonce.clear();
+   m_text_len = m_ad_len = 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/ghash/ghash.h b/third_party/botan/src/lib/utils/ghash/ghash.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ghash/ghash.h
@@ -0,0 +1,110 @@
+/*
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GCM_GHASH_H_
+#define BOTAN_GCM_GHASH_H_
+
+#include <botan/sym_algo.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ghash.h)
+
+namespace Botan {
+
+/**
+* GCM's GHASH
+* This is not intended for general use, but is exposed to allow
+* shared code between GCM and GMAC
+*/
+class BOTAN_PUBLIC_API(2,0) GHASH final : public SymmetricAlgorithm
+   {
+   public:
+      void set_associated_data(const uint8_t ad[], size_t ad_len);
+
+      secure_vector<uint8_t> BOTAN_DEPRECATED("Use other impl")
+         nonce_hash(const uint8_t nonce[], size_t nonce_len)
+         {
+         secure_vector<uint8_t> y0(GCM_BS);
+         nonce_hash(y0, nonce, nonce_len);
+         return y0;
+         }
+
+      void nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t len);
+
+      void start(const uint8_t nonce[], size_t len);
+
+      /*
+      * Assumes input len is multiple of 16
+      */
+      void update(const uint8_t in[], size_t len);
+
+      /*
+      * Incremental update of associated data
+      */
+      void update_associated_data(const uint8_t ad[], size_t len);
+
+      secure_vector<uint8_t> BOTAN_DEPRECATED("Use version taking output params") final()
+         {
+         secure_vector<uint8_t> mac(GCM_BS);
+         final(mac.data(), mac.size());
+         return mac;
+         }
+
+      void final(uint8_t out[], size_t out_len);
+
+      Key_Length_Specification key_spec() const override
+         { return Key_Length_Specification(16); }
+
+      void clear() override;
+
+      void reset();
+
+      std::string name() const override { return "GHASH"; }
+
+      std::string provider() const;
+
+      void ghash_update(secure_vector<uint8_t>& x,
+                        const uint8_t input[], size_t input_len);
+
+      void add_final_block(secure_vector<uint8_t>& x,
+                           size_t ad_len, size_t pt_len);
+   private:
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+      static void ghash_precompute_cpu(const uint8_t H[16], uint64_t H_pow[4*2]);
+
+      static void ghash_multiply_cpu(uint8_t x[16],
+                                     const uint64_t H_pow[4*2],
+                                     const uint8_t input[], size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+      static void ghash_multiply_vperm(uint8_t x[16],
+                                       const uint64_t HM[256],
+                                       const uint8_t input[], size_t blocks);
+#endif
+
+      void key_schedule(const uint8_t key[], size_t key_len) override;
+
+      void ghash_multiply(secure_vector<uint8_t>& x,
+                          const uint8_t input[],
+                          size_t blocks);
+
+      static const size_t GCM_BS = 16;
+
+      secure_vector<uint8_t> m_H;
+      secure_vector<uint8_t> m_H_ad;
+      secure_vector<uint8_t> m_ghash;
+      secure_vector<uint8_t> m_nonce;
+      secure_vector<uint64_t> m_HM;
+      secure_vector<uint64_t> m_H_pow;
+      size_t m_ad_len = 0;
+      size_t m_text_len = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp b/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp
@@ -0,0 +1,201 @@
+/*
+* Hook for CLMUL/PMULL/VPMSUM
+* (C) 2013,2017,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <botan/internal/simd_32.h>
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+  #include <immintrin.h>
+  #include <wmmintrin.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) reverse_vector(const SIMD_4x32& in)
+   {
+#if defined(BOTAN_SIMD_USE_SSE2)
+   const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+   return SIMD_4x32(_mm_shuffle_epi8(in.raw(), BSWAP_MASK));
+#elif defined(BOTAN_SIMD_USE_NEON)
+   const uint8_t maskb[16] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+   const uint8x16_t mask = vld1q_u8(maskb);
+   return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(in.raw()), mask)));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+   const __vector unsigned char mask = {15,14,13,12, 11,10,9,8, 7,6,5,4, 3,2,1,0};
+   return SIMD_4x32(vec_perm(in.raw(), in.raw(), mask));
+#endif
+   }
+
+template<int M>
+BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) clmul(const SIMD_4x32& H, const SIMD_4x32& x)
+   {
+   static_assert(M == 0x00 || M == 0x01 || M == 0x10 || M == 0x11, "Valid clmul mode");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+   return SIMD_4x32(_mm_clmulepi64_si128(x.raw(), H.raw(), M));
+#elif defined(BOTAN_SIMD_USE_NEON)
+   const uint64_t a = vgetq_lane_u64(vreinterpretq_u64_u32(x.raw()), M & 0x01);
+   const uint64_t b = vgetq_lane_u64(vreinterpretq_u64_u32(H.raw()), (M & 0x10) >> 4);
+   return SIMD_4x32(reinterpret_cast<uint32x4_t>(vmull_p64(a, b)));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+   const SIMD_4x32 mask_lo = SIMD_4x32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF);
+
+   SIMD_4x32 i1 = x;
+   SIMD_4x32 i2 = H;
+
+   if(M == 0x11)
+      {
+      i1 &= mask_lo;
+      i2 &= mask_lo;
+      }
+   else if(M == 0x10)
+      {
+      i1 = i1.shift_elems_left<2>();
+      }
+   else if(M == 0x01)
+      {
+      i2 = i2.shift_elems_left<2>();
+      }
+   else if(M == 0x00)
+      {
+      i1 = mask_lo.andc(i1);
+      i2 = mask_lo.andc(i2);
+      }
+
+   return SIMD_4x32((__vector unsigned int)__builtin_crypto_vpmsumd(
+                       (__vector unsigned long)i1.raw(),
+                       (__vector unsigned long)i2.raw())
+      );
+#endif
+   }
+
+inline SIMD_4x32 gcm_reduce(const SIMD_4x32& B0, const SIMD_4x32& B1)
+   {
+   SIMD_4x32 X0 = B1.shr<31>();
+   SIMD_4x32 X1 = B1.shl<1>();
+   SIMD_4x32 X2 = B0.shr<31>();
+   SIMD_4x32 X3 = B0.shl<1>();
+
+   X3 |= X0.shift_elems_right<3>();
+   X3 |= X2.shift_elems_left<1>();
+   X1 |= X0.shift_elems_left<1>();
+
+   X0 = X1.shl<31>() ^ X1.shl<30>() ^ X1.shl<25>();
+
+   X1 ^= X0.shift_elems_left<3>();
+
+   X0 = X1 ^ X3 ^ X0.shift_elems_right<1>();
+   X0 ^= X1.shr<7>() ^ X1.shr<2>() ^ X1.shr<1>();
+   return X0;
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) gcm_multiply(const SIMD_4x32& H, const SIMD_4x32& x)
+   {
+   SIMD_4x32 T0 = clmul<0x11>(H, x);
+   SIMD_4x32 T1 = clmul<0x10>(H, x);
+   SIMD_4x32 T2 = clmul<0x01>(H, x);
+   SIMD_4x32 T3 = clmul<0x00>(H, x);
+
+   T1 ^= T2;
+   T0 ^= T1.shift_elems_right<2>();
+   T3 ^= T1.shift_elems_left<2>();
+
+   return gcm_reduce(T0, T3);
+   }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA)
+   gcm_multiply_x4(const SIMD_4x32& H1, const SIMD_4x32& H2, const SIMD_4x32& H3, const SIMD_4x32& H4,
+                   const SIMD_4x32& X1, const SIMD_4x32& X2, const SIMD_4x32& X3, const SIMD_4x32& X4)
+   {
+   /*
+   * Mutiply with delayed reduction, algorithm by Krzysztof Jankowski
+   * and Pierre Laurent of Intel
+   */
+
+   const SIMD_4x32 lo = (clmul<0x00>(H1, X1) ^ clmul<0x00>(H2, X2)) ^
+                        (clmul<0x00>(H3, X3) ^ clmul<0x00>(H4, X4));
+
+   const SIMD_4x32 hi = (clmul<0x11>(H1, X1) ^ clmul<0x11>(H2, X2)) ^
+                        (clmul<0x11>(H3, X3) ^ clmul<0x11>(H4, X4));
+
+   SIMD_4x32 T;
+
+   T ^= clmul<0x00>(H1 ^ H1.shift_elems_right<2>(), X1 ^ X1.shift_elems_right<2>());
+   T ^= clmul<0x00>(H2 ^ H2.shift_elems_right<2>(), X2 ^ X2.shift_elems_right<2>());
+   T ^= clmul<0x00>(H3 ^ H3.shift_elems_right<2>(), X3 ^ X3.shift_elems_right<2>());
+   T ^= clmul<0x00>(H4 ^ H4.shift_elems_right<2>(), X4 ^ X4.shift_elems_right<2>());
+   T ^= lo;
+   T ^= hi;
+
+   return gcm_reduce(hi ^ T.shift_elems_right<2>(),
+                     lo ^ T.shift_elems_left<2>());
+   }
+
+}
+
+BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+void GHASH::ghash_precompute_cpu(const uint8_t H_bytes[16], uint64_t H_pow[4*2])
+   {
+   const SIMD_4x32 H1 = reverse_vector(SIMD_4x32::load_le(H_bytes));
+   const SIMD_4x32 H2 = gcm_multiply(H1, H1);
+   const SIMD_4x32 H3 = gcm_multiply(H1, H2);
+   const SIMD_4x32 H4 = gcm_multiply(H2, H2);
+
+   H1.store_le(H_pow);
+   H2.store_le(H_pow + 2);
+   H3.store_le(H_pow + 4);
+   H4.store_le(H_pow + 6);
+   }
+
+BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+void GHASH::ghash_multiply_cpu(uint8_t x[16],
+                               const uint64_t H_pow[8],
+                               const uint8_t input[], size_t blocks)
+   {
+   /*
+   * Algorithms 1 and 5 from Intel's CLMUL guide
+   */
+   const SIMD_4x32 H1 = SIMD_4x32::load_le(H_pow);
+
+   SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x));
+
+   if(blocks >= 4)
+      {
+      const SIMD_4x32 H2 = SIMD_4x32::load_le(H_pow + 2);
+      const SIMD_4x32 H3 = SIMD_4x32::load_le(H_pow + 4);
+      const SIMD_4x32 H4 = SIMD_4x32::load_le(H_pow + 6);
+
+      while(blocks >= 4)
+         {
+         const SIMD_4x32 m0 = reverse_vector(SIMD_4x32::load_le(input       ));
+         const SIMD_4x32 m1 = reverse_vector(SIMD_4x32::load_le(input + 16*1));
+         const SIMD_4x32 m2 = reverse_vector(SIMD_4x32::load_le(input + 16*2));
+         const SIMD_4x32 m3 = reverse_vector(SIMD_4x32::load_le(input + 16*3));
+
+         a ^= m0;
+         a = gcm_multiply_x4(H1, H2, H3, H4, m3, m2, m1, a);
+
+         input += 4*16;
+         blocks -= 4;
+         }
+      }
+
+   for(size_t i = 0; i != blocks; ++i)
+      {
+      const SIMD_4x32 m = reverse_vector(SIMD_4x32::load_le(input + 16*i));
+
+      a ^= m;
+      a = gcm_multiply(H1, a);
+      }
+
+   a = reverse_vector(a);
+   a.store_le(x);
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp b/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp
@@ -0,0 +1,62 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+// TODO: extend this to support NEON and AltiVec
+
+BOTAN_FUNC_ISA("ssse3")
+void GHASH::ghash_multiply_vperm(uint8_t x[16],
+                                 const uint64_t HM[256],
+                                 const uint8_t input_bytes[], size_t blocks)
+   {
+   const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+
+   const __m128i* HM_mm = reinterpret_cast<const __m128i*>(HM);
+
+   __m128i X = _mm_loadu_si128(reinterpret_cast<__m128i*>(x));
+   X = _mm_shuffle_epi8(X, BSWAP_MASK);
+
+   const __m128i ones = _mm_set1_epi8(-1);
+
+   for(size_t b = 0; b != blocks; ++b)
+      {
+      __m128i M = _mm_loadu_si128(reinterpret_cast<const __m128i*>(input_bytes) + b);
+      M = _mm_shuffle_epi8(M, BSWAP_MASK);
+
+      X = _mm_xor_si128(X, M);
+
+      __m128i Z = _mm_setzero_si128();
+
+      for(size_t i = 0; i != 64; i += 2)
+         {
+         const __m128i HM0 = _mm_loadu_si128(HM_mm + 2*i);
+         const __m128i HM1 = _mm_loadu_si128(HM_mm + 2*i + 1);
+         const __m128i HM2 = _mm_loadu_si128(HM_mm + 2*i + 2);
+         const __m128i HM3 = _mm_loadu_si128(HM_mm + 2*i + 3);
+
+         const __m128i XMASK1 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones);
+         X = _mm_slli_epi64(X, 1);
+         const __m128i XMASK2 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones);
+         X = _mm_slli_epi64(X, 1);
+
+         Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK1, XMASK1), HM0));
+         Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK1, XMASK1), HM1));
+         Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK2, XMASK2), HM2));
+         Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK2, XMASK2), HM3));
+         }
+
+      X = _mm_shuffle_epi32(Z, _MM_SHUFFLE(1, 0, 3, 2));
+      }
+
+   X = _mm_shuffle_epi8(X, BSWAP_MASK);
+   _mm_storeu_si128(reinterpret_cast<__m128i*>(x), X);
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/http_util/http_util.cpp b/third_party/botan/src/lib/utils/http_util/http_util.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/http_util/http_util.cpp
@@ -0,0 +1,267 @@
+/*
+* Sketchy HTTP client
+* (C) 2013,2016 Jack Lloyd
+*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/http_util.h>
+#include <botan/parsing.h>
+#include <botan/hex.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/socket.h>
+#include <botan/internal/stl_util.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace HTTP {
+
+namespace {
+
+/*
+* Connect to a host, write some bytes, then read until the server
+* closes the socket.
+*/
+std::string http_transact(const std::string& hostname,
+		                  const std::string& service,
+                          const std::string& message,
+                          std::chrono::milliseconds timeout)
+   {
+   std::unique_ptr<OS::Socket> socket;
+
+   const std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
+
+   try
+      {
+      socket = OS::open_socket(hostname, service, timeout);
+      if(!socket)
+         throw Not_Implemented("No socket support enabled in build");
+      }
+   catch(std::exception& e)
+      {
+      throw HTTP_Error("HTTP connection to " + hostname + " failed: " + e.what());
+      }
+
+   // Blocks until entire message has been written
+   socket->write(cast_char_ptr_to_uint8(message.data()),
+                 message.size());
+
+   if(std::chrono::system_clock::now() - start_time > timeout)
+      throw HTTP_Error("Timeout during writing message body");
+
+   std::ostringstream oss;
+   std::vector<uint8_t> buf(BOTAN_DEFAULT_BUFFER_SIZE);
+   while(true)
+      {
+      const size_t got = socket->read(buf.data(), buf.size());
+      if(got == 0) // EOF
+         break;
+
+      if(std::chrono::system_clock::now() - start_time > timeout)
+         throw HTTP_Error("Timeout while reading message body");
+
+      oss.write(cast_uint8_ptr_to_char(buf.data()),
+                static_cast<std::streamsize>(got));
+      }
+
+   return oss.str();
+   }
+
+}
+
+std::string url_encode(const std::string& in)
+   {
+   std::ostringstream out;
+
+   for(auto c : in)
+      {
+      if(c >= 'A' && c <= 'Z')
+         out << c;
+      else if(c >= 'a' && c <= 'z')
+         out << c;
+      else if(c >= '0' && c <= '9')
+         out << c;
+      else if(c == '-' || c == '_' || c == '.' || c == '~')
+         out << c;
+      else
+         out << '%' << hex_encode(cast_char_ptr_to_uint8(&c), 1);
+      }
+
+   return out.str();
+   }
+
+std::ostream& operator<<(std::ostream& o, const Response& resp)
+   {
+   o << "HTTP " << resp.status_code() << " " << resp.status_message() << "\n";
+   for(auto h : resp.headers())
+      o << "Header '" << h.first << "' = '" << h.second << "'\n";
+   o << "Body " << std::to_string(resp.body().size()) << " bytes:\n";
+   o.write(cast_uint8_ptr_to_char(resp.body().data()), resp.body().size());
+   return o;
+   }
+
+Response http_sync(http_exch_fn http_transact,
+                   const std::string& verb,
+                   const std::string& url,
+                   const std::string& content_type,
+                   const std::vector<uint8_t>& body,
+                   size_t allowable_redirects)
+   {
+   if(url.empty())
+      throw HTTP_Error("URL empty");
+
+   const auto protocol_host_sep = url.find("://");
+   if(protocol_host_sep == std::string::npos)
+      throw HTTP_Error("Invalid URL '" + url + "'");
+
+   const auto host_loc_sep = url.find('/', protocol_host_sep + 3);
+
+   std::string hostname, loc, service;
+
+   if(host_loc_sep == std::string::npos)
+      {
+      hostname = url.substr(protocol_host_sep + 3, std::string::npos);
+      loc = "/";
+      }
+   else
+      {
+      hostname = url.substr(protocol_host_sep + 3, host_loc_sep-protocol_host_sep-3);
+      loc = url.substr(host_loc_sep, std::string::npos);
+      }
+
+   const auto port_sep = hostname.find(":");
+   if(port_sep == std::string::npos)
+      {
+      service = "http";
+      // hostname not modified
+      }
+   else
+      {
+      service = hostname.substr(port_sep + 1, std::string::npos);
+      hostname = hostname.substr(0, port_sep);
+      }
+
+   std::ostringstream outbuf;
+
+   outbuf << verb << " " << loc << " HTTP/1.0\r\n";
+   outbuf << "Host: " << hostname << "\r\n";
+
+   if(verb == "GET")
+      {
+      outbuf << "Accept: */*\r\n";
+      outbuf << "Cache-Control: no-cache\r\n";
+      }
+   else if(verb == "POST")
+      outbuf << "Content-Length: " << body.size() << "\r\n";
+
+   if(!content_type.empty())
+      outbuf << "Content-Type: " << content_type << "\r\n";
+   outbuf << "Connection: close\r\n\r\n";
+   outbuf.write(cast_uint8_ptr_to_char(body.data()), body.size());
+
+   std::istringstream io(http_transact(hostname, service, outbuf.str()));
+
+   std::string line1;
+   std::getline(io, line1);
+   if(!io || line1.empty())
+      throw HTTP_Error("No response");
+
+   std::stringstream response_stream(line1);
+   std::string http_version;
+   unsigned int status_code;
+   std::string status_message;
+
+   response_stream >> http_version >> status_code;
+
+   std::getline(response_stream, status_message);
+
+   if(!response_stream || http_version.substr(0,5) != "HTTP/")
+      throw HTTP_Error("Not an HTTP response");
+
+   std::map<std::string, std::string> headers;
+   std::string header_line;
+   while (std::getline(io, header_line) && header_line != "\r")
+      {
+      auto sep = header_line.find(": ");
+      if(sep == std::string::npos || sep > header_line.size() - 2)
+         throw HTTP_Error("Invalid HTTP header " + header_line);
+      const std::string key = header_line.substr(0, sep);
+
+      if(sep + 2 < header_line.size() - 1)
+         {
+         const std::string val = header_line.substr(sep + 2, (header_line.size() - 1) - (sep + 2));
+         headers[key] = val;
+         }
+      }
+
+   if(status_code == 301 && headers.count("Location"))
+      {
+      if(allowable_redirects == 0)
+         throw HTTP_Error("HTTP redirection count exceeded");
+      return GET_sync(headers["Location"], allowable_redirects - 1);
+      }
+
+   std::vector<uint8_t> resp_body;
+   std::vector<uint8_t> buf(4096);
+   while(io.good())
+      {
+      io.read(cast_uint8_ptr_to_char(buf.data()), buf.size());
+      const size_t got = static_cast<size_t>(io.gcount());
+      resp_body.insert(resp_body.end(), buf.data(), &buf[got]);
+      }
+
+   const std::string header_size = search_map(headers, std::string("Content-Length"));
+
+   if(!header_size.empty())
+      {
+      if(resp_body.size() != to_u32bit(header_size))
+         throw HTTP_Error("Content-Length disagreement, header says " +
+                          header_size + " got " + std::to_string(resp_body.size()));
+      }
+
+   return Response(status_code, status_message, resp_body, headers);
+   }
+
+Response http_sync(const std::string& verb,
+                   const std::string& url,
+                   const std::string& content_type,
+                   const std::vector<uint8_t>& body,
+                   size_t allowable_redirects,
+                   std::chrono::milliseconds timeout)
+   {
+   auto transact_with_timeout =
+      [timeout](const std::string& hostname, const std::string& service, const std::string& message)
+      {
+      return http_transact(hostname, service, message, timeout);
+      };
+
+   return http_sync(
+      transact_with_timeout,
+      verb,
+      url,
+      content_type,
+      body,
+      allowable_redirects);
+   }
+
+Response GET_sync(const std::string& url,
+                  size_t allowable_redirects,
+                  std::chrono::milliseconds timeout)
+   {
+   return http_sync("GET", url, "", std::vector<uint8_t>(), allowable_redirects, timeout);
+   }
+
+Response POST_sync(const std::string& url,
+                   const std::string& content_type,
+                   const std::vector<uint8_t>& body,
+                   size_t allowable_redirects,
+                   std::chrono::milliseconds timeout)
+   {
+   return http_sync("POST", url, content_type, body, allowable_redirects, timeout);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/utils/http_util/http_util.h b/third_party/botan/src/lib/utils/http_util/http_util.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/http_util/http_util.h
@@ -0,0 +1,107 @@
+/*
+* HTTP utilities
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_URLGET_H_
+#define BOTAN_UTILS_URLGET_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <vector>
+#include <map>
+#include <string>
+#include <functional>
+#include <chrono>
+
+BOTAN_FUTURE_INTERNAL_HEADER(http_util.h)
+
+namespace Botan {
+
+namespace HTTP {
+
+/**
+* HTTP_Error Exception
+*/
+class BOTAN_PUBLIC_API(2,0) HTTP_Error final : public Exception
+   {
+   public:
+      explicit HTTP_Error(const std::string& msg) :
+         Exception("HTTP error " + msg)
+         {}
+
+      ErrorType error_type() const noexcept override { return ErrorType::HttpError; }
+
+   };
+
+class Response final
+   {
+   public:
+      Response() : m_status_code(0), m_status_message("Uninitialized") {}
+
+      Response(unsigned int status_code, const std::string& status_message,
+               const std::vector<uint8_t>& body,
+               const std::map<std::string, std::string>& headers) :
+         m_status_code(status_code),
+         m_status_message(status_message),
+         m_body(body),
+         m_headers(headers) {}
+
+      unsigned int status_code() const { return m_status_code; }
+
+      const std::vector<uint8_t>& body() const { return m_body; }
+
+      const std::map<std::string, std::string>& headers() const { return m_headers; }
+
+      std::string status_message() const { return m_status_message; }
+
+      void throw_unless_ok()
+         {
+         if(status_code() != 200)
+            throw HTTP_Error(status_message());
+         }
+
+   private:
+      unsigned int m_status_code;
+      std::string m_status_message;
+      std::vector<uint8_t> m_body;
+      std::map<std::string, std::string> m_headers;
+   };
+
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& o, const Response& resp);
+
+typedef std::function<std::string (const std::string&, const std::string&, const std::string&)> http_exch_fn;
+
+BOTAN_PUBLIC_API(2,0) Response http_sync(http_exch_fn fn,
+                                         const std::string& verb,
+                                         const std::string& url,
+                                         const std::string& content_type,
+                                         const std::vector<uint8_t>& body,
+                                         size_t allowable_redirects);
+
+BOTAN_PUBLIC_API(2,0) Response http_sync(const std::string& verb,
+                                         const std::string& url,
+                                         const std::string& content_type,
+                                         const std::vector<uint8_t>& body,
+                                         size_t allowable_redirects,
+                                         std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) Response GET_sync(const std::string& url,
+                                        size_t allowable_redirects = 1,
+                                        std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) Response POST_sync(const std::string& url,
+                                         const std::string& content_type,
+                                         const std::vector<uint8_t>& body,
+                                         size_t allowable_redirects = 1,
+                                         std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) std::string url_encode(const std::string& url);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/loadstor.h b/third_party/botan/src/lib/utils/loadstor.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/loadstor.h
@@ -0,0 +1,701 @@
+/*
+* Load/Store Operators
+* (C) 1999-2007,2015,2017 Jack Lloyd
+*     2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LOAD_STORE_H_
+#define BOTAN_LOAD_STORE_H_
+
+#include <botan/types.h>
+#include <botan/bswap.h>
+#include <botan/mem_ops.h>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(loadstor.h)
+
+#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+   #define BOTAN_ENDIAN_N2L(x) reverse_bytes(x)
+   #define BOTAN_ENDIAN_L2N(x) reverse_bytes(x)
+   #define BOTAN_ENDIAN_N2B(x) (x)
+   #define BOTAN_ENDIAN_B2N(x) (x)
+
+#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+   #define BOTAN_ENDIAN_N2L(x) (x)
+   #define BOTAN_ENDIAN_L2N(x) (x)
+   #define BOTAN_ENDIAN_N2B(x) reverse_bytes(x)
+   #define BOTAN_ENDIAN_B2N(x) reverse_bytes(x)
+
+#endif
+
+namespace Botan {
+
+/**
+* Byte extraction
+* @param byte_num which byte to extract, 0 == highest byte
+* @param input the value to extract from
+* @return byte byte_num of input
+*/
+template<typename T> inline constexpr uint8_t get_byte(size_t byte_num, T input)
+   {
+   return static_cast<uint8_t>(
+      input >> (((~byte_num)&(sizeof(T)-1)) << 3)
+      );
+   }
+
+/**
+* Make a uint16_t from two bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @return i0 || i1
+*/
+inline constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
+   {
+   return static_cast<uint16_t>((static_cast<uint16_t>(i0) << 8) | i1);
+   }
+
+/**
+* Make a uint32_t from four bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @param i2 the third byte
+* @param i3 the fourth byte
+* @return i0 || i1 || i2 || i3
+*/
+inline constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
+   {
+   return ((static_cast<uint32_t>(i0) << 24) |
+           (static_cast<uint32_t>(i1) << 16) |
+           (static_cast<uint32_t>(i2) <<  8) |
+           (static_cast<uint32_t>(i3)));
+   }
+
+/**
+* Make a uint64_t from eight bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @param i2 the third byte
+* @param i3 the fourth byte
+* @param i4 the fifth byte
+* @param i5 the sixth byte
+* @param i6 the seventh byte
+* @param i7 the eighth byte
+* @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7
+*/
+inline constexpr uint64_t make_uint64(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3,
+                                      uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7)
+    {
+   return ((static_cast<uint64_t>(i0) << 56) |
+           (static_cast<uint64_t>(i1) << 48) |
+           (static_cast<uint64_t>(i2) << 40) |
+           (static_cast<uint64_t>(i3) << 32) |
+           (static_cast<uint64_t>(i4) << 24) |
+           (static_cast<uint64_t>(i5) << 16) |
+           (static_cast<uint64_t>(i6) <<  8) |
+           (static_cast<uint64_t>(i7)));
+    }
+
+/**
+* Load a big-endian word
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th T of in, as a big-endian value
+*/
+template<typename T>
+inline T load_be(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(T);
+   T out = 0;
+   for(size_t i = 0; i != sizeof(T); ++i)
+      out = static_cast<T>((out << 8) | in[i]);
+   return out;
+   }
+
+/**
+* Load a little-endian word
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th T of in, as a litte-endian value
+*/
+template<typename T>
+inline T load_le(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(T);
+   T out = 0;
+   for(size_t i = 0; i != sizeof(T); ++i)
+      out = (out << 8) | in[sizeof(T)-1-i];
+   return out;
+   }
+
+/**
+* Load a big-endian uint16_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint16_t of in, as a big-endian value
+*/
+template<>
+inline uint16_t load_be<uint16_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint16_t);
+
+#if defined(BOTAN_ENDIAN_N2B)
+   uint16_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2B(x);
+#else
+   return make_uint16(in[0], in[1]);
+#endif
+   }
+
+/**
+* Load a little-endian uint16_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint16_t of in, as a little-endian value
+*/
+template<>
+inline uint16_t load_le<uint16_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint16_t);
+
+#if defined(BOTAN_ENDIAN_N2L)
+   uint16_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2L(x);
+#else
+   return make_uint16(in[1], in[0]);
+#endif
+   }
+
+/**
+* Load a big-endian uint32_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint32_t of in, as a big-endian value
+*/
+template<>
+inline uint32_t load_be<uint32_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint32_t);
+#if defined(BOTAN_ENDIAN_N2B)
+   uint32_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2B(x);
+#else
+   return make_uint32(in[0], in[1], in[2], in[3]);
+#endif
+   }
+
+/**
+* Load a little-endian uint32_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint32_t of in, as a little-endian value
+*/
+template<>
+inline uint32_t load_le<uint32_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint32_t);
+#if defined(BOTAN_ENDIAN_N2L)
+   uint32_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2L(x);
+#else
+   return make_uint32(in[3], in[2], in[1], in[0]);
+#endif
+   }
+
+/**
+* Load a big-endian uint64_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint64_t of in, as a big-endian value
+*/
+template<>
+inline uint64_t load_be<uint64_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint64_t);
+#if defined(BOTAN_ENDIAN_N2B)
+   uint64_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2B(x);
+#else
+   return make_uint64(in[0], in[1], in[2], in[3],
+                      in[4], in[5], in[6], in[7]);
+#endif
+   }
+
+/**
+* Load a little-endian uint64_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint64_t of in, as a little-endian value
+*/
+template<>
+inline uint64_t load_le<uint64_t>(const uint8_t in[], size_t off)
+   {
+   in += off * sizeof(uint64_t);
+#if defined(BOTAN_ENDIAN_N2L)
+   uint64_t x;
+   typecast_copy(x, in);
+   return BOTAN_ENDIAN_N2L(x);
+#else
+   return make_uint64(in[7], in[6], in[5], in[4],
+                      in[3], in[2], in[1], in[0]);
+#endif
+   }
+
+/**
+* Load two little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[], T& x0, T& x1)
+   {
+   x0 = load_le<T>(in, 0);
+   x1 = load_le<T>(in, 1);
+   }
+
+/**
+* Load four little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[],
+                    T& x0, T& x1, T& x2, T& x3)
+   {
+   x0 = load_le<T>(in, 0);
+   x1 = load_le<T>(in, 1);
+   x2 = load_le<T>(in, 2);
+   x3 = load_le<T>(in, 3);
+   }
+
+/**
+* Load eight little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+* @param x4 where the fifth word will be written
+* @param x5 where the sixth word will be written
+* @param x6 where the seventh word will be written
+* @param x7 where the eighth word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[],
+                    T& x0, T& x1, T& x2, T& x3,
+                    T& x4, T& x5, T& x6, T& x7)
+   {
+   x0 = load_le<T>(in, 0);
+   x1 = load_le<T>(in, 1);
+   x2 = load_le<T>(in, 2);
+   x3 = load_le<T>(in, 3);
+   x4 = load_le<T>(in, 4);
+   x5 = load_le<T>(in, 5);
+   x6 = load_le<T>(in, 6);
+   x7 = load_le<T>(in, 7);
+   }
+
+/**
+* Load a variable number of little-endian words
+* @param out the output array of words
+* @param in the input array of bytes
+* @param count how many words are in in
+*/
+template<typename T>
+inline void load_le(T out[],
+                    const uint8_t in[],
+                    size_t count)
+   {
+   if(count > 0)
+      {
+#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+      typecast_copy(out, in, count);
+
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+      typecast_copy(out, in, count);
+
+      const size_t blocks = count - (count % 4);
+      const size_t left = count - blocks;
+
+      for(size_t i = 0; i != blocks; i += 4)
+         bswap_4(out + i);
+
+      for(size_t i = 0; i != left; ++i)
+         out[blocks+i] = reverse_bytes(out[blocks+i]);
+#else
+      for(size_t i = 0; i != count; ++i)
+         out[i] = load_le<T>(in, i);
+#endif
+      }
+   }
+
+/**
+* Load two big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[], T& x0, T& x1)
+   {
+   x0 = load_be<T>(in, 0);
+   x1 = load_be<T>(in, 1);
+   }
+
+/**
+* Load four big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[],
+                    T& x0, T& x1, T& x2, T& x3)
+   {
+   x0 = load_be<T>(in, 0);
+   x1 = load_be<T>(in, 1);
+   x2 = load_be<T>(in, 2);
+   x3 = load_be<T>(in, 3);
+   }
+
+/**
+* Load eight big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+* @param x4 where the fifth word will be written
+* @param x5 where the sixth word will be written
+* @param x6 where the seventh word will be written
+* @param x7 where the eighth word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[],
+                    T& x0, T& x1, T& x2, T& x3,
+                    T& x4, T& x5, T& x6, T& x7)
+   {
+   x0 = load_be<T>(in, 0);
+   x1 = load_be<T>(in, 1);
+   x2 = load_be<T>(in, 2);
+   x3 = load_be<T>(in, 3);
+   x4 = load_be<T>(in, 4);
+   x5 = load_be<T>(in, 5);
+   x6 = load_be<T>(in, 6);
+   x7 = load_be<T>(in, 7);
+   }
+
+/**
+* Load a variable number of big-endian words
+* @param out the output array of words
+* @param in the input array of bytes
+* @param count how many words are in in
+*/
+template<typename T>
+inline void load_be(T out[],
+                    const uint8_t in[],
+                    size_t count)
+   {
+   if(count > 0)
+      {
+#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+      typecast_copy(out, in, count);
+
+#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+      typecast_copy(out, in, count);
+      const size_t blocks = count - (count % 4);
+      const size_t left = count - blocks;
+
+      for(size_t i = 0; i != blocks; i += 4)
+         bswap_4(out + i);
+
+      for(size_t i = 0; i != left; ++i)
+         out[blocks+i] = reverse_bytes(out[blocks+i]);
+#else
+      for(size_t i = 0; i != count; ++i)
+         out[i] = load_be<T>(in, i);
+#endif
+      }
+   }
+
+/**
+* Store a big-endian uint16_t
+* @param in the input uint16_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint16_t in, uint8_t out[2])
+   {
+#if defined(BOTAN_ENDIAN_N2B)
+   uint16_t o = BOTAN_ENDIAN_N2B(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(0, in);
+   out[1] = get_byte(1, in);
+#endif
+   }
+
+/**
+* Store a little-endian uint16_t
+* @param in the input uint16_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint16_t in, uint8_t out[2])
+   {
+#if defined(BOTAN_ENDIAN_N2L)
+   uint16_t o = BOTAN_ENDIAN_N2L(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(1, in);
+   out[1] = get_byte(0, in);
+#endif
+   }
+
+/**
+* Store a big-endian uint32_t
+* @param in the input uint32_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint32_t in, uint8_t out[4])
+   {
+#if defined(BOTAN_ENDIAN_B2N)
+   uint32_t o = BOTAN_ENDIAN_B2N(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(0, in);
+   out[1] = get_byte(1, in);
+   out[2] = get_byte(2, in);
+   out[3] = get_byte(3, in);
+#endif
+   }
+
+/**
+* Store a little-endian uint32_t
+* @param in the input uint32_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint32_t in, uint8_t out[4])
+   {
+#if defined(BOTAN_ENDIAN_L2N)
+   uint32_t o = BOTAN_ENDIAN_L2N(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(3, in);
+   out[1] = get_byte(2, in);
+   out[2] = get_byte(1, in);
+   out[3] = get_byte(0, in);
+#endif
+   }
+
+/**
+* Store a big-endian uint64_t
+* @param in the input uint64_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint64_t in, uint8_t out[8])
+   {
+#if defined(BOTAN_ENDIAN_B2N)
+   uint64_t o = BOTAN_ENDIAN_B2N(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(0, in);
+   out[1] = get_byte(1, in);
+   out[2] = get_byte(2, in);
+   out[3] = get_byte(3, in);
+   out[4] = get_byte(4, in);
+   out[5] = get_byte(5, in);
+   out[6] = get_byte(6, in);
+   out[7] = get_byte(7, in);
+#endif
+   }
+
+/**
+* Store a little-endian uint64_t
+* @param in the input uint64_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint64_t in, uint8_t out[8])
+   {
+#if defined(BOTAN_ENDIAN_L2N)
+   uint64_t o = BOTAN_ENDIAN_L2N(in);
+   typecast_copy(out, o);
+#else
+   out[0] = get_byte(7, in);
+   out[1] = get_byte(6, in);
+   out[2] = get_byte(5, in);
+   out[3] = get_byte(4, in);
+   out[4] = get_byte(3, in);
+   out[5] = get_byte(2, in);
+   out[6] = get_byte(1, in);
+   out[7] = get_byte(0, in);
+#endif
+   }
+
+/**
+* Store two little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1)
+   {
+   store_le(x0, out + (0 * sizeof(T)));
+   store_le(x1, out + (1 * sizeof(T)));
+   }
+
+/**
+* Store two big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1)
+   {
+   store_be(x0, out + (0 * sizeof(T)));
+   store_be(x1, out + (1 * sizeof(T)));
+   }
+
+/**
+* Store four little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3)
+   {
+   store_le(x0, out + (0 * sizeof(T)));
+   store_le(x1, out + (1 * sizeof(T)));
+   store_le(x2, out + (2 * sizeof(T)));
+   store_le(x3, out + (3 * sizeof(T)));
+   }
+
+/**
+* Store four big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3)
+   {
+   store_be(x0, out + (0 * sizeof(T)));
+   store_be(x1, out + (1 * sizeof(T)));
+   store_be(x2, out + (2 * sizeof(T)));
+   store_be(x3, out + (3 * sizeof(T)));
+   }
+
+/**
+* Store eight little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+* @param x4 the fifth word
+* @param x5 the sixth word
+* @param x6 the seventh word
+* @param x7 the eighth word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3,
+                                 T x4, T x5, T x6, T x7)
+   {
+   store_le(x0, out + (0 * sizeof(T)));
+   store_le(x1, out + (1 * sizeof(T)));
+   store_le(x2, out + (2 * sizeof(T)));
+   store_le(x3, out + (3 * sizeof(T)));
+   store_le(x4, out + (4 * sizeof(T)));
+   store_le(x5, out + (5 * sizeof(T)));
+   store_le(x6, out + (6 * sizeof(T)));
+   store_le(x7, out + (7 * sizeof(T)));
+   }
+
+/**
+* Store eight big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+* @param x4 the fifth word
+* @param x5 the sixth word
+* @param x6 the seventh word
+* @param x7 the eighth word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3,
+                                 T x4, T x5, T x6, T x7)
+   {
+   store_be(x0, out + (0 * sizeof(T)));
+   store_be(x1, out + (1 * sizeof(T)));
+   store_be(x2, out + (2 * sizeof(T)));
+   store_be(x3, out + (3 * sizeof(T)));
+   store_be(x4, out + (4 * sizeof(T)));
+   store_be(x5, out + (5 * sizeof(T)));
+   store_be(x6, out + (6 * sizeof(T)));
+   store_be(x7, out + (7 * sizeof(T)));
+   }
+
+template<typename T>
+void copy_out_be(uint8_t out[], size_t out_bytes, const T in[])
+   {
+   while(out_bytes >= sizeof(T))
+      {
+      store_be(in[0], out);
+      out += sizeof(T);
+      out_bytes -= sizeof(T);
+      in += 1;
+   }
+
+   for(size_t i = 0; i != out_bytes; ++i)
+      out[i] = get_byte(i%8, in[0]);
+   }
+
+template<typename T, typename Alloc>
+void copy_out_vec_be(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in)
+   {
+   copy_out_be(out, out_bytes, in.data());
+   }
+
+template<typename T>
+void copy_out_le(uint8_t out[], size_t out_bytes, const T in[])
+   {
+   while(out_bytes >= sizeof(T))
+      {
+      store_le(in[0], out);
+      out += sizeof(T);
+      out_bytes -= sizeof(T);
+      in += 1;
+   }
+
+   for(size_t i = 0; i != out_bytes; ++i)
+      out[i] = get_byte(sizeof(T) - 1 - (i % 8), in[0]);
+   }
+
+template<typename T, typename Alloc>
+void copy_out_vec_le(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in)
+   {
+   copy_out_le(out, out_bytes, in.data());
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp b/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp
@@ -0,0 +1,75 @@
+/*
+* Mlock Allocator
+* (C) 2012,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/locking_allocator.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/mem_pool.h>
+
+namespace Botan {
+
+void* mlock_allocator::allocate(size_t num_elems, size_t elem_size)
+   {
+   if(!m_pool)
+      return nullptr;
+
+   const size_t n = num_elems * elem_size;
+   if(n / elem_size != num_elems)
+      return nullptr; // overflow!
+
+   return m_pool->allocate(n);
+   }
+
+bool mlock_allocator::deallocate(void* p, size_t num_elems, size_t elem_size) noexcept
+   {
+   if(!m_pool)
+      return false;
+
+   size_t n = num_elems * elem_size;
+
+   /*
+   We return nullptr in allocate if there was an overflow, so if an
+   overflow occurs here we know the pointer was not allocated by this pool.
+   */
+   if(n / elem_size != num_elems)
+      return false;
+
+   return m_pool->deallocate(p, n);
+   }
+
+mlock_allocator::mlock_allocator()
+   {
+   const size_t mem_to_lock = OS::get_memory_locking_limit();
+   const size_t page_size = OS::system_page_size();
+
+   if(mem_to_lock > 0 && mem_to_lock % page_size == 0)
+      {
+      m_locked_pages = OS::allocate_locked_pages(mem_to_lock / page_size);
+
+      if(m_locked_pages.size() > 0)
+         {
+         m_pool.reset(new Memory_Pool(m_locked_pages, page_size));
+         }
+      }
+   }
+
+mlock_allocator::~mlock_allocator()
+   {
+   if(m_pool)
+      {
+      m_pool.reset();
+      // OS::free_locked_pages scrubs the memory before free
+      OS::free_locked_pages(m_locked_pages);
+      }
+   }
+
+mlock_allocator& mlock_allocator::instance()
+   {
+   static mlock_allocator mlock;
+   return mlock;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h b/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h
@@ -0,0 +1,45 @@
+/*
+* Mlock Allocator
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MLOCK_ALLOCATOR_H_
+#define BOTAN_MLOCK_ALLOCATOR_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(locking_allocator.h)
+
+namespace Botan {
+
+class Memory_Pool;
+
+class BOTAN_PUBLIC_API(2,0) mlock_allocator final
+   {
+   public:
+      static mlock_allocator& instance();
+
+      void* allocate(size_t num_elems, size_t elem_size);
+
+      bool deallocate(void* p, size_t num_elems, size_t elem_size) noexcept;
+
+      mlock_allocator(const mlock_allocator&) = delete;
+
+      mlock_allocator& operator=(const mlock_allocator&) = delete;
+
+   private:
+      mlock_allocator();
+
+      ~mlock_allocator();
+
+      std::unique_ptr<Memory_Pool> m_pool;
+      std::vector<void*> m_locked_pages;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/mem_ops.cpp b/third_party/botan/src/lib/utils/mem_ops.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mem_ops.cpp
@@ -0,0 +1,68 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mem_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <cstdlib>
+#include <new>
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+  #include <botan/locking_allocator.h>
+#endif
+
+namespace Botan {
+
+BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size)
+   {
+   if(elems == 0 || elem_size == 0)
+      return nullptr;
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+   if(void* p = mlock_allocator::instance().allocate(elems, elem_size))
+      return p;
+#endif
+
+   void* ptr = std::calloc(elems, elem_size);
+   if(!ptr)
+      throw std::bad_alloc();
+   return ptr;
+   }
+
+void deallocate_memory(void* p, size_t elems, size_t elem_size)
+   {
+   if(p == nullptr)
+      return;
+
+   secure_scrub_memory(p, elems * elem_size);
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+   if(mlock_allocator::instance().deallocate(p, elems, elem_size))
+      return;
+#endif
+
+   std::free(p);
+   }
+
+void initialize_allocator()
+   {
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+   mlock_allocator::instance();
+#endif
+   }
+
+uint8_t ct_compare_u8(const uint8_t x[],
+                      const uint8_t y[],
+                      size_t len)
+   {
+   volatile uint8_t difference = 0;
+
+   for(size_t i = 0; i != len; ++i)
+      difference |= (x[i] ^ y[i]);
+
+   return CT::Mask<uint8_t>::is_zero(difference).value();
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/mem_ops.h b/third_party/botan/src/lib/utils/mem_ops.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mem_ops.h
@@ -0,0 +1,336 @@
+/*
+* Memory Operations
+* (C) 1999-2009,2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MEMORY_OPS_H_
+#define BOTAN_MEMORY_OPS_H_
+
+#include <botan/types.h>
+#include <cstring>
+#include <type_traits>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Allocate a memory buffer by some method. This should only be used for
+* primitive types (uint8_t, uint32_t, etc).
+*
+* @param elems the number of elements
+* @param elem_size the size of each element
+* @return pointer to allocated and zeroed memory, or throw std::bad_alloc on failure
+*/
+BOTAN_PUBLIC_API(2,3) BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size);
+
+/**
+* Free a pointer returned by allocate_memory
+* @param p the pointer returned by allocate_memory
+* @param elems the number of elements, as passed to allocate_memory
+* @param elem_size the size of each element, as passed to allocate_memory
+*/
+BOTAN_PUBLIC_API(2,3) void deallocate_memory(void* p, size_t elems, size_t elem_size);
+
+/**
+* Ensure the allocator is initialized
+*/
+void BOTAN_UNSTABLE_API initialize_allocator();
+
+class Allocator_Initializer
+   {
+   public:
+      Allocator_Initializer() { initialize_allocator(); }
+   };
+
+/**
+* Scrub memory contents in a way that a compiler should not elide,
+* using some system specific technique. Note that this function might
+* not zero the memory (for example, in some hypothetical
+* implementation it might combine the memory contents with the output
+* of a system PRNG), but if you can detect any difference in behavior
+* at runtime then the clearing is side-effecting and you can just
+* use `clear_mem`.
+*
+* Use this function to scrub memory just before deallocating it, or on
+* a stack buffer before returning from the function.
+*
+* @param ptr a pointer to memory to scrub
+* @param n the number of bytes pointed to by ptr
+*/
+BOTAN_PUBLIC_API(2,0) void secure_scrub_memory(void* ptr, size_t n);
+
+/**
+* Memory comparison, input insensitive
+* @param x a pointer to an array
+* @param y a pointer to another array
+* @param len the number of Ts in x and y
+* @return 0xFF iff x[i] == y[i] forall i in [0...n) or 0x00 otherwise
+*/
+BOTAN_PUBLIC_API(2,9) uint8_t ct_compare_u8(const uint8_t x[],
+                                            const uint8_t y[],
+                                            size_t len);
+
+/**
+* Memory comparison, input insensitive
+* @param x a pointer to an array
+* @param y a pointer to another array
+* @param len the number of Ts in x and y
+* @return true iff x[i] == y[i] forall i in [0...n)
+*/
+inline bool constant_time_compare(const uint8_t x[],
+                                  const uint8_t y[],
+                                  size_t len)
+   {
+   return ct_compare_u8(x, y, len) == 0xFF;
+   }
+
+/**
+* Zero out some bytes. Warning: use secure_scrub_memory instead if the
+* memory is about to be freed or otherwise the compiler thinks it can
+* elide the writes.
+*
+* @param ptr a pointer to memory to zero
+* @param bytes the number of bytes to zero in ptr
+*/
+inline void clear_bytes(void* ptr, size_t bytes)
+   {
+   if(bytes > 0)
+      {
+      std::memset(ptr, 0, bytes);
+      }
+   }
+
+/**
+* Zero memory before use. This simply calls memset and should not be
+* used in cases where the compiler cannot see the call as a
+* side-effecting operation (for example, if calling clear_mem before
+* deallocating memory, the compiler would be allowed to omit the call
+* to memset entirely under the as-if rule.)
+*
+* @param ptr a pointer to an array of Ts to zero
+* @param n the number of Ts pointed to by ptr
+*/
+template<typename T> inline void clear_mem(T* ptr, size_t n)
+   {
+   clear_bytes(ptr, sizeof(T)*n);
+   }
+
+// is_trivially_copyable is missing in g++ < 5.0
+#if (BOTAN_GCC_VERSION > 0 && BOTAN_GCC_VERSION < 500)
+#define BOTAN_IS_TRIVIALLY_COPYABLE(T) true
+#else
+#define BOTAN_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
+#endif
+
+/**
+* Copy memory
+* @param out the destination array
+* @param in the source array
+* @param n the number of elements of in/out
+*/
+template<typename T> inline void copy_mem(T* out, const T* in, size_t n)
+   {
+   static_assert(std::is_trivial<typename std::decay<T>::type>::value, "");
+   BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr,
+                            "If n > 0 then args are not null");
+
+   if(in != nullptr && out != nullptr && n > 0)
+      {
+      std::memmove(out, in, sizeof(T)*n);
+      }
+   }
+
+template<typename T> inline void typecast_copy(uint8_t out[], T in[], size_t N)
+   {
+   static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(T), "");
+   std::memcpy(out, in, sizeof(T)*N);
+   }
+
+template<typename T> inline void typecast_copy(T out[], const uint8_t in[], size_t N)
+   {
+   static_assert(std::is_trivial<T>::value, "");
+   std::memcpy(out, in, sizeof(T)*N);
+   }
+
+template<typename T> inline void typecast_copy(uint8_t out[], T in)
+   {
+   typecast_copy(out, &in, 1);
+   }
+
+template<typename T> inline void typecast_copy(T& out, const uint8_t in[])
+   {
+   static_assert(std::is_trivial<typename std::decay<T>::type>::value, "");
+   typecast_copy(&out, in, 1);
+   }
+
+template <class To, class From> inline To typecast_copy(const From *src) noexcept
+   {
+   static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(From) && std::is_trivial<To>::value, "");
+   To dst;
+   std::memcpy(&dst, src, sizeof(To));
+   return dst;
+   }
+
+/**
+* Set memory to a fixed value
+* @param ptr a pointer to an array of bytes
+* @param n the number of Ts pointed to by ptr
+* @param val the value to set each byte to
+*/
+inline void set_mem(uint8_t* ptr, size_t n, uint8_t val)
+   {
+   if(n > 0)
+      {
+      std::memset(ptr, val, n);
+      }
+   }
+
+inline const uint8_t* cast_char_ptr_to_uint8(const char* s)
+   {
+   return reinterpret_cast<const uint8_t*>(s);
+   }
+
+inline const char* cast_uint8_ptr_to_char(const uint8_t* b)
+   {
+   return reinterpret_cast<const char*>(b);
+   }
+
+inline uint8_t* cast_char_ptr_to_uint8(char* s)
+   {
+   return reinterpret_cast<uint8_t*>(s);
+   }
+
+inline char* cast_uint8_ptr_to_char(uint8_t* b)
+   {
+   return reinterpret_cast<char*>(b);
+   }
+
+/**
+* Memory comparison, input insensitive
+* @param p1 a pointer to an array
+* @param p2 a pointer to another array
+* @param n the number of Ts in p1 and p2
+* @return true iff p1[i] == p2[i] forall i in [0...n)
+*/
+template<typename T> inline bool same_mem(const T* p1, const T* p2, size_t n)
+   {
+   volatile T difference = 0;
+
+   for(size_t i = 0; i != n; ++i)
+      difference |= (p1[i] ^ p2[i]);
+
+   return difference == 0;
+   }
+
+/**
+* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
+* @param out the input/output buffer
+* @param in the read-only input buffer
+* @param length the length of the buffers
+*/
+inline void xor_buf(uint8_t out[],
+                    const uint8_t in[],
+                    size_t length)
+   {
+   const size_t blocks = length - (length % 32);
+
+   for(size_t i = 0; i != blocks; i += 32)
+      {
+      uint64_t x[4];
+      uint64_t y[4];
+
+      typecast_copy(x, out + i, 4);
+      typecast_copy(y, in + i, 4);
+
+      x[0] ^= y[0];
+      x[1] ^= y[1];
+      x[2] ^= y[2];
+      x[3] ^= y[3];
+
+      typecast_copy(out + i, x, 4);
+      }
+
+   for(size_t i = blocks; i != length; ++i)
+      {
+      out[i] ^= in[i];
+      }
+   }
+
+/**
+* XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length
+* @param out the output buffer
+* @param in the first input buffer
+* @param in2 the second output buffer
+* @param length the length of the three buffers
+*/
+inline void xor_buf(uint8_t out[],
+                    const uint8_t in[],
+                    const uint8_t in2[],
+                    size_t length)
+   {
+   const size_t blocks = length - (length % 32);
+
+   for(size_t i = 0; i != blocks; i += 32)
+      {
+      uint64_t x[4];
+      uint64_t y[4];
+
+      typecast_copy(x, in + i, 4);
+      typecast_copy(y, in2 + i, 4);
+
+      x[0] ^= y[0];
+      x[1] ^= y[1];
+      x[2] ^= y[2];
+      x[3] ^= y[3];
+
+      typecast_copy(out + i, x, 4);
+      }
+
+   for(size_t i = blocks; i != length; ++i)
+      {
+      out[i] = in[i] ^ in2[i];
+      }
+   }
+
+template<typename Alloc, typename Alloc2>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+             const std::vector<uint8_t, Alloc2>& in,
+             size_t n)
+   {
+   xor_buf(out.data(), in.data(), n);
+   }
+
+template<typename Alloc>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+             const uint8_t* in,
+             size_t n)
+   {
+   xor_buf(out.data(), in, n);
+   }
+
+template<typename Alloc, typename Alloc2>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+             const uint8_t* in,
+             const std::vector<uint8_t, Alloc2>& in2,
+             size_t n)
+   {
+   xor_buf(out.data(), in, in2.data(), n);
+   }
+
+template<typename Alloc, typename Alloc2>
+std::vector<uint8_t, Alloc>&
+operator^=(std::vector<uint8_t, Alloc>& out,
+           const std::vector<uint8_t, Alloc2>& in)
+   {
+   if(out.size() < in.size())
+      out.resize(in.size());
+
+   xor_buf(out.data(), in.data(), in.size());
+   return out;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp b/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp
@@ -0,0 +1,435 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mem_pool.h>
+#include <botan/mem_ops.h>
+#include <algorithm>
+
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+   #include <botan/internal/os_utils.h>
+#endif
+
+namespace Botan {
+
+/*
+* Memory pool theory of operation
+*
+* This allocator is not useful for general purpose but works well within the
+* context of allocating cryptographic keys. It makes several assumptions which
+* don't work for implementing malloc but simplify and speed up the implementation:
+*
+* - There is some set of pages, which cannot be expanded later. These are pages
+*   which were allocated, mlocked and passed to the Memory_Pool constructor.
+*
+* - The allocator is allowed to return null anytime it feels like not servicing
+*   a request, in which case the request will be sent to calloc instead. In
+*   particular, requests which are too small or too large are rejected.
+*
+* - Most allocations are powers of 2, the remainder are usually a multiple of 8
+*
+* - Free requests include the size of the allocation, so there is no need to
+*   track this within the pool.
+*
+* - Alignment is important to the caller. For this allocator, any allocation of
+*   size N is aligned evenly at N bytes.
+*
+* Initially each page is in the free page list. Each page is used for just one
+* size of allocation, with requests bucketed into a small number of common
+* sizes. If the allocation would be too big or too small it is rejected by the pool.
+*
+* The free list is maintained by a bitmap, one per page/Bucket. Since each
+* Bucket only maintains objects of a single size, each bit set or clear
+* indicates the status of one object.
+*
+* An allocation walks the list of buckets and asks each in turn if there is
+* space. If a Bucket does not have any space, it sets a boolean flag m_is_full
+* so that it does not need to rescan when asked again. The flag is cleared on
+* first free from that bucket. If no bucket has space, but there are some free
+* pages left, a free page is claimed as a new Bucket for that size. In this case
+* it is pushed to the front of the list so it is first in line to service new
+* requests.
+*
+* A deallocation also walks the list of buckets for the size and asks each
+* Bucket in turn if it recognizes the pointer. When a Bucket becomes empty as a
+* result of a deallocation, it is recycled back into the free pool. When this
+* happens, the Buckets page goes to the end of the free list. All pages on the
+* free list are marked in the MMU as noaccess, so anything touching them will
+* immediately crash. They are only marked R/W once placed into a new bucket.
+* Making the free list FIFO maximizes the time between the last free of a bucket
+* and that page being writable again, maximizing chances of crashing after a
+* use-after-free.
+*
+* Future work
+* -------------
+*
+* The allocator is protected by a global lock. It would be good to break this
+* up, since almost all of the work can actually be done in parallel especially
+* when allocating objects of different sizes (which can't possibly share a
+* bucket).
+*
+* It may be worthwhile to optimize deallocation by storing the Buckets in order
+* (by pointer value) which would allow binary search to find the owning bucket.
+*
+* A useful addition would be to randomize the allocations. Memory_Pool would be
+* changed to receive also a RandomNumberGenerator& object (presumably the system
+* RNG, or maybe a ChaCha_RNG seeded with system RNG). Then the bucket to use and
+* the offset within the bucket would be chosen randomly, instead of using first fit.
+*
+* Right now we don't make any provision for threading, so if two threads both
+* allocate 32 byte values one after the other, the two allocations will likely
+* share a cache line. Ensuring that distinct threads will (tend to) use distinct
+* buckets would reduce this.
+*
+* Supporting a realloc-style API may be useful.
+*/
+
+namespace {
+
+size_t choose_bucket(size_t n)
+   {
+   const size_t MINIMUM_ALLOCATION = 16;
+   const size_t MAXIMUM_ALLOCATION = 256;
+
+   if(n < MINIMUM_ALLOCATION || n > MAXIMUM_ALLOCATION)
+      return 0;
+
+   // Need to tune these
+
+   const size_t buckets[] = {
+      16, 24, 32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 0,
+   };
+
+   for(size_t i = 0; buckets[i]; ++i)
+      {
+      if(n <= buckets[i])
+         {
+         return buckets[i];
+         }
+      }
+
+   return 0;
+   }
+
+inline bool ptr_in_pool(const void* pool_ptr, size_t poolsize,
+                        const void* buf_ptr, size_t bufsize)
+   {
+   const uintptr_t pool = reinterpret_cast<uintptr_t>(pool_ptr);
+   const uintptr_t buf = reinterpret_cast<uintptr_t>(buf_ptr);
+   return (buf >= pool) && (buf + bufsize <= pool + poolsize);
+   }
+
+// return index of first set bit
+template<typename T>
+size_t find_set_bit(T b)
+   {
+   size_t s = 8*sizeof(T) / 2;
+   size_t bit = 0;
+
+   // In this context we don't need to be const-time
+   while(s > 0)
+      {
+      const T mask = (static_cast<T>(1) << s) - 1;
+      if((b & mask) == 0)
+         {
+         bit += s;
+         b >>= s;
+         }
+      s /= 2;
+      }
+
+   return bit;
+   }
+
+class BitMap final
+   {
+   public:
+      BitMap(size_t bits) : m_len(bits)
+         {
+         m_bits.resize((bits + BITMASK_BITS - 1) / BITMASK_BITS);
+         m_main_mask = static_cast<bitmask_type>(~0);
+         m_last_mask = m_main_mask;
+
+         if(bits % BITMASK_BITS != 0)
+            m_last_mask = (static_cast<bitmask_type>(1) << (bits % BITMASK_BITS)) - 1;
+         }
+
+      bool find_free(size_t* bit);
+
+      void free(size_t bit)
+         {
+         BOTAN_ASSERT_NOMSG(bit <= m_len);
+         const size_t w = bit / BITMASK_BITS;
+         BOTAN_ASSERT_NOMSG(w < m_bits.size());
+         const bitmask_type mask = static_cast<bitmask_type>(1) << (bit % BITMASK_BITS);
+         m_bits[w] = m_bits[w] & (~mask);
+         }
+
+      bool empty() const
+         {
+         for(size_t i = 0; i != m_bits.size(); ++i)
+            {
+            if(m_bits[i] != 0)
+               {
+               return false;
+               }
+            }
+
+         return true;
+         }
+
+  private:
+#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)
+      typedef uint8_t bitmask_type;
+      enum { BITMASK_BITS = 8 };
+#else
+      typedef word bitmask_type;
+      enum { BITMASK_BITS = BOTAN_MP_WORD_BITS };
+#endif
+
+      size_t m_len;
+      bitmask_type m_main_mask;
+      bitmask_type m_last_mask;
+      std::vector<bitmask_type> m_bits;
+   };
+
+bool BitMap::find_free(size_t* bit)
+   {
+   for(size_t i = 0; i != m_bits.size(); ++i)
+      {
+      const bitmask_type mask = (i == m_bits.size() - 1) ? m_last_mask : m_main_mask;
+      if((m_bits[i] & mask) != mask)
+         {
+         size_t free_bit = find_set_bit(~m_bits[i]);
+         const bitmask_type bmask = static_cast<bitmask_type>(1) << (free_bit % BITMASK_BITS);
+         BOTAN_ASSERT_NOMSG((m_bits[i] & bmask) == 0);
+         m_bits[i] |= bmask;
+         *bit = BITMASK_BITS*i + free_bit;
+         return true;
+         }
+      }
+
+   return false;
+   }
+
+}
+
+class Bucket final
+   {
+   public:
+      Bucket(uint8_t* mem, size_t mem_size, size_t item_size) :
+         m_item_size(item_size),
+         m_page_size(mem_size),
+         m_range(mem),
+         m_bitmap(mem_size / item_size),
+         m_is_full(false)
+         {
+         }
+
+      uint8_t* alloc()
+         {
+         if(m_is_full)
+            {
+            // I know I am full
+            return nullptr;
+            }
+
+         size_t offset;
+         if(!m_bitmap.find_free(&offset))
+            {
+            // I just found out I am full
+            m_is_full = true;
+            return nullptr;
+            }
+
+         BOTAN_ASSERT(offset * m_item_size < m_page_size, "Offset is in range");
+         return m_range + m_item_size*offset;
+         }
+
+      bool free(void* p)
+         {
+         if(!in_this_bucket(p))
+            return false;
+
+         /*
+         Zero also any trailing bytes, which should not have been written to,
+         but maybe the user was bad and wrote past the end.
+         */
+         std::memset(p, 0, m_item_size);
+
+         const size_t offset = (reinterpret_cast<uintptr_t>(p) - reinterpret_cast<uintptr_t>(m_range)) / m_item_size;
+
+         m_bitmap.free(offset);
+         m_is_full = false;
+
+         return true;
+         }
+
+      bool in_this_bucket(void* p) const
+         {
+         return ptr_in_pool(m_range, m_page_size, p, m_item_size);
+         }
+
+      bool empty() const
+         {
+         return m_bitmap.empty();
+         }
+
+      uint8_t* ptr() const
+         {
+         return m_range;
+         }
+
+   private:
+      size_t m_item_size;
+      size_t m_page_size;
+      uint8_t* m_range;
+      BitMap m_bitmap;
+      bool m_is_full;
+   };
+
+Memory_Pool::Memory_Pool(const std::vector<void*>& pages, size_t page_size) :
+   m_page_size(page_size)
+   {
+   m_min_page_ptr = ~static_cast<uintptr_t>(0);
+   m_max_page_ptr = 0;
+
+   for(size_t i = 0; i != pages.size(); ++i)
+      {
+      const uintptr_t p = reinterpret_cast<uintptr_t>(pages[i]);
+
+      m_min_page_ptr = std::min(p, m_min_page_ptr);
+      m_max_page_ptr = std::max(p, m_max_page_ptr);
+
+      clear_bytes(pages[i], m_page_size);
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+      OS::page_prohibit_access(pages[i]);
+#endif
+      m_free_pages.push_back(static_cast<uint8_t*>(pages[i]));
+      }
+
+   /*
+   Right now this points to the start of the last page, adjust it to instead
+   point to the first byte of the following page
+   */
+   m_max_page_ptr += page_size;
+   }
+
+Memory_Pool::~Memory_Pool()
+   {
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+   for(size_t i = 0; i != m_free_pages.size(); ++i)
+      {
+      OS::page_allow_access(m_free_pages[i]);
+      }
+#endif
+   }
+
+void* Memory_Pool::allocate(size_t n)
+   {
+   if(n > m_page_size)
+      return nullptr;
+
+   const size_t n_bucket = choose_bucket(n);
+
+   if(n_bucket > 0)
+      {
+      lock_guard_type<mutex_type> lock(m_mutex);
+
+      std::deque<Bucket>& buckets = m_buckets_for[n_bucket];
+
+      /*
+      It would be optimal to pick the bucket with the most usage,
+      since a bucket with say 1 item allocated out of it has a high
+      chance of becoming later freed and then the whole page can be
+      recycled.
+      */
+      for(auto& bucket : buckets)
+         {
+         if(uint8_t* p = bucket.alloc())
+            return p;
+
+         // If the bucket is full, maybe move it to the end of the list?
+         // Otoh bucket search should be very fast
+         }
+
+      if(m_free_pages.size() > 0)
+         {
+         uint8_t* ptr = m_free_pages[0];
+         m_free_pages.pop_front();
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+         OS::page_allow_access(ptr);
+#endif
+         buckets.push_front(Bucket(ptr, m_page_size, n_bucket));
+         void* p = buckets[0].alloc();
+         BOTAN_ASSERT_NOMSG(p != nullptr);
+         return p;
+         }
+      }
+
+   // out of room
+   return nullptr;
+   }
+
+bool Memory_Pool::deallocate(void* p, size_t len) noexcept
+   {
+   // Do a fast range check first, before taking the lock
+   const uintptr_t p_val = reinterpret_cast<uintptr_t>(p);
+   if(p_val < m_min_page_ptr || p_val > m_max_page_ptr)
+      return false;
+
+   const size_t n_bucket = choose_bucket(len);
+
+   if(n_bucket != 0)
+      {
+      try
+         {
+         lock_guard_type<mutex_type> lock(m_mutex);
+
+         std::deque<Bucket>& buckets = m_buckets_for[n_bucket];
+
+         for(size_t i = 0; i != buckets.size(); ++i)
+            {
+            Bucket& bucket = buckets[i];
+            if(bucket.free(p))
+               {
+               if(bucket.empty())
+                  {
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+                  OS::page_prohibit_access(bucket.ptr());
+#endif
+                  m_free_pages.push_back(bucket.ptr());
+
+                  if(i != buckets.size() - 1)
+                     std::swap(buckets.back(), buckets[i]);
+                  buckets.pop_back();
+                  }
+               return true;
+               }
+            }
+         }
+      catch(...)
+         {
+         /*
+         * The only exception throws that can occur in the above code are from
+         * either the STL or BOTAN_ASSERT failures. In either case, such an
+         * error indicates a logic error or data corruption in the memory
+         * allocator such that it is no longer safe to continue executing.
+         *
+         * Since this function is noexcept, simply letting the exception escape
+         * is sufficient for terminate to be called. However in this scenario
+         * it is implementation defined if any stack unwinding is performed.
+         * Since stack unwinding could cause further memory deallocations this
+         * could result in further corruption in this allocator state. To prevent
+         * this, call terminate directly.
+         */
+         std::terminate();
+         }
+      }
+
+   return false;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/mem_pool/mem_pool.h b/third_party/botan/src/lib/utils/mem_pool/mem_pool.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mem_pool/mem_pool.h
@@ -0,0 +1,58 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MEM_POOL_H_
+#define BOTAN_MEM_POOL_H_
+
+#include <botan/types.h>
+#include <botan/mutex.h>
+#include <vector>
+#include <deque>
+#include <map>
+
+namespace Botan {
+
+class Bucket;
+
+class BOTAN_TEST_API Memory_Pool final
+   {
+   public:
+      /**
+      * Initialize a memory pool. The memory is not owned by *this,
+      * it must be freed by the caller.
+      * @param pages a list of pages to allocate from
+      * @param page_size the system page size, each page should
+      *        point to exactly this much memory.
+      */
+      Memory_Pool(const std::vector<void*>& pages,
+                  size_t page_size);
+
+      ~Memory_Pool();
+
+      void* allocate(size_t size);
+
+      bool deallocate(void* p, size_t size) noexcept;
+
+      Memory_Pool(const Memory_Pool&) = delete;
+      Memory_Pool(Memory_Pool&&) = delete;
+
+      Memory_Pool& operator=(const Memory_Pool&) = delete;
+      Memory_Pool& operator=(Memory_Pool&&) = delete;
+
+   private:
+      const size_t m_page_size = 0;
+
+      mutex_type m_mutex;
+
+      std::deque<uint8_t*> m_free_pages;
+      std::map<size_t, std::deque<Bucket>> m_buckets_for;
+      uintptr_t m_min_page_ptr;
+      uintptr_t m_max_page_ptr;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/mul128.h b/third_party/botan/src/lib/utils/mul128.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mul128.h
@@ -0,0 +1,125 @@
+/*
+* 64x64->128 bit multiply operation
+* (C) 2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_MUL128_H_
+#define BOTAN_UTIL_MUL128_H_
+
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mul128.h)
+
+namespace Botan {
+
+#if defined(__SIZEOF_INT128__) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+   #define BOTAN_TARGET_HAS_NATIVE_UINT128
+
+   // Prefer TI mode over __int128 as GCC rejects the latter in pendantic mode
+   #if defined(__GNUG__)
+     typedef unsigned int uint128_t __attribute__((mode(TI)));
+   #else
+     typedef unsigned __int128 uint128_t;
+   #endif
+#endif
+
+}
+
+#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi)      \
+   do {                                      \
+      const uint128_t r = static_cast<uint128_t>(a) * b;   \
+      *hi = (r >> 64) & 0xFFFFFFFFFFFFFFFF;  \
+      *lo = (r      ) & 0xFFFFFFFFFFFFFFFF;  \
+   } while(0)
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) \
+   do { *lo = _umul128(a, b, hi); } while(0)
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do {                           \
+   asm("mulq %3" : "=d" (*hi), "=a" (*lo) : "a" (a), "rm" (b) : "cc"); \
+   } while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do {              \
+   asm("umulh %1,%2,%0" : "=r" (*hi) : "r" (a), "r" (b)); \
+   *lo = a * b;                                           \
+} while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do {                \
+   asm("xmpy.hu %0=%1,%2" : "=f" (*hi) : "f" (a), "f" (b)); \
+   *lo = a * b;                                             \
+} while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do {                      \
+   asm("mulhdu %0,%1,%2" : "=r" (*hi) : "r" (a), "r" (b) : "cc"); \
+   *lo = a * b;                                                   \
+} while(0)
+
+#endif
+
+#endif
+
+namespace Botan {
+
+/**
+* Perform a 64x64->128 bit multiplication
+*/
+inline void mul64x64_128(uint64_t a, uint64_t b, uint64_t* lo, uint64_t* hi)
+   {
+#if defined(BOTAN_FAST_64X64_MUL)
+   BOTAN_FAST_64X64_MUL(a, b, lo, hi);
+#else
+
+   /*
+   * Do a 64x64->128 multiply using four 32x32->64 multiplies plus
+   * some adds and shifts. Last resort for CPUs like UltraSPARC (with
+   * 64-bit registers/ALU, but no 64x64->128 multiply) or 32-bit CPUs.
+   */
+   const size_t HWORD_BITS = 32;
+   const uint32_t HWORD_MASK = 0xFFFFFFFF;
+
+   const uint32_t a_hi = (a >> HWORD_BITS);
+   const uint32_t a_lo = (a  & HWORD_MASK);
+   const uint32_t b_hi = (b >> HWORD_BITS);
+   const uint32_t b_lo = (b  & HWORD_MASK);
+
+   uint64_t x0 = static_cast<uint64_t>(a_hi) * b_hi;
+   uint64_t x1 = static_cast<uint64_t>(a_lo) * b_hi;
+   uint64_t x2 = static_cast<uint64_t>(a_hi) * b_lo;
+   uint64_t x3 = static_cast<uint64_t>(a_lo) * b_lo;
+
+   // this cannot overflow as (2^32-1)^2 + 2^32-1 < 2^64-1
+   x2 += x3 >> HWORD_BITS;
+
+   // this one can overflow
+   x2 += x1;
+
+   // propagate the carry if any
+   x0 += static_cast<uint64_t>(static_cast<bool>(x2 < x1)) << HWORD_BITS;
+
+   *hi = x0 + (x2 >> HWORD_BITS);
+   *lo  = ((x2 & HWORD_MASK) << HWORD_BITS) + (x3 & HWORD_MASK);
+#endif
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/mutex.h b/third_party/botan/src/lib/utils/mutex.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/mutex.h
@@ -0,0 +1,60 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_MUTEX_H_
+#define BOTAN_UTIL_MUTEX_H_
+
+#include <botan/types.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+
+#include <mutex>
+
+namespace Botan {
+
+template<typename T> using lock_guard_type = std::lock_guard<T>;
+typedef std::mutex mutex_type;
+typedef std::recursive_mutex recursive_mutex_type;
+
+}
+
+#else
+
+// No threads
+
+namespace Botan {
+
+template<typename Mutex>
+class lock_guard final
+   {
+   public:
+      explicit lock_guard(Mutex& m) : m_mutex(m)
+         { m_mutex.lock(); }
+
+      ~lock_guard() { m_mutex.unlock(); }
+
+      lock_guard(const lock_guard& other) = delete;
+      lock_guard& operator=(const lock_guard& other) = delete;
+   private:
+      Mutex& m_mutex;
+   };
+
+class noop_mutex final
+   {
+   public:
+      void lock() {}
+      void unlock() {}
+   };
+
+typedef noop_mutex mutex_type;
+typedef noop_mutex recursive_mutex_type;
+template<typename T> using lock_guard_type = lock_guard<T>;
+
+}
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/lib/utils/os_utils.cpp b/third_party/botan/src/lib/utils/os_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/os_utils.cpp
@@ -0,0 +1,757 @@
+/*
+* OS and machine specific utility functions
+* (C) 2015,2016,2017,2018 Jack Lloyd
+* (C) 2016 Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/os_utils.h>
+#include <botan/cpuid.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cstdlib>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+  #include <thread>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
+  #include <string.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+  #include <sys/types.h>
+  #include <sys/resource.h>
+  #include <sys/mman.h>
+  #include <signal.h>
+  #include <stdlib.h>
+  #include <setjmp.h>
+  #include <unistd.h>
+  #include <errno.h>
+  #include <termios.h>
+  #undef B0
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+  #include <emscripten/emscripten.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_IS_ANDROID) || \
+  defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+  #include <sys/auxv.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32)
+  #define NOMINMAX 1
+  #define _WINSOCKAPI_ // stop windows.h including winsock.h
+  #include <windows.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_ANDROID)
+  #include <elf.h>
+  extern "C" char **environ;
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
+  #include <mach/vm_statistics.h>
+#endif
+
+namespace Botan {
+
+// Not defined in OS namespace for historical reasons
+void secure_scrub_memory(void* ptr, size_t n)
+   {
+#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
+   ::RtlSecureZeroMemory(ptr, n);
+
+#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
+   ::explicit_bzero(ptr, n);
+
+#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET)
+   (void)::explicit_memset(ptr, 0, n);
+
+#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1)
+   /*
+   Call memset through a static volatile pointer, which the compiler
+   should not elide. This construct should be safe in conforming
+   compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
+   Clang 3.8 both create code that saves the memset address in the
+   data segment and unconditionally loads and jumps to that address.
+   */
+   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
+   (memset_ptr)(ptr, 0, n);
+#else
+
+   volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);
+
+   for(size_t i = 0; i != n; ++i)
+      p[i] = 0;
+#endif
+   }
+
+uint32_t OS::get_process_id()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   return ::getpid();
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+   return ::GetCurrentProcessId();
+#elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE)
+   return 0; // truly no meaningful value
+#else
+   #error "Missing get_process_id"
+#endif
+   }
+
+unsigned long OS::get_auxval(unsigned long id)
+   {
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
+   return ::getauxval(id);
+#elif defined(BOTAN_TARGET_OS_IS_ANDROID) && defined(BOTAN_TARGET_ARCH_IS_ARM32)
+
+   if(id == 0)
+      return 0;
+
+   char **p = environ;
+
+   while(*p++ != nullptr)
+      ;
+
+   Elf32_auxv_t *e = reinterpret_cast<Elf32_auxv_t*>(p);
+
+   while(e != nullptr)
+      {
+      if(e->a_type == id)
+         return e->a_un.a_val;
+      e++;
+      }
+
+   return 0;
+#elif defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+   unsigned long auxinfo = 0;
+   ::elf_aux_info(id, &auxinfo, sizeof(auxinfo));
+   return auxinfo;
+#else
+   BOTAN_UNUSED(id);
+   return 0;
+#endif
+   }
+
+bool OS::running_in_privileged_state()
+   {
+#if defined(AT_SECURE)
+   return OS::get_auxval(AT_SECURE) != 0;
+#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
+#else
+   return false;
+#endif
+   }
+
+uint64_t OS::get_cpu_cycle_counter()
+   {
+   uint64_t rtc = 0;
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32)
+   LARGE_INTEGER tv;
+   ::QueryPerformanceCounter(&tv);
+   rtc = tv.QuadPart;
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+   if(CPUID::has_rdtsc())
+      {
+      uint32_t rtc_low = 0, rtc_high = 0;
+      asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
+      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
+      }
+
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+   for(;;)
+      {
+      uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
+      asm volatile("mftbu %0" : "=r" (rtc_high));
+      asm volatile("mftb %0" : "=r" (rtc_low));
+      asm volatile("mftbu %0" : "=r" (rtc_high2));
+
+      if(rtc_high == rtc_high2)
+         {
+         rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
+         break;
+         }
+      }
+
+#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
+   asm volatile("rpcc %0" : "=r" (rtc));
+
+   // OpenBSD does not trap access to the %tick register
+#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
+   asm volatile("rd %%tick, %0" : "=r" (rtc));
+
+#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
+   asm volatile("mov %0=ar.itc" : "=r" (rtc));
+
+#elif defined(BOTAN_TARGET_ARCH_IS_S390X)
+   asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
+
+#elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
+   asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
+
+#else
+   //#warning "OS::get_cpu_cycle_counter not implemented"
+#endif
+
+#endif
+
+   return rtc;
+   }
+
+size_t OS::get_cpu_total()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_CONF)
+   const long res = ::sysconf(_SC_NPROCESSORS_CONF);
+   if(res > 0)
+      return static_cast<size_t>(res);
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+   return static_cast<size_t>(std::thread::hardware_concurrency());
+#else
+   return 1;
+#endif
+   }
+
+size_t OS::get_cpu_available()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_ONLN)
+   const long res = ::sysconf(_SC_NPROCESSORS_ONLN);
+   if(res > 0)
+      return static_cast<size_t>(res);
+#endif
+
+   return OS::get_cpu_total();
+   }
+
+uint64_t OS::get_high_resolution_clock()
+   {
+   if(uint64_t cpu_clock = OS::get_cpu_cycle_counter())
+      return cpu_clock;
+
+#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+   return emscripten_get_now();
+#endif
+
+   /*
+   If we got here either we either don't have an asm instruction
+   above, or (for x86) RDTSC is not available at runtime. Try some
+   clock_gettimes and return the first one that works, or otherwise
+   fall back to std::chrono.
+   */
+
+#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
+
+   // The ordering here is somewhat arbitrary...
+   const clockid_t clock_types[] = {
+#if defined(CLOCK_MONOTONIC_HR)
+      CLOCK_MONOTONIC_HR,
+#endif
+#if defined(CLOCK_MONOTONIC_RAW)
+      CLOCK_MONOTONIC_RAW,
+#endif
+#if defined(CLOCK_MONOTONIC)
+      CLOCK_MONOTONIC,
+#endif
+#if defined(CLOCK_PROCESS_CPUTIME_ID)
+      CLOCK_PROCESS_CPUTIME_ID,
+#endif
+#if defined(CLOCK_THREAD_CPUTIME_ID)
+      CLOCK_THREAD_CPUTIME_ID,
+#endif
+   };
+
+   for(clockid_t clock : clock_types)
+      {
+      struct timespec ts;
+      if(::clock_gettime(clock, &ts) == 0)
+         {
+         return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
+         }
+      }
+#endif
+
+   // Plain C++11 fallback
+   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
+   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+   }
+
+uint64_t OS::get_system_timestamp_ns()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
+   struct timespec ts;
+   if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
+      {
+      return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
+      }
+#endif
+
+   auto now = std::chrono::system_clock::now().time_since_epoch();
+   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+   }
+
+size_t OS::system_page_size()
+   {
+   const size_t default_page_size = 4096;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   long p = ::sysconf(_SC_PAGESIZE);
+   if(p > 1)
+      return static_cast<size_t>(p);
+   else
+      return default_page_size;
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+   BOTAN_UNUSED(default_page_size);
+   SYSTEM_INFO sys_info;
+   ::GetSystemInfo(&sys_info);
+   return sys_info.dwPageSize;
+#else
+   return default_page_size;
+#endif
+   }
+
+size_t OS::get_memory_locking_limit()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) && defined(RLIMIT_MEMLOCK)
+   /*
+   * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
+   * unprivileged mlock calls.
+   *
+   * Linux defaults to only 64 KiB of mlockable memory per process
+   * (too small) but BSDs offer a small fraction of total RAM (more
+   * than we need). Bound the total mlock size to 512 KiB which is
+   * enough to run the entire test suite without spilling to non-mlock
+   * memory (and thus presumably also enough for many useful
+   * programs), but small enough that we should not cause problems
+   * even if many processes are mlocking on the same machine.
+   */
+   const size_t user_req = read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
+
+   const size_t mlock_requested = std::min<size_t>(user_req, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
+
+   if(mlock_requested > 0)
+      {
+      struct ::rlimit limits;
+
+      ::getrlimit(RLIMIT_MEMLOCK, &limits);
+
+      if(limits.rlim_cur < limits.rlim_max)
+         {
+         limits.rlim_cur = limits.rlim_max;
+         ::setrlimit(RLIMIT_MEMLOCK, &limits);
+         ::getrlimit(RLIMIT_MEMLOCK, &limits);
+         }
+
+      return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
+      }
+
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+   SIZE_T working_min = 0, working_max = 0;
+   if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max))
+      {
+      return 0;
+      }
+
+   // According to Microsoft MSDN:
+   // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead
+   // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
+   // But the information in the book seems to be inaccurate/outdated
+   // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
+   // On all three OS the value is 11 instead of 8
+   const size_t overhead = OS::system_page_size() * 11;
+   if(working_min > overhead)
+      {
+      const size_t lockable_bytes = working_min - overhead;
+      return std::min<size_t>(lockable_bytes, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024);
+      }
+#endif
+
+   // Not supported on this platform
+   return 0;
+   }
+
+bool OS::read_env_variable(std::string& value_out, const std::string& name)
+   {
+   value_out = "";
+
+   if(running_in_privileged_state())
+      return false;
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+   char val[128] = { 0 };
+   size_t req_size = 0;
+   if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0)
+      {
+      value_out = std::string(val, req_size);
+      return true;
+      }
+#else
+   if(const char* val = std::getenv(name.c_str()))
+      {
+      value_out = val;
+      return true;
+      }
+#endif
+
+   return false;
+   }
+
+size_t OS::read_env_variable_sz(const std::string& name, size_t def)
+   {
+   std::string value;
+   if(read_env_variable(value, name))
+      {
+      try
+         {
+         const size_t val = std::stoul(value, nullptr);
+         return val;
+         }
+      catch(std::exception&) { /* ignore it */ }
+      }
+
+   return def;
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+
+namespace {
+
+int get_locked_fd()
+   {
+#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
+   // On Darwin, tagging anonymous pages allows vmmap to track these.
+   // Allowed from 240 to 255 for userland applications
+   static constexpr int default_locked_fd = 255;
+   int locked_fd = default_locked_fd;
+
+   if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd))
+      {
+      if(locked_fdl < 240 || locked_fdl > 255)
+         {
+         locked_fdl = default_locked_fd;
+         }
+      locked_fd = static_cast<int>(locked_fdl);
+      }
+   return VM_MAKE_TAG(locked_fd);
+#else
+   return -1;
+#endif
+   }
+
+}
+
+#endif
+
+std::vector<void*> OS::allocate_locked_pages(size_t count)
+   {
+   std::vector<void*> result;
+
+#if (defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+
+   result.reserve(count);
+
+   const size_t page_size = OS::system_page_size();
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+   static const int locked_fd = get_locked_fd();
+#endif
+
+   for(size_t i = 0; i != count; ++i)
+      {
+      void* ptr = nullptr;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+
+#if !defined(MAP_ANONYMOUS)
+   #define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#if !defined(MAP_NOCORE)
+#if defined(MAP_CONCEAL)
+   #define MAP_NOCORE MAP_CONCEAL
+#else
+   #define MAP_NOCORE 0
+#endif
+#endif
+
+#if !defined(PROT_MAX)
+   #define PROT_MAX(p) 0
+#endif
+      const int pflags = PROT_READ | PROT_WRITE;
+
+      ptr = ::mmap(nullptr, 3*page_size,
+                   pflags | PROT_MAX(pflags),
+                   MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE,
+                   /*fd=*/locked_fd, /*offset=*/0);
+
+      if(ptr == MAP_FAILED)
+         {
+         continue;
+         }
+
+      // lock the data page
+      if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0)
+         {
+         ::munmap(ptr, 3*page_size);
+         continue;
+         }
+
+#if defined(MADV_DONTDUMP)
+      // we ignore errors here, as DONTDUMP is just a bonus
+      ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
+#endif
+
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+      ptr = ::VirtualAlloc(nullptr, 3*page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+
+      if(ptr == nullptr)
+         continue;
+
+      if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0)
+         {
+         ::VirtualFree(ptr, 0, MEM_RELEASE);
+         continue;
+         }
+#endif
+
+      std::memset(ptr, 0, 3*page_size); // zero data page and both guard pages
+
+      // Make guard page preceeding the data page
+      page_prohibit_access(static_cast<uint8_t*>(ptr));
+      // Make guard page following the data page
+      page_prohibit_access(static_cast<uint8_t*>(ptr) + 2*page_size);
+
+      result.push_back(static_cast<uint8_t*>(ptr) + page_size);
+      }
+#else
+   BOTAN_UNUSED(count);
+#endif
+
+   return result;
+   }
+
+void OS::page_allow_access(void* page)
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   const size_t page_size = OS::system_page_size();
+   ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+   const size_t page_size = OS::system_page_size();
+   DWORD old_perms = 0;
+   ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
+   BOTAN_UNUSED(old_perms);
+#else
+   BOTAN_UNUSED(page);
+#endif
+   }
+
+void OS::page_prohibit_access(void* page)
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   const size_t page_size = OS::system_page_size();
+   ::mprotect(page, page_size, PROT_NONE);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+   const size_t page_size = OS::system_page_size();
+   DWORD old_perms = 0;
+   ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
+   BOTAN_UNUSED(old_perms);
+#else
+   BOTAN_UNUSED(page);
+#endif
+   }
+
+void OS::free_locked_pages(const std::vector<void*>& pages)
+   {
+   const size_t page_size = OS::system_page_size();
+
+   for(size_t i = 0; i != pages.size(); ++i)
+      {
+      void* ptr = pages[i];
+
+      secure_scrub_memory(ptr, page_size);
+
+      // ptr points to the data page, guard pages are before and after
+      page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
+      page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+      ::munlock(ptr, page_size);
+      ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3*page_size);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+      ::VirtualUnlock(ptr, page_size);
+      ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
+#endif
+      }
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+
+namespace {
+
+static ::sigjmp_buf g_sigill_jmp_buf;
+
+void botan_sigill_handler(int)
+   {
+   siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
+   }
+
+}
+
+#endif
+
+int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
+   {
+   volatile int probe_result = -3;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+   struct sigaction old_sigaction;
+   struct sigaction sigaction;
+
+   sigaction.sa_handler = botan_sigill_handler;
+   sigemptyset(&sigaction.sa_mask);
+   sigaction.sa_flags = 0;
+
+   int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
+
+   if(rc != 0)
+      throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
+
+   rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
+
+   if(rc == 0)
+      {
+      // first call to sigsetjmp
+      probe_result = probe_fn();
+      }
+   else if(rc == 1)
+      {
+      // non-local return from siglongjmp in signal handler: return error
+      probe_result = -1;
+      }
+
+   // Restore old SIGILL handler, if any
+   rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
+   if(rc != 0)
+      throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
+
+#else
+   BOTAN_UNUSED(probe_fn);
+#endif
+
+   return probe_result;
+   }
+
+std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal()
+   {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   class POSIX_Echo_Suppression : public Echo_Suppression
+      {
+      public:
+         POSIX_Echo_Suppression()
+            {
+            m_stdin_fd = fileno(stdin);
+            if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
+               throw System_Error("Getting terminal status failed", errno);
+
+            struct termios noecho_flags = m_old_termios;
+            noecho_flags.c_lflag &= ~ECHO;
+            noecho_flags.c_lflag |= ECHONL;
+
+            if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
+               throw System_Error("Clearing terminal echo bit failed", errno);
+            }
+
+         void reenable_echo() override
+            {
+            if(m_stdin_fd > 0)
+               {
+               if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
+                  throw System_Error("Restoring terminal echo bit failed", errno);
+               m_stdin_fd = -1;
+               }
+            }
+
+         ~POSIX_Echo_Suppression()
+            {
+            try
+               {
+               reenable_echo();
+               }
+            catch(...)
+               {
+               }
+            }
+
+      private:
+         int m_stdin_fd;
+         struct termios m_old_termios;
+      };
+
+   return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression);
+
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+
+   class Win32_Echo_Suppression : public Echo_Suppression
+      {
+      public:
+         Win32_Echo_Suppression()
+            {
+            m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
+            if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
+               throw System_Error("Getting console mode failed", ::GetLastError());
+
+            DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+            if(::SetConsoleMode(m_input_handle, new_mode) == 0)
+               throw System_Error("Setting console mode failed", ::GetLastError());
+            }
+
+         void reenable_echo() override
+            {
+            if(m_input_handle != INVALID_HANDLE_VALUE)
+               {
+               if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
+                  throw System_Error("Setting console mode failed", ::GetLastError());
+               m_input_handle = INVALID_HANDLE_VALUE;
+               }
+            }
+
+         ~Win32_Echo_Suppression()
+            {
+            try
+               {
+               reenable_echo();
+               }
+            catch(...)
+               {
+               }
+            }
+
+      private:
+         HANDLE m_input_handle;
+         DWORD m_console_state;
+      };
+
+   return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression);
+
+#else
+
+   // Not supported on this platform, return null
+   return std::unique_ptr<Echo_Suppression>();
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/os_utils.h b/third_party/botan/src/lib/utils/os_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/os_utils.h
@@ -0,0 +1,200 @@
+/*
+* OS specific utility functions
+* (C) 2015,2016,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OS_UTILS_H_
+#define BOTAN_OS_UTILS_H_
+
+#include <botan/types.h>
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+/**
+* @return process ID assigned by the operating system.
+* On Unix and Windows systems, this always returns a result
+* On IncludeOS it returns 0 since there is no process ID to speak of
+* in a unikernel.
+*/
+uint32_t BOTAN_TEST_API get_process_id();
+
+/**
+* Test if we are currently running with elevated permissions
+* eg setuid, setgid, or with POSIX caps set.
+*/
+bool running_in_privileged_state();
+
+/**
+* @return CPU processor clock, if available
+*
+* On Windows, calls QueryPerformanceCounter.
+*
+* Under GCC or Clang on supported platforms the hardware cycle counter is queried.
+* Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA.
+* If no CPU cycle counter is available on this system, returns zero.
+*/
+uint64_t BOTAN_TEST_API get_cpu_cycle_counter();
+
+size_t BOTAN_TEST_API get_cpu_total();
+size_t BOTAN_TEST_API get_cpu_available();
+
+/**
+* Return the ELF auxiliary vector cooresponding to the given ID.
+* This only makes sense on Unix-like systems and is currently
+* only supported on Linux, Android, and FreeBSD.
+*
+* Returns zero if not supported on the current system or if
+* the id provided is not known.
+*/
+unsigned long get_auxval(unsigned long id);
+
+/*
+* @return best resolution timestamp available
+*
+* The epoch and update rate of this clock is arbitrary and depending
+* on the hardware it may not tick at a constant rate.
+*
+* Uses hardware cycle counter, if available.
+* On POSIX platforms clock_gettime is used with a monotonic timer
+* As a final fallback std::chrono::high_resolution_clock is used.
+*/
+uint64_t BOTAN_TEST_API get_high_resolution_clock();
+
+/**
+* @return system clock (reflecting wall clock) with best resolution
+* available, normalized to nanoseconds resolution.
+*/
+uint64_t BOTAN_TEST_API get_system_timestamp_ns();
+
+/**
+* @return maximum amount of memory (in bytes) Botan could/should
+* hyptothetically allocate for the memory poool. Reads environment
+* variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool.
+*/
+size_t get_memory_locking_limit();
+
+/**
+* Return the size of a memory page, if that can be derived on the
+* current system. Otherwise returns some default value (eg 4096)
+*/
+size_t system_page_size();
+
+/**
+* Read the value of an environment variable, setting it to value_out if it
+* exists.  Returns false and sets value_out to empty string if no such variable
+* is set. If the process seems to be running in a privileged state (such as
+* setuid) then always returns false and does not examine the environment.
+*/
+bool read_env_variable(std::string& value_out, const std::string& var_name);
+
+/**
+* Read the value of an environment variable and convert it to an
+* integer. If not set or conversion fails, returns the default value.
+*
+* If the process seems to be running in a privileged state (such as setuid)
+* then always returns nullptr, similiar to glibc's secure_getenv.
+*/
+size_t read_env_variable_sz(const std::string& var_name, size_t def_value = 0);
+
+/**
+* Request count pages of RAM which are locked into memory using mlock,
+* VirtualLock, or some similar OS specific API. Free it with free_locked_pages.
+*
+* Returns an empty list on failure. This function is allowed to return fewer
+* than count pages.
+*
+* The contents of the allocated pages are undefined.
+*
+* Each page is preceded by and followed by a page which is marked
+* as noaccess, such that accessing it will cause a crash. This turns
+* out of bound reads/writes into crash events.
+*
+* @param count requested number of locked pages
+*/
+std::vector<void*> allocate_locked_pages(size_t count);
+
+/**
+* Free memory allocated by allocate_locked_pages
+* @param pages a list of pages returned by allocate_locked_pages
+*/
+void free_locked_pages(const std::vector<void*>& pages);
+
+/**
+* Set the MMU to prohibit access to this page
+*/
+void page_prohibit_access(void* page);
+
+/**
+* Set the MMU to allow R/W access to this page
+*/
+void page_allow_access(void* page);
+
+
+/**
+* Run a probe instruction to test for support for a CPU instruction.
+* Runs in system-specific env that catches illegal instructions; this
+* function always fails if the OS doesn't provide this.
+* Returns value of probe_fn, if it could run.
+* If error occurs, returns negative number.
+* This allows probe_fn to indicate errors of its own, if it wants.
+* For example the instruction might not only be only available on some
+* CPUs, but also buggy on some subset of these - the probe function
+* can test to make sure the instruction works properly before
+* indicating that the instruction is available.
+*
+* @warning on Unix systems uses signal handling in a way that is not
+* thread safe. It should only be called in a single-threaded context
+* (ie, at static init time).
+*
+* If probe_fn throws an exception the result is undefined.
+*
+* Return codes:
+* -1 illegal instruction detected
+*/
+int BOTAN_TEST_API run_cpu_instruction_probe(std::function<int ()> probe_fn);
+
+/**
+* Represents a terminal state
+*/
+class BOTAN_UNSTABLE_API Echo_Suppression
+   {
+   public:
+      /**
+      * Reenable echo on this terminal. Can be safely called
+      * multiple times. May throw if an error occurs.
+      */
+      virtual void reenable_echo() = 0;
+
+      /**
+      * Implicitly calls reenable_echo, but swallows/ignored all
+      * errors which would leave the terminal in an invalid state.
+      */
+      virtual ~Echo_Suppression() = default;
+   };
+
+/**
+* Suppress echo on the terminal
+* Returns null if this operation is not supported on the current system.
+*/
+std::unique_ptr<Echo_Suppression> BOTAN_UNSTABLE_API suppress_echo_on_terminal();
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/parsing.cpp b/third_party/botan/src/lib/utils/parsing.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/parsing.cpp
@@ -0,0 +1,462 @@
+/*
+* Various string utils and parsing functions
+* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+#include <botan/charset.h>
+#include <botan/loadstor.h>
+#include <algorithm>
+#include <cctype>
+#include <limits>
+#include <set>
+
+#if defined(BOTAN_HAS_ASN1)
+  #include <botan/asn1_oid.h>
+#endif
+
+namespace Botan {
+
+uint16_t to_uint16(const std::string& str)
+   {
+   const uint32_t x = to_u32bit(str);
+
+   if(x >> 16)
+      throw Invalid_Argument("Integer value exceeds 16 bit range");
+
+   return static_cast<uint16_t>(x);
+   }
+
+uint32_t to_u32bit(const std::string& str)
+   {
+   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
+   for(const char chr : str)
+      {
+      if(chr < '0' || chr > '9')
+         {
+         std::string chrAsString(1, chr);
+         throw Invalid_Argument("String contains non-digit char: " + chrAsString);
+         }
+      }
+
+   const unsigned long int x = std::stoul(str);
+
+   if(sizeof(unsigned long int) > 4)
+      {
+      // x might be uint64
+      if (x > std::numeric_limits<uint32_t>::max())
+         {
+         throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
+         }
+      }
+
+   return static_cast<uint32_t>(x);
+   }
+
+/*
+* Convert a string into a time duration
+*/
+uint32_t timespec_to_u32bit(const std::string& timespec)
+   {
+   if(timespec.empty())
+      return 0;
+
+   const char suffix = timespec[timespec.size()-1];
+   std::string value = timespec.substr(0, timespec.size()-1);
+
+   uint32_t scale = 1;
+
+   if(Charset::is_digit(suffix))
+      value += suffix;
+   else if(suffix == 's')
+      scale = 1;
+   else if(suffix == 'm')
+      scale = 60;
+   else if(suffix == 'h')
+      scale = 60 * 60;
+   else if(suffix == 'd')
+      scale = 24 * 60 * 60;
+   else if(suffix == 'y')
+      scale = 365 * 24 * 60 * 60;
+   else
+      throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec);
+
+   return scale * to_u32bit(value);
+   }
+
+/*
+* Parse a SCAN-style algorithm name
+*/
+std::vector<std::string> parse_algorithm_name(const std::string& namex)
+   {
+   if(namex.find('(') == std::string::npos &&
+      namex.find(')') == std::string::npos)
+      return std::vector<std::string>(1, namex);
+
+   std::string name = namex, substring;
+   std::vector<std::string> elems;
+   size_t level = 0;
+
+   elems.push_back(name.substr(0, name.find('(')));
+   name = name.substr(name.find('('));
+
+   for(auto i = name.begin(); i != name.end(); ++i)
+      {
+      char c = *i;
+
+      if(c == '(')
+         ++level;
+      if(c == ')')
+         {
+         if(level == 1 && i == name.end() - 1)
+            {
+            if(elems.size() == 1)
+               elems.push_back(substring.substr(1));
+            else
+               elems.push_back(substring);
+            return elems;
+            }
+
+         if(level == 0 || (level == 1 && i != name.end() - 1))
+            throw Invalid_Algorithm_Name(namex);
+         --level;
+         }
+
+      if(c == ',' && level == 1)
+         {
+         if(elems.size() == 1)
+            elems.push_back(substring.substr(1));
+         else
+            elems.push_back(substring);
+         substring.clear();
+         }
+      else
+         substring += c;
+      }
+
+   if(!substring.empty())
+      throw Invalid_Algorithm_Name(namex);
+
+   return elems;
+   }
+
+std::vector<std::string> split_on(const std::string& str, char delim)
+   {
+   return split_on_pred(str, [delim](char c) { return c == delim; });
+   }
+
+std::vector<std::string> split_on_pred(const std::string& str,
+                                       std::function<bool (char)> pred)
+   {
+   std::vector<std::string> elems;
+   if(str.empty()) return elems;
+
+   std::string substr;
+   for(auto i = str.begin(); i != str.end(); ++i)
+      {
+      if(pred(*i))
+         {
+         if(!substr.empty())
+            elems.push_back(substr);
+         substr.clear();
+         }
+      else
+         substr += *i;
+      }
+
+   if(substr.empty())
+      throw Invalid_Argument("Unable to split string: " + str);
+   elems.push_back(substr);
+
+   return elems;
+   }
+
+/*
+* Join a string
+*/
+std::string string_join(const std::vector<std::string>& strs, char delim)
+   {
+   std::string out = "";
+
+   for(size_t i = 0; i != strs.size(); ++i)
+      {
+      if(i != 0)
+         out += delim;
+      out += strs[i];
+      }
+
+   return out;
+   }
+
+/*
+* Parse an ASN.1 OID string
+*/
+std::vector<uint32_t> parse_asn1_oid(const std::string& oid)
+   {
+#if defined(BOTAN_HAS_ASN1)
+   return OID(oid).get_components();
+#else
+   BOTAN_UNUSED(oid);
+   throw Not_Implemented("ASN1 support not available");
+#endif
+   }
+
+/*
+* X.500 String Comparison
+*/
+bool x500_name_cmp(const std::string& name1, const std::string& name2)
+   {
+   auto p1 = name1.begin();
+   auto p2 = name2.begin();
+
+   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+   while(p1 != name1.end() && p2 != name2.end())
+      {
+      if(Charset::is_space(*p1))
+         {
+         if(!Charset::is_space(*p2))
+            return false;
+
+         while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+         while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+         if(p1 == name1.end() && p2 == name2.end())
+            return true;
+         if(p1 == name1.end() || p2 == name2.end())
+            return false;
+         }
+
+      if(!Charset::caseless_cmp(*p1, *p2))
+         return false;
+      ++p1;
+      ++p2;
+      }
+
+   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+   if((p1 != name1.end()) || (p2 != name2.end()))
+      return false;
+   return true;
+   }
+
+/*
+* Convert a decimal-dotted string to binary IP
+*/
+uint32_t string_to_ipv4(const std::string& str)
+   {
+   std::vector<std::string> parts = split_on(str, '.');
+
+   if(parts.size() != 4)
+      throw Decoding_Error("Invalid IP string " + str);
+
+   uint32_t ip = 0;
+
+   for(auto part = parts.begin(); part != parts.end(); ++part)
+      {
+      uint32_t octet = to_u32bit(*part);
+
+      if(octet > 255)
+         throw Decoding_Error("Invalid IP string " + str);
+
+      ip = (ip << 8) | (octet & 0xFF);
+      }
+
+   return ip;
+   }
+
+/*
+* Convert an IP address to decimal-dotted string
+*/
+std::string ipv4_to_string(uint32_t ip)
+   {
+   std::string str;
+
+   for(size_t i = 0; i != sizeof(ip); ++i)
+      {
+      if(i)
+         str += ".";
+      str += std::to_string(get_byte(i, ip));
+      }
+
+   return str;
+   }
+
+std::string erase_chars(const std::string& str, const std::set<char>& chars)
+   {
+   std::string out;
+
+   for(auto c: str)
+      if(chars.count(c) == 0)
+         out += c;
+
+   return out;
+   }
+
+std::string replace_chars(const std::string& str,
+                          const std::set<char>& chars,
+                          char to_char)
+   {
+   std::string out = str;
+
+   for(size_t i = 0; i != out.size(); ++i)
+      if(chars.count(out[i]))
+         out[i] = to_char;
+
+   return out;
+   }
+
+std::string replace_char(const std::string& str, char from_char, char to_char)
+   {
+   std::string out = str;
+
+   for(size_t i = 0; i != out.size(); ++i)
+      if(out[i] == from_char)
+         out[i] = to_char;
+
+   return out;
+   }
+
+namespace {
+
+std::string tolower_string(const std::string& in)
+   {
+   std::string s = in;
+   for(size_t i = 0; i != s.size(); ++i)
+      {
+      const int cu = static_cast<unsigned char>(s[i]);
+      if(std::isalpha(cu))
+         s[i] = static_cast<char>(std::tolower(cu));
+      }
+   return s;
+   }
+
+}
+
+bool host_wildcard_match(const std::string& issued_, const std::string& host_)
+   {
+   const std::string issued = tolower_string(issued_);
+   const std::string host = tolower_string(host_);
+
+   if(host.empty() || issued.empty())
+      return false;
+
+   /*
+   If there are embedded nulls in your issued name
+   Well I feel bad for you son
+   */
+   if(std::count(issued.begin(), issued.end(), char(0)) > 0)
+      return false;
+
+   // If more than one wildcard, then issued name is invalid
+   const size_t stars = std::count(issued.begin(), issued.end(), '*');
+   if(stars > 1)
+      return false;
+
+   // '*' is not a valid character in DNS names so should not appear on the host side
+   if(std::count(host.begin(), host.end(), '*') != 0)
+      return false;
+
+   // Similarly a DNS name can't end in .
+   if(host[host.size() - 1] == '.')
+      return false;
+
+   // And a host can't have an empty name component, so reject that
+   if(host.find("..") != std::string::npos)
+      return false;
+
+   // Exact match: accept
+   if(issued == host)
+      {
+      return true;
+      }
+
+   /*
+   Otherwise it might be a wildcard
+
+   If the issued size is strictly longer than the hostname size it
+   couldn't possibly be a match, even if the issued value is a
+   wildcard. The only exception is when the wildcard ends up empty
+   (eg www.example.com matches www*.example.com)
+   */
+   if(issued.size() > host.size() + 1)
+      {
+      return false;
+      }
+
+   // If no * at all then not a wildcard, and so not a match
+   if(stars != 1)
+      {
+      return false;
+      }
+
+   /*
+   Now walk through the issued string, making sure every character
+   matches. When we come to the (singular) '*', jump forward in the
+   hostname by the corresponding amount. We know exactly how much
+   space the wildcard takes because it must be exactly `len(host) -
+   len(issued) + 1 chars`.
+
+   We also verify that the '*' comes in the leftmost component, and
+   doesn't skip over any '.' in the hostname.
+   */
+   size_t dots_seen = 0;
+   size_t host_idx = 0;
+
+   for(size_t i = 0; i != issued.size(); ++i)
+      {
+      dots_seen += (issued[i] == '.');
+
+      if(issued[i] == '*')
+         {
+         // Fail: wildcard can only come in leftmost component
+         if(dots_seen > 0)
+            {
+            return false;
+            }
+
+         /*
+         Since there is only one * we know the tail of the issued and
+         hostname must be an exact match. In this case advance host_idx
+         to match.
+         */
+         const size_t advance = (host.size() - issued.size() + 1);
+
+         if(host_idx + advance > host.size()) // shouldn't happen
+            return false;
+
+         // Can't be any intervening .s that we would have skipped
+         if(std::count(host.begin() + host_idx,
+                       host.begin() + host_idx + advance, '.') != 0)
+            return false;
+
+         host_idx += advance;
+         }
+      else
+         {
+         if(issued[i] != host[host_idx])
+            {
+            return false;
+            }
+
+         host_idx += 1;
+         }
+      }
+
+   // Wildcard issued name must have at least 3 components
+   if(dots_seen < 2)
+      {
+      return false;
+      }
+
+   return true;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/parsing.h b/third_party/botan/src/lib/utils/parsing.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/parsing.h
@@ -0,0 +1,179 @@
+/*
+* Various string utils and parsing functions
+* (C) 1999-2007,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PARSING_UTILS_H_
+#define BOTAN_PARSING_UTILS_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+#include <set>
+
+#include <istream>
+#include <functional>
+#include <map>
+
+BOTAN_FUTURE_INTERNAL_HEADER(parsing.h)
+
+namespace Botan {
+
+/**
+* Parse a SCAN-style algorithm name
+* @param scan_name the name
+* @return the name components
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string>
+parse_algorithm_name(const std::string& scan_name);
+
+/**
+* Split a string
+* @param str the input string
+* @param delim the delimitor
+* @return string split by delim
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string> split_on(
+   const std::string& str, char delim);
+
+/**
+* Split a string on a character predicate
+* @param str the input string
+* @param pred the predicate
+*
+* This function will likely be removed in a future release
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string>
+split_on_pred(const std::string& str,
+              std::function<bool (char)> pred);
+
+/**
+* Erase characters from a string
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string erase_chars(const std::string& str, const std::set<char>& chars);
+
+/**
+* Replace a character in a string
+* @param str the input string
+* @param from_char the character to replace
+* @param to_char the character to replace it with
+* @return str with all instances of from_char replaced by to_char
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string replace_char(const std::string& str,
+                         char from_char,
+                         char to_char);
+
+/**
+* Replace a character in a string
+* @param str the input string
+* @param from_chars the characters to replace
+* @param to_char the character to replace it with
+* @return str with all instances of from_chars replaced by to_char
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string replace_chars(const std::string& str,
+                          const std::set<char>& from_chars,
+                          char to_char);
+
+/**
+* Join a string
+* @param strs strings to join
+* @param delim the delimitor
+* @return string joined by delim
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string string_join(const std::vector<std::string>& strs,
+                        char delim);
+
+/**
+* Parse an ASN.1 OID
+* @param oid the OID in string form
+* @return OID components
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint32_t>
+BOTAN_DEPRECATED("Use OID::from_string(oid).get_components()") parse_asn1_oid(const std::string& oid);
+
+/**
+* Compare two names using the X.509 comparison algorithm
+* @param name1 the first name
+* @param name2 the second name
+* @return true if name1 is the same as name2 by the X.509 comparison rules
+*/
+BOTAN_PUBLIC_API(2,0)
+bool x500_name_cmp(const std::string& name1,
+                   const std::string& name2);
+
+/**
+* Convert a string to a number
+* @param str the string to convert
+* @return number value of the string
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t to_u32bit(const std::string& str);
+
+/**
+* Convert a string to a number
+* @param str the string to convert
+* @return number value of the string
+*/
+BOTAN_PUBLIC_API(2,3) uint16_t to_uint16(const std::string& str);
+
+/**
+* Convert a time specification to a number
+* @param timespec the time specification
+* @return number of seconds represented by timespec
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t BOTAN_DEPRECATED("Not used anymore")
+timespec_to_u32bit(const std::string& timespec);
+
+/**
+* Convert a string representation of an IPv4 address to a number
+* @param ip_str the string representation
+* @return integer IPv4 address
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t string_to_ipv4(const std::string& ip_str);
+
+/**
+* Convert an IPv4 address to a string
+* @param ip_addr the IPv4 address to convert
+* @return string representation of the IPv4 address
+*/
+BOTAN_PUBLIC_API(2,0) std::string ipv4_to_string(uint32_t ip_addr);
+
+std::map<std::string, std::string> BOTAN_PUBLIC_API(2,0) read_cfg(std::istream& is);
+
+/**
+* Accepts key value pairs deliminated by commas:
+*
+* "" (returns empty map)
+* "K=V" (returns map {'K': 'V'})
+* "K1=V1,K2=V2"
+* "K1=V1,K2=V2,K3=V3"
+* "K1=V1,K2=V2,K3=a_value\,with\,commas_and_\=equals"
+*
+* Values may be empty, keys must be non-empty and unique. Duplicate
+* keys cause an exception.
+*
+* Within both key and value, comma and equals can be escaped with
+* backslash. Backslash can also be escaped.
+*/
+std::map<std::string, std::string> BOTAN_PUBLIC_API(2,8) read_kv(const std::string& kv);
+
+std::string BOTAN_PUBLIC_API(2,0) clean_ws(const std::string& s);
+
+/**
+* Check if the given hostname is a match for the specified wildcard
+*/
+bool BOTAN_PUBLIC_API(2,0) host_wildcard_match(const std::string& wildcard,
+                                               const std::string& host);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp b/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/poly_dbl.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* The minimum weight irreducible binary polynomial of size n
+*
+* See http://www.hpl.hp.com/techreports/98/HPL-98-135.pdf
+*/
+enum class MinWeightPolynomial : uint64_t {
+   P64   = 0x1B,
+   P128  = 0x87,
+   P192  = 0x87,
+   P256  = 0x425,
+   P512  = 0x125,
+   P1024 = 0x80043,
+};
+
+template<size_t LIMBS, MinWeightPolynomial P>
+void poly_double(uint8_t out[], const uint8_t in[])
+   {
+   uint64_t W[LIMBS];
+   load_be(W, in, LIMBS);
+
+   const uint64_t POLY = static_cast<uint64_t>(P);
+
+   const uint64_t carry = POLY * (W[0] >> 63);
+
+   BOTAN_IF_CONSTEXPR(LIMBS > 0)
+      {
+      for(size_t i = 0; i != LIMBS - 1; ++i)
+         W[i] = (W[i] << 1) ^ (W[i+1] >> 63);
+      }
+
+   W[LIMBS-1] = (W[LIMBS-1] << 1) ^ carry;
+
+   copy_out_be(out, LIMBS*8, W);
+   }
+
+template<size_t LIMBS, MinWeightPolynomial P>
+void poly_double_le(uint8_t out[], const uint8_t in[])
+   {
+   uint64_t W[LIMBS];
+   load_le(W, in, LIMBS);
+
+   const uint64_t POLY = static_cast<uint64_t>(P);
+
+   const uint64_t carry = POLY * (W[LIMBS-1] >> 63);
+
+   BOTAN_IF_CONSTEXPR(LIMBS > 0)
+      {
+      for(size_t i = 0; i != LIMBS - 1; ++i)
+         W[LIMBS-1-i] = (W[LIMBS-1-i] << 1) ^ (W[LIMBS-2-i] >> 63);
+      }
+
+   W[0] = (W[0] << 1) ^ carry;
+
+   copy_out_le(out, LIMBS*8, W);
+   }
+
+}
+
+void poly_double_n(uint8_t out[], const uint8_t in[], size_t n)
+   {
+   switch(n)
+      {
+      case 8:
+         return poly_double<1, MinWeightPolynomial::P64>(out, in);
+      case 16:
+         return poly_double<2, MinWeightPolynomial::P128>(out, in);
+      case 24:
+         return poly_double<3, MinWeightPolynomial::P192>(out, in);
+      case 32:
+         return poly_double<4, MinWeightPolynomial::P256>(out, in);
+      case 64:
+         return poly_double<8, MinWeightPolynomial::P512>(out, in);
+      case 128:
+         return poly_double<16, MinWeightPolynomial::P1024>(out, in);
+      default:
+         throw Invalid_Argument("Unsupported size for poly_double_n");
+      }
+   }
+
+void poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n)
+   {
+   switch(n)
+      {
+      case 8:
+         return poly_double_le<1, MinWeightPolynomial::P64>(out, in);
+      case 16:
+         return poly_double_le<2, MinWeightPolynomial::P128>(out, in);
+      case 24:
+         return poly_double_le<3, MinWeightPolynomial::P192>(out, in);
+      case 32:
+         return poly_double_le<4, MinWeightPolynomial::P256>(out, in);
+      case 64:
+         return poly_double_le<8, MinWeightPolynomial::P512>(out, in);
+      case 128:
+         return poly_double_le<16, MinWeightPolynomial::P1024>(out, in);
+      default:
+         throw Invalid_Argument("Unsupported size for poly_double_n_le");
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h b/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h
@@ -0,0 +1,39 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POLY_DBL_H_
+#define BOTAN_POLY_DBL_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Polynomial doubling in GF(2^n)
+*/
+void BOTAN_TEST_API poly_double_n(uint8_t out[], const uint8_t in[], size_t n);
+
+/**
+* Returns true iff poly_double_n is implemented for this size.
+*/
+inline bool poly_double_supported_size(size_t n)
+   {
+   return (n == 8 || n == 16 || n == 24 || n == 32 || n == 64 || n == 128);
+   }
+
+inline void poly_double_n(uint8_t buf[], size_t n)
+   {
+   return poly_double_n(buf, buf, n);
+   }
+
+/*
+* Little endian convention - used for XTS
+*/
+void BOTAN_TEST_API poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/prefetch.h b/third_party/botan/src/lib/utils/prefetch.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/prefetch.h
@@ -0,0 +1,39 @@
+/*
+* Prefetching Operations
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PREFETCH_H_
+#define BOTAN_PREFETCH_H_
+
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+template<typename T>
+inline void prefetch_readonly(const T* addr, size_t length)
+   {
+#if defined(__GNUG__)
+   const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T);
+
+   for(size_t i = 0; i <= length; i += Ts_per_cache_line)
+      __builtin_prefetch(addr + i, 0);
+#endif
+   }
+
+template<typename T>
+inline void prefetch_readwrite(const T* addr, size_t length)
+   {
+#if defined(__GNUG__)
+   const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T);
+
+   for(size_t i = 0; i <= length; i += Ts_per_cache_line)
+      __builtin_prefetch(addr + i, 1);
+#endif
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/read_cfg.cpp b/third_party/botan/src/lib/utils/read_cfg.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/read_cfg.cpp
@@ -0,0 +1,63 @@
+/*
+* Simple config/test file reader
+* (C) 2013,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string clean_ws(const std::string& s)
+   {
+   const char* ws = " \t\n";
+   auto start = s.find_first_not_of(ws);
+   auto end = s.find_last_not_of(ws);
+
+   if(start == std::string::npos)
+      return "";
+
+   if(end == std::string::npos)
+      return s.substr(start, end);
+   else
+      return s.substr(start, start + end + 1);
+   }
+
+std::map<std::string, std::string> read_cfg(std::istream& is)
+   {
+   std::map<std::string, std::string> kv;
+   size_t line = 0;
+
+   while(is.good())
+      {
+      std::string s;
+
+      std::getline(is, s);
+
+      ++line;
+
+      if(s.empty() || s[0] == '#')
+         continue;
+
+      s = clean_ws(s.substr(0, s.find('#')));
+
+      if(s.empty())
+         continue;
+
+      auto eq = s.find("=");
+
+      if(eq == std::string::npos || eq == 0 || eq == s.size() - 1)
+         throw Decoding_Error("Bad read_cfg input '" + s + "' on line " + std::to_string(line));
+
+      const std::string key = clean_ws(s.substr(0, eq));
+      const std::string val = clean_ws(s.substr(eq + 1, std::string::npos));
+
+      kv[key] = val;
+      }
+
+   return kv;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/read_kv.cpp b/third_party/botan/src/lib/utils/read_kv.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/read_kv.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::map<std::string, std::string> read_kv(const std::string& kv)
+   {
+   std::map<std::string, std::string> m;
+   if(kv == "")
+      return m;
+
+   std::vector<std::string> parts;
+
+   try
+      {
+      parts = split_on(kv, ',');
+      }
+   catch(std::exception&)
+      {
+      throw Invalid_Argument("Bad KV spec");
+      }
+
+   bool escaped = false;
+   bool reading_key = true;
+   std::string cur_key;
+   std::string cur_val;
+
+   for(char c : kv)
+      {
+      if(c == '\\' && !escaped)
+         {
+         escaped = true;
+         }
+      else if(c == ',' && !escaped)
+         {
+         if(cur_key.empty())
+            throw Invalid_Argument("Bad KV spec empty key");
+
+         if(m.find(cur_key) != m.end())
+            throw Invalid_Argument("Bad KV spec duplicated key");
+         m[cur_key] = cur_val;
+         cur_key = "";
+         cur_val = "";
+         reading_key = true;
+         }
+      else if(c == '=' && !escaped)
+         {
+         if(reading_key == false)
+            throw Invalid_Argument("Bad KV spec unexpected equals sign");
+         reading_key = false;
+         }
+      else
+         {
+         if(reading_key)
+            cur_key += c;
+         else
+            cur_val += c;
+
+         if(escaped)
+            escaped = false;
+         }
+      }
+
+   if(!cur_key.empty())
+      {
+      if(reading_key == false)
+         {
+         if(m.find(cur_key) != m.end())
+            throw Invalid_Argument("Bad KV spec duplicated key");
+         m[cur_key] = cur_val;
+         }
+      else
+         throw Invalid_Argument("Bad KV spec incomplete string");
+      }
+
+   return m;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/rotate.h b/third_party/botan/src/lib/utils/rotate.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/rotate.h
@@ -0,0 +1,106 @@
+/*
+* Word Rotation Operations
+* (C) 1999-2008,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WORD_ROTATE_H_
+#define BOTAN_WORD_ROTATE_H_
+
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rotate.h)
+
+namespace Botan {
+
+/**
+* Bit rotation left by a compile-time constant amount
+* @param input the input word
+* @return input rotated left by ROT bits
+*/
+template<size_t ROT, typename T>
+inline constexpr T rotl(T input)
+   {
+   static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant");
+   return static_cast<T>((input << ROT) | (input >> (8*sizeof(T) - ROT)));
+   }
+
+/**
+* Bit rotation right by a compile-time constant amount
+* @param input the input word
+* @return input rotated right by ROT bits
+*/
+template<size_t ROT, typename T>
+inline constexpr T rotr(T input)
+   {
+   static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant");
+   return static_cast<T>((input >> ROT) | (input << (8*sizeof(T) - ROT)));
+   }
+
+/**
+* Bit rotation left, variable rotation amount
+* @param input the input word
+* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1
+* @return input rotated left by rot bits
+*/
+template<typename T>
+inline T rotl_var(T input, size_t rot)
+   {
+   return rot ? static_cast<T>((input << rot) | (input >> (sizeof(T)*8 - rot))) : input;
+   }
+
+/**
+* Bit rotation right, variable rotation amount
+* @param input the input word
+* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1
+* @return input rotated right by rot bits
+*/
+template<typename T>
+inline T rotr_var(T input, size_t rot)
+   {
+   return rot ? static_cast<T>((input >> rot) | (input << (sizeof(T)*8 - rot))) : input;
+   }
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64) || defined(BOTAN_TARGET_ARCH_IS_X86_32)
+
+template<>
+inline uint32_t rotl_var(uint32_t input, size_t rot)
+   {
+   asm("roll %1,%0" : "+r" (input) : "c" (static_cast<uint8_t>(rot)));
+   return input;
+   }
+
+template<>
+inline uint32_t rotr_var(uint32_t input, size_t rot)
+   {
+   asm("rorl %1,%0" : "+r" (input) : "c" (static_cast<uint8_t>(rot)));
+   return input;
+   }
+
+#endif
+
+#endif
+
+
+template<typename T>
+BOTAN_DEPRECATED("Use rotl<N> or rotl_var")
+inline T rotate_left(T input, size_t rot)
+   {
+   // rotl_var does not reduce
+   return rotl_var(input, rot % (8 * sizeof(T)));
+   }
+
+template<typename T>
+BOTAN_DEPRECATED("Use rotr<N> or rotr_var")
+inline T rotate_right(T input, size_t rot)
+   {
+   // rotr_var does not reduce
+   return rotr_var(input, rot % (8 * sizeof(T)));
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/rounding.h b/third_party/botan/src/lib/utils/rounding.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/rounding.h
@@ -0,0 +1,56 @@
+/*
+* Integer Rounding Functions
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ROUNDING_H_
+#define BOTAN_ROUNDING_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Round up
+* @param n a non-negative integer
+* @param align_to the alignment boundary
+* @return n rounded up to a multiple of align_to
+*/
+inline size_t round_up(size_t n, size_t align_to)
+   {
+   BOTAN_ARG_CHECK(align_to != 0, "align_to must not be 0");
+
+   if(n % align_to)
+      n += align_to - (n % align_to);
+   return n;
+   }
+
+/**
+* Round down
+* @param n an integer
+* @param align_to the alignment boundary
+* @return n rounded down to a multiple of align_to
+*/
+template<typename T>
+inline constexpr T round_down(T n, T align_to)
+   {
+   return (align_to == 0) ? n : (n - (n % align_to));
+   }
+
+/**
+* Clamp
+*/
+inline size_t clamp(size_t n, size_t lower_bound, size_t upper_bound)
+   {
+   if(n < lower_bound)
+      return lower_bound;
+   if(n > upper_bound)
+      return upper_bound;
+   return n;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/safeint.h b/third_party/botan/src/lib/utils/safeint.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/safeint.h
@@ -0,0 +1,41 @@
+/*
+* Safe(r) Integer Handling
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_SAFE_INT_H_
+#define BOTAN_UTILS_SAFE_INT_H_
+
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Integer_Overflow_Detected final : public Exception
+   {
+   public:
+      Integer_Overflow_Detected(const std::string& file, int line) :
+         Exception("Integer overflow detected at " + file + ":" + std::to_string(line))
+         {}
+
+      ErrorType error_type() const noexcept override { return ErrorType::InternalError; }
+   };
+
+inline size_t checked_add(size_t x, size_t y, const char* file, int line)
+   {
+   // TODO: use __builtin_x_overflow on GCC and Clang
+   size_t z = x + y;
+   if(z < x)
+      {
+      throw Integer_Overflow_Detected(file, line);
+      }
+   return z;
+   }
+
+#define BOTAN_CHECKED_ADD(x,y) checked_add(x,y,__FILE__,__LINE__)
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/simd/simd_32.h b/third_party/botan/src/lib/utils/simd/simd_32.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/simd/simd_32.h
@@ -0,0 +1,621 @@
+/*
+* Lightweight wrappers for SIMD operations
+* (C) 2009,2011,2016,2017,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SIMD_32_H_
+#define BOTAN_SIMD_32_H_
+
+#include <botan/types.h>
+
+#if defined(BOTAN_TARGET_SUPPORTS_SSE2)
+  #include <emmintrin.h>
+  #define BOTAN_SIMD_USE_SSE2
+
+#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
+  #include <botan/bswap.h>
+  #include <botan/loadstor.h>
+  #include <altivec.h>
+  #undef vector
+  #undef bool
+  #define BOTAN_SIMD_USE_ALTIVEC
+
+#elif defined(BOTAN_TARGET_SUPPORTS_NEON)
+  #include <botan/cpuid.h>
+  #include <arm_neon.h>
+  #define BOTAN_SIMD_USE_NEON
+
+#else
+  #error "No SIMD instruction set enabled"
+#endif
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+  #define BOTAN_SIMD_ISA "sse2"
+  #define BOTAN_VPERM_ISA "ssse3"
+  #define BOTAN_CLMUL_ISA "pclmul"
+#elif defined(BOTAN_SIMD_USE_NEON)
+  #if defined(BOTAN_TARGET_ARCH_IS_ARM64)
+    #define BOTAN_SIMD_ISA "+simd"
+    #define BOTAN_CLMUL_ISA "+crypto"
+  #else
+    #define BOTAN_SIMD_ISA "fpu=neon"
+  #endif
+  #define BOTAN_VPERM_ISA BOTAN_SIMD_ISA
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+  #define BOTAN_SIMD_ISA "altivec"
+  #define BOTAN_VPERM_ISA "altivec"
+  #define BOTAN_CLMUL_ISA "crypto"
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+   typedef __m128i native_simd_type;
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+   typedef __vector unsigned int native_simd_type;
+#elif defined(BOTAN_SIMD_USE_NEON)
+   typedef uint32x4_t native_simd_type;
+#endif
+
+/**
+* 4x32 bit SIMD register
+*
+* This class is not a general purpose SIMD type, and only offers
+* instructions needed for evaluation of specific crypto primitives.
+* For example it does not currently have equality operators of any
+* kind.
+*
+* Implemented for SSE2, VMX (Altivec), and NEON.
+*/
+class SIMD_4x32 final
+   {
+   public:
+
+      SIMD_4x32& operator=(const SIMD_4x32& other) = default;
+      SIMD_4x32(const SIMD_4x32& other) = default;
+
+      SIMD_4x32& operator=(SIMD_4x32&& other) = default;
+      SIMD_4x32(SIMD_4x32&& other) = default;
+
+      /**
+      * Zero initialize SIMD register with 4 32-bit elements
+      */
+      SIMD_4x32() // zero initialized
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_setzero_si128();
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_splat_u32(0);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vdupq_n_u32(0);
+#endif
+         }
+
+      /**
+      * Load SIMD register with 4 32-bit elements
+      */
+      explicit SIMD_4x32(const uint32_t B[4])
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_loadu_si128(reinterpret_cast<const __m128i*>(B));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         __vector unsigned int val = { B[0], B[1], B[2], B[3]};
+         m_simd = val;
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vld1q_u32(B);
+#endif
+         }
+
+      /**
+      * Load SIMD register with 4 32-bit elements
+      */
+      SIMD_4x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_set_epi32(B3, B2, B1, B0);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         __vector unsigned int val = {B0, B1, B2, B3};
+         m_simd = val;
+#elif defined(BOTAN_SIMD_USE_NEON)
+         // Better way to do this?
+         const uint32_t B[4] = { B0, B1, B2, B3 };
+         m_simd = vld1q_u32(B);
+#endif
+         }
+
+      /**
+      * Load SIMD register with one 32-bit element repeated
+      */
+      static SIMD_4x32 splat(uint32_t B)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_set1_epi32(B));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vdupq_n_u32(B));
+#else
+         return SIMD_4x32(B, B, B, B);
+#endif
+         }
+
+      /**
+      * Load SIMD register with one 8-bit element repeated
+      */
+      static SIMD_4x32 splat_u8(uint8_t B)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_set1_epi8(B));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vreinterpretq_u32_u8(vdupq_n_u8(B)));
+#else
+         const uint32_t B4 = make_uint32(B, B, B, B);
+         return SIMD_4x32(B4, B4, B4, B4);
+#endif
+         }
+
+      /**
+      * Load a SIMD register with little-endian convention
+      */
+      static SIMD_4x32 load_le(const void* in)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_loadu_si128(reinterpret_cast<const __m128i*>(in)));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         uint32_t R[4];
+         Botan::load_le(R, static_cast<const uint8_t*>(in), 4);
+         return SIMD_4x32(R);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(in)));
+         return CPUID::is_big_endian() ? l.bswap() : l;
+#endif
+         }
+
+      /**
+      * Load a SIMD register with big-endian convention
+      */
+      static SIMD_4x32 load_be(const void* in)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return load_le(in).bswap();
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         uint32_t R[4];
+         Botan::load_be(R, static_cast<const uint8_t*>(in), 4);
+         return SIMD_4x32(R);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+         SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(in)));
+         return CPUID::is_little_endian() ? l.bswap() : l;
+#endif
+         }
+
+      void store_le(uint32_t out[4]) const
+         {
+         this->store_le(reinterpret_cast<uint8_t*>(out));
+         }
+
+      void store_le(uint64_t out[2]) const
+         {
+         this->store_le(reinterpret_cast<uint8_t*>(out));
+         }
+
+      /**
+      * Load a SIMD register with little-endian convention
+      */
+      void store_le(uint8_t out[]) const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+
+         _mm_storeu_si128(reinterpret_cast<__m128i*>(out), raw());
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+         union {
+            __vector unsigned int V;
+            uint32_t R[4];
+            } vec;
+         vec.V = raw();
+         Botan::store_le(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+         if(CPUID::is_little_endian())
+            {
+            vst1q_u8(out, vreinterpretq_u8_u32(m_simd));
+            }
+         else
+            {
+            vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd));
+            }
+#endif
+         }
+
+      /**
+      * Load a SIMD register with big-endian convention
+      */
+      void store_be(uint8_t out[]) const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+
+         bswap().store_le(out);
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+         union {
+            __vector unsigned int V;
+            uint32_t R[4];
+            } vec;
+         vec.V = m_simd;
+         Botan::store_be(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+         if(CPUID::is_little_endian())
+            {
+            vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd));
+            }
+         else
+            {
+            vst1q_u8(out, vreinterpretq_u8_u32(m_simd));
+            }
+#endif
+         }
+
+      /*
+      * This is used for SHA-2/SHACAL2
+      * Return rotr(ROT1) ^ rotr(ROT2) ^ rotr(ROT3)
+      */
+      template<size_t ROT1, size_t ROT2, size_t ROT3>
+      SIMD_4x32 rho() const
+         {
+         const SIMD_4x32 rot1 = this->rotr<ROT1>();
+         const SIMD_4x32 rot2 = this->rotr<ROT2>();
+         const SIMD_4x32 rot3 = this->rotr<ROT3>();
+         return (rot1 ^ rot2 ^ rot3);
+         }
+
+      /**
+      * Left rotation by a compile time constant
+      */
+      template<size_t ROT>
+      SIMD_4x32 rotl() const
+         {
+         static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+
+         return SIMD_4x32(_mm_or_si128(_mm_slli_epi32(m_simd, static_cast<int>(ROT)),
+                                       _mm_srli_epi32(m_simd, static_cast<int>(32-ROT))));
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+         const unsigned int r = static_cast<unsigned int>(ROT);
+         __vector unsigned int rot = {r, r, r, r};
+         return SIMD_4x32(vec_rl(m_simd, rot));
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+
+#if defined(BOTAN_TARGET_ARCH_IS_ARM64)
+
+         BOTAN_IF_CONSTEXPR(ROT == 8)
+            {
+            const uint8_t maskb[16] = { 3,0,1,2, 7,4,5,6, 11,8,9,10, 15,12,13,14 };
+            const uint8x16_t mask = vld1q_u8(maskb);
+            return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(m_simd), mask)));
+            }
+         else BOTAN_IF_CONSTEXPR(ROT == 16)
+            {
+            return SIMD_4x32(vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(m_simd))));
+            }
+#endif
+         return SIMD_4x32(vorrq_u32(vshlq_n_u32(m_simd, static_cast<int>(ROT)),
+                                    vshrq_n_u32(m_simd, static_cast<int>(32-ROT))));
+#endif
+         }
+
+      /**
+      * Right rotation by a compile time constant
+      */
+      template<size_t ROT>
+      SIMD_4x32 rotr() const
+         {
+         return this->rotl<32-ROT>();
+         }
+
+      /**
+      * Add elements of a SIMD vector
+      */
+      SIMD_4x32 operator+(const SIMD_4x32& other) const
+         {
+         SIMD_4x32 retval(*this);
+         retval += other;
+         return retval;
+         }
+
+      /**
+      * Subtract elements of a SIMD vector
+      */
+      SIMD_4x32 operator-(const SIMD_4x32& other) const
+         {
+         SIMD_4x32 retval(*this);
+         retval -= other;
+         return retval;
+         }
+
+      /**
+      * XOR elements of a SIMD vector
+      */
+      SIMD_4x32 operator^(const SIMD_4x32& other) const
+         {
+         SIMD_4x32 retval(*this);
+         retval ^= other;
+         return retval;
+         }
+
+      /**
+      * Binary OR elements of a SIMD vector
+      */
+      SIMD_4x32 operator|(const SIMD_4x32& other) const
+         {
+         SIMD_4x32 retval(*this);
+         retval |= other;
+         return retval;
+         }
+
+      /**
+      * Binary AND elements of a SIMD vector
+      */
+      SIMD_4x32 operator&(const SIMD_4x32& other) const
+         {
+         SIMD_4x32 retval(*this);
+         retval &= other;
+         return retval;
+         }
+
+      void operator+=(const SIMD_4x32& other)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_add_epi32(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_add(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vaddq_u32(m_simd, other.m_simd);
+#endif
+         }
+
+      void operator-=(const SIMD_4x32& other)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_sub_epi32(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_sub(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vsubq_u32(m_simd, other.m_simd);
+#endif
+         }
+
+      void operator^=(const SIMD_4x32& other)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_xor_si128(m_simd, other.m_simd);
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_xor(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = veorq_u32(m_simd, other.m_simd);
+#endif
+         }
+
+      void operator|=(const SIMD_4x32& other)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_or_si128(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_or(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vorrq_u32(m_simd, other.m_simd);
+#endif
+         }
+
+      void operator&=(const SIMD_4x32& other)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         m_simd = _mm_and_si128(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         m_simd = vec_and(m_simd, other.m_simd);
+#elif defined(BOTAN_SIMD_USE_NEON)
+         m_simd = vandq_u32(m_simd, other.m_simd);
+#endif
+         }
+
+
+      template<int SHIFT> SIMD_4x32 shl() const
+         {
+         static_assert(SHIFT > 0 && SHIFT <= 31, "Invalid shift count");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_slli_epi32(m_simd, SHIFT));
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         const unsigned int s = static_cast<unsigned int>(SHIFT);
+         const __vector unsigned int shifts = {s, s, s, s};
+         return SIMD_4x32(vec_sl(m_simd, shifts));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vshlq_n_u32(m_simd, SHIFT));
+#endif
+         }
+
+      template<int SHIFT> SIMD_4x32 shr() const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_srli_epi32(m_simd, SHIFT));
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         const unsigned int s = static_cast<unsigned int>(SHIFT);
+         const __vector unsigned int shifts = {s, s, s, s};
+         return SIMD_4x32(vec_sr(m_simd, shifts));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vshrq_n_u32(m_simd, SHIFT));
+#endif
+         }
+
+      SIMD_4x32 operator~() const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_xor_si128(m_simd, _mm_set1_epi32(0xFFFFFFFF)));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         return SIMD_4x32(vec_nor(m_simd, m_simd));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vmvnq_u32(m_simd));
+#endif
+         }
+
+      // (~reg) & other
+      SIMD_4x32 andc(const SIMD_4x32& other) const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_andnot_si128(m_simd, other.m_simd));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         /*
+         AltiVec does arg1 & ~arg2 rather than SSE's ~arg1 & arg2
+         so swap the arguments
+         */
+         return SIMD_4x32(vec_andc(other.m_simd, m_simd));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         // NEON is also a & ~b
+         return SIMD_4x32(vbicq_u32(other.m_simd, m_simd));
+#endif
+         }
+
+      /**
+      * Return copy *this with each word byte swapped
+      */
+      SIMD_4x32 bswap() const
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+
+         __m128i T = m_simd;
+         T = _mm_shufflehi_epi16(T, _MM_SHUFFLE(2, 3, 0, 1));
+         T = _mm_shufflelo_epi16(T, _MM_SHUFFLE(2, 3, 0, 1));
+         return SIMD_4x32(_mm_or_si128(_mm_srli_epi16(T, 8), _mm_slli_epi16(T, 8)));
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+         union {
+            __vector unsigned int V;
+            uint32_t R[4];
+            } vec;
+
+         vec.V = m_simd;
+         bswap_4(vec.R);
+         return SIMD_4x32(vec.R[0], vec.R[1], vec.R[2], vec.R[3]);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(m_simd))));
+#endif
+         }
+
+      template<size_t I>
+      SIMD_4x32 shift_elems_left() const
+         {
+         static_assert(I <= 3, "Invalid shift count");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_slli_si128(raw(), 4*I));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vextq_u32(vdupq_n_u32(0), raw(), 4-I));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         const __vector unsigned int zero = vec_splat_u32(0);
+
+         const __vector unsigned char shuf[3] = {
+            { 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
+            { 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7 },
+            { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 1, 2, 3 },
+         };
+
+         return SIMD_4x32(vec_perm(raw(), zero, shuf[I-1]));
+#endif
+         }
+
+      template<size_t I>
+      SIMD_4x32 shift_elems_right() const
+         {
+         static_assert(I <= 3, "Invalid shift count");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+         return SIMD_4x32(_mm_srli_si128(raw(), 4*I));
+#elif defined(BOTAN_SIMD_USE_NEON)
+         return SIMD_4x32(vextq_u32(raw(), vdupq_n_u32(0), I));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         const __vector unsigned int zero = vec_splat_u32(0);
+
+         const __vector unsigned char shuf[3] = {
+            { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 },
+            { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 },
+            { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 },
+         };
+
+         return SIMD_4x32(vec_perm(raw(), zero, shuf[I-1]));
+#endif
+         }
+
+      /**
+      * 4x4 Transposition on SIMD registers
+      */
+      static void transpose(SIMD_4x32& B0, SIMD_4x32& B1,
+                            SIMD_4x32& B2, SIMD_4x32& B3)
+         {
+#if defined(BOTAN_SIMD_USE_SSE2)
+         const __m128i T0 = _mm_unpacklo_epi32(B0.m_simd, B1.m_simd);
+         const __m128i T1 = _mm_unpacklo_epi32(B2.m_simd, B3.m_simd);
+         const __m128i T2 = _mm_unpackhi_epi32(B0.m_simd, B1.m_simd);
+         const __m128i T3 = _mm_unpackhi_epi32(B2.m_simd, B3.m_simd);
+
+         B0.m_simd = _mm_unpacklo_epi64(T0, T1);
+         B1.m_simd = _mm_unpackhi_epi64(T0, T1);
+         B2.m_simd = _mm_unpacklo_epi64(T2, T3);
+         B3.m_simd = _mm_unpackhi_epi64(T2, T3);
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+         const __vector unsigned int T0 = vec_mergeh(B0.m_simd, B2.m_simd);
+         const __vector unsigned int T1 = vec_mergeh(B1.m_simd, B3.m_simd);
+         const __vector unsigned int T2 = vec_mergel(B0.m_simd, B2.m_simd);
+         const __vector unsigned int T3 = vec_mergel(B1.m_simd, B3.m_simd);
+
+         B0.m_simd = vec_mergeh(T0, T1);
+         B1.m_simd = vec_mergel(T0, T1);
+         B2.m_simd = vec_mergeh(T2, T3);
+         B3.m_simd = vec_mergel(T2, T3);
+
+#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM32)
+         const uint32x4x2_t T0 = vzipq_u32(B0.m_simd, B2.m_simd);
+         const uint32x4x2_t T1 = vzipq_u32(B1.m_simd, B3.m_simd);
+         const uint32x4x2_t O0 = vzipq_u32(T0.val[0], T1.val[0]);
+         const uint32x4x2_t O1 = vzipq_u32(T0.val[1], T1.val[1]);
+
+         B0.m_simd = O0.val[0];
+         B1.m_simd = O0.val[1];
+         B2.m_simd = O1.val[0];
+         B3.m_simd = O1.val[1];
+
+#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
+         const uint32x4_t T0 = vzip1q_u32(B0.m_simd, B2.m_simd);
+         const uint32x4_t T2 = vzip2q_u32(B0.m_simd, B2.m_simd);
+         const uint32x4_t T1 = vzip1q_u32(B1.m_simd, B3.m_simd);
+         const uint32x4_t T3 = vzip2q_u32(B1.m_simd, B3.m_simd);
+
+         B0.m_simd = vzip1q_u32(T0, T1);
+         B1.m_simd = vzip2q_u32(T0, T1);
+         B2.m_simd = vzip1q_u32(T2, T3);
+         B3.m_simd = vzip2q_u32(T2, T3);
+#endif
+         }
+
+      native_simd_type raw() const BOTAN_FUNC_ISA(BOTAN_SIMD_ISA) { return m_simd; }
+
+      explicit SIMD_4x32(native_simd_type x) : m_simd(x) {}
+   private:
+      native_simd_type m_simd;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h b/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h
@@ -0,0 +1,299 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SIMD_AVX2_H_
+#define BOTAN_SIMD_AVX2_H_
+
+#include <botan/types.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+class SIMD_8x32 final
+   {
+   public:
+
+      SIMD_8x32& operator=(const SIMD_8x32& other) = default;
+      SIMD_8x32(const SIMD_8x32& other) = default;
+
+      SIMD_8x32& operator=(SIMD_8x32&& other) = default;
+      SIMD_8x32(SIMD_8x32&& other) = default;
+
+      BOTAN_FUNC_ISA("avx2")
+      BOTAN_FORCE_INLINE SIMD_8x32()
+         {
+         m_avx2 = _mm256_setzero_si256();
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      explicit SIMD_8x32(const uint32_t B[8])
+         {
+         m_avx2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(B));
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      explicit SIMD_8x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3,
+                         uint32_t B4, uint32_t B5, uint32_t B6, uint32_t B7)
+         {
+         m_avx2 = _mm256_set_epi32(B7, B6, B5, B4, B3, B2, B1, B0);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static SIMD_8x32 splat(uint32_t B)
+         {
+         return SIMD_8x32(_mm256_set1_epi32(B));
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static SIMD_8x32 load_le(const uint8_t* in)
+         {
+         return SIMD_8x32(_mm256_loadu_si256(reinterpret_cast<const __m256i*>(in)));
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static SIMD_8x32 load_be(const uint8_t* in)
+         {
+         return load_le(in).bswap();
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void store_le(uint8_t out[]) const
+         {
+         _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_avx2);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void store_be(uint8_t out[]) const
+         {
+         bswap().store_le(out);
+         }
+
+      template<size_t ROT>
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 rotl() const
+         {
+         static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant");
+
+#if defined(__AVX512VL__)
+         return SIMD_8x32(_mm256_rol_epi32(m_avx2, ROT));
+#else
+         BOTAN_IF_CONSTEXPR(ROT == 8)
+            {
+            const __m256i shuf_rotl_8 = _mm256_set_epi8(14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3,
+                                                        14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3);
+
+            return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_8));
+            }
+         else BOTAN_IF_CONSTEXPR(ROT == 16)
+            {
+            const __m256i shuf_rotl_16 = _mm256_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2,
+                                                         13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2);
+
+            return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_16));
+            }
+         else
+            {
+            return SIMD_8x32(_mm256_or_si256(_mm256_slli_epi32(m_avx2, static_cast<int>(ROT)),
+                                             _mm256_srli_epi32(m_avx2, static_cast<int>(32-ROT))));
+            }
+#endif
+         }
+
+      template<size_t ROT>
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 rotr() const
+         {
+         return this->rotl<32-ROT>();
+         }
+
+      template<size_t ROT1, size_t ROT2, size_t ROT3>
+      SIMD_8x32 BOTAN_FUNC_ISA("avx2") rho() const
+         {
+         SIMD_8x32 res;
+
+         const SIMD_8x32 rot1 = this->rotr<ROT1>();
+         const SIMD_8x32 rot2 = this->rotr<ROT2>();
+         const SIMD_8x32 rot3 = this->rotr<ROT3>();
+
+         return rot1 ^ rot2 ^ rot3;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator+(const SIMD_8x32& other) const
+         {
+         SIMD_8x32 retval(*this);
+         retval += other;
+         return retval;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator-(const SIMD_8x32& other) const
+         {
+         SIMD_8x32 retval(*this);
+         retval -= other;
+         return retval;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator^(const SIMD_8x32& other) const
+         {
+         SIMD_8x32 retval(*this);
+         retval ^= other;
+         return retval;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator|(const SIMD_8x32& other) const
+         {
+         SIMD_8x32 retval(*this);
+         retval |= other;
+         return retval;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator&(const SIMD_8x32& other) const
+         {
+         SIMD_8x32 retval(*this);
+         retval &= other;
+         return retval;
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void operator+=(const SIMD_8x32& other)
+         {
+         m_avx2 = _mm256_add_epi32(m_avx2, other.m_avx2);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void operator-=(const SIMD_8x32& other)
+         {
+         m_avx2 = _mm256_sub_epi32(m_avx2, other.m_avx2);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void operator^=(const SIMD_8x32& other)
+         {
+         m_avx2 = _mm256_xor_si256(m_avx2, other.m_avx2);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void operator|=(const SIMD_8x32& other)
+         {
+         m_avx2 = _mm256_or_si256(m_avx2, other.m_avx2);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      void operator&=(const SIMD_8x32& other)
+         {
+         m_avx2 = _mm256_and_si256(m_avx2, other.m_avx2);
+         }
+
+      template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shl() const
+         {
+         return SIMD_8x32(_mm256_slli_epi32(m_avx2, SHIFT));
+         }
+
+      template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shr() const
+         {
+         return SIMD_8x32(_mm256_srli_epi32(m_avx2, SHIFT));
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 operator~() const
+         {
+         return SIMD_8x32(_mm256_xor_si256(m_avx2, _mm256_set1_epi32(0xFFFFFFFF)));
+         }
+
+      // (~reg) & other
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 andc(const SIMD_8x32& other) const
+         {
+         return SIMD_8x32(_mm256_andnot_si256(m_avx2, other.m_avx2));
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32 bswap() const
+         {
+         const uint8_t BSWAP_MASK[32] = { 3, 2, 1, 0,
+                                          7, 6, 5, 4,
+                                          11, 10, 9, 8,
+                                          15, 14, 13, 12,
+                                          19, 18, 17, 16,
+                                          23, 22, 21, 20,
+                                          27, 26, 25, 24,
+                                          31, 30, 29, 28 };
+
+         const __m256i bswap = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(BSWAP_MASK));
+
+         const __m256i output = _mm256_shuffle_epi8(m_avx2, bswap);
+
+         return SIMD_8x32(output);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static void transpose(SIMD_8x32& B0, SIMD_8x32& B1,
+                            SIMD_8x32& B2, SIMD_8x32& B3)
+         {
+         const __m256i T0 = _mm256_unpacklo_epi32(B0.m_avx2, B1.m_avx2);
+         const __m256i T1 = _mm256_unpacklo_epi32(B2.m_avx2, B3.m_avx2);
+         const __m256i T2 = _mm256_unpackhi_epi32(B0.m_avx2, B1.m_avx2);
+         const __m256i T3 = _mm256_unpackhi_epi32(B2.m_avx2, B3.m_avx2);
+
+         B0.m_avx2 = _mm256_unpacklo_epi64(T0, T1);
+         B1.m_avx2 = _mm256_unpackhi_epi64(T0, T1);
+         B2.m_avx2 = _mm256_unpacklo_epi64(T2, T3);
+         B3.m_avx2 = _mm256_unpackhi_epi64(T2, T3);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static void transpose(SIMD_8x32& B0, SIMD_8x32& B1,
+                            SIMD_8x32& B2, SIMD_8x32& B3,
+                            SIMD_8x32& B4, SIMD_8x32& B5,
+                            SIMD_8x32& B6, SIMD_8x32& B7)
+         {
+         transpose(B0, B1, B2, B3);
+         transpose(B4, B5, B6, B7);
+
+         swap_tops(B0, B4);
+         swap_tops(B1, B5);
+         swap_tops(B2, B6);
+         swap_tops(B3, B7);
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static void reset_registers()
+         {
+         _mm256_zeroupper();
+         }
+
+      BOTAN_FUNC_ISA("avx2")
+      static void zero_registers()
+         {
+         _mm256_zeroall();
+         }
+
+      __m256i BOTAN_FUNC_ISA("avx2") handle() const { return m_avx2; }
+
+      BOTAN_FUNC_ISA("avx2")
+      SIMD_8x32(__m256i x) : m_avx2(x) {}
+
+   private:
+
+      BOTAN_FUNC_ISA("avx2")
+      static void swap_tops(SIMD_8x32& A, SIMD_8x32& B)
+         {
+         SIMD_8x32 T0 = _mm256_permute2x128_si256(A.handle(), B.handle(), 0 + (2 << 4));
+         SIMD_8x32 T1 = _mm256_permute2x128_si256(A.handle(), B.handle(), 1 + (3 << 4));
+         A = T0;
+         B = T1;
+         }
+
+      __m256i m_avx2;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/socket/socket.cpp b/third_party/botan/src/lib/utils/socket/socket.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/socket.cpp
@@ -0,0 +1,371 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2016 Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/socket.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <chrono>
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+  /*
+  * We don't need serial port support anyway, and asking for it causes
+  * macro conflicts with termios.h when this file is included in the
+  * amalgamation.
+  */
+  #define BOOST_ASIO_DISABLE_SERIAL_PORT
+  #include <boost/asio.hpp>
+  #include <boost/asio/system_timer.hpp>
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+  #include <sys/socket.h>
+  #include <sys/time.h>
+  #include <netinet/in.h>
+  #include <netdb.h>
+  #include <string.h>
+  #include <unistd.h>
+  #include <errno.h>
+  #include <fcntl.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+  #include <ws2tcpip.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+
+class Asio_Socket final : public OS::Socket
+   {
+   public:
+      Asio_Socket(const std::string& hostname,
+                  const std::string& service,
+                  std::chrono::milliseconds timeout) :
+         m_timeout(timeout), m_timer(m_io), m_tcp(m_io)
+         {
+         m_timer.expires_from_now(m_timeout);
+         check_timeout();
+
+         boost::asio::ip::tcp::resolver resolver(m_io);
+         boost::asio::ip::tcp::resolver::query query(hostname, service);
+         boost::asio::ip::tcp::resolver::iterator dns_iter = resolver.resolve(query);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+
+         auto connect_cb = [&ec](const boost::system::error_code& e,
+                                 boost::asio::ip::tcp::resolver::iterator) { ec = e; };
+
+         boost::asio::async_connect(m_tcp, dns_iter, connect_cb);
+
+         while(ec == boost::asio::error::would_block)
+            {
+            m_io.run_one();
+            }
+
+         if(ec)
+            throw boost::system::system_error(ec);
+         if(m_tcp.is_open() == false)
+            throw System_Error("Connection to host " + hostname + " failed");
+         }
+
+      void write(const uint8_t buf[], size_t len) override
+         {
+         m_timer.expires_from_now(m_timeout);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+
+         m_tcp.async_send(boost::asio::buffer(buf, len),
+                           [&ec](boost::system::error_code e, size_t) { ec = e; });
+
+         while(ec == boost::asio::error::would_block) { m_io.run_one(); }
+
+         if(ec)
+            {
+            throw boost::system::system_error(ec);
+            }
+         }
+
+      size_t read(uint8_t buf[], size_t len) override
+         {
+         m_timer.expires_from_now(m_timeout);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+         size_t got = 0;
+
+         m_tcp.async_read_some(boost::asio::buffer(buf, len),
+                               [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; });
+
+         while(ec == boost::asio::error::would_block) { m_io.run_one(); }
+
+         if(ec)
+            {
+            if(ec == boost::asio::error::eof)
+               return 0;
+            throw boost::system::system_error(ec); // Some other error.
+            }
+
+         return got;
+         }
+
+   private:
+      void check_timeout()
+         {
+         if(m_tcp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now())
+            {
+            boost::system::error_code err;
+            m_tcp.close(err);
+            }
+
+         m_timer.async_wait(std::bind(&Asio_Socket::check_timeout, this));
+         }
+
+      const std::chrono::milliseconds m_timeout;
+      boost::asio::io_service m_io;
+      boost::asio::system_timer m_timer;
+      boost::asio::ip::tcp::socket m_tcp;
+   };
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+class BSD_Socket final : public OS::Socket
+   {
+   private:
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+      typedef SOCKET socket_type;
+      typedef int socket_op_ret_type;
+      typedef int socklen_type;
+      typedef int sendrecv_len_type;
+      static socket_type invalid_socket() { return INVALID_SOCKET; }
+      static void close_socket(socket_type s) { ::closesocket(s); }
+      static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
+
+      static bool nonblocking_connect_in_progress()
+         {
+         return (::WSAGetLastError() == WSAEWOULDBLOCK);
+         }
+
+      static void set_nonblocking(socket_type s)
+         {
+         u_long nonblocking = 1;
+         ::ioctlsocket(s, FIONBIO, &nonblocking);
+         }
+
+      static void socket_init()
+         {
+         WSAData wsa_data;
+         WORD wsa_version = MAKEWORD(2, 2);
+
+         if (::WSAStartup(wsa_version, &wsa_data) != 0)
+            {
+            throw System_Error("WSAStartup() failed", WSAGetLastError());
+            }
+
+         if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+            {
+            ::WSACleanup();
+            throw System_Error("Could not find a usable version of Winsock.dll");
+            }
+         }
+
+      static void socket_fini()
+         {
+         ::WSACleanup();
+         }
+#else
+      typedef int socket_type;
+      typedef ssize_t socket_op_ret_type;
+      typedef socklen_t socklen_type;
+      typedef size_t sendrecv_len_type;
+      static socket_type invalid_socket() { return -1; }
+      static void close_socket(socket_type s) { ::close(s); }
+      static std::string get_last_socket_error() { return ::strerror(errno); }
+      static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
+      static void set_nonblocking(socket_type s)
+         {
+         if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+            throw System_Error("Setting socket to non-blocking state failed", errno);
+         }
+
+      static void socket_init() {}
+      static void socket_fini() {}
+#endif
+
+   public:
+      BSD_Socket(const std::string& hostname,
+                 const std::string& service,
+                 std::chrono::microseconds timeout) : m_timeout(timeout)
+         {
+         socket_init();
+
+         m_socket = invalid_socket();
+
+         addrinfo hints;
+         clear_mem(&hints, 1);
+         hints.ai_family = AF_UNSPEC;
+         hints.ai_socktype = SOCK_STREAM;
+         addrinfo* res;
+
+         int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+
+         if(rc != 0)
+            {
+            throw System_Error("Name resolution failed for " + hostname, rc);
+            }
+
+         for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next)
+            {
+            if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+               continue;
+
+            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+            if(m_socket == invalid_socket())
+               {
+               // unsupported socket type?
+               continue;
+               }
+
+            set_nonblocking(m_socket);
+
+            int err = ::connect(m_socket, rp->ai_addr, static_cast<socklen_type>(rp->ai_addrlen));
+
+            if(err == -1)
+               {
+               int active = 0;
+               if(nonblocking_connect_in_progress())
+                  {
+                  struct timeval timeout_tv = make_timeout_tv();
+                  fd_set write_set;
+                  FD_ZERO(&write_set);
+                  // Weirdly, Winsock uses a SOCKET type but wants FD_SET to get an int instead
+                  FD_SET(static_cast<int>(m_socket), &write_set);
+
+                  active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout_tv);
+
+                  if(active)
+                     {
+                     int socket_error = 0;
+                     socklen_t len = sizeof(socket_error);
+
+                     if(::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&socket_error), &len) < 0)
+                        throw System_Error("Error calling getsockopt", errno);
+
+                     if(socket_error != 0)
+                        {
+                        active = 0;
+                        }
+                     }
+                  }
+
+               if(active == 0)
+                  {
+                  close_socket(m_socket);
+                  m_socket = invalid_socket();
+                  continue;
+                  }
+               }
+            }
+
+         ::freeaddrinfo(res);
+
+         if(m_socket == invalid_socket())
+            {
+            throw System_Error("Connecting to " + hostname +
+                                " for service " + service + " failed", errno);
+            }
+         }
+
+      ~BSD_Socket()
+         {
+         close_socket(m_socket);
+         m_socket = invalid_socket();
+         socket_fini();
+         }
+
+      void write(const uint8_t buf[], size_t len) override
+         {
+         fd_set write_set;
+         FD_ZERO(&write_set);
+         FD_SET(m_socket, &write_set);
+
+         size_t sent_so_far = 0;
+         while(sent_so_far != len)
+            {
+            struct timeval timeout = make_timeout_tv();
+            int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
+
+            if(active == 0)
+               throw System_Error("Timeout during socket write");
+
+            const size_t left = len - sent_so_far;
+            socket_op_ret_type sent = ::send(m_socket, cast_uint8_ptr_to_char(&buf[sent_so_far]), static_cast<sendrecv_len_type>(left), 0);
+            if(sent < 0)
+               throw System_Error("Socket write failed", errno);
+            else
+               sent_so_far += static_cast<size_t>(sent);
+            }
+         }
+
+      size_t read(uint8_t buf[], size_t len) override
+         {
+         fd_set read_set;
+         FD_ZERO(&read_set);
+         FD_SET(m_socket, &read_set);
+
+         struct timeval timeout = make_timeout_tv();
+         int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
+
+         if(active == 0)
+            throw System_Error("Timeout during socket read");
+
+         socket_op_ret_type got = ::recv(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0);
+
+         if(got < 0)
+            throw System_Error("Socket read failed", errno);
+
+         return static_cast<size_t>(got);
+         }
+
+   private:
+      struct timeval make_timeout_tv() const
+         {
+         struct timeval tv;
+         tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
+         tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);;
+         return tv;
+         }
+
+      const std::chrono::microseconds m_timeout;
+      socket_type m_socket;
+   };
+
+#endif
+
+}
+
+std::unique_ptr<OS::Socket>
+OS::open_socket(const std::string& hostname,
+                const std::string& service,
+                std::chrono::milliseconds timeout)
+   {
+#if defined(BOTAN_HAS_BOOST_ASIO)
+   return std::unique_ptr<OS::Socket>(new Asio_Socket(hostname, service, timeout));
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+   return std::unique_ptr<OS::Socket>(new BSD_Socket(hostname, service, timeout));
+
+#else
+   BOTAN_UNUSED(hostname);
+   BOTAN_UNUSED(service);
+   BOTAN_UNUSED(timeout);
+   // No sockets for you
+   return std::unique_ptr<Socket>();
+#endif
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/socket/socket.h b/third_party/botan/src/lib/utils/socket/socket.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/socket.h
@@ -0,0 +1,64 @@
+/*
+* OS specific utility functions
+* (C) 2015,2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SOCKET_H_
+#define BOTAN_SOCKET_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+
+/**
+* A wrapper around a simple blocking TCP socket
+*/
+class BOTAN_TEST_API Socket
+   {
+   public:
+      /**
+      * The socket will be closed upon destruction
+      */
+      virtual ~Socket() = default;
+
+      /**
+      * Write to the socket. Blocks until all bytes sent.
+      * Throws on error.
+      */
+      virtual void write(const uint8_t buf[], size_t len) = 0;
+
+      /**
+      * Reads up to len bytes, returns bytes written to buf.
+      * Returns 0 on EOF. Throws on error.
+      */
+      virtual size_t read(uint8_t buf[], size_t len) = 0;
+   };
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<Socket>
+BOTAN_TEST_API open_socket(const std::string& hostname,
+                           const std::string& service,
+                           std::chrono::milliseconds timeout);
+
+} // OS
+} // Botan
+
+#endif
diff --git a/third_party/botan/src/lib/utils/socket/socket_udp.cpp b/third_party/botan/src/lib/utils/socket/socket_udp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/socket_udp.cpp
@@ -0,0 +1,344 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2016 Daniel Neus
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/socket_udp.h>
+#include <botan/internal/uri.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <chrono>
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+   /*
+   * We don't need serial port support anyway, and asking for it
+   * causes macro conflicts with Darwin's termios.h when this
+   * file is included in the amalgamation. GH #350
+   */
+   #define BOOST_ASIO_DISABLE_SERIAL_PORT
+   #include <boost/asio.hpp>
+   #include <boost/asio/system_timer.hpp>
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+   #include <sys/socket.h>
+   #include <sys/time.h>
+   #include <netinet/in.h>
+   #include <netdb.h>
+   #include <string.h>
+   #include <unistd.h>
+   #include <errno.h>
+   #include <fcntl.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+   #include <ws2tcpip.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+class Asio_SocketUDP final : public OS::SocketUDP
+   {
+   public:
+      Asio_SocketUDP(const std::string& hostname,
+                     const std::string& service,
+                     std::chrono::microseconds timeout) :
+         m_timeout(timeout), m_timer(m_io), m_udp(m_io)
+         {
+         m_timer.expires_from_now(m_timeout);
+         check_timeout();
+
+         boost::asio::ip::udp::resolver resolver(m_io);
+         boost::asio::ip::udp::resolver::query query(hostname, service);
+         boost::asio::ip::udp::resolver::iterator dns_iter = resolver.resolve(query);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+
+         auto connect_cb = [&ec](const boost::system::error_code& e,
+         boost::asio::ip::udp::resolver::iterator) { ec = e; };
+
+         boost::asio::async_connect(m_udp, dns_iter, connect_cb);
+
+         while(ec == boost::asio::error::would_block)
+            {
+            m_io.run_one();
+            }
+
+         if(ec)
+            { throw boost::system::system_error(ec); }
+         if(m_udp.is_open() == false)
+            { throw System_Error("Connection to host " + hostname + " failed"); }
+         }
+
+      void write(const uint8_t buf[], size_t len) override
+         {
+         m_timer.expires_from_now(m_timeout);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+
+         m_udp.async_send(boost::asio::buffer(buf, len),
+         [&ec](boost::system::error_code e, size_t) { ec = e; });
+
+         while(ec == boost::asio::error::would_block)
+            {
+            m_io.run_one();
+            }
+
+         if(ec)
+            {
+            throw boost::system::system_error(ec);
+            }
+         }
+
+      size_t read(uint8_t buf[], size_t len) override
+         {
+         m_timer.expires_from_now(m_timeout);
+
+         boost::system::error_code ec = boost::asio::error::would_block;
+         size_t got = 0;
+
+         m_udp.async_receive(boost::asio::buffer(buf, len),
+         [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; });
+
+         while(ec == boost::asio::error::would_block)
+            {
+            m_io.run_one();
+            }
+
+         if(ec)
+            {
+            if(ec == boost::asio::error::eof)
+               { return 0; }
+            throw boost::system::system_error(ec); // Some other error.
+            }
+
+         return got;
+         }
+
+   private:
+      void check_timeout()
+         {
+         if(m_udp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now())
+            {
+            boost::system::error_code err;
+            m_udp.close(err);
+            }
+
+         m_timer.async_wait(std::bind(&Asio_SocketUDP::check_timeout, this));
+         }
+
+      const std::chrono::microseconds m_timeout;
+      boost::asio::io_service m_io;
+      boost::asio::system_timer m_timer;
+      boost::asio::ip::udp::socket m_udp;
+   };
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+class BSD_SocketUDP final : public OS::SocketUDP
+   {
+   public:
+      BSD_SocketUDP(const std::string& hostname,
+                    const std::string& service,
+                    std::chrono::microseconds timeout) : m_timeout(timeout)
+         {
+         socket_init();
+
+         m_socket = invalid_socket();
+
+         addrinfo* res;
+         addrinfo hints;
+         clear_mem(&hints, 1);
+         hints.ai_family = AF_UNSPEC;
+         hints.ai_socktype = SOCK_DGRAM;
+
+         int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+
+         if(rc != 0)
+            {
+            throw System_Error("Name resolution failed for " + hostname, rc);
+            }
+
+         for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next)
+            {
+            if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+               { continue; }
+
+            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+            if(m_socket == invalid_socket())
+               {
+               // unsupported socket type?
+               continue;
+               }
+
+            set_nonblocking(m_socket);
+            memcpy(&sa, res->ai_addr, res->ai_addrlen);
+            salen = static_cast<socklen_t>(res->ai_addrlen);
+            }
+
+         ::freeaddrinfo(res);
+
+         if(m_socket == invalid_socket())
+            {
+            throw System_Error("Connecting to " + hostname +
+                               " for service " + service + " failed", errno);
+            }
+         }
+
+      ~BSD_SocketUDP()
+         {
+         close_socket(m_socket);
+         m_socket = invalid_socket();
+         socket_fini();
+         }
+
+      void write(const uint8_t buf[], size_t len) override
+         {
+         fd_set write_set;
+         FD_ZERO(&write_set);
+         FD_SET(m_socket, &write_set);
+
+         size_t sent_so_far = 0;
+         while(sent_so_far != len)
+            {
+            struct timeval timeout = make_timeout_tv();
+            int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
+
+            if(active == 0)
+               { throw System_Error("Timeout during socket write"); }
+
+            const size_t left = len - sent_so_far;
+            socket_op_ret_type sent = ::sendto(m_socket, cast_uint8_ptr_to_char(buf + sent_so_far),
+                                               static_cast<sendrecv_len_type>(left), 0,
+                                               reinterpret_cast<sockaddr*>(&sa), salen);
+            if(sent < 0)
+               { throw System_Error("Socket write failed", errno); }
+            else
+               { sent_so_far += static_cast<size_t>(sent); }
+            }
+         }
+
+      size_t read(uint8_t buf[], size_t len) override
+         {
+         fd_set read_set;
+         FD_ZERO(&read_set);
+         FD_SET(m_socket, &read_set);
+
+         struct timeval timeout = make_timeout_tv();
+         int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
+
+         if(active == 0)
+            { throw System_Error("Timeout during socket read"); }
+
+         socket_op_ret_type got = ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0, nullptr, nullptr);
+
+         if(got < 0)
+            { throw System_Error("Socket read failed", errno); }
+
+         return static_cast<size_t>(got);
+         }
+
+   private:
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+      typedef SOCKET socket_type;
+      typedef int socket_op_ret_type;
+      typedef int sendrecv_len_type;
+      static socket_type invalid_socket() { return INVALID_SOCKET; }
+      static void close_socket(socket_type s) { ::closesocket(s); }
+      static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
+
+      static bool nonblocking_connect_in_progress()
+         {
+         return (::WSAGetLastError() == WSAEWOULDBLOCK);
+         }
+
+      static void set_nonblocking(socket_type s)
+         {
+         u_long nonblocking = 1;
+         ::ioctlsocket(s, FIONBIO, &nonblocking);
+         }
+
+      static void socket_init()
+         {
+         WSAData wsa_data;
+         WORD wsa_version = MAKEWORD(2, 2);
+
+         if(::WSAStartup(wsa_version, &wsa_data) != 0)
+            {
+            throw System_Error("WSAStartup() failed", WSAGetLastError());
+            }
+
+         if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+            {
+            ::WSACleanup();
+            throw System_Error("Could not find a usable version of Winsock.dll");
+            }
+         }
+
+      static void socket_fini()
+         {
+         ::WSACleanup();
+         }
+#else
+      typedef int socket_type;
+      typedef ssize_t socket_op_ret_type;
+      typedef size_t sendrecv_len_type;
+      static socket_type invalid_socket() { return -1; }
+      static void close_socket(socket_type s) { ::close(s); }
+      static std::string get_last_socket_error() { return ::strerror(errno); }
+      static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
+      static void set_nonblocking(socket_type s)
+         {
+         if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+            { throw System_Error("Setting socket to non-blocking state failed", errno); }
+         }
+
+      static void socket_init() {}
+      static void socket_fini() {}
+#endif
+      sockaddr_storage sa;
+      socklen_t salen;
+      struct timeval make_timeout_tv() const
+         {
+         struct timeval tv;
+         tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
+         tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);;
+         return tv;
+         }
+
+      const std::chrono::microseconds m_timeout;
+      socket_type m_socket;
+   };
+#endif
+}
+
+std::unique_ptr<OS::SocketUDP>
+OS::open_socket_udp(const std::string& hostname,
+                    const std::string& service,
+                    std::chrono::microseconds timeout)
+   {
+#if defined(BOTAN_HAS_BOOST_ASIO)
+   return std::unique_ptr<OS::SocketUDP>(new Asio_SocketUDP(hostname, service, timeout));
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+   return std::unique_ptr<OS::SocketUDP>(new BSD_SocketUDP(hostname, service, timeout));
+#else
+   BOTAN_UNUSED(hostname);
+   BOTAN_UNUSED(service);
+   BOTAN_UNUSED(timeout);
+   return std::unique_ptr<OS::SocketUDP>();
+#endif
+   }
+
+std::unique_ptr<OS::SocketUDP>
+OS::open_socket_udp(const std::string& uri_string,
+                    std::chrono::microseconds timeout)
+   {
+   const auto uri = URI::fromAny(uri_string);
+   if(uri.port == 0)
+      { throw Invalid_Argument("UDP port not specified"); }
+   return open_socket_udp(uri.host, std::to_string(uri.port), timeout);
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/socket/socket_udp.h b/third_party/botan/src/lib/utils/socket/socket_udp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/socket_udp.h
@@ -0,0 +1,73 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SOCKET_UDP_H_
+#define BOTAN_SOCKET_UDP_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+
+/**
+* A wrapper around a simple blocking UDP socket
+*/
+class BOTAN_TEST_API SocketUDP
+   {
+   public:
+      /**
+      * The socket will be closed upon destruction
+      */
+      virtual ~SocketUDP() = default;
+
+      /**
+      * Write to the socket. Returns immediately.
+      * Throws on error.
+      */
+      virtual void write(const uint8_t buf[], size_t len) = 0;
+
+      /**
+      * Reads up to len bytes, returns bytes written to buf.
+      * Returns 0 on EOF. Throws on error.
+      */
+      virtual size_t read(uint8_t buf[], size_t len) = 0;
+   };
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<SocketUDP>
+BOTAN_TEST_API open_socket_udp(const std::string& hostname,
+                               const std::string& service,
+                               std::chrono::microseconds timeout);
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<SocketUDP>
+BOTAN_TEST_API open_socket_udp(const std::string& uri,
+                               std::chrono::microseconds timeout);
+
+
+} // OS
+} // Botan
+
+#endif
diff --git a/third_party/botan/src/lib/utils/socket/uri.cpp b/third_party/botan/src/lib/utils/socket/uri.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/uri.cpp
@@ -0,0 +1,188 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/uri.h>
+#include <botan/exceptn.h>
+
+#include <regex>
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+   #include <arpa/inet.h>
+   #include <sys/socket.h>
+   #include <netinet/in.h>
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+   #include <ws2tcpip.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+namespace {
+
+constexpr bool isdigit(char ch)
+   {
+   return ch >= '0' && ch <= '9';
+   }
+
+bool isDomain(const std::string& domain)
+   {
+#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726)
+   // GCC 4.8 does not support regex
+   BOTAN_UNUSED(domain);
+   return true;
+#else
+   std::regex re(
+      R"(^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$)");
+   std::cmatch m;
+   return std::regex_match(domain.c_str(), m, re);
+#endif
+   }
+
+bool isIPv4(const std::string& ip)
+   {
+   sockaddr_storage inaddr;
+   return !!inet_pton(AF_INET, ip.c_str(), &inaddr);
+   }
+
+bool isIPv6(const std::string& ip)
+   {
+   sockaddr_storage in6addr;
+   return !!inet_pton(AF_INET6, ip.c_str(), &in6addr);
+   }
+}
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string& uri)
+   {
+   unsigned port = 0;
+   const auto port_pos = uri.find(':');
+   if(port_pos != std::string::npos)
+      {
+      for(char c : uri.substr(port_pos+1))
+         {
+         if(!isdigit(c))
+            { throw Invalid_Argument("invalid"); }
+         port = port*10 + c - '0';
+         if(port > 65535)
+            { throw Invalid_Argument("invalid"); }
+         }
+      }
+   const auto domain = uri.substr(0, port_pos);
+   if(isIPv4(domain))
+      { throw Invalid_Argument("invalid"); }
+   if(!isDomain(domain))
+      { throw Invalid_Argument("invalid"); }
+   return {Type::Domain, domain, uint16_t(port)};
+   }
+
+URI URI::fromIPv4(const std::string& uri)
+   {
+   unsigned port = 0;
+   const auto port_pos = uri.find(':');
+   if(port_pos != std::string::npos)
+      {
+      for(char c : uri.substr(port_pos+1))
+         {
+         if(!isdigit(c))
+            { throw Invalid_Argument("invalid"); }
+         port = port*10 + c - '0';
+         if(port > 65535)
+            { throw Invalid_Argument("invalid"); }
+         }
+      }
+   const auto ip = uri.substr(0, port_pos);
+   if(!isIPv4(ip))
+      { throw Invalid_Argument("invalid"); }
+   return { Type::IPv4, ip, uint16_t(port) };
+   }
+
+URI URI::fromIPv6(const std::string& uri)
+   {
+   unsigned port = 0;
+   const auto port_pos = uri.find(']');
+   const bool with_braces = (port_pos != std::string::npos);
+   if((uri[0]=='[') != with_braces)
+      { throw Invalid_Argument("invalid"); }
+
+   if(with_braces && (uri.size() > port_pos + 1))
+      {
+      if(uri[port_pos+1]!=':')
+         { throw Invalid_Argument("invalid"); }
+      for(char c : uri.substr(port_pos+2))
+         {
+         if(!isdigit(c))
+            { throw Invalid_Argument("invalid"); }
+         port = port*10 + c - '0';
+         if(port > 65535)
+            { throw Invalid_Argument("invalid"); }
+         }
+      }
+   const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
+   if(!isIPv6(ip))
+      { throw Invalid_Argument("invalid"); }
+   return { Type::IPv6, ip, uint16_t(port) };
+   }
+
+URI URI::fromAny(const std::string& uri)
+   {
+
+   bool colon_seen=false;
+   bool non_number=false;
+   if(uri[0]=='[')
+      { return fromIPv6(uri); }
+   for(auto c : uri)
+      {
+      if(c == ':')
+         {
+         if(colon_seen) //seen two ':'
+            { return fromIPv6(uri); }
+         colon_seen = true;
+         }
+      else if(!isdigit(c) && c !=  '.')
+         {
+         non_number=true;
+         }
+      }
+   if(!non_number)
+      {
+      if(isIPv4(uri.substr(0, uri.find(':'))))
+         {
+         return fromIPv4(uri);
+         }
+      }
+   return fromDomain(uri);
+   }
+
+std::string URI::to_string() const
+   {
+   if(type == Type::NotSet)
+      {
+      throw Invalid_Argument("not set");
+      }
+
+   if(port != 0)
+      {
+      if(type == Type::IPv6)
+         { return "[" + host + "]:" + std::to_string(port); }
+      return host + ":" + std::to_string(port);
+      }
+   return host;
+   }
+
+}
+
+#else
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/socket/uri.h b/third_party/botan/src/lib/utils/socket/uri.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/socket/uri.h
@@ -0,0 +1,49 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_URI_H_
+#define BOTAN_URI_H_
+
+#include <cstdint>
+#include <string>
+
+#include <botan/build.h>
+
+namespace Botan {
+
+struct BOTAN_TEST_API URI
+   {
+   enum class Type : uint8_t
+      {
+      NotSet,
+      IPv4,
+      IPv6,
+      Domain,
+      };
+   static URI fromAny(const std::string& uri);
+   static URI fromIPv4(const std::string& uri);
+   static URI fromIPv6(const std::string& uri);
+   static URI fromDomain(const std::string& uri);
+   URI() = default;
+   URI(Type xtype, const std::string& xhost, unsigned short xport)
+      : type { xtype }
+      , host { xhost }
+      , port { xport }
+      {}
+   bool operator==(const URI& a) const
+      {
+      return type == a.type && host == a.host && port == a.port;
+      }
+   std::string to_string() const;
+
+   const Type type{Type::NotSet};
+   const std::string host{};
+   const uint16_t port{};
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp b/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp
@@ -0,0 +1,166 @@
+/*
+* SQLite wrapper
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sqlite3.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <sqlite3.h>
+
+namespace Botan {
+
+Sqlite3_Database::Sqlite3_Database(const std::string& db_filename)
+   {
+   int rc = ::sqlite3_open(db_filename.c_str(), &m_db);
+
+   if(rc)
+      {
+      const std::string err_msg = ::sqlite3_errmsg(m_db);
+      ::sqlite3_close(m_db);
+      m_db = nullptr;
+      throw SQL_DB_Error("sqlite3_open failed - " + err_msg);
+      }
+   }
+
+Sqlite3_Database::~Sqlite3_Database()
+   {
+   if(m_db)
+      ::sqlite3_close(m_db);
+   m_db = nullptr;
+   }
+
+std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::new_statement(const std::string& base_sql) const
+   {
+   return std::make_shared<Sqlite3_Statement>(m_db, base_sql);
+   }
+
+size_t Sqlite3_Database::row_count(const std::string& table_name)
+   {
+   auto stmt = new_statement("select count(*) from " + table_name);
+
+   if(stmt->step())
+      return stmt->get_size_t(0);
+   else
+      throw SQL_DB_Error("Querying size of table " + table_name + " failed");
+   }
+
+void Sqlite3_Database::create_table(const std::string& table_schema)
+   {
+   char* errmsg = nullptr;
+   int rc = ::sqlite3_exec(m_db, table_schema.c_str(), nullptr, nullptr, &errmsg);
+
+   if(rc != SQLITE_OK)
+      {
+      const std::string err_msg = errmsg;
+      ::sqlite3_free(errmsg);
+      ::sqlite3_close(m_db);
+      m_db = nullptr;
+      throw SQL_DB_Error("sqlite3_exec for table failed - " + err_msg);
+      }
+   }
+
+Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, const std::string& base_sql)
+   {
+   int rc = ::sqlite3_prepare_v2(db, base_sql.c_str(), -1, &m_stmt, nullptr);
+
+   if(rc != SQLITE_OK)
+      throw SQL_DB_Error("sqlite3_prepare failed on " + base_sql, rc);
+   }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::string& val)
+   {
+   int rc = ::sqlite3_bind_text(m_stmt, column, val.c_str(), -1, SQLITE_TRANSIENT);
+   if(rc != SQLITE_OK)
+      throw SQL_DB_Error("sqlite3_bind_text failed", rc);
+   }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val)
+   {
+   if(val != static_cast<size_t>(static_cast<int>(val))) // is this cast legit?
+      throw SQL_DB_Error("sqlite3 cannot store " + std::to_string(val) + " without truncation");
+   int rc = ::sqlite3_bind_int(m_stmt, column, val);
+   if(rc != SQLITE_OK)
+      throw SQL_DB_Error("sqlite3_bind_int failed", rc);
+   }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::chrono::system_clock::time_point time)
+   {
+   const int timeval = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count();
+   bind(column, timeval);
+   }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<uint8_t>& val)
+   {
+   int rc = ::sqlite3_bind_blob(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT);
+   if(rc != SQLITE_OK)
+      throw SQL_DB_Error("sqlite3_bind_text failed", rc);
+   }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len)
+   {
+   int rc = ::sqlite3_bind_blob(m_stmt, column, p, len, SQLITE_TRANSIENT);
+   if(rc != SQLITE_OK)
+      throw SQL_DB_Error("sqlite3_bind_text failed", rc);
+   }
+
+std::pair<const uint8_t*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column)
+   {
+   BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_BLOB,
+                "Return value is a blob");
+
+   const void* session_blob = ::sqlite3_column_blob(m_stmt, column);
+   const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column);
+
+   BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative");
+
+   return std::make_pair(static_cast<const uint8_t*>(session_blob),
+                         static_cast<size_t>(session_blob_size));
+   }
+
+std::string Sqlite3_Database::Sqlite3_Statement::get_str(int column)
+   {
+   BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_TEXT,
+                "Return value is text");
+
+   const unsigned char* str = ::sqlite3_column_text(m_stmt, column);
+
+   return std::string(cast_uint8_ptr_to_char(str));
+   }
+
+size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column)
+   {
+   BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER,
+                "Return count is an integer");
+
+   const int sessions_int = ::sqlite3_column_int(m_stmt, column);
+
+   BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative");
+
+   return static_cast<size_t>(sessions_int);
+   }
+
+size_t Sqlite3_Database::Sqlite3_Statement::spin()
+   {
+   size_t steps = 0;
+   while(step())
+      {
+      ++steps;
+      }
+
+   return steps;
+   }
+
+bool Sqlite3_Database::Sqlite3_Statement::step()
+   {
+   return (::sqlite3_step(m_stmt) == SQLITE_ROW);
+   }
+
+Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement()
+   {
+   ::sqlite3_finalize(m_stmt);
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/sqlite3/sqlite3.h b/third_party/botan/src/lib/utils/sqlite3/sqlite3.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/sqlite3/sqlite3.h
@@ -0,0 +1,58 @@
+/*
+* SQLite3 wrapper
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_SQLITE3_H_
+#define BOTAN_UTILS_SQLITE3_H_
+
+#include <botan/database.h>
+
+class sqlite3;
+class sqlite3_stmt;
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Sqlite3_Database final : public SQL_Database
+   {
+   public:
+      Sqlite3_Database(const std::string& file);
+
+      ~Sqlite3_Database();
+
+      size_t row_count(const std::string& table_name) override;
+
+      void create_table(const std::string& table_schema) override;
+
+      std::shared_ptr<Statement> new_statement(const std::string& sql) const override;
+   private:
+      class Sqlite3_Statement final : public Statement
+         {
+         public:
+            void bind(int column, const std::string& val) override;
+            void bind(int column, size_t val) override;
+            void bind(int column, std::chrono::system_clock::time_point time) override;
+            void bind(int column, const std::vector<uint8_t>& val) override;
+            void bind(int column, const uint8_t* data, size_t len) override;
+
+            std::pair<const uint8_t*, size_t> get_blob(int column) override;
+            std::string get_str(int column) override;
+            size_t get_size_t(int column) override;
+
+            size_t spin() override;
+            bool step() override;
+
+            Sqlite3_Statement(sqlite3* db, const std::string& base_sql);
+            ~Sqlite3_Statement();
+         private:
+            sqlite3_stmt* m_stmt;
+         };
+
+      sqlite3* m_db;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/stl_compatibility.h b/third_party/botan/src/lib/utils/stl_compatibility.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/stl_compatibility.h
@@ -0,0 +1,80 @@
+/*
+* STL standards compatibility functions
+* (C) 2017 Tomasz Frydrych
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STL_COMPATIBILITY_H_
+#define BOTAN_STL_COMPATIBILITY_H_
+
+#include <botan/types.h>
+#include <memory>
+
+#if __cplusplus < 201402L
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(stl_compatability.h)
+
+namespace Botan
+{
+/*
+* std::make_unique functionality similar as we have in C++14.
+* C++11 version based on proposal for C++14 implemenatation by Stephan T. Lavavej
+* source: https://isocpp.org/files/papers/N3656.txt
+*/
+#if __cplusplus >= 201402L
+template <typename T, typename ... Args>
+constexpr auto make_unique(Args&&... args)
+   {
+   return std::make_unique<T>(std::forward<Args>(args)...);
+   }
+
+template<class T>
+constexpr auto make_unique(std::size_t size)
+   {
+   return std::make_unique<T>(size);
+   }
+
+#else
+namespace stlCompatibilityDetails
+{
+template<class T> struct _Unique_if
+   {
+   typedef std::unique_ptr<T> _Single_object;
+   };
+
+template<class T> struct _Unique_if<T[]>
+   {
+   typedef std::unique_ptr<T[]> _Unknown_bound;
+   };
+
+template<class T, size_t N> struct _Unique_if<T[N]>
+   {
+   typedef void _Known_bound;
+   };
+}  // namespace stlCompatibilityDetails
+
+template<class T, class... Args>
+typename stlCompatibilityDetails::_Unique_if<T>::_Single_object make_unique(Args&&... args)
+   {
+   return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+   }
+
+template<class T>
+typename stlCompatibilityDetails::_Unique_if<T>::_Unknown_bound make_unique(size_t n)
+   {
+   typedef typename std::remove_extent<T>::type U;
+   return std::unique_ptr<T>(new U[n]());
+   }
+
+template<class T, class... Args>
+typename stlCompatibilityDetails::_Unique_if<T>::_Known_bound make_unique(Args&&...) = delete;
+
+#endif
+
+}  // namespace Botan
+#endif
diff --git a/third_party/botan/src/lib/utils/stl_util.h b/third_party/botan/src/lib/utils/stl_util.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/stl_util.h
@@ -0,0 +1,110 @@
+/*
+* STL Utility Functions
+* (C) 1999-2007 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STL_UTIL_H_
+#define BOTAN_STL_UTIL_H_
+
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+inline std::vector<uint8_t> to_byte_vector(const std::string& s)
+   {
+   return std::vector<uint8_t>(s.cbegin(), s.cend());
+   }
+
+inline std::string to_string(const secure_vector<uint8_t> &bytes)
+   {
+   return std::string(bytes.cbegin(), bytes.cend());
+   }
+
+/**
+* Return the keys of a map as a std::set
+*/
+template<typename K, typename V>
+std::set<K> map_keys_as_set(const std::map<K, V>& kv)
+   {
+   std::set<K> s;
+   for(auto&& i : kv)
+      {
+      s.insert(i.first);
+      }
+   return s;
+   }
+
+/*
+* Searching through a std::map
+* @param mapping the map to search
+* @param key is what to look for
+* @param null_result is the value to return if key is not in mapping
+* @return mapping[key] or null_result
+*/
+template<typename K, typename V>
+inline V search_map(const std::map<K, V>& mapping,
+                    const K& key,
+                    const V& null_result = V())
+   {
+   auto i = mapping.find(key);
+   if(i == mapping.end())
+      return null_result;
+   return i->second;
+   }
+
+template<typename K, typename V, typename R>
+inline R search_map(const std::map<K, V>& mapping, const K& key,
+                    const R& null_result, const R& found_result)
+   {
+   auto i = mapping.find(key);
+   if(i == mapping.end())
+      return null_result;
+   return found_result;
+   }
+
+/*
+* Insert a key/value pair into a multimap
+*/
+template<typename K, typename V>
+void multimap_insert(std::multimap<K, V>& multimap,
+                     const K& key, const V& value)
+   {
+   multimap.insert(std::make_pair(key, value));
+   }
+
+/**
+* Existence check for values
+*/
+template<typename T>
+bool value_exists(const std::vector<T>& vec,
+                  const T& val)
+   {
+   for(size_t i = 0; i != vec.size(); ++i)
+      if(vec[i] == val)
+         return true;
+   return false;
+   }
+
+template<typename T, typename Pred>
+void map_remove_if(Pred pred, T& assoc)
+   {
+   auto i = assoc.begin();
+   while(i != assoc.end())
+      {
+      if(pred(i->first))
+         assoc.erase(i++);
+      else
+         i++;
+      }
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/thread_utils/barrier.cpp b/third_party/botan/src/lib/utils/thread_utils/barrier.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/barrier.cpp
@@ -0,0 +1,36 @@
+/*
+* Barrier
+* (C) 2016 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/barrier.h>
+
+namespace Botan {
+
+void Barrier::wait(size_t delta)
+    {
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_value += delta;
+    }
+
+void Barrier::sync()
+    {
+    std::unique_lock<std::mutex> lock(m_mutex);
+
+    if(m_value > 1)
+        {
+        --m_value;
+        const size_t current_syncs = m_syncs;
+        m_cond.wait(lock, [this, &current_syncs] { return m_syncs != current_syncs; });
+        }
+    else
+        {
+        m_value = 0;
+        ++m_syncs;
+        m_cond.notify_all();
+        }
+    }
+
+}
diff --git a/third_party/botan/src/lib/utils/thread_utils/barrier.h b/third_party/botan/src/lib/utils/thread_utils/barrier.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/barrier.h
@@ -0,0 +1,42 @@
+/*
+* Barrier
+* (C) 2016 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_BARRIER_H_
+#define BOTAN_UTIL_BARRIER_H_
+
+#include <mutex>
+#include <condition_variable>
+
+namespace Botan {
+
+/**
+Barrier implements a barrier synchronization primitive. wait() will
+indicate how many threads to synchronize; each thread needing
+synchronization should call sync(). When sync() returns, the barrier
+is reset to zero, and the m_syncs counter is incremented. m_syncs is a
+counter to ensure that wait() can be called after a sync() even if the
+previously sleeping threads have not awoken.)
+*/
+class Barrier final
+   {
+   public:
+      explicit Barrier(int value = 0) : m_value(value), m_syncs(0) {}
+
+      void wait(size_t delta);
+
+      void sync();
+
+   private:
+      size_t m_value;
+      size_t m_syncs;
+      std::mutex m_mutex;
+      std::condition_variable m_cond;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp b/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp
@@ -0,0 +1,58 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/rwlock.h>
+
+namespace Botan {
+
+RWLock::RWLock() : m_state(0) {}
+
+void RWLock::lock()
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+   while(m_state & is_writing)
+      m_gate1.wait(lock);
+   m_state |= is_writing;
+   while(m_state & readers_mask)
+      m_gate2.wait(lock);
+   }
+
+void RWLock::unlock()
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+   m_state = 0;
+   m_gate1.notify_all();
+   }
+
+void RWLock::lock_shared()
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+   while((m_state & is_writing) || (m_state & readers_mask) == readers_mask)
+      m_gate1.wait(lock);
+   const uint32_t num_readers = (m_state & readers_mask) + 1;
+   m_state &= ~readers_mask;
+   m_state |= num_readers;
+   }
+
+void RWLock::unlock_shared()
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+   const uint32_t num_readers = (m_state & readers_mask) - 1;
+   m_state &= ~readers_mask;
+   m_state |= num_readers;
+   if(m_state & is_writing)
+      {
+      if(num_readers == 0)
+         m_gate2.notify_one();
+      }
+   else
+      {
+      if(num_readers == readers_mask - 1)
+         m_gate1.notify_one();
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/thread_utils/rwlock.h b/third_party/botan/src/lib/utils/thread_utils/rwlock.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/rwlock.h
@@ -0,0 +1,42 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RWLOCK_H_
+#define BOTAN_RWLOCK_H_
+
+#include <botan/types.h>
+#include <mutex>
+#include <condition_variable>
+
+namespace Botan {
+
+/**
+* A read-write lock. Writers are favored.
+*/
+class BOTAN_TEST_API RWLock final
+   {
+   public:
+      RWLock();
+
+      void lock();
+      void unlock();
+
+      void lock_shared();
+      void unlock_shared();
+   private:
+      std::mutex m_mutex;
+      std::condition_variable m_gate1;
+      std::condition_variable m_gate2;
+      uint32_t m_state;
+
+      // 2**31 concurrent readers should be enough for anyone
+      static const uint32_t is_writing = static_cast<uint32_t>(1) << 31;
+      static const uint32_t readers_mask = ~is_writing;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp b/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp
@@ -0,0 +1,38 @@
+/*
+* Semaphore
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/semaphore.h>
+
+// Based on code by Pierre Gaston (http://p9as.blogspot.com/2012/06/c11-semaphores.html)
+
+namespace Botan {
+
+void Semaphore::release(size_t n)
+   {
+   for(size_t i = 0; i != n; ++i)
+      {
+      std::lock_guard<std::mutex> lock(m_mutex);
+
+      if(m_value++ < 0)
+         {
+         ++m_wakeups;
+         m_cond.notify_one();
+         }
+      }
+   }
+
+void Semaphore::acquire()
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+   if(m_value-- <= 0)
+       {
+       m_cond.wait(lock, [this] { return m_wakeups > 0; });
+       --m_wakeups;
+       }
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/thread_utils/semaphore.h b/third_party/botan/src/lib/utils/thread_utils/semaphore.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/semaphore.h
@@ -0,0 +1,34 @@
+/*
+* Semaphore
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SEMAPHORE_H_
+#define BOTAN_SEMAPHORE_H_
+
+#include <condition_variable>
+#include <mutex>
+
+namespace Botan {
+
+class Semaphore final
+   {
+   public:
+      explicit Semaphore(int value = 0) : m_value(value), m_wakeups(0) {}
+
+      void acquire();
+
+      void release(size_t n = 1);
+
+   private:
+      int m_value;
+      int m_wakeups;
+      std::mutex m_mutex;
+      std::condition_variable m_cond;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp b/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp
@@ -0,0 +1,103 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/thread_pool.h>
+#include <botan/internal/os_utils.h>
+#include <botan/exceptn.h>
+#include <thread>
+
+namespace Botan {
+
+//static
+Thread_Pool& Thread_Pool::global_instance()
+   {
+   static Thread_Pool g_thread_pool(OS::read_env_variable_sz("BOTAN_THREAD_POOL_SIZE"));
+   return g_thread_pool;
+   }
+
+Thread_Pool::Thread_Pool(size_t pool_size)
+   {
+   if(pool_size == 0)
+      {
+      pool_size = OS::get_cpu_available();
+
+      /*
+      * For large machines don't create too many threads, unless
+      * explicitly asked to by the caller.
+      */
+      if(pool_size > 16)
+         pool_size = 16;
+      }
+
+   if(pool_size <= 1)
+      pool_size = 2;
+
+   m_shutdown = false;
+
+   for(size_t i = 0; i != pool_size; ++i)
+      {
+      m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this));
+      }
+   }
+
+void Thread_Pool::shutdown()
+   {
+      {
+      std::unique_lock<std::mutex> lock(m_mutex);
+
+      if(m_shutdown == true)
+         return;
+
+      m_shutdown = true;
+
+      m_more_tasks.notify_all();
+      }
+
+   for(auto&& thread : m_workers)
+      {
+      thread.join();
+      }
+   m_workers.clear();
+   }
+
+void Thread_Pool::queue_thunk(std::function<void ()> fn)
+   {
+   std::unique_lock<std::mutex> lock(m_mutex);
+
+   if(m_shutdown)
+      throw Invalid_State("Cannot add work after thread pool has shut down");
+
+   m_tasks.push_back(fn);
+   m_more_tasks.notify_one();
+   }
+
+void Thread_Pool::worker_thread()
+   {
+   for(;;)
+      {
+      std::function<void()> task;
+
+         {
+         std::unique_lock<std::mutex> lock(m_mutex);
+         m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); });
+
+         if(m_tasks.empty())
+            {
+            if(m_shutdown)
+               return;
+            else
+               continue;
+            }
+
+         task = m_tasks.front();
+         m_tasks.pop_front();
+         }
+
+      task();
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/thread_utils/thread_pool.h b/third_party/botan/src/lib/utils/thread_utils/thread_pool.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/thread_utils/thread_pool.h
@@ -0,0 +1,82 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREAD_POOL_H_
+#define BOTAN_THREAD_POOL_H_
+
+#include <botan/types.h>
+#include <functional>
+#include <deque>
+#include <vector>
+#include <memory>
+#include <utility>
+#include <type_traits>
+#include <mutex>
+#include <thread>
+#include <future>
+#include <condition_variable>
+
+namespace Botan {
+
+class BOTAN_TEST_API Thread_Pool
+   {
+   public:
+      /**
+      * Return an instance to a shared thread pool
+      */
+      static Thread_Pool& global_instance();
+
+      /**
+      * Initialize a thread pool with some number of threads
+      * @param pool_size number of threads in the pool, if 0
+      *        then some default value is chosen
+      */
+      Thread_Pool(size_t pool_size = 0);
+
+      ~Thread_Pool() { shutdown(); }
+
+      void shutdown();
+
+      size_t worker_count() const { return m_workers.size(); }
+
+      Thread_Pool(const Thread_Pool&) = delete;
+      Thread_Pool& operator=(const Thread_Pool&) = delete;
+
+      Thread_Pool(Thread_Pool&&) = delete;
+      Thread_Pool& operator=(Thread_Pool&&) = delete;
+
+      /*
+      * Enqueue some work
+      */
+      void queue_thunk(std::function<void ()>);
+
+      template<class F, class... Args>
+      auto run(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>
+         {
+         typedef typename std::result_of<F(Args...)>::type return_type;
+
+         auto future_work = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+         auto task = std::make_shared<std::packaged_task<return_type ()>>(future_work);
+         auto future_result = task->get_future();
+         queue_thunk([task]() { (*task)(); });
+         return future_result;
+         }
+
+   private:
+      void worker_thread();
+
+      // Only touched in constructor and destructor
+      std::vector<std::thread> m_workers;
+
+      std::mutex m_mutex;
+      std::condition_variable m_more_tasks;
+      std::deque<std::function<void ()>> m_tasks;
+      bool m_shutdown;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/timer.cpp b/third_party/botan/src/lib/utils/timer.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/timer.cpp
@@ -0,0 +1,150 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/timer.h>
+#include <botan/internal/os_utils.h>
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+
+namespace Botan {
+
+void Timer::start()
+   {
+   stop();
+   m_timer_start = OS::get_system_timestamp_ns();
+   m_cpu_cycles_start = OS::get_cpu_cycle_counter();
+   }
+
+void Timer::stop()
+   {
+   if(m_timer_start)
+      {
+      if(m_cpu_cycles_start != 0)
+         {
+         const uint64_t cycles_taken = OS::get_cpu_cycle_counter() - m_cpu_cycles_start;
+         if(cycles_taken > 0)
+            {
+            m_cpu_cycles_used += static_cast<size_t>(cycles_taken * m_clock_cycle_ratio);
+            }
+         }
+
+      const uint64_t now = OS::get_system_timestamp_ns();
+
+      if(now > m_timer_start)
+         {
+         const uint64_t dur = now - m_timer_start;
+
+         m_time_used += dur;
+
+         if(m_event_count == 0)
+            {
+            m_min_time = m_max_time = dur;
+            }
+         else
+            {
+            m_max_time = std::max(m_max_time, dur);
+            m_min_time = std::min(m_min_time, dur);
+            }
+         }
+
+      m_timer_start = 0;
+      ++m_event_count;
+      }
+   }
+
+bool Timer::operator<(const Timer& other) const
+   {
+   if(this->doing() != other.doing())
+      return (this->doing() < other.doing());
+
+   return (this->get_name() < other.get_name());
+   }
+
+std::string Timer::to_string() const
+   {
+   if(m_custom_msg.size() > 0)
+      {
+      return m_custom_msg;
+      }
+   else if(this->buf_size() == 0)
+      {
+      return result_string_ops();
+      }
+   else
+      {
+      return result_string_bps();
+      }
+   }
+
+std::string Timer::result_string_bps() const
+   {
+   const size_t MiB = 1024 * 1024;
+
+   const double MiB_total = static_cast<double>(events()) / MiB;
+   const double MiB_per_sec = MiB_total / seconds();
+
+   std::ostringstream oss;
+   oss << get_name();
+
+   if(!doing().empty())
+      {
+      oss << " " << doing();
+      }
+
+   if(buf_size() > 0)
+      {
+      oss << " buffer size " << buf_size() << " bytes:";
+      }
+
+   if(events() == 0)
+      oss << " " << "N/A";
+   else
+      oss << " " << std::fixed << std::setprecision(3) << MiB_per_sec << " MiB/sec";
+
+   if(cycles_consumed() != 0)
+      {
+      const double cycles_per_byte = static_cast<double>(cycles_consumed()) / events();
+      oss << " " << std::fixed << std::setprecision(2) << cycles_per_byte << " cycles/byte";
+      }
+
+   oss << " (" << MiB_total << " MiB in " << milliseconds() << " ms)\n";
+
+   return oss.str();
+   }
+
+std::string Timer::result_string_ops() const
+   {
+   std::ostringstream oss;
+
+   oss << get_name() << " ";
+
+   if(events() == 0)
+      {
+      oss << "no events\n";
+      }
+   else
+      {
+      oss << static_cast<uint64_t>(events_per_second())
+          << ' ' << doing() << "/sec; "
+          << std::setprecision(2) << std::fixed
+          << ms_per_event() << " ms/op";
+
+      if(cycles_consumed() != 0)
+         {
+         const double cycles_per_op = static_cast<double>(cycles_consumed()) / events();
+         const int precision = (cycles_per_op < 10000) ? 2 : 0;
+         oss << " " << std::fixed << std::setprecision(precision) << cycles_per_op << " cycles/op";
+         }
+
+      oss << " (" << events() << " " << (events() == 1 ? "op" : "ops")
+          << " in " << milliseconds() << " ms)\n";
+      }
+
+   return oss.str();
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/timer.h b/third_party/botan/src/lib/utils/timer.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/timer.h
@@ -0,0 +1,185 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TIMER_H_
+#define BOTAN_TIMER_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+class BOTAN_TEST_API Timer final
+   {
+   public:
+      Timer(const std::string& name,
+            const std::string& provider,
+            const std::string& doing,
+            uint64_t event_mult,
+            size_t buf_size,
+            double clock_cycle_ratio,
+            uint64_t clock_speed)
+         : m_name(name + ((provider.empty() || provider == "base") ? "" : " [" + provider + "]"))
+         , m_doing(doing)
+         , m_buf_size(buf_size)
+         , m_event_mult(event_mult)
+         , m_clock_cycle_ratio(clock_cycle_ratio)
+         , m_clock_speed(clock_speed)
+         {}
+
+      Timer(const std::string& name) :
+         Timer(name, "", "", 1, 0, 0.0, 0)
+         {}
+
+      Timer(const std::string& name, size_t buf_size) :
+         Timer(name, "", "", buf_size, buf_size, 0.0, 0)
+         {}
+
+      Timer(const Timer& other) = default;
+      Timer& operator=(const Timer& other) = default;
+
+      void start();
+
+      void stop();
+
+      bool under(std::chrono::milliseconds msec)
+         {
+         return (milliseconds() < msec.count());
+         }
+
+      class Timer_Scope final
+         {
+         public:
+            explicit Timer_Scope(Timer& timer)
+               : m_timer(timer)
+               {
+               m_timer.start();
+               }
+            ~Timer_Scope()
+               {
+               try
+                  {
+                  m_timer.stop();
+                  }
+               catch(...) {}
+               }
+         private:
+            Timer& m_timer;
+         };
+
+      template<typename F>
+      auto run(F f) -> decltype(f())
+         {
+         Timer_Scope timer(*this);
+         return f();
+         }
+
+      template<typename F>
+      void run_until_elapsed(std::chrono::milliseconds msec, F f)
+         {
+         while(this->under(msec))
+            {
+            run(f);
+            }
+         }
+
+      uint64_t value() const
+         {
+         return m_time_used;
+         }
+
+      double seconds() const
+         {
+         return milliseconds() / 1000.0;
+         }
+
+      double milliseconds() const
+         {
+         return value() / 1000000.0;
+         }
+
+      double ms_per_event() const
+         {
+         return milliseconds() / events();
+         }
+
+      uint64_t cycles_consumed() const
+         {
+         if(m_clock_speed != 0)
+            {
+            return static_cast<uint64_t>((m_clock_speed * value()) / 1000.0);
+            }
+         return m_cpu_cycles_used;
+         }
+
+      uint64_t events() const
+         {
+         return m_event_count * m_event_mult;
+         }
+
+      const std::string& get_name() const
+         {
+         return m_name;
+         }
+
+      const std::string& doing() const
+         {
+         return m_doing;
+         }
+
+      size_t buf_size() const
+         {
+         return m_buf_size;
+         }
+
+      double bytes_per_second() const
+         {
+         return seconds() > 0.0 ? events() / seconds() : 0.0;
+         }
+
+      double events_per_second() const
+         {
+         return seconds() > 0.0 ? events() / seconds() : 0.0;
+         }
+
+      double seconds_per_event() const
+         {
+         return events() > 0 ? seconds() / events() : 0.0;
+         }
+
+      void set_custom_msg(const std::string& s)
+         {
+         m_custom_msg = s;
+         }
+
+      bool operator<(const Timer& other) const;
+
+      std::string to_string() const;
+
+   private:
+      std::string result_string_bps() const;
+      std::string result_string_ops() const;
+
+      // const data
+      std::string m_name, m_doing;
+      size_t m_buf_size;
+      uint64_t m_event_mult;
+      double m_clock_cycle_ratio;
+      uint64_t m_clock_speed;
+
+      // set at runtime
+      std::string m_custom_msg;
+      uint64_t m_time_used = 0, m_timer_start = 0;
+      uint64_t m_event_count = 0;
+
+      uint64_t m_max_time = 0, m_min_time = 0;
+      uint64_t m_cpu_cycles_start = 0, m_cpu_cycles_used = 0;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/types.h b/third_party/botan/src/lib/utils/types.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/types.h
@@ -0,0 +1,112 @@
+/*
+* Low Level Types
+* (C) 1999-2007 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TYPES_H_
+#define BOTAN_TYPES_H_
+
+#include <botan/build.h> // IWYU pragma: export
+#include <botan/assert.h> // IWYU pragma: export
+#include <cstddef> // IWYU pragma: export
+#include <cstdint> // IWYU pragma: export
+#include <memory> // IWYU pragma: export
+
+namespace Botan {
+
+/**
+* @mainpage Botan Crypto Library API Reference
+*
+* <dl>
+* <dt>Abstract Base Classes<dd>
+*        BlockCipher, HashFunction, KDF, MessageAuthenticationCode, RandomNumberGenerator,
+*        StreamCipher, SymmetricAlgorithm, AEAD_Mode, Cipher_Mode
+* <dt>Public Key Interface Classes<dd>
+*        PK_Key_Agreement, PK_Signer, PK_Verifier, PK_Encryptor, PK_Decryptor
+* <dt>Authenticated Encryption Modes<dd>
+*        @ref CCM_Mode "CCM", @ref ChaCha20Poly1305_Mode "ChaCha20Poly1305", @ref EAX_Mode "EAX",
+*        @ref GCM_Mode "GCM", @ref OCB_Mode "OCB", @ref SIV_Mode "SIV"
+* <dt>Block Ciphers<dd>
+*        @ref aria.h "ARIA", @ref aes.h "AES", @ref Blowfish, @ref camellia.h "Camellia", @ref Cascade_Cipher "Cascade",
+*        @ref CAST_128 "CAST-128", @ref CAST_128 "CAST-256", DES, @ref DESX "DES-X", @ref TripleDES "3DES",
+*        @ref GOST_28147_89 "GOST 28147-89", IDEA, KASUMI, Lion, MISTY1, Noekeon, SEED, Serpent, SHACAL2, SM4,
+*        @ref Threefish_512 "Threefish", Twofish, XTEA
+* <dt>Stream Ciphers<dd>
+*        ChaCha, @ref CTR_BE "CTR", OFB, RC4, Salsa20
+* <dt>Hash Functions<dd>
+*        BLAKE2b, @ref GOST_34_11 "GOST 34.11", @ref Keccak_1600 "Keccak", MD4, MD5, @ref RIPEMD_160 "RIPEMD-160",
+*        @ref SHA_160 "SHA-1", @ref SHA_224 "SHA-224", @ref SHA_256 "SHA-256", @ref SHA_384 "SHA-384",
+*        @ref SHA_512 "SHA-512", @ref Skein_512 "Skein-512", SM3, Streebog, Tiger, Whirlpool
+* <dt>Non-Cryptographic Checksums<dd>
+*        Adler32, CRC24, CRC32
+* <dt>Message Authentication Codes<dd>
+*        @ref CBC_MAC "CBC-MAC", CMAC, HMAC, Poly1305, SipHash, ANSI_X919_MAC
+* <dt>Random Number Generators<dd>
+*        AutoSeeded_RNG, HMAC_DRBG, Processor_RNG, System_RNG
+* <dt>Key Derivation<dd>
+*        HKDF, @ref KDF1 "KDF1 (IEEE 1363)", @ref KDF1_18033 "KDF1 (ISO 18033-2)", @ref KDF2 "KDF2 (IEEE 1363)",
+*        @ref sp800_108.h "SP800-108", @ref SP800_56C "SP800-56C", @ref PKCS5_PBKDF1 "PBKDF1 (PKCS#5),
+*        @ref PKCS5_PBKDF2 "PBKDF2 (PKCS#5)"
+* <dt>Password Hashing<dd>
+*        @ref argon2.h "Argon2", @ref scrypt.h "scrypt", @ref bcrypt.h "bcrypt", @ref passhash9.h "passhash9"
+* <dt>Public Key Cryptosystems<dd>
+*        @ref dlies.h "DLIES", @ref ecies.h "ECIES", @ref elgamal.h "ElGamal"
+*        @ref rsa.h "RSA", @ref newhope.h "NewHope", @ref mceliece.h "McEliece" and @ref mceies.h "MCEIES",
+*        @ref sm2.h "SM2"
+* <dt>Public Key Signature Schemes<dd>
+*        @ref dsa.h "DSA", @ref ecdsa.h "ECDSA", @ref ecgdsa.h "ECGDSA", @ref eckcdsa.h "ECKCDSA",
+*        @ref gost_3410.h "GOST 34.10-2001", @ref sm2.h "SM2", @ref xmss.h "XMSS"
+* <dt>Key Agreement<dd>
+*        @ref dh.h "DH", @ref ecdh.h "ECDH"
+* <dt>Compression<dd>
+*        @ref bzip2.h "bzip2", @ref lzma.h "lzma", @ref zlib.h "zlib"
+* <dt>TLS<dd>
+*        TLS::Client, TLS::Server, TLS::Policy, TLS::Protocol_Version, TLS::Callbacks, TLS::Ciphersuite,
+*        TLS::Session, TLS::Session_Manager, Credentials_Manager
+* <dt>X.509<dd>
+*        X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, X509_Cert_Options,
+*        Certificate_Store, Certificate_Store_In_SQL, Certificate_Store_In_SQLite
+* </dl>
+*/
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::int32_t;
+using std::int64_t;
+using std::size_t;
+
+/*
+* These typedefs are no longer used within the library headers
+* or code. They are kept only for compatability with software
+* written against older versions.
+*/
+using byte   = std::uint8_t;
+using u16bit = std::uint16_t;
+using u32bit = std::uint32_t;
+using u64bit = std::uint64_t;
+using s32bit = std::int32_t;
+
+#if (BOTAN_MP_WORD_BITS == 32)
+  typedef uint32_t word;
+#elif (BOTAN_MP_WORD_BITS == 64)
+  typedef uint64_t word;
+#else
+  #error BOTAN_MP_WORD_BITS must be 32 or 64
+#endif
+
+/*
+* Should this assert fail on your system please contact the developers
+* for assistance in porting.
+*/
+static_assert(sizeof(std::size_t) == 8 || sizeof(std::size_t) == 4,
+              "This platform has an unexpected size for size_t");
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/uuid/uuid.cpp b/third_party/botan/src/lib/utils/uuid/uuid.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/uuid/uuid.cpp
@@ -0,0 +1,82 @@
+/*
+* UUID type
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/uuid.h>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <sstream>
+
+namespace Botan {
+
+UUID::UUID(RandomNumberGenerator& rng)
+   {
+   m_uuid.resize(16);
+   rng.randomize(m_uuid.data(), m_uuid.size());
+
+   // Mark as a random v4 UUID (RFC 4122 sec 4.4)
+   m_uuid[6] = 0x40 | (m_uuid[6] & 0x0F);
+
+   // Set reserved bits
+   m_uuid[8] = 0x80 | (m_uuid[8] & 0x3F);
+   }
+
+UUID::UUID(const std::vector<uint8_t>& blob)
+   {
+   if(blob.size() != 16)
+      {
+      throw Invalid_Argument("Bad UUID blob " + hex_encode(blob));
+      }
+
+   m_uuid = blob;
+   }
+
+UUID::UUID(const std::string& uuid_str)
+   {
+   if(uuid_str.size() != 36 ||
+      uuid_str[8] != '-' ||
+      uuid_str[13] != '-' ||
+      uuid_str[18] != '-' ||
+      uuid_str[23] != '-')
+      {
+      throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+      }
+
+   std::string just_hex;
+   for(size_t i = 0; i != uuid_str.size(); ++i)
+      {
+      char c = uuid_str[i];
+
+      if(c == '-')
+         continue;
+
+      just_hex += c;
+      }
+
+   m_uuid = hex_decode(just_hex);
+
+   if(m_uuid.size() != 16)
+      {
+      throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+      }
+   }
+
+std::string UUID::to_string() const
+   {
+   if(is_valid() == false)
+      throw Invalid_State("UUID object is empty cannot convert to string");
+
+   std::string h = hex_encode(m_uuid);
+
+   h.insert(8, "-");
+   h.insert(13, "-");
+   h.insert(18, "-");
+   h.insert(23, "-");
+
+   return h;
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/uuid/uuid.h b/third_party/botan/src/lib/utils/uuid/uuid.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/uuid/uuid.h
@@ -0,0 +1,69 @@
+/*
+* UUID type
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UUID_H_
+#define BOTAN_UUID_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(uuid.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+class BOTAN_UNSTABLE_API UUID final
+   {
+   public:
+      /**
+      * Create an uninitialized UUID object
+      */
+      UUID() : m_uuid() {}
+
+      /**
+      * Create a random UUID
+      */
+      UUID(RandomNumberGenerator& rng);
+
+      /**
+      * Load a UUID from a 16 byte vector
+      */
+      UUID(const std::vector<uint8_t>& blob);
+
+      UUID& operator=(const UUID& other) = default;
+      UUID(const UUID& other) = default;
+
+      /**
+      * Decode a UUID string
+      */
+      UUID(const std::string& uuid_str);
+
+      /**
+      * Convert the UUID to a string
+      */
+      std::string to_string() const;
+
+      const std::vector<uint8_t>& binary_value() const { return m_uuid; }
+
+      bool operator==(const UUID& other) const
+         {
+         return m_uuid == other.m_uuid;
+         }
+
+      bool operator!=(const UUID& other) const { return !(*this == other); }
+
+      bool is_valid() const { return m_uuid.size() == 16; }
+
+   private:
+      std::vector<uint8_t> m_uuid;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/utils/version.cpp b/third_party/botan/src/lib/utils/version.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/version.cpp
@@ -0,0 +1,100 @@
+/*
+* Version Information
+* (C) 1999-2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/version.h>
+#include <sstream>
+
+namespace Botan {
+
+/*
+  These are intentionally compiled rather than inlined, so an
+  application running against a shared library can test the true
+  version they are running against.
+*/
+
+#define QUOTE(name) #name
+#define STR(macro) QUOTE(macro)
+
+const char* short_version_cstr()
+   {
+   return STR(BOTAN_VERSION_MAJOR) "."
+          STR(BOTAN_VERSION_MINOR) "."
+          STR(BOTAN_VERSION_PATCH)
+#if defined(BOTAN_VERSION_SUFFIX)
+          STR(BOTAN_VERSION_SUFFIX)
+#endif
+      ;
+   }
+
+const char* version_cstr()
+   {
+
+   /*
+   It is intentional that this string is a compile-time constant;
+   it makes it much easier to find in binaries.
+   */
+
+   return "Botan " STR(BOTAN_VERSION_MAJOR) "."
+                   STR(BOTAN_VERSION_MINOR) "."
+                   STR(BOTAN_VERSION_PATCH)
+#if defined(BOTAN_VERSION_SUFFIX)
+                   STR(BOTAN_VERSION_SUFFIX)
+#endif
+                   " ("
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+                   "UNSAFE FUZZER MODE BUILD "
+#endif
+                   BOTAN_VERSION_RELEASE_TYPE
+#if (BOTAN_VERSION_DATESTAMP != 0)
+                   ", dated " STR(BOTAN_VERSION_DATESTAMP)
+#endif
+                   ", revision " BOTAN_VERSION_VC_REVISION
+                   ", distribution " BOTAN_DISTRIBUTION_INFO ")";
+   }
+
+#undef STR
+#undef QUOTE
+
+/*
+* Return the version as a string
+*/
+std::string version_string()
+   {
+   return std::string(version_cstr());
+   }
+
+std::string short_version_string()
+   {
+   return std::string(short_version_cstr());
+   }
+
+uint32_t version_datestamp() { return BOTAN_VERSION_DATESTAMP; }
+
+/*
+* Return parts of the version as integers
+*/
+uint32_t version_major() { return BOTAN_VERSION_MAJOR; }
+uint32_t version_minor() { return BOTAN_VERSION_MINOR; }
+uint32_t version_patch() { return BOTAN_VERSION_PATCH; }
+
+std::string runtime_version_check(uint32_t major,
+                                  uint32_t minor,
+                                  uint32_t patch)
+   {
+   if(major != version_major() || minor != version_minor() || patch != version_patch())
+      {
+      std::ostringstream oss;
+      oss << "Warning: linked version (" << short_version_string() << ")"
+          << " does not match version built against "
+          << "(" << major << '.' << minor << '.' << patch << ")\n";
+      return oss.str();
+      }
+
+   return "";
+   }
+
+}
diff --git a/third_party/botan/src/lib/utils/version.h b/third_party/botan/src/lib/utils/version.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/utils/version.h
@@ -0,0 +1,101 @@
+/*
+* Version Information
+* (C) 1999-2011,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_VERSION_H_
+#define BOTAN_VERSION_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/*
+* Get information describing the version
+*/
+
+/**
+* Get a human-readable string identifying the version of Botan.
+* No particular format should be assumed.
+* @return version string
+*/
+BOTAN_PUBLIC_API(2,0) std::string version_string();
+
+/**
+* Same as version_string() except returning a pointer to a statically
+* allocated string.
+* @return version string
+*/
+BOTAN_PUBLIC_API(2,0) const char* version_cstr();
+
+/**
+* Return a version string of the form "MAJOR.MINOR.PATCH" where
+* each of the values is an integer.
+*/
+BOTAN_PUBLIC_API(2,4) std::string short_version_string();
+
+/**
+* Same as version_short_string except returning a pointer to the string.
+*/
+BOTAN_PUBLIC_API(2,4) const char* short_version_cstr();
+
+/**
+* Return the date this version of botan was released, in an integer of
+* the form YYYYMMDD. For instance a version released on May 21, 2013
+* would return the integer 20130521. If the currently running version
+* is not an official release, this function will return 0 instead.
+*
+* @return release date, or zero if unreleased
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_datestamp();
+
+/**
+* Get the major version number.
+* @return major version number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_major();
+
+/**
+* Get the minor version number.
+* @return minor version number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_minor();
+
+/**
+* Get the patch number.
+* @return patch number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_patch();
+
+/**
+* Usable for checking that the DLL version loaded at runtime exactly
+* matches the compile-time version. Call using BOTAN_VERSION_* macro
+* values. Returns the empty string if an exact match, otherwise an
+* appropriate message. Added with 1.11.26.
+*/
+BOTAN_PUBLIC_API(2,0) std::string
+runtime_version_check(uint32_t major,
+                      uint32_t minor,
+                      uint32_t patch);
+
+/*
+* Macros for compile-time version checks
+*/
+#define BOTAN_VERSION_CODE_FOR(a,b,c) ((a << 16) | (b << 8) | (c))
+
+/**
+* Compare using BOTAN_VERSION_CODE_FOR, as in
+*  # if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,8,0)
+*  #    error "Botan version too old"
+*  # endif
+*/
+#define BOTAN_VERSION_CODE BOTAN_VERSION_CODE_FOR(BOTAN_VERSION_MAJOR, \
+                                                  BOTAN_VERSION_MINOR, \
+                                                  BOTAN_VERSION_PATCH)
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/asn1_alt_name.cpp b/third_party/botan/src/lib/x509/asn1_alt_name.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/asn1_alt_name.cpp
@@ -0,0 +1,266 @@
+/*
+* AlternativeName
+* (C) 1999-2007 Jack Lloyd
+*     2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_alt_name.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/internal/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/loadstor.h>
+#include <botan/x509_dn.h>
+
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create an AlternativeName
+*/
+AlternativeName::AlternativeName(const std::string& email_addr,
+                                 const std::string& uri,
+                                 const std::string& dns,
+                                 const std::string& ip)
+   {
+   add_attribute("RFC822", email_addr);
+   add_attribute("DNS", dns);
+   add_attribute("URI", uri);
+   add_attribute("IP", ip);
+   }
+
+/*
+* Add an attribute to an alternative name
+*/
+void AlternativeName::add_attribute(const std::string& type,
+                                    const std::string& value)
+   {
+   if(type.empty() || value.empty())
+      return;
+
+   auto range = m_alt_info.equal_range(type);
+   for(auto j = range.first; j != range.second; ++j)
+      if(j->second == value)
+         return;
+
+   multimap_insert(m_alt_info, type, value);
+   }
+
+/*
+* Add an OtherName field
+*/
+void AlternativeName::add_othername(const OID& oid, const std::string& value,
+                                    ASN1_Tag type)
+   {
+   if(value.empty())
+      return;
+   multimap_insert(m_othernames, oid, ASN1_String(value, type));
+   }
+
+/*
+* Return all of the alternative names
+*/
+std::multimap<std::string, std::string> AlternativeName::contents() const
+   {
+   std::multimap<std::string, std::string> names;
+
+   for(auto i = m_alt_info.begin(); i != m_alt_info.end(); ++i)
+      {
+      multimap_insert(names, i->first, i->second);
+      }
+
+   for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+      {
+      multimap_insert(names, i->first.to_formatted_string(), i->second.value());
+      }
+
+   return names;
+   }
+
+bool AlternativeName::has_field(const std::string& attr) const
+   {
+   auto range = m_alt_info.equal_range(attr);
+   return (range.first != range.second);
+   }
+
+std::string AlternativeName::get_first_attribute(const std::string& attr) const
+   {
+   auto i = m_alt_info.lower_bound(attr);
+   if(i != m_alt_info.end() && i->first == attr)
+      return i->second;
+
+   return "";
+   }
+
+std::vector<std::string> AlternativeName::get_attribute(const std::string& attr) const
+   {
+   std::vector<std::string> results;
+   auto range = m_alt_info.equal_range(attr);
+   for(auto i = range.first; i != range.second; ++i)
+      results.push_back(i->second);
+   return results;
+   }
+
+X509_DN AlternativeName::dn() const
+   {
+   X509_DN dn;
+   auto range = m_alt_info.equal_range("DN");
+
+   for(auto i = range.first; i != range.second; ++i)
+      {
+      std::istringstream strm(i->second);
+      strm >> dn;
+      }
+
+   return dn;
+   }
+
+/*
+* Return if this object has anything useful
+*/
+bool AlternativeName::has_items() const
+   {
+   return (m_alt_info.size() > 0 || m_othernames.size() > 0);
+   }
+
+namespace {
+
+/*
+* DER encode an AlternativeName entry
+*/
+void encode_entries(DER_Encoder& encoder,
+                    const std::multimap<std::string, std::string>& attr,
+                    const std::string& type, ASN1_Tag tagging)
+   {
+   auto range = attr.equal_range(type);
+
+   for(auto i = range.first; i != range.second; ++i)
+      {
+      if(type == "RFC822" || type == "DNS" || type == "URI")
+         {
+         ASN1_String asn1_string(i->second, IA5_STRING);
+         encoder.add_object(tagging, CONTEXT_SPECIFIC, asn1_string.value());
+         }
+      else if(type == "IP")
+         {
+         const uint32_t ip = string_to_ipv4(i->second);
+         uint8_t ip_buf[4] = { 0 };
+         store_be(ip, ip_buf);
+         encoder.add_object(tagging, CONTEXT_SPECIFIC, ip_buf, 4);
+         }
+      else if (type == "DN")
+         {
+         std::stringstream ss(i->second);
+         X509_DN dn;
+         ss >> dn;
+         encoder.encode(dn);
+         }
+      }
+   }
+
+}
+
+/*
+* DER encode an AlternativeName extension
+*/
+void AlternativeName::encode_into(DER_Encoder& der) const
+   {
+   der.start_cons(SEQUENCE);
+
+   encode_entries(der, m_alt_info, "RFC822", ASN1_Tag(1));
+   encode_entries(der, m_alt_info, "DNS", ASN1_Tag(2));
+   encode_entries(der, m_alt_info, "DN", ASN1_Tag(4));
+   encode_entries(der, m_alt_info, "URI", ASN1_Tag(6));
+   encode_entries(der, m_alt_info, "IP", ASN1_Tag(7));
+
+   for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+      {
+      der.start_explicit(0)
+         .encode(i->first)
+         .start_explicit(0)
+            .encode(i->second)
+         .end_explicit()
+      .end_explicit();
+      }
+
+   der.end_cons();
+   }
+
+/*
+* Decode a BER encoded AlternativeName
+*/
+void AlternativeName::decode_from(BER_Decoder& source)
+   {
+   BER_Decoder names = source.start_cons(SEQUENCE);
+
+   // FIXME this is largely a duplication of GeneralName::decode_from
+
+   while(names.more_items())
+      {
+      BER_Object obj = names.get_next_object();
+
+      if(obj.is_a(0, CONTEXT_SPECIFIC))
+         {
+         BER_Decoder othername(obj);
+
+         OID oid;
+         othername.decode(oid);
+         if(othername.more_items())
+            {
+            BER_Object othername_value_outer = othername.get_next_object();
+            othername.verify_end();
+
+            if(othername_value_outer.is_a(0, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+               throw Decoding_Error("Invalid tags on otherName value");
+
+            BER_Decoder othername_value_inner(othername_value_outer);
+
+            BER_Object value = othername_value_inner.get_next_object();
+            othername_value_inner.verify_end();
+
+            if(ASN1_String::is_string_type(value.type()) && value.get_class() == UNIVERSAL)
+               {
+               add_othername(oid, ASN1::to_string(value), value.type());
+               }
+            }
+         }
+      if(obj.is_a(1, CONTEXT_SPECIFIC))
+         {
+         add_attribute("RFC822", ASN1::to_string(obj));
+         }
+      else if(obj.is_a(2, CONTEXT_SPECIFIC))
+         {
+         add_attribute("DNS", ASN1::to_string(obj));
+         }
+      else if(obj.is_a(6, CONTEXT_SPECIFIC))
+         {
+         add_attribute("URI", ASN1::to_string(obj));
+         }
+      else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+         {
+         BER_Decoder dec(obj);
+         X509_DN dn;
+         std::stringstream ss;
+
+         dec.decode(dn);
+         ss << dn;
+
+         add_attribute("DN", ss.str());
+         }
+      else if(obj.is_a(7, CONTEXT_SPECIFIC))
+         {
+         if(obj.length() == 4)
+            {
+            const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
+            add_attribute("IP", ipv4_to_string(ip));
+            }
+         }
+
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/asn1_alt_name.h b/third_party/botan/src/lib/x509/asn1_alt_name.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/asn1_alt_name.h
@@ -0,0 +1,63 @@
+/*
+* (C) 1999-2007 Jack Lloyd
+*     2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_ALT_NAME_H_
+#define BOTAN_X509_ALT_NAME_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_str.h>
+#include <botan/asn1_oid.h>
+#include <botan/x509_dn.h>
+#include <map>
+
+namespace Botan {
+
+/**
+* Alternative Name
+*/
+class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object
+   {
+   public:
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      std::multimap<std::string, std::string> contents() const;
+
+      bool has_field(const std::string& attr) const;
+      std::vector<std::string> get_attribute(const std::string& attr) const;
+
+      std::string get_first_attribute(const std::string& attr) const;
+
+      void add_attribute(const std::string& type, const std::string& value);
+      void add_othername(const OID& oid, const std::string& value, ASN1_Tag type);
+
+      const std::multimap<std::string, std::string>& get_attributes() const
+         {
+         return m_alt_info;
+         }
+
+      const std::multimap<OID, ASN1_String>& get_othernames() const
+         {
+         return m_othernames;
+         }
+
+      X509_DN dn() const;
+
+      bool has_items() const;
+
+      AlternativeName(const std::string& email_addr = "",
+                      const std::string& uri = "",
+                      const std::string& dns = "",
+                      const std::string& ip_address = "");
+   private:
+      std::multimap<std::string, std::string> m_alt_info;
+      std::multimap<OID, ASN1_String> m_othernames;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/cert_status.cpp b/third_party/botan/src/lib/x509/cert_status.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/cert_status.cpp
@@ -0,0 +1,126 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cert_status.h>
+
+namespace Botan {
+
+//static
+const char* to_string(Certificate_Status_Code code)
+   {
+   switch(code)
+      {
+      case Certificate_Status_Code::VERIFIED:
+         return "Verified";
+      case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
+         return "OCSP response accepted as affirming unrevoked status for certificate";
+      case Certificate_Status_Code::OCSP_SIGNATURE_OK:
+         return "Signature on OCSP response was found valid";
+      case Certificate_Status_Code::VALID_CRL_CHECKED:
+         return "Valid CRL examined";
+
+      case Certificate_Status_Code::CERT_SERIAL_NEGATIVE:
+         return "Certificate serial number is negative";
+      case Certificate_Status_Code::DN_TOO_LONG:
+         return "Distinguished name too long";
+      case Certificate_Status_Code::OCSP_NO_REVOCATION_URL:
+         return "OCSP URL not available";
+      case Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE:
+         return "OCSP server not available";
+
+      case Certificate_Status_Code::NO_REVOCATION_DATA:
+         return "No revocation data";
+      case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
+         return "Signature method too weak";
+      case Certificate_Status_Code::UNTRUSTED_HASH:
+         return "Hash function used is considered too weak for security";
+
+      case Certificate_Status_Code::CERT_NOT_YET_VALID:
+         return "Certificate is not yet valid";
+      case Certificate_Status_Code::CERT_HAS_EXPIRED:
+         return "Certificate has expired";
+      case Certificate_Status_Code::OCSP_NOT_YET_VALID:
+         return "OCSP is not yet valid";
+      case Certificate_Status_Code::OCSP_HAS_EXPIRED:
+         return "OCSP response has expired";
+      case Certificate_Status_Code::OCSP_IS_TOO_OLD:
+         return "OCSP response is too old";
+      case Certificate_Status_Code::CRL_NOT_YET_VALID:
+         return "CRL response is not yet valid";
+      case Certificate_Status_Code::CRL_HAS_EXPIRED:
+         return "CRL has expired";
+
+      case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
+         return "Certificate issuer not found";
+      case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
+         return "Cannot establish trust";
+      case Certificate_Status_Code::CERT_CHAIN_LOOP:
+         return "Loop in certificate chain";
+      case Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT:
+         return "Certificate chain does not end in a CA certificate";
+      case Certificate_Status_Code::CHAIN_NAME_MISMATCH:
+         return "Certificate issuer does not match subject of issuing cert";
+
+      case Certificate_Status_Code::POLICY_ERROR:
+         return "Certificate policy error";
+      case Certificate_Status_Code::DUPLICATE_CERT_POLICY:
+         return "Certificate contains duplicate policy";
+      case Certificate_Status_Code::INVALID_USAGE:
+         return "Certificate does not allow the requested usage";
+      case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
+         return "Certificate chain too long";
+      case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
+         return "CA certificate not allowed to issue certs";
+      case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
+         return "CA certificate not allowed to issue CRLs";
+      case Certificate_Status_Code::NO_MATCHING_CRLDP:
+         return "No CRL with matching distribution point for certificate";
+      case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
+         return "OCSP cert not listed";
+      case Certificate_Status_Code::OCSP_BAD_STATUS:
+         return "OCSP bad status";
+      case Certificate_Status_Code::CERT_NAME_NOMATCH:
+         return "Certificate does not match provided name";
+      case Certificate_Status_Code::NAME_CONSTRAINT_ERROR:
+         return "Certificate does not pass name constraint";
+      case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION:
+         return "Unknown critical extension encountered";
+      case Certificate_Status_Code::DUPLICATE_CERT_EXTENSION:
+         return "Duplicate certificate extension encountered";
+      case Certificate_Status_Code::EXT_IN_V1_V2_CERT:
+         return "Encountered extension in certificate with version that does not allow it";
+      case Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT:
+         return "Encountered v2 identifiers in v1 certificate";
+      case Certificate_Status_Code::OCSP_SIGNATURE_ERROR:
+         return "OCSP signature error";
+      case Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND:
+         return "Unable to find certificate issusing OCSP response";
+      case Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE:
+         return "OCSP issuer's keyusage prohibits OCSP";
+      case Certificate_Status_Code::OCSP_RESPONSE_INVALID:
+         return "OCSP parsing valid";
+      case Certificate_Status_Code::OCSP_NO_HTTP:
+         return "OCSP requests not available, no HTTP support compiled in";
+      case Certificate_Status_Code::CERT_IS_REVOKED:
+         return "Certificate is revoked";
+      case Certificate_Status_Code::CRL_BAD_SIGNATURE:
+         return "CRL bad signature";
+      case Certificate_Status_Code::SIGNATURE_ERROR:
+         return "Signature error";
+      case Certificate_Status_Code::CERT_PUBKEY_INVALID:
+         return "Certificate public key invalid";
+      case Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN:
+         return "Certificate signed with unknown/unavailable algorithm";
+      case Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS:
+         return "Certificate signature has invalid parameters";
+
+      // intentionally no default so we are warned if new enum values are added
+      }
+
+   return nullptr;
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/cert_status.h b/third_party/botan/src/lib/x509/cert_status.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/cert_status.h
@@ -0,0 +1,105 @@
+/*
+* Path validation result enums
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PATH_RESULT_H_
+#define BOTAN_X509_PATH_RESULT_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Certificate validation status code
+*/
+enum class Certificate_Status_Code {
+   OK = 0,
+   VERIFIED = 0,
+
+   // Revocation status
+   OCSP_RESPONSE_GOOD = 1,
+   OCSP_SIGNATURE_OK = 2,
+   VALID_CRL_CHECKED = 3,
+   OCSP_NO_HTTP = 4,
+
+   // Warnings
+   FIRST_WARNING_STATUS = 500,
+   CERT_SERIAL_NEGATIVE = 500,
+   DN_TOO_LONG = 501,
+   OCSP_NO_REVOCATION_URL = 502,
+   OCSP_SERVER_NOT_AVAILABLE = 503,
+
+   // Typo versions of above - will be removed in future major release
+   OSCP_NO_REVOCATION_URL = 502,
+   OSCP_SERVER_NOT_AVAILABLE = 503,
+
+   // Errors
+   FIRST_ERROR_STATUS = 1000,
+
+   SIGNATURE_METHOD_TOO_WEAK = 1000,
+   UNTRUSTED_HASH = 1001,
+   NO_REVOCATION_DATA = 1002,
+   NO_MATCHING_CRLDP = 1003,
+
+   // Time problems
+   CERT_NOT_YET_VALID = 2000,
+   CERT_HAS_EXPIRED = 2001,
+   OCSP_NOT_YET_VALID = 2002,
+   OCSP_HAS_EXPIRED = 2003,
+   CRL_NOT_YET_VALID = 2004,
+   CRL_HAS_EXPIRED = 2005,
+   OCSP_IS_TOO_OLD = 2006,
+
+   // Chain generation problems
+   CERT_ISSUER_NOT_FOUND = 3000,
+   CANNOT_ESTABLISH_TRUST = 3001,
+   CERT_CHAIN_LOOP = 3002,
+   CHAIN_LACKS_TRUST_ROOT = 3003,
+   CHAIN_NAME_MISMATCH = 3004,
+
+   // Validation errors
+   POLICY_ERROR = 4000,
+   INVALID_USAGE = 4001,
+   CERT_CHAIN_TOO_LONG = 4002,
+   CA_CERT_NOT_FOR_CERT_ISSUER = 4003,
+   NAME_CONSTRAINT_ERROR = 4004,
+
+   // Revocation errors
+   CA_CERT_NOT_FOR_CRL_ISSUER = 4005,
+   OCSP_CERT_NOT_LISTED = 4006,
+   OCSP_BAD_STATUS = 4007,
+
+   // Other problems
+   CERT_NAME_NOMATCH = 4008,
+   UNKNOWN_CRITICAL_EXTENSION = 4009,
+   DUPLICATE_CERT_EXTENSION = 4010,
+   OCSP_SIGNATURE_ERROR = 4501,
+   OCSP_ISSUER_NOT_FOUND = 4502,
+   OCSP_RESPONSE_MISSING_KEYUSAGE = 4503,
+   OCSP_RESPONSE_INVALID = 4504,
+   EXT_IN_V1_V2_CERT = 4505,
+   DUPLICATE_CERT_POLICY = 4506,
+   V2_IDENTIFIERS_IN_V1_CERT = 4507,
+
+   // Hard failures
+   CERT_IS_REVOKED = 5000,
+   CRL_BAD_SIGNATURE = 5001,
+   SIGNATURE_ERROR = 5002,
+   CERT_PUBKEY_INVALID = 5003,
+   SIGNATURE_ALGO_UNKNOWN = 5004,
+   SIGNATURE_ALGO_BAD_PARAMS = 5005
+};
+
+/**
+* Convert a status code to a human readable diagnostic message
+* @param code the certifcate status
+* @return string literal constant, or nullptr if code unknown
+*/
+BOTAN_PUBLIC_API(2,0) const char* to_string(Certificate_Status_Code code);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor.cpp b/third_party/botan/src/lib/x509/certstor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor.cpp
@@ -0,0 +1,232 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor.h>
+#include <botan/internal/filesystem.h>
+#include <botan/hash.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+Certificate_Store::~Certificate_Store() {}
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+   {
+   const auto certs = find_all_certs(subject_dn, key_id);
+
+   if(certs.empty())
+      {
+      return nullptr;  // certificate not found
+      }
+
+   // `count` might be greater than 1, but we'll just select the first match
+   return certs.front();
+   }
+
+std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
+   {
+   return {};
+   }
+
+void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert)
+   {
+   for(const auto& c : m_certs)
+      if(*c == cert)
+         return;
+
+   m_certs.push_back(std::make_shared<const X509_Certificate>(cert));
+   }
+
+void Certificate_Store_In_Memory::add_certificate(std::shared_ptr<const X509_Certificate> cert)
+   {
+   for(const auto& c : m_certs)
+      if(*c == *cert)
+         return;
+
+   m_certs.push_back(cert);
+   }
+
+std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const
+   {
+   std::vector<X509_DN> subjects;
+   for(const auto& cert : m_certs)
+      subjects.push_back(cert->subject_dn());
+   return subjects;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
+                                       const std::vector<uint8_t>& key_id) const
+   {
+   for(const auto& cert : m_certs)
+      {
+      // Only compare key ids if set in both call and in the cert
+      if(key_id.size())
+         {
+         std::vector<uint8_t> skid = cert->subject_key_id();
+
+         if(skid.size() && skid != key_id) // no match
+            continue;
+         }
+
+      if(cert->subject_dn() == subject_dn)
+         return cert;
+      }
+
+   return nullptr;
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_In_Memory::find_all_certs(
+      const X509_DN& subject_dn,
+      const std::vector<uint8_t>& key_id) const
+   {
+   std::vector<std::shared_ptr<const X509_Certificate>> matches;
+
+   for(const auto& cert : m_certs)
+      {
+      if(key_id.size())
+         {
+         std::vector<uint8_t> skid = cert->subject_key_id();
+
+         if(skid.size() && skid != key_id) // no match
+            continue;
+         }
+
+      if(cert->subject_dn() == subject_dn)
+         matches.push_back(cert);
+      }
+
+   return matches;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   if(key_hash.size() != 20)
+      throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash");
+
+   std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-1"));
+
+   for(const auto& cert : m_certs){
+      hash->update(cert->subject_public_key_bitstring());
+      if(key_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+         return cert;
+   }
+
+   return nullptr;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+   {
+   if(subject_hash.size() != 32)
+      throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash");
+
+   std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+
+   for(const auto& cert : m_certs){
+      hash->update(cert->raw_subject_dn());
+      if(subject_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+         return cert;
+   }
+
+   return nullptr;
+   }
+
+void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl)
+   {
+   std::shared_ptr<const X509_CRL> crl_s = std::make_shared<const X509_CRL>(crl);
+   return add_crl(crl_s);
+   }
+
+void Certificate_Store_In_Memory::add_crl(std::shared_ptr<const X509_CRL> crl)
+   {
+   X509_DN crl_issuer = crl->issuer_dn();
+
+   for(auto& c : m_crls)
+      {
+      // Found an update of a previously existing one; replace it
+      if(c->issuer_dn() == crl_issuer)
+         {
+         if(c->this_update() <= crl->this_update())
+            c = crl;
+         return;
+         }
+      }
+
+   // Totally new CRL, add to the list
+   m_crls.push_back(crl);
+   }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
+   {
+   const std::vector<uint8_t>& key_id = subject.authority_key_id();
+
+   for(const auto& c : m_crls)
+      {
+      // Only compare key ids if set in both call and in the CRL
+      if(key_id.size())
+         {
+         std::vector<uint8_t> akid = c->authority_key_id();
+
+         if(akid.size() && akid != key_id) // no match
+            continue;
+         }
+
+      if(c->issuer_dn() == subject.issuer_dn())
+         return c;
+      }
+
+   return {};
+   }
+
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
+   {
+   add_certificate(cert);
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
+   {
+   if(dir.empty())
+      return;
+
+   std::vector<std::string> maybe_certs = get_files_recursive(dir);
+
+   if(maybe_certs.empty())
+      {
+      maybe_certs.push_back(dir);
+      }
+
+   for(auto&& cert_file : maybe_certs)
+      {
+      try
+         {
+         DataSource_Stream src(cert_file, true);
+         while(!src.end_of_data())
+            {
+            try
+               {
+               m_certs.push_back(std::make_shared<X509_Certificate>(src));
+               }
+            catch(std::exception&)
+               {
+               // stop searching for other certificate at first exception
+               break;
+               }
+            }
+         }
+      catch(std::exception&)
+         {
+         }
+      }
+   }
+#endif
+
+}
diff --git a/third_party/botan/src/lib/x509/certstor.h b/third_party/botan/src/lib/x509/certstor.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor.h
@@ -0,0 +1,165 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_H_
+#define BOTAN_CERT_STORE_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+
+namespace Botan {
+
+/**
+* Certificate Store Interface
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store
+   {
+   public:
+      virtual ~Certificate_Store();
+
+      /**
+      * Find a certificate by Subject DN and (optionally) key identifier
+      * @param subject_dn the subject's distinguished name
+      * @param key_id an optional key id
+      * @return a matching certificate or nullptr otherwise
+      * If more than one certificate in the certificate store matches, then
+      * a single value is selected arbitrarily.
+      */
+      virtual std::shared_ptr<const X509_Certificate>
+         find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const;
+
+      /**
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      virtual std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+         const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const = 0;
+
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-1 hash of
+      * public key. Used for OCSP.
+      * @param key_hash SHA-1 hash of the subject's public key
+      * @return a matching certificate or nullptr otherwise
+      */
+      virtual std::shared_ptr<const X509_Certificate>
+         find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const = 0;
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-256 hash of
+      * raw subject name. Used for OCSP.
+      * @param subject_hash SHA-256 hash of the subject's raw name
+      * @return a matching certificate or nullptr otherwise
+      */
+      virtual std::shared_ptr<const X509_Certificate>
+         find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const = 0;
+
+      /**
+      * Finds a CRL for the given certificate
+      * @param subject the subject certificate
+      * @return the CRL for subject or nullptr otherwise
+      */
+      virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const;
+
+      /**
+      * @return whether the certificate is known
+      * @param cert certififcate to be searched
+      */
+      bool certificate_known(const X509_Certificate& cert) const
+         {
+         return find_cert(cert.subject_dn(), cert.subject_key_id()) != nullptr;
+         }
+
+      // remove this (used by TLS::Server)
+      virtual std::vector<X509_DN> all_subjects() const = 0;
+   };
+
+/**
+* In Memory Certificate Store
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_Memory final : public Certificate_Store
+   {
+   public:
+      /**
+      * Attempt to parse all files in dir (including subdirectories)
+      * as certificates. Ignores errors.
+      */
+      explicit Certificate_Store_In_Memory(const std::string& dir);
+
+      /**
+      * Adds given certificate to the store.
+      */
+      explicit Certificate_Store_In_Memory(const X509_Certificate& cert);
+
+      /**
+      * Create an empty store.
+      */
+      Certificate_Store_In_Memory() = default;
+
+      /**
+      * Add a certificate to the store.
+      * @param cert certificate to be added
+      */
+      void add_certificate(const X509_Certificate& cert);
+
+      /**
+      * Add a certificate already in a shared_ptr to the store.
+      * @param cert certificate to be added
+      */
+      void add_certificate(std::shared_ptr<const X509_Certificate> cert);
+
+      /**
+      * Add a certificate revocation list (CRL) to the store.
+      * @param crl CRL to be added
+      */
+      void add_crl(const X509_CRL& crl);
+
+      /**
+      * Add a certificate revocation list (CRL) to the store as a shared_ptr
+      * @param crl CRL to be added
+      */
+      void add_crl(std::shared_ptr<const X509_CRL> crl);
+
+      /**
+      * @return DNs for all certificates managed by the store
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /*
+      * Find a certificate by Subject DN and (optionally) key identifier
+      * @return the first certificate that matches
+      */
+      std::shared_ptr<const X509_Certificate> find_cert(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const override;
+
+      /*
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+         const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+      * Finds a CRL for the given certificate
+      */
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+   private:
+      // TODO: Add indexing on the DN and key id to avoid linear search
+      std::vector<std::shared_ptr<const X509_Certificate>> m_certs;
+      std::vector<std::shared_ptr<const X509_CRL>> m_crls;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp b/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
@@ -0,0 +1,148 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/build.h>
+
+#include <botan/certstor_flatfile.h>
+#include <botan/data_src.h>
+#include <botan/pem.h>
+
+namespace Botan {
+namespace {
+std::vector<std::vector<uint8_t>> decode_all_certificates(DataSource& source)
+   {
+   std::vector<std::vector<uint8_t>> pems;
+
+   while(!source.end_of_data())
+      {
+      std::string label;
+      std::vector<uint8_t> cert;
+      try
+         {
+         cert = unlock(PEM_Code::decode(source, label));
+
+         if(label == "CERTIFICATE" || label == "X509 CERTIFICATE" || label == "TRUSTED CERTIFICATE")
+            {
+            pems.push_back(cert);
+            }
+         }
+      catch(const Decoding_Error&) {}
+      }
+
+   return pems;
+   }
+}
+
+Flatfile_Certificate_Store::Flatfile_Certificate_Store(const std::string& file, bool ignore_non_ca)
+   {
+   if(file.empty())
+      {
+      throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store invalid file path");
+      }
+
+   DataSource_Stream file_stream(file);
+
+   for(const std::vector<uint8_t>& der : decode_all_certificates(file_stream))
+      {
+      std::shared_ptr<const X509_Certificate> cert = std::make_shared<const X509_Certificate>(der.data(), der.size());
+
+      /*
+      * Various weird or misconfigured system roots include intermediate certificates,
+      * or even stranger certificates which are not valid for cert issuance at all.
+      * Previously this code would error on such cases as an obvious misconfiguration,
+      * but we cannot fix the trust store. So instead just ignore any such certificate.
+      */
+      if(cert->is_self_signed() && cert->is_CA_cert())
+         {
+         m_all_subjects.push_back(cert->subject_dn());
+         m_dn_to_cert[cert->subject_dn()].push_back(cert);
+         m_pubkey_sha1_to_cert.emplace(cert->subject_public_key_bitstring_sha1(), cert);
+         m_subject_dn_sha256_to_cert.emplace(cert->raw_subject_dn_sha256(), cert);
+         }
+      else if(!ignore_non_ca)
+         {
+         throw Invalid_Argument("Flatfile_Certificate_Store received non CA cert " + cert->subject_dn().to_string());
+         }
+      }
+
+   if(m_all_subjects.empty())
+      {
+      throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store cert file is empty");
+      }
+   }
+
+std::vector<X509_DN> Flatfile_Certificate_Store::all_subjects() const
+   {
+   return m_all_subjects;
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Flatfile_Certificate_Store::find_all_certs(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const
+   {
+   std::vector<std::shared_ptr<const X509_Certificate>> found_certs;
+   try
+      {
+      const auto certs = m_dn_to_cert.at(subject_dn);
+
+      for(auto cert : certs)
+         {
+         if(key_id.empty() || key_id == cert->subject_key_id())
+            {
+            found_certs.push_back(cert);
+            }
+         }
+      }
+   catch(const std::out_of_range&)
+      {
+      return {};
+      }
+
+   return found_certs;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   if(key_hash.size() != 20)
+      {
+      throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_pubkey_sha1 invalid hash");
+      }
+
+   auto found_cert = m_pubkey_sha1_to_cert.find(key_hash);
+
+   if(found_cert != m_pubkey_sha1_to_cert.end())
+      {
+      return found_cert->second;
+      }
+
+   return nullptr;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+   {
+   if(subject_hash.size() != 32)
+      { throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256 invalid hash"); }
+
+   auto found_cert = m_subject_dn_sha256_to_cert.find(subject_hash);
+
+   if(found_cert != m_subject_dn_sha256_to_cert.end())
+      {
+      return found_cert->second;
+      }
+
+   return nullptr;
+   }
+
+std::shared_ptr<const X509_CRL> Flatfile_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+   {
+   BOTAN_UNUSED(subject);
+   return {};
+   }
+}
diff --git a/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h b/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
@@ -0,0 +1,77 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_FLATFILE_H_
+#define BOTAN_CERT_STORE_FLATFILE_H_
+
+#include <botan/certstor.h>
+
+#include <vector>
+#include <memory>
+#include <map>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by a file of PEMs of trusted CAs.
+*/
+class BOTAN_PUBLIC_API(2, 11) Flatfile_Certificate_Store final : public Certificate_Store
+   {
+   public:
+      /**
+      * Construct a new Certificate_Store given a file path to a file including
+      * PEMs of trusted self-signed CAs.
+      *
+      * @param file the name of the file to read certificates from
+      * @param ignore_non_ca if true, certs that are not self-signed CA certs will
+      * be ignored. Otherwise (if false), an exception will be thrown instead.
+      */
+      Flatfile_Certificate_Store(const std::string& file, bool ignore_non_ca = false);
+
+      Flatfile_Certificate_Store(const Flatfile_Certificate_Store&) = default;
+      Flatfile_Certificate_Store(Flatfile_Certificate_Store&&) = default;
+      Flatfile_Certificate_Store& operator=(const Flatfile_Certificate_Store&) = default;
+      Flatfile_Certificate_Store& operator=(Flatfile_Certificate_Store&&) = default;
+
+      /**
+      * @return DNs for all certificates managed by the store
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /**
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+               const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-1 hash of
+      * public key.
+      * @return a matching certificate or nullptr otherwise
+      */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+       * Fetching CRLs is not supported by this certificate store. This will
+       * always return an empty list.
+       */
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+   private:
+      std::vector<X509_DN> m_all_subjects;
+      std::map<X509_DN, std::vector<std::shared_ptr<const X509_Certificate>>> m_dn_to_cert;
+      std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_pubkey_sha1_to_cert;
+      std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_subject_dn_sha256_to_cert;
+   };
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp b/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
@@ -0,0 +1,334 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sql.h>
+#include <botan/pk_keys.h>
+#include <botan/ber_dec.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
+                                                   const std::string& passwd,
+                                                   RandomNumberGenerator& rng,
+                                                   const std::string& table_prefix) :
+   m_rng(rng),
+   m_database(db),
+   m_prefix(table_prefix),
+   m_password(passwd)
+   {
+   m_database->create_table("CREATE TABLE IF NOT EXISTS " +
+                             m_prefix + "certificates (                \
+                                 fingerprint       BLOB PRIMARY KEY,   \
+                                 subject_dn        BLOB,               \
+                                 key_id            BLOB,               \
+                                 priv_fingerprint  BLOB,               \
+                                 certificate       BLOB UNIQUE NOT NULL\
+                             )");
+   m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "keys (\
+                                 fingerprint BLOB PRIMARY KEY,                \
+                                 key         BLOB UNIQUE NOT NULL             \
+                             )");
+   m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "revoked (\
+                                 fingerprint BLOB PRIMARY KEY,                   \
+                                 reason      BLOB NOT NULL,                      \
+                                 time        BLOB NOT NULL                       \
+                            )");
+   }
+
+// Certificate handling
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+   {
+   std::shared_ptr<SQL_Database::Statement> stmt;
+
+   const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+   if(key_id.empty())
+      {
+      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1 LIMIT 1");
+      stmt->bind(1, dn_encoding);
+      }
+   else
+      {
+      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+                                        subject_dn == ?1 AND (key_id == NULL OR key_id == ?2) LIMIT 1");
+      stmt->bind(1, dn_encoding);
+      stmt->bind(2,key_id);
+      }
+
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      return std::make_shared<X509_Certificate>(std::vector<uint8_t>(blob.first, blob.first + blob.second));
+      }
+
+   return std::shared_ptr<const X509_Certificate>();
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+   {
+   std::vector<std::shared_ptr<const X509_Certificate>> certs;
+
+   std::shared_ptr<SQL_Database::Statement> stmt;
+
+   const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+   if(key_id.empty())
+      {
+      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1");
+      stmt->bind(1, dn_encoding);
+      }
+   else
+      {
+      stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+                                        subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
+      stmt->bind(1, dn_encoding);
+      stmt->bind(2, key_id);
+      }
+
+   std::shared_ptr<const X509_Certificate> cert;
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      certs.push_back(std::make_shared<X509_Certificate>(
+            std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+      }
+
+   return certs;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& /*key_hash*/) const
+   {
+   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_pubkey_sha1");
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& /*subject_hash*/) const
+   {
+   throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
+   }
+
+std::shared_ptr<const X509_CRL>
+Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const
+   {
+   auto all_crls = generate_crls();
+
+   for(auto crl: all_crls)
+      {
+      if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn())
+         return std::shared_ptr<X509_CRL>(new X509_CRL(crl));
+      }
+
+   return std::shared_ptr<X509_CRL>();
+   }
+
+std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const
+   {
+   std::vector<X509_DN> ret;
+   auto stmt = m_database->new_statement("SELECT subject_dn FROM " + m_prefix + "certificates");
+
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      BER_Decoder dec(blob.first,blob.second);
+      X509_DN dn;
+
+      dn.decode_from(dec);
+
+      ret.push_back(dn);
+      }
+
+   return ret;
+   }
+
+bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert)
+   {
+   const std::vector<uint8_t> dn_encoding = cert.subject_dn().BER_encode();
+   const std::vector<uint8_t> cert_encoding = cert.BER_encode();
+
+   auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " +
+                                     m_prefix + "certificates (\
+                                         fingerprint,          \
+                                         subject_dn,           \
+                                         key_id,               \
+                                         priv_fingerprint,     \
+                                         certificate           \
+                                     ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
+
+   stmt->bind(1,cert.fingerprint("SHA-256"));
+   stmt->bind(2,dn_encoding);
+   stmt->bind(3,cert.subject_key_id());
+   stmt->bind(4,std::vector<uint8_t>());
+   stmt->bind(5,cert_encoding);
+   stmt->spin();
+
+   return true;
+   }
+
+
+bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert)
+   {
+   if(!find_cert(cert.subject_dn(),cert.subject_key_id()))
+      return false;
+
+   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
+
+   stmt->bind(1,cert.fingerprint("SHA-256"));
+   stmt->spin();
+
+   return true;
+   }
+
+// Private key handling
+std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const
+   {
+   auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix + "keys "
+       "JOIN " + m_prefix + "certificates ON " +
+       m_prefix + "keys.fingerprint == " + m_prefix + "certificates.priv_fingerprint "
+       "WHERE " + m_prefix + "certificates.fingerprint == ?1");
+   stmt->bind(1,cert.fingerprint("SHA-256"));
+
+   std::shared_ptr<const Private_Key> key;
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      DataSource_Memory src(blob.first,blob.second);
+      key.reset(PKCS8::load_key(src, m_rng, m_password));
+      }
+
+   return key;
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const
+   {
+   auto fpr = key.fingerprint_private("SHA-256");
+   auto stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE priv_fingerprint == ?1");
+
+   stmt->bind(1,fpr);
+
+   std::vector<std::shared_ptr<const X509_Certificate>> certs;
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      certs.push_back(std::make_shared<X509_Certificate>(
+            std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+      }
+
+   return certs;
+   }
+
+bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
+   insert_cert(cert);
+
+   if(find_key(cert))
+      return false;
+
+   auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
+   auto fpr = key.fingerprint_private("SHA-256");
+
+   auto stmt1 = m_database->new_statement(
+         "INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )");
+
+   stmt1->bind(1,fpr);
+   stmt1->bind(2,pkcs8.data(),pkcs8.size());
+   stmt1->spin();
+
+   auto stmt2 = m_database->new_statement(
+         "UPDATE " + m_prefix + "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2");
+
+   stmt2->bind(1,fpr);
+   stmt2->bind(2,cert.fingerprint("SHA-256"));
+   stmt2->spin();
+
+   return true;
+   }
+
+void Certificate_Store_In_SQL::remove_key(const Private_Key& key)
+   {
+   auto fpr = key.fingerprint_private("SHA-256");
+   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1");
+
+   stmt->bind(1,fpr);
+   stmt->spin();
+   }
+
+// Revocation
+void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time)
+   {
+   insert_cert(cert);
+
+   auto stmt1 = m_database->new_statement(
+         "INSERT OR REPLACE INTO " + m_prefix + "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
+
+   stmt1->bind(1,cert.fingerprint("SHA-256"));
+   stmt1->bind(2,code);
+
+   if(time.time_is_set())
+      {
+      stmt1->bind(3, time.BER_encode());
+      }
+   else
+      {
+      stmt1->bind(3, static_cast<size_t>(-1));
+      }
+
+   stmt1->spin();
+   }
+
+void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert)
+   {
+   auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1");
+
+   stmt->bind(1,cert.fingerprint("SHA-256"));
+   stmt->spin();
+   }
+
+std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const
+   {
+   auto stmt = m_database->new_statement(
+         "SELECT certificate,reason,time FROM " + m_prefix + "revoked "
+         "JOIN " + m_prefix + "certificates ON " +
+         m_prefix + "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
+
+   std::map<X509_DN,std::vector<CRL_Entry>> crls;
+   while(stmt->step())
+      {
+      auto blob = stmt->get_blob(0);
+      auto cert = X509_Certificate(
+            std::vector<uint8_t>(blob.first,blob.first + blob.second));
+      auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
+      auto ent = CRL_Entry(cert,code);
+
+      auto i = crls.find(cert.issuer_dn());
+      if(i == crls.end())
+         {
+         crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent})));
+         }
+      else
+         {
+         i->second.push_back(ent);
+         }
+      }
+
+   std::vector<X509_CRL> ret;
+   X509_Time t(std::chrono::system_clock::now());
+
+   for(auto p: crls)
+      {
+      ret.push_back(X509_CRL(p.first,t,t,p.second));
+      }
+
+   return ret;
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h b/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
@@ -0,0 +1,119 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQL_H_
+#define BOTAN_CERT_STORE_SQL_H_
+
+#include <botan/certstor.h>
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/database.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+ * Certificate and private key store backed by an SQL database.
+ */
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQL : public Certificate_Store
+   {
+   public:
+      /**
+      * Create/open a certificate store.
+      * @param db underlying database storage
+      * @param passwd password to encrypt private keys in the database
+      * @param rng used for encrypting keys
+      * @param table_prefix optional prefix for db table names
+      */
+      explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db,
+                                        const std::string& passwd,
+                                        RandomNumberGenerator& rng,
+                                        const std::string& table_prefix = "");
+
+      /**
+      * Returns the first certificate with matching subject DN and optional key ID.
+      */
+      std::shared_ptr<const X509_Certificate>
+         find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      /*
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+         const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+      * Returns all subject DNs known to the store instance.
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /**
+      * Inserts "cert" into the store, returns false if the certificate is
+      * already known and true if insertion was successful.
+      */
+      bool insert_cert(const X509_Certificate& cert);
+
+      /**
+      * Removes "cert" from the store. Returns false if the certificate could not
+      * be found and true if removal was successful.
+      */
+      bool remove_cert(const X509_Certificate& cert);
+
+      /// Returns the private key for "cert" or an empty shared_ptr if none was found.
+      std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const;
+
+      /// Returns all certificates for private key "key".
+      std::vector<std::shared_ptr<const X509_Certificate>>
+         find_certs_for_key(const Private_Key& key) const;
+
+      /**
+      * Inserts "key" for "cert" into the store, returns false if the key is
+      * already known and true if insertion was successful.
+      */
+      bool insert_key(const X509_Certificate& cert, const Private_Key& key);
+
+      /// Removes "key" from the store.
+      void remove_key(const Private_Key& key);
+
+      /// Marks "cert" as revoked starting from "time".
+      void revoke_cert(const X509_Certificate&, CRL_Code, const X509_Time& time = X509_Time());
+
+      /// Reverses the revokation for "cert".
+      void affirm_cert(const X509_Certificate&);
+
+      /**
+      * Generates Certificate Revocation Lists for all certificates marked as revoked.
+      * A CRL is returned for each unique issuer DN.
+      */
+      std::vector<X509_CRL> generate_crls() const;
+
+      /**
+      * Generates a CRL for all certificates issued by the given issuer.
+      */
+      std::shared_ptr<const X509_CRL>
+         find_crl_for(const X509_Certificate& issuer) const override;
+
+   private:
+      RandomNumberGenerator& m_rng;
+      std::shared_ptr<SQL_Database> m_database;
+      std::string m_prefix;
+      std::string m_password;
+      mutex_type m_mutex;
+   };
+
+}
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp b/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
@@ -0,0 +1,19 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path,
+                                                         const std::string& passwd,
+                                                         RandomNumberGenerator& rng,
+                                                         const std::string& table_prefix) :
+   Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, rng, table_prefix)
+   {}
+}
diff --git a/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h b/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
@@ -0,0 +1,34 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQLITE_H_
+#define BOTAN_CERT_STORE_SQLITE_H_
+
+#include <botan/certstor_sql.h>
+
+namespace Botan {
+
+/**
+* Certificate and private key store backed by an sqlite (https://sqlite.org) database.
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQLite final : public Certificate_Store_In_SQL
+   {
+   public:
+      /**
+      * Create/open a certificate store.
+      * @param db_path path to the database file
+      * @param passwd password to encrypt private keys in the database
+      * @param rng used for encrypting keys
+      * @param table_prefix optional prefix for db table names
+      */
+      Certificate_Store_In_SQLite(const std::string& db_path,
+                                  const std::string& passwd,
+                                  RandomNumberGenerator& rng,
+                                  const std::string& table_prefix = "");
+   };
+}
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp b/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
@@ -0,0 +1,69 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_system.h>
+#include <botan/x509cert.h>
+
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+   #include <botan/certstor_macos.h>
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+   #include <botan/certstor_windows.h>
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+   #include <botan/certstor_flatfile.h>
+#endif
+
+namespace Botan {
+
+System_Certificate_Store::System_Certificate_Store()
+   {
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+   m_system_store = std::make_shared<Certificate_Store_MacOS>();
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+   m_system_store = std::make_shared<Certificate_Store_Windows>();
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+   m_system_store = std::make_shared<Flatfile_Certificate_Store>(BOTAN_SYSTEM_CERT_BUNDLE, true);
+#else
+   throw Not_Implemented("No system certificate store available in this build");
+#endif
+   }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+   {
+   return m_system_store->find_cert(subject_dn, key_id);
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+System_Certificate_Store::find_all_certs(const X509_DN& subject_dn,
+                                         const std::vector<uint8_t>& key_id) const
+   {
+   return m_system_store->find_all_certs(subject_dn, key_id);
+   }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   return m_system_store->find_cert_by_pubkey_sha1(key_hash);
+   }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+   {
+   return m_system_store->find_cert_by_raw_subject_dn_sha256(subject_hash);
+   }
+
+std::shared_ptr<const X509_CRL>
+System_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+   {
+   return m_system_store->find_crl_for(subject);
+   }
+
+std::vector<X509_DN> System_Certificate_Store::all_subjects() const
+   {
+   return m_system_store->all_subjects();
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/certstor_system/certstor_system.h b/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
@@ -0,0 +1,42 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYSTEM_CERT_STORE_H_
+#define BOTAN_SYSTEM_CERT_STORE_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,11) System_Certificate_Store final : public Certificate_Store
+   {
+   public:
+
+      System_Certificate_Store();
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      std::vector<std::shared_ptr<const X509_Certificate>>
+         find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      std::shared_ptr<const X509_Certificate>
+         find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+      std::vector<X509_DN> all_subjects() const override;
+
+   private:
+      std::shared_ptr<Certificate_Store> m_system_store;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp b/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
@@ -0,0 +1,473 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019-2020 René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/build.h>
+
+#include <algorithm>
+#include <array>
+
+#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+#include <botan/assert.h>
+#include <botan/ber_dec.h>
+#include <botan/certstor_macos.h>
+#include <botan/data_src.h>
+#include <botan/der_enc.h>
+#include <botan/exceptn.h>
+#include <botan/x509_dn.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+ * Abstract RAII wrapper for CFTypeRef-style object handles
+ * All of those xxxRef types are eventually typedefs to void*
+ */
+template<typename T>
+class scoped_CFType
+   {
+   public:
+      explicit scoped_CFType(T value)
+         : m_value(value)
+         {
+         }
+
+      scoped_CFType(const scoped_CFType<T>& rhs) = delete;
+      scoped_CFType(scoped_CFType<T>&& rhs) :
+         m_value(std::move(rhs.m_value))
+         {
+         rhs.m_value = nullptr;
+         }
+
+      ~scoped_CFType()
+         {
+         if(m_value)
+            {
+            CFRelease(m_value);
+            }
+         }
+
+      operator bool() const { return m_value != nullptr; }
+
+      void assign(T value)
+         {
+         BOTAN_ASSERT(m_value == nullptr, "scoped_CFType was not set yet");
+         m_value = value;
+         }
+
+      T& get() { return m_value; }
+      const T& get() const { return m_value; }
+
+   private:
+      T m_value;
+   };
+
+/**
+ * Apple's DN parser "normalizes" ASN1 'PrintableString' into upper-case values
+ * and strips leading, trailing as well as multiple white spaces.
+ * See: opensource.apple.com/source/Security/Security-55471/sec/Security/SecCertificate.c.auto.html
+ */
+X509_DN normalize(const X509_DN& dn)
+   {
+   X509_DN result;
+
+   for(const auto& rdn : dn.dn_info())
+      {
+      // TODO: C++14 - use std::get<ASN1_String>(), resp. std::get<OID>()
+      const auto oid = rdn.first;
+      auto str = rdn.second;
+
+      if(str.tagging() == ASN1_Tag::PRINTABLE_STRING)
+         {
+         std::string normalized;
+         normalized.reserve(str.value().size());
+         for(const char c : str.value())
+            {
+            if(c != ' ')
+               {
+               // store all 'normal' characters as upper case
+               normalized.push_back(::toupper(c));
+               }
+            else if(!normalized.empty() && normalized.back() != ' ')
+               {
+               // remove leading and squash multiple white spaces
+               normalized.push_back(c);
+               }
+            }
+
+         if(normalized.back() == ' ')
+            {
+            // remove potential remaining single trailing white space char
+            normalized.erase(normalized.end() - 1);
+            }
+
+         str = ASN1_String(normalized, str.tagging());
+         }
+
+      result.add_attribute(oid, str);
+      }
+
+   return result;
+   }
+
+std::vector<uint8_t> normalizeAndSerialize(const X509_DN& dn)
+   {
+   std::vector<uint8_t> result_dn;
+   DER_Encoder encoder(result_dn);
+   normalize(dn).encode_into(encoder);
+   return result_dn;
+   }
+
+std::string to_string(const CFStringRef cfstring)
+   {
+   const char* ccstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);
+
+   if(ccstr != nullptr)
+      {
+      return std::string(ccstr);
+      }
+
+   auto utf16_pairs    = CFStringGetLength(cfstring);
+   auto max_utf8_bytes = CFStringGetMaximumSizeForEncoding(utf16_pairs, kCFStringEncodingUTF8);
+
+   std::vector<char> cstr(max_utf8_bytes, '\0');
+   auto result = CFStringGetCString(cfstring,
+                                    cstr.data(), cstr.size(),
+                                    kCFStringEncodingUTF8);
+
+   return (result) ? std::string(cstr.data()) : std::string();
+   }
+
+std::string to_string(const OSStatus status)
+   {
+   scoped_CFType<CFStringRef> eCFString(
+      SecCopyErrorMessageString(status, nullptr));
+   return to_string(eCFString.get());
+   }
+
+void check_success(const OSStatus status, const std::string context)
+   {
+   if(errSecSuccess == status)
+      {
+      return;
+      }
+
+   throw Internal_Error(
+      std::string("failed to " + context + ": " + to_string(status)));
+   }
+
+template <typename T>
+void check_notnull(const T& value, const std::string context)
+   {
+   if(value)
+      {
+      return;
+      }
+
+   throw Internal_Error(std::string("failed to ") + context);
+   }
+
+}  // namespace
+
+/**
+ * Internal class implementation (i.e. Pimpl) to keep the required platform-
+ * dependent members of Certificate_Store_MacOS contained in this compilation
+ * unit.
+ */
+class Certificate_Store_MacOS_Impl
+   {
+   private:
+      static constexpr const char* system_roots =
+         "/System/Library/Keychains/SystemRootCertificates.keychain";
+      static constexpr const char* system_keychain =
+         "/Library/Keychains/System.keychain";
+
+   public:
+      /**
+       * Wraps a list of search query parameters that are later passed into
+       * Apple's certifificate store API. The class provides some convenience
+       * functionality and handles the query paramenter's data lifetime.
+       */
+      class Query
+         {
+         public:
+            Query() = default;
+            ~Query() = default;
+            Query(Query&& other) = default;
+            Query& operator=(Query&& other) = default;
+
+            Query(const Query& other) = delete;
+            Query& operator=(const Query& other) = delete;
+
+         public:
+            void addParameter(CFStringRef key, CFTypeRef value)
+               {
+               m_keys.emplace_back(key);
+               m_values.emplace_back(value);
+               }
+
+            void addParameter(CFStringRef key, std::vector<uint8_t> value)
+               {
+               // TODO C++17: std::vector::emplace_back will return the reference
+               //             to the inserted object straight away.
+               m_data_store.emplace_back(std::move(value));
+               const auto& data = m_data_store.back();
+
+               m_data_refs.emplace_back(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                        data.data(),
+                                        data.size(),
+                                        kCFAllocatorNull));
+               const auto& data_ref = m_data_refs.back();
+               check_notnull(data_ref, "create CFDataRef of search object failed");
+
+               addParameter(key, data_ref.get());
+               }
+
+            /**
+             * Amends the user-provided search query with generic filter rules
+             * for the associated system keychains and transforms it into a
+             * representation that can be passed to the Apple keychain API.
+             */
+            scoped_CFType<CFDictionaryRef> prepare(const CFArrayRef& keychains,
+                                                   const SecPolicyRef& policy)
+               {
+               addParameter(kSecClass,            kSecClassCertificate);
+               addParameter(kSecReturnRef,        kCFBooleanTrue);
+               addParameter(kSecMatchLimit,       kSecMatchLimitAll);
+               addParameter(kSecMatchTrustedOnly, kCFBooleanTrue);
+               addParameter(kSecMatchSearchList,  keychains);
+               addParameter(kSecMatchPolicy,      policy);
+
+               BOTAN_ASSERT_EQUAL(m_keys.size(), m_values.size(), "valid key-value pairs");
+
+               auto query = scoped_CFType<CFDictionaryRef>(CFDictionaryCreate(
+                               kCFAllocatorDefault, (const void**)m_keys.data(),
+                               (const void**)m_values.data(), m_keys.size(),
+                               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+               check_notnull(query, "create search query");
+
+               return query;
+               }
+
+         private:
+            using Data     = std::vector<std::vector<uint8_t>>;
+            using DataRefs = std::vector<scoped_CFType<CFDataRef>>;
+            using Keys     = std::vector<CFStringRef>;
+            using Values   = std::vector<CFTypeRef>;
+
+            Data     m_data_store; //! makes sure that data parameters are kept alive
+            DataRefs m_data_refs;  //! keeps track of CFDataRef objects refering into \p m_data_store
+            Keys     m_keys;       //! ordered list of search parameter keys
+            Values   m_values;     //! ordered list of search parameter values
+         };
+
+   public:
+      Certificate_Store_MacOS_Impl() :
+         m_policy(SecPolicyCreateBasicX509()),
+         m_system_roots(nullptr),
+         m_system_chain(nullptr),
+         m_keychains(nullptr)
+         {
+         check_success(SecKeychainOpen(system_roots, &m_system_roots.get()),
+                       "open system root certificates");
+         check_success(SecKeychainOpen(system_keychain, &m_system_chain.get()),
+                       "open system keychain");
+         check_notnull(m_system_roots, "open system root certificate chain");
+         check_notnull(m_system_chain, "open system certificate chain");
+
+         // m_keychains is merely a convenience list view into all open keychain
+         // objects. This list is required in prepareQuery().
+         std::array<const void*, 2> keychains{{
+               m_system_roots.get(),
+               m_system_chain.get()
+               }};
+
+         m_keychains.assign(
+            CFArrayCreate(kCFAllocatorDefault,
+                          keychains.data(),
+                          keychains.size(),
+                          &kCFTypeArrayCallBacks));
+         check_notnull(m_keychains, "initialize keychain array");
+         }
+
+      std::shared_ptr<const X509_Certificate> findOne(Query query) const
+         {
+         query.addParameter(kSecMatchLimit, kSecMatchLimitOne);
+
+         scoped_CFType<CFTypeRef> result(nullptr);
+         search(std::move(query), &result.get());
+
+         return (result) ? readCertificate(result.get()) : nullptr;
+         }
+
+      std::vector<std::shared_ptr<const X509_Certificate>> findAll(Query query) const
+         {
+         query.addParameter(kSecMatchLimit, kSecMatchLimitAll);
+
+         scoped_CFType<CFArrayRef> result(nullptr);
+         search(std::move(query), (CFTypeRef*)&result.get());
+
+         std::vector<std::shared_ptr<const X509_Certificate>> output;
+
+         if(result)
+            {
+            const auto count = CFArrayGetCount(result.get());
+            BOTAN_ASSERT(count > 0, "certificate result list contains data");
+
+            for(unsigned int i = 0; i < count; ++i)
+               {
+               auto cert = CFArrayGetValueAtIndex(result.get(), i);
+               output.emplace_back(readCertificate(cert));
+               }
+            }
+
+         return output;
+         }
+
+   protected:
+      void search(Query query, CFTypeRef* result) const
+         {
+         scoped_CFType<CFDictionaryRef> fullQuery(query.prepare(keychains(), policy()));
+
+         auto status = SecItemCopyMatching(fullQuery.get(), result);
+
+         if(errSecItemNotFound == status)
+            {
+            return;  // no matches
+            }
+
+         check_success(status, "look up certificate");
+         check_notnull(result, "look up certificate (invalid result value)");
+         }
+
+      /**
+       * Convert a CFTypeRef object into a Botan::X509_Certificate
+       */
+      std::shared_ptr<const X509_Certificate> readCertificate(CFTypeRef object) const
+         {
+         if(!object || CFGetTypeID(object) != SecCertificateGetTypeID())
+            {
+            throw Internal_Error("cannot convert CFTypeRef to SecCertificateRef");
+            }
+
+         auto cert = static_cast<SecCertificateRef>(const_cast<void*>(object));
+
+         scoped_CFType<CFDataRef> derData(SecCertificateCopyData(cert));
+         check_notnull(derData, "read extracted certificate");
+
+         const auto data   = CFDataGetBytePtr(derData.get());
+         const auto length = CFDataGetLength(derData.get());
+
+         DataSource_Memory ds(data, length);
+         return std::make_shared<Botan::X509_Certificate>(ds);
+         }
+
+      CFArrayRef keychains() const { return m_keychains.get(); }
+      SecPolicyRef policy() const { return m_policy.get(); }
+
+   private:
+      scoped_CFType<SecPolicyRef>    m_policy;
+      scoped_CFType<SecKeychainRef>  m_system_roots;
+      scoped_CFType<SecKeychainRef>  m_system_chain;
+      scoped_CFType<CFArrayRef>      m_keychains;
+   };
+
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+//   Implementation of Botan::Certificate_Store interface ...
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+
+Certificate_Store_MacOS::Certificate_Store_MacOS() :
+   m_impl(std::make_shared<Certificate_Store_MacOS_Impl>())
+   {
+   }
+
+std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const
+   {
+   // Note: This fetches and parses all certificates in the trust store.
+   //       Apple's API provides SecCertificateCopyNormalizedSubjectSequence
+   //       which facilitates reading the certificate DN without parsing the
+   //       entire certificate via Botan::X509_Certificate. However, this
+   //       function applies the same DN "normalization" as stated above.
+   const auto certificates = m_impl->findAll({});
+
+   std::vector<X509_DN> output;
+   std::transform(certificates.cbegin(), certificates.cend(),
+                  std::back_inserter(output),
+                  [](const std::shared_ptr<const X509_Certificate> cert)
+      {
+      return cert->subject_dn();
+      });
+
+   return output;
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert(const X509_DN& subject_dn,
+                                   const std::vector<uint8_t>& key_id) const
+   {
+   Certificate_Store_MacOS_Impl::Query query;
+   query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+   if(!key_id.empty())
+      {
+      query.addParameter(kSecAttrSubjectKeyID, key_id);
+      }
+
+   return m_impl->findOne(std::move(query));
+   }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const
+   {
+   Certificate_Store_MacOS_Impl::Query query;
+   query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+   if(!key_id.empty())
+      {
+      query.addParameter(kSecAttrSubjectKeyID, key_id);
+      }
+
+   return m_impl->findAll(std::move(query));
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   if(key_hash.size() != 20)
+      {
+      throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");
+      }
+
+   Certificate_Store_MacOS_Impl::Query query;
+   query.addParameter(kSecAttrPublicKeyHash, key_hash);
+
+   return m_impl->findOne(std::move(query));
+   }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+   {
+   BOTAN_UNUSED(subject_hash);
+   throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256");
+   }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const
+   {
+   BOTAN_UNUSED(subject);
+   return {};
+   }
+
+}  // namespace Botan
diff --git a/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h b/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
@@ -0,0 +1,81 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+#define BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+
+#include <memory>
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class Certificate_Store_MacOS_Impl;
+
+/**
+* Certificate Store that is backed by the system trust store on macOS. This
+* opens a handle to the macOS keychain and serves certificate queries directly
+* from there.
+*/
+class BOTAN_PUBLIC_API(2, 10) Certificate_Store_MacOS final : public Certificate_Store
+   {
+   public:
+      Certificate_Store_MacOS();
+
+      Certificate_Store_MacOS(const Certificate_Store_MacOS&) = default;
+      Certificate_Store_MacOS(Certificate_Store_MacOS&&) = default;
+      Certificate_Store_MacOS& operator=(const Certificate_Store_MacOS&) = default;
+      Certificate_Store_MacOS& operator=(Certificate_Store_MacOS&&) = default;
+
+      /**
+      * @return DNs for all certificates managed by the store
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /**
+      * Find a certificate by Subject DN and (optionally) key identifier
+      * @return the first certificate that matches
+      */
+      std::shared_ptr<const X509_Certificate> find_cert(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+               const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-1 hash of
+      * public key.
+      * @return a matching certificate or nullptr otherwise
+      */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      /**
+       * @throws Botan::Not_Implemented
+       */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+       * Fetching CRLs is not supported by the keychain on macOS. This will
+       * always return an empty list.
+       */
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+   private:
+      std::shared_ptr<Certificate_Store_MacOS_Impl> m_impl;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp b/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
@@ -0,0 +1,257 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2018-2019 Patrik Fiedler, Tim Oesterreich
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_windows.h>
+#include <botan/der_enc.h>
+
+#include <array>
+#include <vector>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+#include <wincrypt.h>
+
+#define WINCRYPT_UNUSED_PARAM 0 // for avoiding warnings when passing NULL to unused params in win32 api that accept integer types
+
+namespace Botan {
+namespace {
+
+using Cert_Pointer = std::shared_ptr<const Botan::X509_Certificate>;
+using Cert_Vector = std::vector<Cert_Pointer>;
+const std::array<const char*, 2> cert_store_names{"Root", "CA"};
+
+/**
+ * Abstract RAII wrapper for PCCERT_CONTEXT and HCERTSTORE
+ * The Windows API partly takes care of those pointers destructions itself.
+ * Especially, iteratively calling `CertFindCertificateInStore` with the previous PCCERT_CONTEXT
+ * will free the context and return a new one. In this case, this guard takes care of freeing the context
+ * in case of an exception and at the end of the iterative process.
+ */
+template<class T>
+class Handle_Guard
+   {
+   public:
+      Handle_Guard(T context)
+         : m_context(context)
+         {
+         }
+
+      Handle_Guard(const Handle_Guard<T>& rhs) = delete;
+      Handle_Guard(Handle_Guard<T>&& rhs) :
+         m_context(std::move(rhs.m_context))
+         {
+         rhs.m_context = nullptr;
+         }
+
+      ~Handle_Guard()
+         {
+         close<T>();
+         }
+
+      operator bool() const
+         {
+         return m_context != nullptr;
+         }
+
+      bool assign(T context)
+         {
+         m_context = context;
+         return m_context != nullptr;
+         }
+
+      T& get()
+         {
+         return m_context;
+         }
+
+      const T& get() const
+         {
+         return m_context;
+         }
+
+      T operator->()
+         {
+         return m_context;
+         }
+
+   private:
+      template<class T2 = T>
+      typename std::enable_if<std::is_same<T2, PCCERT_CONTEXT>::value>::type close()
+         {
+         if(m_context)
+            {
+            CertFreeCertificateContext(m_context);
+            }
+         }
+
+      template<class T2 = T>
+      typename std::enable_if<std::is_same<T2, HCERTSTORE>::value>::type close()
+         {
+         if(m_context)
+            {
+            // second parameter is a flag that tells the store how to deallocate memory
+            // using the default "0", this function works like decreasing the reference counter
+            // in a shared_ptr
+            CertCloseStore(m_context, 0);
+            }
+         }
+
+      T m_context;
+   };
+
+HCERTSTORE open_cert_store(const char* cert_store_name)
+   {
+   auto store = CertOpenSystemStoreA(WINCRYPT_UNUSED_PARAM, cert_store_name);
+   if(!store)
+      {
+      throw Botan::Internal_Error(
+         "failed to open windows certificate store '" + std::string(cert_store_name) +
+         "' (Error Code: " +
+         std::to_string(::GetLastError()) + ")");
+      }
+   return store;
+   }
+
+Cert_Vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, const DWORD& find_type,
+                               std::function<bool(const Cert_Vector& certs, Cert_Pointer cert)> filter,
+                               bool return_on_first_found)
+   {
+   Cert_Vector certs;
+   for(const auto store_name : cert_store_names)
+      {
+      Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+      Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+      while(cert_context.assign(CertFindCertificateInStore(
+                                   windows_cert_store.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+                                   WINCRYPT_UNUSED_PARAM, find_type,
+                                   &blob, cert_context.get())))
+         {
+         auto cert = std::make_shared<X509_Certificate>(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+         if(filter(certs, cert))
+            {
+            if(return_on_first_found)
+               {
+               return {cert};
+               }
+            certs.push_back(cert);
+            }
+         }
+      }
+
+   return certs;
+   }
+
+bool already_contains_certificate(const Cert_Vector& certs, Cert_Pointer cert)
+   {
+   return std::any_of(certs.begin(), certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c)
+      {
+      return *c == *cert;
+      });
+   }
+
+Cert_Vector find_cert_by_dn_and_key_id(const Botan::X509_DN& subject_dn,
+                                       const std::vector<uint8_t>& key_id,
+                                       bool return_on_first_found)
+   {
+   _CRYPTOAPI_BLOB blob;
+   DWORD find_type;
+   std::vector<uint8_t> dn_data;
+
+   // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN
+   if(key_id.empty())
+      {
+      find_type = CERT_FIND_SUBJECT_NAME;
+      DER_Encoder encoder(dn_data);
+      subject_dn.encode_into(encoder);
+      blob.cbData = static_cast<DWORD>(dn_data.size());
+      blob.pbData = reinterpret_cast<BYTE*>(dn_data.data());
+      }
+   else
+      {
+      find_type = CERT_FIND_KEY_IDENTIFIER;
+      blob.cbData = static_cast<DWORD>(key_id.size());
+      blob.pbData = const_cast<BYTE*>(key_id.data());
+      }
+
+   auto filter = [&](const Cert_Vector& certs, Cert_Pointer cert)
+      {
+      return !already_contains_certificate(certs, cert) && (key_id.empty() || cert->subject_dn() == subject_dn);
+      };
+
+   return search_cert_stores(blob, find_type, filter, return_on_first_found);
+   }
+} // namespace
+
+Certificate_Store_Windows::Certificate_Store_Windows() {}
+
+std::vector<X509_DN> Certificate_Store_Windows::all_subjects() const
+   {
+   std::vector<X509_DN> subject_dns;
+   for(const auto store_name : cert_store_names)
+      {
+      Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+      Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+
+      // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of
+      // freeing the previous context.
+      while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get())))
+         {
+         X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+         subject_dns.push_back(cert.subject_dn());
+         }
+      }
+
+   return subject_dns;
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert(const Botan::X509_DN& subject_dn,
+      const std::vector<uint8_t>& key_id) const
+   {
+   const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true);
+   return certs.empty() ? nullptr : certs.front();
+   }
+
+Cert_Vector Certificate_Store_Windows::find_all_certs(
+   const X509_DN& subject_dn,
+   const std::vector<uint8_t>& key_id) const
+   {
+   return find_cert_by_dn_and_key_id(subject_dn, key_id, false);
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   if(key_hash.size() != 20)
+      {
+      throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash");
+      }
+
+   CRYPT_HASH_BLOB blob;
+   blob.cbData = static_cast<DWORD>(key_hash.size());
+   blob.pbData = const_cast<BYTE*>(key_hash.data());
+
+   auto filter = [](const Cert_Vector&, Cert_Pointer) { return true; };
+
+   const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true);
+   return certs.empty() ? nullptr : certs.front();
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256(
+   const std::vector<uint8_t>& subject_hash) const
+   {
+   BOTAN_UNUSED(subject_hash);
+   throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256");
+   }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_Windows::find_crl_for(const X509_Certificate& subject) const
+   {
+   // TODO: this could be implemented by using the CertFindCRLInStore function
+   BOTAN_UNUSED(subject);
+   return {};
+   }
+}
diff --git a/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h b/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
@@ -0,0 +1,70 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+#define BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by the system trust store on Windows.
+*/
+class BOTAN_PUBLIC_API(2, 11) Certificate_Store_Windows final : public Certificate_Store
+   {
+   public:
+      Certificate_Store_Windows();
+
+      Certificate_Store_Windows(const Certificate_Store_Windows&) = default;
+      Certificate_Store_Windows(Certificate_Store_Windows&&) = default;
+      Certificate_Store_Windows& operator=(const Certificate_Store_Windows&) = default;
+      Certificate_Store_Windows& operator=(Certificate_Store_Windows&&) = default;
+
+      /**
+      * @return DNs for all certificates managed by the store
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /**
+      * Find a certificate by Subject DN and (optionally) key identifier
+      * @return the first certificate that matches
+      */
+      std::shared_ptr<const X509_Certificate> find_cert(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+               const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-1 hash of
+      * public key.
+      * @return a matching certificate or nullptr otherwise
+      */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      /**
+       * @throws Botan::Not_Implemented
+       */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+       * Not Yet Implemented
+       * @return nullptr;
+       */
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+   };
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/crl_ent.cpp b/third_party/botan/src/lib/x509/crl_ent.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/crl_ent.cpp
@@ -0,0 +1,140 @@
+/*
+* CRL Entry
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crl_ent.h>
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+struct CRL_Entry_Data
+   {
+   std::vector<uint8_t> m_serial;
+   X509_Time m_time;
+   CRL_Code m_reason = UNSPECIFIED;
+   Extensions m_extensions;
+   };
+
+/*
+* Create a CRL_Entry
+*/
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why)
+   {
+   m_data.reset(new CRL_Entry_Data);
+   m_data->m_serial = cert.serial_number();
+   m_data->m_time = X509_Time(std::chrono::system_clock::now());
+   m_data->m_reason = why;
+
+   if(why != UNSPECIFIED)
+      {
+      m_data->m_extensions.add(new Cert_Extension::CRL_ReasonCode(why));
+      }
+   }
+
+/*
+* Compare two CRL_Entrys for equality
+*/
+bool operator==(const CRL_Entry& a1, const CRL_Entry& a2)
+   {
+   if(a1.serial_number() != a2.serial_number())
+      return false;
+   if(a1.expire_time() != a2.expire_time())
+      return false;
+   if(a1.reason_code() != a2.reason_code())
+      return false;
+   return true;
+   }
+
+/*
+* Compare two CRL_Entrys for inequality
+*/
+bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
+   {
+   return !(a1 == a2);
+   }
+
+/*
+* DER encode a CRL_Entry
+*/
+void CRL_Entry::encode_into(DER_Encoder& der) const
+   {
+   der.start_cons(SEQUENCE)
+      .encode(BigInt::decode(serial_number()))
+      .encode(expire_time())
+      .start_cons(SEQUENCE)
+         .encode(extensions())
+      .end_cons()
+   .end_cons();
+   }
+
+/*
+* Decode a BER encoded CRL_Entry
+*/
+void CRL_Entry::decode_from(BER_Decoder& source)
+   {
+   BigInt serial_number_bn;
+
+   std::unique_ptr<CRL_Entry_Data> data(new CRL_Entry_Data);
+
+   BER_Decoder entry = source.start_cons(SEQUENCE);
+
+   entry.decode(serial_number_bn).decode(data->m_time);
+   data->m_serial = BigInt::encode(serial_number_bn);
+
+   if(entry.more_items())
+      {
+      entry.decode(data->m_extensions);
+      if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_ReasonCode>())
+         {
+         data->m_reason = ext->get_reason();
+         }
+      else
+         {
+         data->m_reason = UNSPECIFIED;
+         }
+      }
+
+   entry.end_cons();
+
+   m_data.reset(data.release());
+   }
+
+const CRL_Entry_Data& CRL_Entry::data() const
+   {
+   if(!m_data)
+      {
+      throw Invalid_State("CRL_Entry_Data uninitialized");
+      }
+
+   return *m_data.get();
+   }
+
+const std::vector<uint8_t>& CRL_Entry::serial_number() const
+   {
+   return data().m_serial;
+   }
+
+const X509_Time& CRL_Entry::expire_time() const
+   {
+   return data().m_time;
+   }
+
+CRL_Code CRL_Entry::reason_code() const
+   {
+   return data().m_reason;
+   }
+
+const Extensions& CRL_Entry::extensions() const
+   {
+   return data().m_extensions;
+   }
+
+
+}
diff --git a/third_party/botan/src/lib/x509/crl_ent.h b/third_party/botan/src/lib/x509/crl_ent.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/crl_ent.h
@@ -0,0 +1,106 @@
+/*
+* CRL Entry
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRL_ENTRY_H_
+#define BOTAN_CRL_ENTRY_H_
+
+#include <botan/asn1_time.h>
+
+namespace Botan {
+
+class Extensions;
+class X509_Certificate;
+struct CRL_Entry_Data;
+
+/**
+* X.509v2 CRL Reason Code.
+* This will become an enum class in a future major release
+*/
+enum CRL_Code : uint32_t {
+   UNSPECIFIED            = 0,
+   KEY_COMPROMISE         = 1,
+   CA_COMPROMISE          = 2,
+   AFFILIATION_CHANGED    = 3,
+   SUPERSEDED             = 4,
+   CESSATION_OF_OPERATION = 5,
+   CERTIFICATE_HOLD       = 6,
+   REMOVE_FROM_CRL        = 8,
+   PRIVLEDGE_WITHDRAWN    = 9,
+   PRIVILEGE_WITHDRAWN    = 9,
+   AA_COMPROMISE          = 10,
+
+   DELETE_CRL_ENTRY       = 0xFF00,
+   OCSP_GOOD              = 0xFF01,
+   OCSP_UNKNOWN           = 0xFF02
+};
+
+/**
+* This class represents CRL entries
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Entry final : public ASN1_Object
+   {
+   public:
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      /**
+      * Get the serial number of the certificate associated with this entry.
+      * @return certificate's serial number
+      */
+      const std::vector<uint8_t>& serial_number() const;
+
+      /**
+      * Get the revocation date of the certificate associated with this entry
+      * @return certificate's revocation date
+      */
+      const X509_Time& expire_time() const;
+
+      /**
+      * Get the entries reason code
+      * @return reason code
+      */
+      CRL_Code reason_code() const;
+
+      /**
+      * Get the extensions on this CRL entry
+      */
+      const Extensions& extensions() const;
+
+      /**
+      * Create uninitialized CRL_Entry object
+      */
+      CRL_Entry() = default;
+
+      /**
+      * Construct an CRL entry.
+      * @param cert the certificate to revoke
+      * @param reason the reason code to set in the entry
+      */
+      CRL_Entry(const X509_Certificate& cert,
+                CRL_Code reason = UNSPECIFIED);
+
+   private:
+      friend class X509_CRL;
+
+      const CRL_Entry_Data& data() const;
+
+      std::shared_ptr<CRL_Entry_Data> m_data;
+   };
+
+/**
+* Test two CRL entries for equality in all fields.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator==(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* Test two CRL entries for inequality in at least one field.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const CRL_Entry&, const CRL_Entry&);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/datastor.cpp b/third_party/botan/src/lib/x509/datastor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/datastor.cpp
@@ -0,0 +1,205 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/datastor.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/hex.h>
+#include <botan/internal/stl_util.h>
+
+namespace Botan {
+
+/*
+* Data_Store Equality Comparison
+*/
+bool Data_Store::operator==(const Data_Store& other) const
+   {
+   return (m_contents == other.m_contents);
+   }
+
+/*
+* Check if this key has at least one value
+*/
+bool Data_Store::has_value(const std::string& key) const
+   {
+   return (m_contents.lower_bound(key) != m_contents.end());
+   }
+
+/*
+* Search based on an arbitrary predicate
+*/
+std::multimap<std::string, std::string> Data_Store::search_for(
+   std::function<bool (std::string, std::string)> predicate) const
+   {
+   std::multimap<std::string, std::string> out;
+
+   for(auto i = m_contents.begin(); i != m_contents.end(); ++i)
+      if(predicate(i->first, i->second))
+         out.insert(std::make_pair(i->first, i->second));
+
+   return out;
+   }
+
+/*
+* Search based on key equality
+*/
+std::vector<std::string> Data_Store::get(const std::string& looking_for) const
+   {
+   std::vector<std::string> out;
+   auto range = m_contents.equal_range(looking_for);
+   for(auto i = range.first; i != range.second; ++i)
+      out.push_back(i->second);
+   return out;
+   }
+
+/*
+* Get a single atom
+*/
+std::string Data_Store::get1(const std::string& key) const
+   {
+   std::vector<std::string> vals = get(key);
+
+   if(vals.empty())
+      throw Invalid_State("Data_Store::get1: No values set for " + key);
+   if(vals.size() > 1)
+      throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+   return vals[0];
+   }
+
+std::string Data_Store::get1(const std::string& key,
+                             const std::string& default_value) const
+   {
+   std::vector<std::string> vals = get(key);
+
+   if(vals.size() > 1)
+      throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+   if(vals.empty())
+      return default_value;
+
+   return vals[0];
+   }
+
+/*
+* Get a single std::vector atom
+*/
+std::vector<uint8_t>
+Data_Store::get1_memvec(const std::string& key) const
+   {
+   std::vector<std::string> vals = get(key);
+
+   if(vals.empty())
+      return std::vector<uint8_t>();
+
+   if(vals.size() > 1)
+      throw Invalid_State("Data_Store::get1_memvec: Multiple values for " +
+                          key);
+
+   return hex_decode(vals[0]);
+   }
+
+/*
+* Get a single uint32_t atom
+*/
+uint32_t Data_Store::get1_uint32(const std::string& key,
+                                 uint32_t default_val) const
+   {
+   std::vector<std::string> vals = get(key);
+
+   if(vals.empty())
+      return default_val;
+   else if(vals.size() > 1)
+      throw Invalid_State("Data_Store::get1_uint32: Multiple values for " + key);
+
+   return to_u32bit(vals[0]);
+   }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const std::string& val)
+   {
+   multimap_insert(m_contents, key, val);
+   }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, uint32_t val)
+   {
+   add(key, std::to_string(val));
+   }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const secure_vector<uint8_t>& val)
+   {
+   add(key, hex_encode(val.data(), val.size()));
+   }
+
+void Data_Store::add(const std::string& key, const std::vector<uint8_t>& val)
+   {
+   add(key, hex_encode(val.data(), val.size()));
+   }
+
+/*
+* Insert a mapping of key/value pairs
+*/
+void Data_Store::add(const std::multimap<std::string, std::string>& in)
+   {
+   std::multimap<std::string, std::string>::const_iterator i = in.begin();
+   while(i != in.end())
+      {
+      m_contents.insert(*i);
+      ++i;
+      }
+   }
+
+/*
+* Create and populate a X509_DN
+*/
+X509_DN create_dn(const Data_Store& info)
+   {
+   auto names = info.search_for(
+      [](const std::string& key, const std::string&)
+      {
+         return (key.find("X520.") != std::string::npos);
+      });
+
+   X509_DN dn;
+
+   for(auto i = names.begin(); i != names.end(); ++i)
+      dn.add_attribute(i->first, i->second);
+
+   return dn;
+   }
+
+/*
+* Create and populate an AlternativeName
+*/
+AlternativeName create_alt_name(const Data_Store& info)
+   {
+   auto names = info.search_for(
+      [](const std::string& key, const std::string&)
+      {
+         return (key == "RFC822" ||
+                 key == "DNS" ||
+                 key == "URI" ||
+                 key == "IP");
+      });
+
+   AlternativeName alt_name;
+
+   for(auto i = names.begin(); i != names.end(); ++i)
+      alt_name.add_attribute(i->first, i->second);
+
+   return alt_name;
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/datastor.h b/third_party/botan/src/lib/x509/datastor.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/datastor.h
@@ -0,0 +1,86 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_STORE_H_
+#define BOTAN_DATA_STORE_H_
+
+#include <botan/x509_dn.h>
+#include <botan/asn1_alt_name.h>
+#include <functional>
+#include <string>
+#include <vector>
+#include <map>
+
+BOTAN_FUTURE_INTERNAL_HEADER(datastor.h)
+
+namespace Botan {
+
+/**
+* Data Store
+*
+* This class is used internally by the library, and exposed for ABI
+* reasons. There is no reason for applications to use this type directly.
+* It will be removed in a future major release.
+*/
+class BOTAN_UNSTABLE_API Data_Store final
+   {
+   public:
+      /**
+      * A search function
+      */
+      bool operator==(const Data_Store&) const;
+
+      std::multimap<std::string, std::string> search_for(
+         std::function<bool (std::string, std::string)> predicate) const;
+
+      std::vector<std::string> get(const std::string&) const;
+
+      std::string get1(const std::string& key) const;
+
+      std::string get1(const std::string& key,
+                       const std::string& default_value) const;
+
+      std::vector<uint8_t> get1_memvec(const std::string&) const;
+      uint32_t get1_uint32(const std::string&, uint32_t = 0) const;
+
+      bool has_value(const std::string&) const;
+
+      void add(const std::multimap<std::string, std::string>&);
+      void add(const std::string&, const std::string&);
+      void add(const std::string&, uint32_t);
+      void add(const std::string&, const secure_vector<uint8_t>&);
+      void add(const std::string&, const std::vector<uint8_t>&);
+   private:
+      std::multimap<std::string, std::string> m_contents;
+   };
+
+/*
+* Data Store Extraction Operations
+*/
+
+/*
+* Create and populate a X509_DN
+* @param info data store containing DN information
+* @return DN containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) X509_DN
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_dn(const Data_Store& info);
+
+/*
+* Create and populate an AlternativeName
+* @param info data store containing AlternativeName information
+* @return AlternativeName containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) AlternativeName
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_alt_name(const Data_Store& info);
+
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/key_constraint.cpp b/third_party/botan/src/lib/x509/key_constraint.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/key_constraint.cpp
@@ -0,0 +1,106 @@
+/*
+* KeyUsage
+* (C) 1999-2007,2016 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/key_constraint.h>
+#include <botan/pk_keys.h>
+#include <vector>
+
+namespace Botan {
+
+std::string key_constraints_to_string(Key_Constraints constraints)
+   {
+   std::vector<std::string> str;
+
+   if(constraints == NO_CONSTRAINTS)
+      return "no_constraints";
+
+   if(constraints & DIGITAL_SIGNATURE)
+      str.push_back("digital_signature");
+
+   if(constraints & NON_REPUDIATION)
+      str.push_back("non_repudiation");
+
+   if(constraints & KEY_ENCIPHERMENT)
+      str.push_back("key_encipherment");
+
+   if(constraints & DATA_ENCIPHERMENT)
+      str.push_back("data_encipherment");
+
+   if(constraints & KEY_AGREEMENT)
+      str.push_back("key_agreement");
+
+   if(constraints & KEY_CERT_SIGN)
+      str.push_back("key_cert_sign");
+
+   if(constraints & CRL_SIGN)
+      str.push_back("crl_sign");
+
+   if(constraints & ENCIPHER_ONLY)
+      str.push_back("encipher_only");
+
+   if(constraints & DECIPHER_ONLY)
+      str.push_back("decipher_only");
+
+   // Not 0 (checked at start) but nothing matched above!
+   if(str.empty())
+      return "other_unknown_constraints";
+
+   if(str.size() == 1)
+      return str[0];
+
+   std::string out;
+   for(size_t i = 0; i < str.size() - 1; ++i)
+      {
+      out += str[i];
+      out += ',';
+      }
+   out += str[str.size() - 1];
+
+   return out;
+   }
+
+/*
+* Make sure the given key constraints are permitted for the given key type
+*/
+void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+                                                Key_Constraints constraints)
+   {
+   const std::string name = pub_key.algo_name();
+
+   size_t permitted = 0;
+
+   const bool can_agree = (name == "DH" || name == "ECDH");
+   const bool can_encrypt = (name == "RSA" || name == "ElGamal");
+
+   const bool can_sign =
+      (name == "RSA" || name == "DSA" ||
+       name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA" || name == "Ed25519" ||
+       name == "GOST-34.10" || name == "GOST-34.10-2012-256" || name == "GOST-34.10-2012-512");
+
+   if(can_agree)
+      {
+      permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY;
+      }
+
+   if(can_encrypt)
+      {
+      permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT;
+      }
+
+   if(can_sign)
+      {
+      permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN;
+      }
+
+   if(Key_Constraints(constraints & permitted) != constraints)
+      {
+      throw Invalid_Argument("Invalid " + name + " constraints " + key_constraints_to_string(constraints));
+      }
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/key_constraint.h b/third_party/botan/src/lib/x509/key_constraint.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/key_constraint.h
@@ -0,0 +1,49 @@
+/*
+* Enumerations
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENUMS_H_
+#define BOTAN_ENUMS_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* X.509v3 Key Constraints.
+* If updating update copy in ffi.h
+*/
+enum Key_Constraints {
+   NO_CONSTRAINTS     = 0,
+   DIGITAL_SIGNATURE  = 1 << 15,
+   NON_REPUDIATION    = 1 << 14,
+   KEY_ENCIPHERMENT   = 1 << 13,
+   DATA_ENCIPHERMENT  = 1 << 12,
+   KEY_AGREEMENT      = 1 << 11,
+   KEY_CERT_SIGN      = 1 << 10,
+   CRL_SIGN           = 1 << 9,
+   ENCIPHER_ONLY      = 1 << 8,
+   DECIPHER_ONLY      = 1 << 7
+};
+
+class Public_Key;
+
+/**
+* Check that key constraints are permitted for a specific public key.
+* @param pub_key the public key on which the constraints shall be enforced on
+* @param constraints the constraints that shall be enforced on the key
+* @throw Invalid_Argument if the given constraints are not permitted for this key
+*/
+BOTAN_PUBLIC_API(2,0) void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+                                                                Key_Constraints constraints);
+
+std::string BOTAN_PUBLIC_API(2,0) key_constraints_to_string(Key_Constraints);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/name_constraint.cpp b/third_party/botan/src/lib/x509/name_constraint.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/name_constraint.cpp
@@ -0,0 +1,273 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/name_constraint.h>
+#include <botan/asn1_alt_name.h>
+#include <botan/ber_dec.h>
+#include <botan/loadstor.h>
+#include <botan/x509_dn.h>
+#include <botan/x509cert.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+class DER_Encoder;
+
+GeneralName::GeneralName(const std::string& str) : GeneralName()
+   {
+   size_t p = str.find(':');
+
+   if(p != std::string::npos)
+      {
+      m_type = str.substr(0, p);
+      m_name = str.substr(p + 1, std::string::npos);
+      }
+   else
+      {
+      throw Invalid_Argument("Failed to decode Name Constraint");
+      }
+   }
+
+void GeneralName::encode_into(DER_Encoder&) const
+   {
+   throw Not_Implemented("GeneralName encoding");
+   }
+
+void GeneralName::decode_from(class BER_Decoder& ber)
+   {
+   BER_Object obj = ber.get_next_object();
+
+   if(obj.is_a(1, CONTEXT_SPECIFIC))
+      {
+      m_type = "RFC822";
+      m_name = ASN1::to_string(obj);
+      }
+   else if(obj.is_a(2, CONTEXT_SPECIFIC))
+      {
+      m_type = "DNS";
+      m_name = ASN1::to_string(obj);
+      }
+   else if(obj.is_a(6, CONTEXT_SPECIFIC))
+      {
+      m_type = "URI";
+      m_name = ASN1::to_string(obj);
+      }
+   else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+      {
+      m_type = "DN";
+      X509_DN dn;
+      BER_Decoder dec(obj);
+      std::stringstream ss;
+
+      dn.decode_from(dec);
+      ss << dn;
+
+      m_name = ss.str();
+      }
+   else if(obj.is_a(7, CONTEXT_SPECIFIC))
+      {
+      if(obj.length() == 8)
+         {
+         m_type = "IP";
+         m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" +
+                  ipv4_to_string(load_be<uint32_t>(obj.bits(), 1));
+         }
+      else if(obj.length() == 32)
+         {
+         throw Decoding_Error("Unsupported IPv6 name constraint");
+         }
+      else
+         {
+         throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
+         }
+      }
+   else
+      {
+      throw Decoding_Error("Found unknown GeneralName type");
+      }
+   }
+
+GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const
+   {
+   std::vector<std::string> nam;
+   std::function<bool(const GeneralName*, const std::string&)> match_fn;
+
+   const X509_DN& dn = cert.subject_dn();
+   const AlternativeName& alt_name = cert.subject_alt_name();
+
+   if(type() == "DNS")
+      {
+      match_fn = std::mem_fn(&GeneralName::matches_dns);
+
+      nam = alt_name.get_attribute("DNS");
+
+      if(nam.empty())
+         {
+         nam = dn.get_attribute("CN");
+         }
+      }
+   else if(type() == "DN")
+      {
+      match_fn = std::mem_fn(&GeneralName::matches_dn);
+
+      nam.push_back(dn.to_string());
+
+      const auto alt_dn = alt_name.dn();
+      if(alt_dn.empty() == false)
+         {
+         nam.push_back(alt_dn.to_string());
+         }
+      }
+   else if(type() == "IP")
+      {
+      match_fn = std::mem_fn(&GeneralName::matches_ip);
+      nam = alt_name.get_attribute("IP");
+      }
+   else
+      {
+      return MatchResult::UnknownType;
+      }
+
+   if(nam.empty())
+      {
+      return MatchResult::NotFound;
+      }
+
+   bool some = false;
+   bool all = true;
+
+   for(const std::string& n: nam)
+      {
+      bool m = match_fn(this, n);
+
+      some |= m;
+      all &= m;
+      }
+
+   if(all)
+      {
+      return MatchResult::All;
+      }
+   else if(some)
+      {
+      return MatchResult::Some;
+      }
+   else
+      {
+      return MatchResult::None;
+      }
+   }
+
+bool GeneralName::matches_dns(const std::string& nam) const
+   {
+   if(nam.size() == name().size())
+      {
+      return nam == name();
+      }
+   else if(name().size() > nam.size())
+      {
+      return false;
+      }
+   else // name.size() < nam.size()
+      {
+      std::string constr = name().front() == '.' ? name() : "." + name();
+      // constr is suffix of nam
+      return constr == nam.substr(nam.size() - constr.size(), constr.size());
+      }
+   }
+
+bool GeneralName::matches_dn(const std::string& nam) const
+   {
+   std::stringstream ss(nam);
+   std::stringstream tt(name());
+   X509_DN nam_dn, my_dn;
+
+   ss >> nam_dn;
+   tt >> my_dn;
+
+   auto attr = nam_dn.get_attributes();
+   bool ret = true;
+   size_t trys = 0;
+
+   for(const auto& c: my_dn.dn_info())
+      {
+      auto i = attr.equal_range(c.first);
+
+      if(i.first != i.second)
+         {
+         trys += 1;
+         ret = ret && (i.first->second == c.second.value());
+         }
+      }
+
+   return trys > 0 && ret;
+   }
+
+bool GeneralName::matches_ip(const std::string& nam) const
+   {
+   uint32_t ip = string_to_ipv4(nam);
+   std::vector<std::string> p = split_on(name(), '/');
+
+   if(p.size() != 2)
+      throw Decoding_Error("failed to parse IPv4 address");
+
+   uint32_t net = string_to_ipv4(p.at(0));
+   uint32_t mask = string_to_ipv4(p.at(1));
+
+   return (ip & mask) == net;
+   }
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
+   {
+   os << gn.type() << ":" << gn.name();
+   return os;
+   }
+
+GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
+   {
+   size_t p0, p1;
+   const auto min = std::stoull(str, &p0, 10);
+   const auto max = std::stoull(str.substr(p0 + 1), &p1, 10);
+   GeneralName gn(str.substr(p0 + p1 + 2));
+
+   if(p0 > 0 && p1 > 0)
+      {
+      m_minimum = static_cast<size_t>(min);
+      m_maximum = static_cast<size_t>(max);
+      m_base = gn;
+      }
+   else
+      {
+      throw Invalid_Argument("Failed to decode Name Constraint");
+      }
+   }
+
+void GeneralSubtree::encode_into(DER_Encoder&) const
+   {
+   throw Not_Implemented("General Subtree encoding");
+   }
+
+void GeneralSubtree::decode_from(class BER_Decoder& ber)
+   {
+   ber.start_cons(SEQUENCE)
+      .decode(m_base)
+      .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
+   .end_cons();
+
+   if(m_minimum != 0)
+     throw Decoding_Error("GeneralSubtree minimum must be 0");
+
+   m_maximum = std::numeric_limits<std::size_t>::max();
+   }
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
+   {
+   os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+   return os;
+   }
+}
diff --git a/third_party/botan/src/lib/x509/name_constraint.h b/third_party/botan/src/lib/x509/name_constraint.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/name_constraint.h
@@ -0,0 +1,182 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NAME_CONSTRAINT_H_
+#define BOTAN_NAME_CONSTRAINT_H_
+
+#include <botan/asn1_obj.h>
+#include <ostream>
+#include <limits>
+
+namespace Botan {
+
+class BER_Encoder;
+class DER_Encoder;
+class X509_Certificate;
+
+/**
+* @brief X.509 GeneralName Type
+*
+* Handles parsing GeneralName types in their BER and canonical string
+* encoding. Allows matching GeneralNames against each other using
+* the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
+   {
+   public:
+      enum MatchResult : int
+            {
+            All,
+            Some,
+            None,
+            NotFound,
+            UnknownType,
+            };
+
+      /**
+      * Creates an empty GeneralName.
+      */
+      GeneralName() = default;
+
+      /**
+      * Creates a new GeneralName for its string format.
+      * @param str type and name, colon-separated, e.g., "DNS:google.com"
+      */
+      GeneralName(const std::string& str);
+
+      void encode_into(DER_Encoder&) const override;
+
+      void decode_from(BER_Decoder&) override;
+
+      /**
+      * @return Type of the name. Can be DN, DNS, IP, RFC822 or URI.
+      */
+      const std::string& type() const { return m_type; }
+
+      /**
+      * @return The name as string. Format depends on type.
+      */
+      const std::string& name() const { return m_name; }
+
+      /**
+      * Checks whether a given certificate (partially) matches this name.
+      * @param cert certificate to be matched
+      * @return the match result
+      */
+      MatchResult matches(const X509_Certificate& cert) const;
+
+   private:
+      std::string m_type;
+      std::string m_name;
+
+      bool matches_dns(const std::string&) const;
+      bool matches_dn(const std::string&) const;
+      bool matches_ip(const std::string&) const;
+   };
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+
+/**
+* @brief A single Name Constraint
+*
+* The Name Constraint extension adds a minimum and maximum path
+* length to a GeneralName to form a constraint. The length limits
+* are currently unused.
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
+   {
+   public:
+      /**
+      * Creates an empty name constraint.
+      */
+      GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+      {}
+
+      /***
+      * Creates a new name constraint.
+      * @param base name
+      * @param min minimum path length
+      * @param max maximum path length
+      */
+      GeneralSubtree(const GeneralName& base, size_t min, size_t max)
+      : m_base(base), m_minimum(min), m_maximum(max)
+      {}
+
+      /**
+      * Creates a new name constraint for its string format.
+      * @param str name constraint
+      */
+      GeneralSubtree(const std::string& str);
+
+      void encode_into(DER_Encoder&) const override;
+
+      void decode_from(BER_Decoder&) override;
+
+      /**
+      * @return name
+      */
+      const GeneralName& base() const { return m_base; }
+
+      /**
+      * @return minimum path length
+      */
+      size_t minimum() const { return m_minimum; }
+
+      /**
+      * @return maximum path length
+      */
+      size_t maximum() const { return m_maximum; }
+
+   private:
+      GeneralName m_base;
+      size_t m_minimum;
+      size_t m_maximum;
+   };
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs);
+
+/**
+* @brief Name Constraints
+*
+* Wraps the Name Constraints associated with a certificate.
+*/
+class BOTAN_PUBLIC_API(2,0) NameConstraints final
+   {
+   public:
+      /**
+      * Creates an empty name NameConstraints.
+      */
+      NameConstraints() : m_permitted_subtrees(), m_excluded_subtrees() {}
+
+      /**
+      * Creates NameConstraints from a list of permitted and excluded subtrees.
+      * @param permitted_subtrees names for which the certificate is permitted
+      * @param excluded_subtrees names for which the certificate is not permitted
+      */
+      NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+                    std::vector<GeneralSubtree>&& excluded_subtrees)
+      : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+      {}
+
+      /**
+      * @return permitted names
+      */
+      const std::vector<GeneralSubtree>& permitted() const { return m_permitted_subtrees; }
+
+      /**
+      * @return excluded names
+      */
+      const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+
+   private:
+      std::vector<GeneralSubtree> m_permitted_subtrees;
+      std::vector<GeneralSubtree> m_excluded_subtrees;
+};
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/ocsp.cpp b/third_party/botan/src/lib/x509/ocsp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/ocsp.cpp
@@ -0,0 +1,363 @@
+/*
+* OCSP
+* (C) 2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/certstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/oids.h>
+#include <botan/base64.h>
+#include <botan/pubkey.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+  #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+namespace OCSP {
+
+namespace {
+
+// TODO: should this be in a header somewhere?
+void decode_optional_list(BER_Decoder& ber,
+                          ASN1_Tag tag,
+                          std::vector<X509_Certificate>& output)
+   {
+   BER_Object obj = ber.get_next_object();
+
+   if(obj.is_a(tag, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+      {
+      ber.push_back(obj);
+      return;
+      }
+
+   BER_Decoder list(obj);
+
+   while(list.more_items())
+      {
+      BER_Object certbits = list.get_next_object();
+      X509_Certificate cert(certbits.bits(), certbits.length());
+      output.push_back(std::move(cert));
+      }
+   }
+
+}
+
+Request::Request(const X509_Certificate& issuer_cert,
+                 const X509_Certificate& subject_cert) :
+   m_issuer(issuer_cert),
+   m_certid(m_issuer, BigInt::decode(subject_cert.serial_number()))
+   {
+   if(subject_cert.issuer_dn() != issuer_cert.subject_dn())
+      throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
+   }
+
+Request::Request(const X509_Certificate& issuer_cert,
+                 const BigInt& subject_serial) :
+   m_issuer(issuer_cert),
+   m_certid(m_issuer, subject_serial)
+   {
+   }
+
+std::vector<uint8_t> Request::BER_encode() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).start_cons(SEQUENCE)
+        .start_cons(SEQUENCE)
+          .start_explicit(0)
+            .encode(static_cast<size_t>(0)) // version #
+          .end_explicit()
+            .start_cons(SEQUENCE)
+              .start_cons(SEQUENCE)
+                .encode(m_certid)
+              .end_cons()
+            .end_cons()
+          .end_cons()
+      .end_cons();
+
+   return output;
+   }
+
+std::string Request::base64_encode() const
+   {
+   return Botan::base64_encode(BER_encode());
+   }
+
+Response::Response(Certificate_Status_Code status)
+   {
+   m_status = Response_Status_Code::Successful;
+   m_dummy_response_status = status;
+   }
+
+Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
+   m_response_bits(response_bits, response_bits + response_bits_len)
+   {
+   m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+   BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE);
+
+   size_t resp_status = 0;
+
+   response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
+
+   m_status = static_cast<Response_Status_Code>(resp_status);
+
+   if(m_status != Response_Status_Code::Successful)
+      { return; }
+
+   if(response_outer.more_items())
+      {
+      BER_Decoder response_bytes =
+         response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
+
+      response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"),
+                                      "Unknown response type in OCSP response");
+
+      BER_Decoder basicresponse =
+         BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
+
+      basicresponse.start_cons(SEQUENCE)
+           .raw_bytes(m_tbs_bits)
+         .end_cons()
+         .decode(m_sig_algo)
+         .decode(m_signature, BIT_STRING);
+      decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
+
+      size_t responsedata_version = 0;
+      Extensions extensions;
+
+      BER_Decoder(m_tbs_bits)
+         .decode_optional(responsedata_version, ASN1_Tag(0),
+                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+         .decode_optional(m_signer_name, ASN1_Tag(1),
+                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+         .decode_optional_string(m_key_hash, OCTET_STRING, 2,
+                                 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+         .decode(m_produced_at)
+
+         .decode_list(m_responses)
+
+         .decode_optional(extensions, ASN1_Tag(1),
+                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+      }
+
+   response_outer.end_cons();
+   }
+
+Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const
+   {
+   if (m_responses.empty())
+      return m_dummy_response_status;
+
+   try
+      {
+      std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
+
+      const std::vector<std::string> sig_info =
+         split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+      if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+         return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+      std::string padding = sig_info[1];
+      const Signature_Format format = pub_key->default_x509_signature_format();
+
+      PK_Verifier verifier(*pub_key, padding, format);
+
+      if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature))
+         return Certificate_Status_Code::OCSP_SIGNATURE_OK;
+      else
+         return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+      }
+   catch(Exception&)
+      {
+      return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+      }
+   }
+
+Certificate_Status_Code Response::check_signature(const std::vector<Certificate_Store*>& trusted_roots,
+                                                  const std::vector<std::shared_ptr<const X509_Certificate>>& ee_cert_path) const
+   {
+   if (m_responses.empty())
+      return m_dummy_response_status;
+
+   std::shared_ptr<const X509_Certificate> signing_cert;
+
+   for(size_t i = 0; i != trusted_roots.size(); ++i)
+      {
+      if(m_signer_name.empty() && m_key_hash.empty())
+         return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+      if(!m_signer_name.empty())
+         {
+         signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector<uint8_t>());
+         if(signing_cert)
+            {
+            break;
+            }
+         }
+
+      if(m_key_hash.size() > 0)
+         {
+         signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash);
+         if(signing_cert)
+            {
+            break;
+            }
+         }
+      }
+
+   if(!signing_cert && ee_cert_path.size() > 1)
+      {
+      // End entity cert is not allowed to sign their own OCSP request :)
+      for(size_t i = 1; i < ee_cert_path.size(); ++i)
+         {
+         // Check all CA certificates in the (assumed validated) EE cert path
+         if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name)
+            {
+            signing_cert = ee_cert_path[i];
+            break;
+            }
+
+         if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash)
+            {
+            signing_cert = ee_cert_path[i];
+            break;
+            }
+         }
+      }
+
+   if(!signing_cert && m_certs.size() > 0)
+      {
+      for(size_t i = 0; i < m_certs.size(); ++i)
+         {
+         // Check all CA certificates in the (assumed validated) EE cert path
+         if(!m_signer_name.empty() && m_certs[i].subject_dn() == m_signer_name)
+            {
+            signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+            break;
+            }
+
+         if(m_key_hash.size() > 0 && m_certs[i].subject_public_key_bitstring_sha1() == m_key_hash)
+            {
+            signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+            break;
+            }
+         }
+      }
+
+   if(!signing_cert)
+      return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
+
+   if(!signing_cert->allowed_usage(CRL_SIGN) &&
+      !signing_cert->allowed_extended_usage("PKIX.OCSPSigning"))
+      {
+      return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
+      }
+
+   return this->verify_signature(*signing_cert);
+   }
+
+Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
+      const X509_Certificate& subject,
+      std::chrono::system_clock::time_point ref_time,
+      std::chrono::seconds max_age) const
+   {
+   if(m_responses.empty())
+      { return m_dummy_response_status; }
+
+   for(const auto& response : m_responses)
+      {
+      if(response.certid().is_id_for(issuer, subject))
+         {
+         X509_Time x509_ref_time(ref_time);
+
+         if(response.cert_status() == 1)
+            { return Certificate_Status_Code::CERT_IS_REVOKED; }
+
+         if(response.this_update() > x509_ref_time)
+            { return Certificate_Status_Code::OCSP_NOT_YET_VALID; }
+
+         if(response.next_update().time_is_set())
+            {
+            if(x509_ref_time > response.next_update())
+               { return Certificate_Status_Code::OCSP_HAS_EXPIRED; }
+            }
+         else if(max_age > std::chrono::seconds::zero() && ref_time - response.this_update().to_std_timepoint() > max_age)
+            { return Certificate_Status_Code::OCSP_IS_TOO_OLD; }
+
+         if(response.cert_status() == 0)
+            { return Certificate_Status_Code::OCSP_RESPONSE_GOOD; }
+         else
+            { return Certificate_Status_Code::OCSP_BAD_STATUS; }
+         }
+      }
+
+   return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
+   }
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+Response online_check(const X509_Certificate& issuer,
+                      const BigInt& subject_serial,
+                      const std::string& ocsp_responder,
+                      Certificate_Store* trusted_roots,
+                      std::chrono::milliseconds timeout)
+   {
+   if(ocsp_responder.empty())
+      throw Invalid_Argument("No OCSP responder specified");
+
+   OCSP::Request req(issuer, subject_serial);
+
+   auto http = HTTP::POST_sync(ocsp_responder,
+                               "application/ocsp-request",
+                               req.BER_encode(),
+                               1,
+                               timeout);
+
+   http.throw_unless_ok();
+
+   // Check the MIME type?
+
+   OCSP::Response response(http.body());
+
+   std::vector<Certificate_Store*> trusted_roots_vec;
+   trusted_roots_vec.push_back(trusted_roots);
+
+   if(trusted_roots)
+      response.check_signature(trusted_roots_vec);
+
+   return response;
+   }
+
+
+Response online_check(const X509_Certificate& issuer,
+                      const X509_Certificate& subject,
+                      Certificate_Store* trusted_roots,
+                      std::chrono::milliseconds timeout)
+   {
+   if(subject.issuer_dn() != issuer.subject_dn())
+      throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
+
+   return online_check(issuer,
+                       BigInt::decode(subject.serial_number()),
+                       subject.ocsp_responder(),
+                       trusted_roots,
+                       timeout);
+   }
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/lib/x509/ocsp.h b/third_party/botan/src/lib/x509/ocsp.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/ocsp.h
@@ -0,0 +1,236 @@
+/*
+* OCSP
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_H_
+#define BOTAN_OCSP_H_
+
+#include <botan/cert_status.h>
+#include <botan/ocsp_types.h>
+#include <botan/x509_dn.h>
+#include <chrono>
+
+namespace Botan {
+
+class Certificate_Store;
+
+namespace OCSP {
+
+/**
+* An OCSP request.
+*/
+class BOTAN_PUBLIC_API(2,0) Request final
+   {
+   public:
+      /**
+      * Create an OCSP request.
+      * @param issuer_cert issuer certificate
+      * @param subject_cert subject certificate
+      */
+      Request(const X509_Certificate& issuer_cert,
+              const X509_Certificate& subject_cert);
+
+      Request(const X509_Certificate& issuer_cert,
+              const BigInt& subject_serial);
+
+      /**
+      * @return BER-encoded OCSP request
+      */
+      std::vector<uint8_t> BER_encode() const;
+
+      /**
+      * @return Base64-encoded OCSP request
+      */
+      std::string base64_encode() const;
+
+      /**
+      * @return issuer certificate
+      */
+      const X509_Certificate& issuer() const { return m_issuer; }
+
+      /**
+      * @return subject certificate
+      */
+      const X509_Certificate& subject() const { throw Not_Implemented("Method have been deprecated"); }
+
+      const std::vector<uint8_t>& issuer_key_hash() const
+         { return m_certid.issuer_key_hash(); }
+   private:
+      X509_Certificate m_issuer;
+      CertID m_certid;
+   };
+
+/**
+* OCSP response status.
+*
+* see https://tools.ietf.org/html/rfc6960#section-4.2.1
+*/
+enum class Response_Status_Code {
+   Successful = 0,
+   Malformed_Request = 1,
+   Internal_Error = 2,
+   Try_Later = 3,
+   Sig_Required = 5,
+   Unauthorized = 6
+};
+
+/**
+* OCSP response.
+*
+* Note this class is only usable as an OCSP client
+*/
+class BOTAN_PUBLIC_API(2,0) Response final
+   {
+   public:
+      /**
+      * Creates an empty OCSP response.
+      */
+      Response() = default;
+
+      /**
+      * Create a fake OCSP response from a given status code.
+      * @param status the status code the check functions will return
+      */
+      Response(Certificate_Status_Code status);
+
+      /**
+      * Parses an OCSP response.
+      * @param response_bits response bits received
+      */
+      Response(const std::vector<uint8_t>& response_bits) :
+         Response(response_bits.data(), response_bits.size())
+         {}
+
+      /**
+      * Parses an OCSP response.
+      * @param response_bits response bits received
+      * @param response_bits_len length of response in bytes
+      */
+      Response(const uint8_t response_bits[],
+               size_t response_bits_len);
+
+      /**
+      * Check signature and return status
+      * The optional cert_path is the (already validated!) certificate path of
+      * the end entity which is being inquired about
+      * @param trust_roots list of certstores containing trusted roots
+      * @param cert_path optionally, the (already verified!) certificate path for the certificate
+      * this is an OCSP response for. This is necessary to find the correct intermediate CA in
+      * some cases.
+      */
+      Certificate_Status_Code check_signature(const std::vector<Certificate_Store*>& trust_roots,
+                                              const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = {}) const;
+
+      /**
+      * Verify that issuer's key signed this response
+      * @param issuer certificate of issuer
+      * @return if signature valid OCSP_SIGNATURE_OK else an error code
+      */
+      Certificate_Status_Code verify_signature(const X509_Certificate& issuer) const;
+
+      /**
+      * @return the status of the response
+      */
+      Response_Status_Code status() const { return m_status; }
+
+      /**
+      * @return the time this OCSP response was supposedly produced at
+      */
+      const X509_Time& produced_at() const { return m_produced_at; }
+
+      /**
+      * @return DN of signer, if provided in response (may be empty)
+      */
+      const X509_DN& signer_name() const { return m_signer_name; }
+
+      /**
+      * @return key hash, if provided in response (may be empty)
+      */
+      const std::vector<uint8_t>& signer_key_hash() const { return m_key_hash; }
+
+      const std::vector<uint8_t>& raw_bits() const { return m_response_bits; }
+
+      /**
+       * Searches the OCSP response for issuer and subject certificate.
+       * @param issuer issuer certificate
+       * @param subject subject certificate
+       * @param ref_time the reference time
+       * @param max_age the maximum age the response should be considered valid
+       *                if next_update is not set
+       * @return OCSP status code, possible values:
+       *         CERT_IS_REVOKED,
+       *         OCSP_NOT_YET_VALID,
+       *         OCSP_HAS_EXPIRED,
+       *         OCSP_IS_TOO_OLD,
+       *         OCSP_RESPONSE_GOOD,
+       *         OCSP_BAD_STATUS,
+       *         OCSP_CERT_NOT_LISTED
+       */
+      Certificate_Status_Code status_for(const X509_Certificate& issuer,
+                                         const X509_Certificate& subject,
+                                         std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now(),
+                                         std::chrono::seconds max_age = std::chrono::seconds::zero()) const;
+
+      /**
+       * @return the certificate chain, if provided in response
+       */
+      const std::vector<X509_Certificate> &certificates() const { return  m_certs; }
+
+   private:
+      Response_Status_Code m_status;
+      std::vector<uint8_t> m_response_bits;
+      X509_Time m_produced_at;
+      X509_DN m_signer_name;
+      std::vector<uint8_t> m_key_hash;
+      std::vector<uint8_t> m_tbs_bits;
+      AlgorithmIdentifier m_sig_algo;
+      std::vector<uint8_t> m_signature;
+      std::vector<X509_Certificate> m_certs;
+
+      std::vector<SingleResponse> m_responses;
+
+      Certificate_Status_Code m_dummy_response_status;
+   };
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject_serial the subject's serial number
+* @param ocsp_responder the OCSP responder to query
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,1)
+Response online_check(const X509_Certificate& issuer,
+                      const BigInt& subject_serial,
+                      const std::string& ocsp_responder,
+                      Certificate_Store* trusted_roots,
+                      std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject subject certificate
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,0)
+Response online_check(const X509_Certificate& issuer,
+                      const X509_Certificate& subject,
+                      Certificate_Store* trusted_roots,
+                      std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+#endif
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/ocsp_types.cpp b/third_party/botan/src/lib/x509/ocsp_types.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/ocsp_types.cpp
@@ -0,0 +1,105 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+CertID::CertID(const X509_Certificate& issuer,
+               const BigInt& subject_serial)
+   {
+   /*
+   In practice it seems some responders, including, notably,
+   ocsp.verisign.com, will reject anything but SHA-1 here
+   */
+   std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-160"));
+
+   m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM);
+   m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring()));
+   m_issuer_dn_hash = unlock(hash->process(issuer.raw_subject_dn()));
+   m_subject_serial = subject_serial;
+   }
+
+bool CertID::is_id_for(const X509_Certificate& issuer,
+                       const X509_Certificate& subject) const
+   {
+   try
+      {
+      if(BigInt::decode(subject.serial_number()) != m_subject_serial)
+         return false;
+
+      const std::string hash_algo = m_hash_id.get_oid().to_formatted_string();
+      std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(hash_algo);
+
+      if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn())))
+         return false;
+
+      if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring())))
+         return false;
+      }
+   catch(...)
+      {
+      return false;
+      }
+
+   return true;
+   }
+
+void CertID::encode_into(class DER_Encoder& to) const
+   {
+   to.start_cons(SEQUENCE)
+      .encode(m_hash_id)
+      .encode(m_issuer_dn_hash, OCTET_STRING)
+      .encode(m_issuer_key_hash, OCTET_STRING)
+      .encode(m_subject_serial)
+      .end_cons();
+   }
+
+void CertID::decode_from(class BER_Decoder& from)
+   {
+   from.start_cons(SEQUENCE)
+      .decode(m_hash_id)
+      .decode(m_issuer_dn_hash, OCTET_STRING)
+      .decode(m_issuer_key_hash, OCTET_STRING)
+      .decode(m_subject_serial)
+      .end_cons();
+
+   }
+
+void SingleResponse::encode_into(class DER_Encoder&) const
+   {
+   throw Not_Implemented("SingleResponse::encode_into");
+   }
+
+void SingleResponse::decode_from(class BER_Decoder& from)
+   {
+   BER_Object cert_status;
+   Extensions extensions;
+
+   from.start_cons(SEQUENCE)
+      .decode(m_certid)
+      .get_next(cert_status)
+      .decode(m_thisupdate)
+      .decode_optional(m_nextupdate, ASN1_Tag(0),
+                       ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+      .decode_optional(extensions,
+                       ASN1_Tag(1),
+                       ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+      .end_cons();
+
+   m_cert_status = cert_status.type();
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/x509/ocsp_types.h b/third_party/botan/src/lib/x509/ocsp_types.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/ocsp_types.h
@@ -0,0 +1,68 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_TYPES_H_
+#define BOTAN_OCSP_TYPES_H_
+
+#include <botan/x509cert.h>
+#include <botan/asn1_time.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+class BOTAN_PUBLIC_API(2,0) CertID final : public ASN1_Object
+   {
+   public:
+      CertID() = default;
+
+      CertID(const X509_Certificate& issuer,
+             const BigInt& subject_serial);
+
+      bool is_id_for(const X509_Certificate& issuer,
+                     const X509_Certificate& subject) const;
+
+      void encode_into(class DER_Encoder& to) const override;
+
+      void decode_from(class BER_Decoder& from) override;
+
+      const std::vector<uint8_t>& issuer_key_hash() const { return m_issuer_key_hash; }
+
+   private:
+      AlgorithmIdentifier m_hash_id;
+      std::vector<uint8_t> m_issuer_dn_hash;
+      std::vector<uint8_t> m_issuer_key_hash;
+      BigInt m_subject_serial;
+   };
+
+class BOTAN_PUBLIC_API(2,0) SingleResponse final : public ASN1_Object
+   {
+   public:
+      const CertID& certid() const { return m_certid; }
+
+      size_t cert_status() const { return m_cert_status; }
+
+      X509_Time this_update() const { return m_thisupdate; }
+
+      X509_Time next_update() const { return m_nextupdate; }
+
+      void encode_into(class DER_Encoder& to) const override;
+
+      void decode_from(class BER_Decoder& from) override;
+   private:
+      CertID m_certid;
+      size_t m_cert_status = 2; // unknown
+      X509_Time m_thisupdate;
+      X509_Time m_nextupdate;
+   };
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/pkcs10.cpp b/third_party/botan/src/lib/x509/pkcs10.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/pkcs10.cpp
@@ -0,0 +1,303 @@
+/*
+* PKCS #10
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs10.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+struct PKCS10_Data
+   {
+   X509_DN m_subject_dn;
+   std::vector<uint8_t> m_public_key_bits;
+   AlternativeName m_alt_name;
+   std::string m_challenge;
+   Extensions m_extensions;
+   };
+
+std::string PKCS10_Request::PEM_label() const
+   {
+   return "CERTIFICATE REQUEST";
+   }
+
+std::vector<std::string> PKCS10_Request::alternate_PEM_labels() const
+   {
+   return { "NEW CERTIFICATE REQUEST" };
+   }
+
+PKCS10_Request::PKCS10_Request(DataSource& src)
+   {
+   load_data(src);
+   }
+
+PKCS10_Request::PKCS10_Request(const std::vector<uint8_t>& vec)
+   {
+   DataSource_Memory src(vec.data(), vec.size());
+   load_data(src);
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+PKCS10_Request::PKCS10_Request(const std::string& fsname)
+   {
+   DataSource_Stream src(fsname, true);
+   load_data(src);
+   }
+#endif
+
+//static
+PKCS10_Request PKCS10_Request::create(const Private_Key& key,
+                                      const X509_DN& subject_dn,
+                                      const Extensions& extensions,
+                                      const std::string& hash_fn,
+                                      RandomNumberGenerator& rng,
+                                      const std::string& padding_scheme,
+                                      const std::string& challenge)
+   {
+   AlgorithmIdentifier sig_algo;
+   std::unique_ptr<PK_Signer> signer = choose_sig_format(sig_algo, key, rng, hash_fn, padding_scheme);
+
+   const size_t PKCS10_VERSION = 0;
+
+   DER_Encoder tbs_req;
+
+   tbs_req.start_cons(SEQUENCE)
+      .encode(PKCS10_VERSION)
+      .encode(subject_dn)
+      .raw_bytes(key.subject_public_key())
+      .start_explicit(0);
+
+   if(challenge.empty() == false)
+      {
+      std::vector<uint8_t> value;
+      DER_Encoder(value).encode(ASN1_String(challenge, DIRECTORY_STRING));
+      tbs_req.encode(Attribute("PKCS9.ChallengePassword", value));
+      }
+
+   std::vector<uint8_t> extension_req;
+   DER_Encoder(extension_req).start_cons(SEQUENCE).encode(extensions).end_cons();
+   tbs_req.encode(Attribute("PKCS9.ExtensionRequest", extension_req));
+
+   // end the start_explicit above
+   tbs_req.end_explicit().end_cons();
+
+   const std::vector<uint8_t> req =
+      X509_Object::make_signed(signer.get(), rng, sig_algo,
+                               tbs_req.get_contents());
+
+   return PKCS10_Request(req);
+   }
+
+/*
+* Decode the CertificateRequestInfo
+*/
+namespace {
+
+std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
+   {
+   std::unique_ptr<PKCS10_Data> data(new PKCS10_Data);
+
+   BER_Decoder cert_req_info(body);
+
+   size_t version;
+   cert_req_info.decode(version);
+   if(version != 0)
+      throw Decoding_Error("Unknown version code in PKCS #10 request: " +
+                           std::to_string(version));
+
+   cert_req_info.decode(data->m_subject_dn);
+
+   BER_Object public_key = cert_req_info.get_next_object();
+   if(public_key.is_a(SEQUENCE, CONSTRUCTED) == false)
+      throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for public key", public_key.tagging());
+
+   data->m_public_key_bits = ASN1::put_in_sequence(public_key.bits(), public_key.length());
+
+   BER_Object attr_bits = cert_req_info.get_next_object();
+
+   std::set<std::string> pkcs9_email;
+
+   if(attr_bits.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+      {
+      BER_Decoder attributes(attr_bits);
+      while(attributes.more_items())
+         {
+         Attribute attr;
+         attributes.decode(attr);
+
+         const OID& oid = attr.get_oid();
+         BER_Decoder value(attr.get_parameters());
+
+         if(oid == OID::from_string("PKCS9.EmailAddress"))
+            {
+            ASN1_String email;
+            value.decode(email);
+            pkcs9_email.insert(email.value());
+            }
+         else if(oid == OID::from_string("PKCS9.ChallengePassword"))
+            {
+            ASN1_String challenge_password;
+            value.decode(challenge_password);
+            data->m_challenge = challenge_password.value();
+            }
+         else if(oid == OID::from_string("PKCS9.ExtensionRequest"))
+            {
+            value.decode(data->m_extensions).verify_end();
+            }
+         }
+      attributes.verify_end();
+      }
+   else if(attr_bits.is_set())
+      throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for attributes", attr_bits.tagging());
+
+   cert_req_info.verify_end();
+
+   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+      {
+      data->m_alt_name = ext->get_alt_name();
+      }
+
+   for(std::string email : pkcs9_email)
+      {
+      data->m_alt_name.add_attribute("RFC882", email);
+      }
+
+   return data;
+   }
+
+}
+
+void PKCS10_Request::force_decode()
+   {
+   m_data.reset();
+
+   std::unique_ptr<PKCS10_Data> data = decode_pkcs10(signed_body());
+
+   m_data.reset(data.release());
+
+   if(!this->check_signature(subject_public_key()))
+      throw Decoding_Error("PKCS #10 request: Bad signature detected");
+   }
+
+const PKCS10_Data& PKCS10_Request::data() const
+   {
+   if(m_data == nullptr)
+      throw Decoding_Error("PKCS10_Request decoding failed");
+   return *m_data.get();
+   }
+
+/*
+* Return the challenge password (if any)
+*/
+std::string PKCS10_Request::challenge_password() const
+   {
+   return data().m_challenge;
+   }
+
+/*
+* Return the name of the requestor
+*/
+const X509_DN& PKCS10_Request::subject_dn() const
+   {
+   return data().m_subject_dn;
+   }
+
+/*
+* Return the public key of the requestor
+*/
+const std::vector<uint8_t>& PKCS10_Request::raw_public_key() const
+   {
+   return data().m_public_key_bits;
+   }
+
+/*
+* Return the public key of the requestor
+*/
+Public_Key* PKCS10_Request::subject_public_key() const
+   {
+   DataSource_Memory source(raw_public_key());
+   return X509::load_key(source);
+   }
+
+/*
+* Return the alternative names of the requestor
+*/
+const AlternativeName& PKCS10_Request::subject_alt_name() const
+   {
+   return data().m_alt_name;
+   }
+
+/*
+* Return the X509v3 extensions
+*/
+const Extensions& PKCS10_Request::extensions() const
+   {
+   return data().m_extensions;
+   }
+
+/*
+* Return the key constraints (if any)
+*/
+Key_Constraints PKCS10_Request::constraints() const
+   {
+   if(auto ext = extensions().get(OID::from_string("X509v3.KeyUsage")))
+      {
+      return dynamic_cast<Cert_Extension::Key_Usage&>(*ext).get_constraints();
+      }
+
+   return NO_CONSTRAINTS;
+   }
+
+/*
+* Return the extendend key constraints (if any)
+*/
+std::vector<OID> PKCS10_Request::ex_constraints() const
+   {
+   if(auto ext = extensions().get(OID::from_string("X509v3.ExtendedKeyUsage")))
+      {
+      return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).get_oids();
+      }
+
+   return {};
+   }
+
+/*
+* Return is a CA certificate is requested
+*/
+bool PKCS10_Request::is_CA() const
+   {
+   if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints")))
+      {
+      return dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext).get_is_ca();
+      }
+
+   return false;
+   }
+
+/*
+* Return the desired path limit (if any)
+*/
+size_t PKCS10_Request::path_limit() const
+   {
+   if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints")))
+      {
+      Cert_Extension::Basic_Constraints& basic_constraints = dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext);
+      if(basic_constraints.get_is_ca())
+         {
+         return basic_constraints.get_path_limit();
+         }
+      }
+
+   return 0;
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/pkcs10.h b/third_party/botan/src/lib/x509/pkcs10.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/pkcs10.h
@@ -0,0 +1,148 @@
+/*
+* PKCS #10
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS10_H_
+#define BOTAN_PKCS10_H_
+
+#include <botan/x509_obj.h>
+#include <botan/x509_dn.h>
+#include <botan/key_constraint.h>
+#include <botan/asn1_attribute.h>
+#include <botan/asn1_alt_name.h>
+#include <vector>
+
+namespace Botan {
+
+class Private_Key;
+class Extensions;
+struct PKCS10_Data;
+
+/**
+* PKCS #10 Certificate Request.
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
+   {
+   public:
+      /**
+      * Get the subject public key.
+      * @return subject public key
+      */
+      Public_Key* subject_public_key() const;
+
+      /**
+      * Get the raw DER encoded public key.
+      * @return raw DER encoded public key
+      */
+      const std::vector<uint8_t>& raw_public_key() const;
+
+      /**
+      * Get the subject DN.
+      * @return subject DN
+      */
+      const X509_DN& subject_dn() const;
+
+      /**
+      * Get the subject alternative name.
+      * @return subject alternative name.
+      */
+      const AlternativeName& subject_alt_name() const;
+
+      /**
+      * Get the key constraints for the key associated with this
+      * PKCS#10 object.
+      * @return key constraints
+      */
+      Key_Constraints constraints() const;
+
+      /**
+      * Get the extendend key constraints (if any).
+      * @return extended key constraints
+      */
+      std::vector<OID> ex_constraints() const;
+
+      /**
+      * Find out whether this is a CA request.
+      * @result true if it is a CA request, false otherwise.
+      */
+      bool is_CA() const;
+
+      /**
+      * Return the constraint on the path length defined
+      * in the BasicConstraints extension.
+      * @return path limit
+      */
+      size_t path_limit() const;
+
+      /**
+      * Get the challenge password for this request
+      * @return challenge password for this request
+      */
+      std::string challenge_password() const;
+
+      /**
+      * Get the X509v3 extensions.
+      * @return X509v3 extensions
+      */
+      const Extensions& extensions() const;
+
+      /**
+      * Create a PKCS#10 Request from a data source.
+      * @param source the data source providing the DER encoded request
+      */
+      explicit PKCS10_Request(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+      /**
+      * Create a PKCS#10 Request from a file.
+      * @param filename the name of the file containing the DER or PEM
+      * encoded request file
+      */
+      explicit PKCS10_Request(const std::string& filename);
+#endif
+
+      /**
+      * Create a PKCS#10 Request from binary data.
+      * @param vec a std::vector containing the DER value
+      */
+      explicit PKCS10_Request(const std::vector<uint8_t>& vec);
+
+      /**
+      * Create a new PKCS10 certificate request
+      * @param key the key that will be included in the certificate request
+      * @param subject_dn the DN to be placed in the request
+      * @param extensions extensions to include in the request
+      * @param hash_fn the hash function to use to create the signature
+      * @param rng a random number generator
+      * @param padding_scheme if set specifies the padding scheme, otherwise an
+      *        algorithm-specific default is used.
+      * @param challenge a challenge string to be included in the PKCS10 request,
+      *        sometimes used for revocation purposes.
+      */
+      static PKCS10_Request create(const Private_Key& key,
+                                   const X509_DN& subject_dn,
+                                   const Extensions& extensions,
+                                   const std::string& hash_fn,
+                                   RandomNumberGenerator& rng,
+                                   const std::string& padding_scheme = "",
+                                   const std::string& challenge = "");
+
+   private:
+      std::string PEM_label() const override;
+
+      std::vector<std::string> alternate_PEM_labels() const override;
+
+      void force_decode() override;
+
+      const PKCS10_Data& data() const;
+
+      std::shared_ptr<PKCS10_Data> m_data;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509_ca.cpp b/third_party/botan/src/lib/x509/x509_ca.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_ca.cpp
@@ -0,0 +1,338 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ca.h>
+#include <botan/x509self.h>
+#include <botan/pkcs10.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/bigint.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/key_constraint.h>
+#include <botan/emsa.h>
+#include <botan/scan_name.h>
+#include <algorithm>
+#include <iterator>
+#include <map>
+
+namespace Botan {
+
+/*
+* Load the certificate and private key
+*/
+X509_CA::X509_CA(const X509_Certificate& c,
+                 const Private_Key& key,
+                 const std::string& hash_fn,
+                 RandomNumberGenerator& rng) :
+   m_ca_cert(c),
+   m_hash_fn(hash_fn)
+   {
+   if(!m_ca_cert.is_CA_cert())
+      throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+   std::map<std::string,std::string> opts;
+   // constructor without additional options: use the padding used in the CA certificate
+   // sig_oid_str = <sig_alg>/<padding>, so padding with all its options will look
+   // like a cipher mode to the scanner
+   std::string sig_oid_str = OIDS::oid2str_or_throw(c.signature_algorithm().get_oid());
+   SCAN_Name scanner(sig_oid_str);
+   std::string pad = scanner.cipher_mode();
+   if(!pad.empty())
+      opts.insert({"padding",pad});
+
+   m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+   }
+
+/*
+* Load the certificate and private key, and additional options
+*/
+X509_CA::X509_CA(const X509_Certificate& ca_certificate,
+        const Private_Key& key,
+        const std::map<std::string,std::string>& opts,
+        const std::string& hash_fn,
+        RandomNumberGenerator& rng) : m_ca_cert(ca_certificate), m_hash_fn(hash_fn)
+   {
+   if(!m_ca_cert.is_CA_cert())
+      throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+   m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+   }
+
+/*
+* X509_CA Destructor
+*/
+X509_CA::~X509_CA()
+   {
+   /* for unique_ptr */
+   }
+
+namespace {
+
+Extensions choose_extensions(const PKCS10_Request& req,
+                             const X509_Certificate& ca_cert,
+                             const std::string& hash_fn)
+   {
+   Key_Constraints constraints;
+   if(req.is_CA())
+      {
+      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+      }
+   else
+      {
+      std::unique_ptr<Public_Key> key(req.subject_public_key());
+      verify_cert_constraints_valid_for_key_type(*key, req.constraints());
+      constraints = req.constraints();
+      }
+
+   Extensions extensions = req.extensions();
+
+   extensions.replace(
+      new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()),
+      true);
+
+   if(constraints != NO_CONSTRAINTS)
+      {
+      extensions.replace(new Cert_Extension::Key_Usage(constraints), true);
+      }
+
+   extensions.replace(new Cert_Extension::Authority_Key_ID(ca_cert.subject_key_id()));
+   extensions.replace(new Cert_Extension::Subject_Key_ID(req.raw_public_key(), hash_fn));
+
+   extensions.replace(
+      new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name()));
+
+   extensions.replace(
+      new Cert_Extension::Extended_Key_Usage(req.ex_constraints()));
+
+   return extensions;
+   }
+
+}
+
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+                                       RandomNumberGenerator& rng,
+                                       const BigInt& serial_number,
+                                       const X509_Time& not_before,
+                                       const X509_Time& not_after) const
+   {
+   auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+   return make_cert(m_signer.get(), rng, serial_number,
+                    m_ca_sig_algo, req.raw_public_key(),
+                    not_before, not_after,
+                    m_ca_cert.subject_dn(), req.subject_dn(),
+                    extensions);
+   }
+
+/*
+* Sign a PKCS #10 certificate request
+*/
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+                                       RandomNumberGenerator& rng,
+                                       const X509_Time& not_before,
+                                       const X509_Time& not_after) const
+   {
+   auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+   return make_cert(m_signer.get(), rng, m_ca_sig_algo,
+                    req.raw_public_key(),
+                    not_before, not_after,
+                    m_ca_cert.subject_dn(), req.subject_dn(),
+                    extensions);
+   }
+
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+                                    RandomNumberGenerator& rng,
+                                    const AlgorithmIdentifier& sig_algo,
+                                    const std::vector<uint8_t>& pub_key,
+                                    const X509_Time& not_before,
+                                    const X509_Time& not_after,
+                                    const X509_DN& issuer_dn,
+                                    const X509_DN& subject_dn,
+                                    const Extensions& extensions)
+   {
+   const size_t SERIAL_BITS = 128;
+   BigInt serial_no(rng, SERIAL_BITS);
+
+   return make_cert(signer, rng, serial_no, sig_algo, pub_key,
+                    not_before, not_after, issuer_dn, subject_dn, extensions);
+   }
+
+/*
+* Create a new certificate
+*/
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+                                    RandomNumberGenerator& rng,
+                                    const BigInt& serial_no,
+                                    const AlgorithmIdentifier& sig_algo,
+                                    const std::vector<uint8_t>& pub_key,
+                                    const X509_Time& not_before,
+                                    const X509_Time& not_after,
+                                    const X509_DN& issuer_dn,
+                                    const X509_DN& subject_dn,
+                                    const Extensions& extensions)
+   {
+   const size_t X509_CERT_VERSION = 3;
+
+   // clang-format off
+   return X509_Certificate(X509_Object::make_signed(
+      signer, rng, sig_algo,
+      DER_Encoder().start_cons(SEQUENCE)
+         .start_explicit(0)
+            .encode(X509_CERT_VERSION-1)
+         .end_explicit()
+
+         .encode(serial_no)
+
+         .encode(sig_algo)
+         .encode(issuer_dn)
+
+         .start_cons(SEQUENCE)
+            .encode(not_before)
+            .encode(not_after)
+         .end_cons()
+
+         .encode(subject_dn)
+         .raw_bytes(pub_key)
+
+         .start_explicit(3)
+            .start_cons(SEQUENCE)
+               .encode(extensions)
+             .end_cons()
+         .end_explicit()
+      .end_cons()
+      .get_contents()
+      ));
+   // clang-format on
+   }
+
+/*
+* Create a new, empty CRL
+*/
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+                          uint32_t next_update) const
+   {
+   return new_crl(rng,
+                  std::chrono::system_clock::now(),
+                  std::chrono::seconds(next_update));
+   }
+
+/*
+* Update a CRL with new entries
+*/
+X509_CRL X509_CA::update_crl(const X509_CRL& crl,
+                             const std::vector<CRL_Entry>& new_revoked,
+                             RandomNumberGenerator& rng,
+                             uint32_t next_update) const
+   {
+   return update_crl(crl, new_revoked, rng,
+                     std::chrono::system_clock::now(),
+                     std::chrono::seconds(next_update));
+   }
+
+
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+                          std::chrono::system_clock::time_point issue_time,
+                          std::chrono::seconds next_update) const
+   {
+   std::vector<CRL_Entry> empty;
+   return make_crl(empty, 1, rng, issue_time, next_update);
+   }
+
+X509_CRL X509_CA::update_crl(const X509_CRL& last_crl,
+                             const std::vector<CRL_Entry>& new_revoked,
+                             RandomNumberGenerator& rng,
+                             std::chrono::system_clock::time_point issue_time,
+                             std::chrono::seconds next_update) const
+   {
+   std::vector<CRL_Entry> revoked = last_crl.get_revoked();
+
+   std::copy(new_revoked.begin(), new_revoked.end(),
+             std::back_inserter(revoked));
+
+   return make_crl(revoked, last_crl.crl_number() + 1, rng, issue_time, next_update);
+   }
+
+/*
+* Create a CRL
+*/
+X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
+                           uint32_t crl_number,
+                           RandomNumberGenerator& rng,
+                           std::chrono::system_clock::time_point issue_time,
+                           std::chrono::seconds next_update) const
+   {
+   const size_t X509_CRL_VERSION = 2;
+
+   auto expire_time = issue_time + next_update;
+
+   Extensions extensions;
+   extensions.add(new Cert_Extension::Authority_Key_ID(m_ca_cert.subject_key_id()));
+   extensions.add(new Cert_Extension::CRL_Number(crl_number));
+
+   // clang-format off
+   const std::vector<uint8_t> crl = X509_Object::make_signed(
+      m_signer.get(), rng, m_ca_sig_algo,
+      DER_Encoder().start_cons(SEQUENCE)
+         .encode(X509_CRL_VERSION-1)
+         .encode(m_ca_sig_algo)
+         .encode(m_ca_cert.subject_dn())
+         .encode(X509_Time(issue_time))
+         .encode(X509_Time(expire_time))
+         .encode_if(revoked.size() > 0,
+              DER_Encoder()
+                 .start_cons(SEQUENCE)
+                    .encode_list(revoked)
+                 .end_cons()
+            )
+         .start_explicit(0)
+            .start_cons(SEQUENCE)
+               .encode(extensions)
+            .end_cons()
+         .end_explicit()
+      .end_cons()
+      .get_contents());
+   // clang-format on
+
+   return X509_CRL(crl);
+   }
+
+/*
+* Return the CA's certificate
+*/
+X509_Certificate X509_CA::ca_certificate() const
+   {
+   return m_ca_cert;
+   }
+
+/*
+* Choose a signing format for the key
+*/
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+                             RandomNumberGenerator& rng,
+                             const std::string& hash_fn,
+                             AlgorithmIdentifier& sig_algo)
+   {
+   return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, "").release();
+   }
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+                             const std::map<std::string,std::string>& opts,
+                             RandomNumberGenerator& rng,
+                             const std::string& hash_fn,
+                             AlgorithmIdentifier& sig_algo)
+   {
+   std::string padding;
+   if(opts.count("padding"))
+      padding = opts.at("padding");
+   return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, padding).release();
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/x509_ca.h b/third_party/botan/src/lib/x509/x509_ca.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_ca.h
@@ -0,0 +1,260 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CA_H_
+#define BOTAN_X509_CA_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <chrono>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+  #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+class BigInt;
+class Private_Key;
+class PKCS10_Request;
+class PK_Signer;
+
+/**
+* This class represents X.509 Certificate Authorities (CAs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CA final
+   {
+   public:
+      /**
+      * Sign a PKCS#10 Request.
+      * @param req the request to sign
+      * @param rng the rng to use
+      * @param not_before the starting time for the certificate
+      * @param not_after the expiration time for the certificate
+      * @return resulting certificate
+      */
+      X509_Certificate sign_request(const PKCS10_Request& req,
+                                    RandomNumberGenerator& rng,
+                                    const X509_Time& not_before,
+                                    const X509_Time& not_after) const;
+
+      /**
+      * Sign a PKCS#10 Request.
+      * @param req the request to sign
+      * @param rng the rng to use
+      * @param serial_number the serial number the cert will be assigned.
+      * @param not_before the starting time for the certificate
+      * @param not_after the expiration time for the certificate
+      * @return resulting certificate
+      */
+      X509_Certificate sign_request(const PKCS10_Request& req,
+                                    RandomNumberGenerator& rng,
+                                    const BigInt& serial_number,
+                                    const X509_Time& not_before,
+                                    const X509_Time& not_after) const;
+
+      /**
+      * Get the certificate of this CA.
+      * @return CA certificate
+      */
+      X509_Certificate ca_certificate() const;
+
+      /**
+      * Create a new and empty CRL for this CA.
+      * @param rng the random number generator to use
+      * @param issue_time the issue time (typically system_clock::now)
+      * @param next_update the time interval after issue_data within which
+      *        a new CRL will be produced.
+      * @return new CRL
+      */
+      X509_CRL new_crl(RandomNumberGenerator& rng,
+                       std::chrono::system_clock::time_point issue_time,
+                       std::chrono::seconds next_update) const;
+
+      /**
+      * Create a new CRL by with additional entries.
+      * @param last_crl the last CRL of this CA to add the new entries to
+      * @param new_entries contains the new CRL entries to be added to the CRL
+      * @param rng the random number generator to use
+      * @param issue_time the issue time (typically system_clock::now)
+      * @param next_update the time interval after issue_data within which
+      *        a new CRL will be produced.
+      */
+      X509_CRL update_crl(const X509_CRL& last_crl,
+                          const std::vector<CRL_Entry>& new_entries,
+                          RandomNumberGenerator& rng,
+                          std::chrono::system_clock::time_point issue_time,
+                          std::chrono::seconds next_update) const;
+
+      /**
+      * Create a new and empty CRL for this CA.
+      * @param rng the random number generator to use
+      * @param next_update the time to set in next update in seconds
+      * as the offset from the current time
+      * @return new CRL
+      */
+      X509_CRL new_crl(RandomNumberGenerator& rng,
+                       uint32_t next_update = 604800) const;
+
+      /**
+      * Create a new CRL by with additional entries.
+      * @param last_crl the last CRL of this CA to add the new entries to
+      * @param new_entries contains the new CRL entries to be added to the CRL
+      * @param rng the random number generator to use
+      * @param next_update the time to set in next update in seconds
+      * as the offset from the current time
+      */
+      X509_CRL update_crl(const X509_CRL& last_crl,
+                          const std::vector<CRL_Entry>& new_entries,
+                          RandomNumberGenerator& rng,
+                          uint32_t next_update = 604800) const;
+
+      /**
+      * Interface for creating new certificates
+      * @param signer a signing object
+      * @param rng a random number generator
+      * @param sig_algo the signature algorithm identifier
+      * @param pub_key the serialized public key
+      * @param not_before the start time of the certificate
+      * @param not_after the end time of the certificate
+      * @param issuer_dn the DN of the issuer
+      * @param subject_dn the DN of the subject
+      * @param extensions an optional list of certificate extensions
+      * @returns newly minted certificate
+      */
+      static X509_Certificate make_cert(PK_Signer* signer,
+                                        RandomNumberGenerator& rng,
+                                        const AlgorithmIdentifier& sig_algo,
+                                        const std::vector<uint8_t>& pub_key,
+                                        const X509_Time& not_before,
+                                        const X509_Time& not_after,
+                                        const X509_DN& issuer_dn,
+                                        const X509_DN& subject_dn,
+                                        const Extensions& extensions);
+
+      /**
+      * Interface for creating new certificates
+      * @param signer a signing object
+      * @param rng a random number generator
+      * @param serial_number the serial number the cert will be assigned
+      * @param sig_algo the signature algorithm identifier
+      * @param pub_key the serialized public key
+      * @param not_before the start time of the certificate
+      * @param not_after the end time of the certificate
+      * @param issuer_dn the DN of the issuer
+      * @param subject_dn the DN of the subject
+      * @param extensions an optional list of certificate extensions
+      * @returns newly minted certificate
+      */
+      static X509_Certificate make_cert(PK_Signer* signer,
+                                        RandomNumberGenerator& rng,
+                                        const BigInt& serial_number,
+                                        const AlgorithmIdentifier& sig_algo,
+                                        const std::vector<uint8_t>& pub_key,
+                                        const X509_Time& not_before,
+                                        const X509_Time& not_after,
+                                        const X509_DN& issuer_dn,
+                                        const X509_DN& subject_dn,
+                                        const Extensions& extensions);
+
+      /**
+      * Create a new CA object.
+      * @param ca_certificate the certificate of the CA
+      * @param key the private key of the CA
+      * @param hash_fn name of a hash function to use for signing
+      * @param rng the random generator to use
+      */
+      X509_CA(const X509_Certificate& ca_certificate,
+              const Private_Key& key,
+              const std::string& hash_fn,
+              RandomNumberGenerator& rng);
+
+      /**
+      * Create a new CA object.
+      * @param ca_certificate the certificate of the CA
+      * @param key the private key of the CA
+      * @param opts additional options, e.g. padding, as key value pairs
+      * @param hash_fn name of a hash function to use for signing
+      * @param rng the random generator to use
+      */
+      X509_CA(const X509_Certificate& ca_certificate,
+              const Private_Key& key,
+              const std::map<std::string,std::string>& opts,
+              const std::string& hash_fn,
+              RandomNumberGenerator& rng);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+      BOTAN_DEPRECATED("Use version taking RNG object")
+      X509_CA(const X509_Certificate& ca_certificate,
+              const Private_Key& key,
+              const std::string& hash_fn) :
+         X509_CA(ca_certificate, key, hash_fn, system_rng())
+         {}
+#endif
+
+      X509_CA(const X509_CA&) = delete;
+      X509_CA& operator=(const X509_CA&) = delete;
+
+      X509_CA(X509_CA&&) = default;
+      X509_CA& operator=(X509_CA&&) = default;
+
+      ~X509_CA();
+
+   private:
+      X509_CRL make_crl(const std::vector<CRL_Entry>& entries,
+                        uint32_t crl_number,
+                        RandomNumberGenerator& rng,
+                        std::chrono::system_clock::time_point issue_time,
+                        std::chrono::seconds next_update) const;
+
+      AlgorithmIdentifier m_ca_sig_algo;
+      X509_Certificate m_ca_cert;
+      std::string m_hash_fn;
+      std::unique_ptr<PK_Signer> m_signer;
+   };
+
+/**
+* Choose the default signature format for a certain public key signature
+* scheme.
+* @param key will be the key to choose a padding scheme for
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+BOTAN_PUBLIC_API(2,0) PK_Signer* choose_sig_format(const Private_Key& key,
+                                       RandomNumberGenerator& rng,
+                                       const std::string& hash_fn,
+                                       AlgorithmIdentifier& alg_id);
+
+/**
+* @verbatim
+* Choose the default signature format for a certain public key signature
+* scheme.
+*
+* The only option recognized by opts at this moment is "padding"
+* Find an entry from src/build-data/oids.txt under [signature] of the form
+* <sig_algo>/<padding>[(<hash_algo>)] and add {"padding",<padding>}
+* to opts.
+* @endverbatim
+*
+* @param key will be the key to choose a padding scheme for
+* @param opts contains additional options for building the certificate
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+PK_Signer* choose_sig_format(const Private_Key& key,
+                             const std::map<std::string,std::string>& opts,
+                             RandomNumberGenerator& rng,
+                             const std::string& hash_fn,
+                             AlgorithmIdentifier& alg_id);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509_crl.cpp b/third_party/botan/src/lib/x509/x509_crl.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_crl.cpp
@@ -0,0 +1,268 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+
+#include <sstream>
+
+namespace Botan {
+
+struct CRL_Data
+   {
+   X509_DN m_issuer;
+   X509_Time m_this_update;
+   X509_Time m_next_update;
+   std::vector<CRL_Entry> m_entries;
+   Extensions m_extensions;
+
+   // cached values from extensions
+   size_t m_crl_number = 0;
+   std::vector<uint8_t> m_auth_key_id;
+   std::string m_issuing_distribution_point;
+   };
+
+std::string X509_CRL::PEM_label() const
+   {
+   return "X509 CRL";
+   }
+
+std::vector<std::string> X509_CRL::alternate_PEM_labels() const
+   {
+   return { "CRL" };
+   }
+
+X509_CRL::X509_CRL(DataSource& src)
+   {
+   load_data(src);
+   }
+
+X509_CRL::X509_CRL(const std::vector<uint8_t>& vec)
+   {
+   DataSource_Memory src(vec.data(), vec.size());
+   load_data(src);
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_CRL::X509_CRL(const std::string& fsname)
+   {
+   DataSource_Stream src(fsname, true);
+   load_data(src);
+   }
+#endif
+
+X509_CRL::X509_CRL(const X509_DN& issuer,
+                   const X509_Time& this_update,
+                   const X509_Time& next_update,
+                   const std::vector<CRL_Entry>& revoked) :
+   X509_Object()
+   {
+   m_data.reset(new CRL_Data);
+   m_data->m_issuer = issuer;
+   m_data->m_this_update = this_update;
+   m_data->m_next_update = next_update;
+   m_data->m_entries = revoked;
+   }
+
+/**
+* Check if this particular certificate is listed in the CRL
+*/
+bool X509_CRL::is_revoked(const X509_Certificate& cert) const
+   {
+   /*
+   If the cert wasn't issued by the CRL issuer, it's possible the cert
+   is revoked, but not by this CRL. Maybe throw an exception instead?
+   */
+   if(cert.issuer_dn() != issuer_dn())
+      return false;
+
+   std::vector<uint8_t> crl_akid = authority_key_id();
+   std::vector<uint8_t> cert_akid = cert.authority_key_id();
+
+   if(!crl_akid.empty() && !cert_akid.empty())
+      {
+      if(crl_akid != cert_akid)
+         return false;
+      }
+
+   std::vector<uint8_t> cert_serial = cert.serial_number();
+
+   bool is_revoked = false;
+
+   // FIXME would be nice to avoid a linear scan here - maybe sort the entries?
+   for(const CRL_Entry& entry : get_revoked())
+      {
+      if(cert_serial == entry.serial_number())
+         {
+         if(entry.reason_code() == REMOVE_FROM_CRL)
+            is_revoked = false;
+         else
+            is_revoked = true;
+         }
+      }
+
+   return is_revoked;
+   }
+
+/*
+* Decode the TBSCertList data
+*/
+namespace {
+
+std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
+                                          const AlgorithmIdentifier& sig_algo)
+   {
+   std::unique_ptr<CRL_Data> data(new CRL_Data);
+
+   BER_Decoder tbs_crl(body);
+
+   size_t version;
+   tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+
+   if(version != 0 && version != 1)
+      throw X509_CRL::X509_CRL_Error("Unknown X.509 CRL version " +
+                           std::to_string(version+1));
+
+   AlgorithmIdentifier sig_algo_inner;
+   tbs_crl.decode(sig_algo_inner);
+
+   if(sig_algo != sig_algo_inner)
+      throw X509_CRL::X509_CRL_Error("Algorithm identifier mismatch");
+
+   tbs_crl.decode(data->m_issuer)
+      .decode(data->m_this_update)
+      .decode(data->m_next_update);
+
+   BER_Object next = tbs_crl.get_next_object();
+
+   if(next.is_a(SEQUENCE, CONSTRUCTED))
+      {
+      BER_Decoder cert_list(std::move(next));
+
+      while(cert_list.more_items())
+         {
+         CRL_Entry entry;
+         cert_list.decode(entry);
+         data->m_entries.push_back(entry);
+         }
+      next = tbs_crl.get_next_object();
+      }
+
+   if(next.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+      {
+      BER_Decoder crl_options(std::move(next));
+      crl_options.decode(data->m_extensions).verify_end();
+      next = tbs_crl.get_next_object();
+      }
+
+   if(next.is_set())
+      throw X509_CRL::X509_CRL_Error("Unknown tag in CRL");
+
+   tbs_crl.verify_end();
+
+   // Now cache some fields from the extensions
+   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Number>())
+      {
+      data->m_crl_number = ext->get_crl_number();
+      }
+   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+      {
+      data->m_auth_key_id = ext->get_key_id();
+      }
+   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Issuing_Distribution_Point>())
+      {
+      std::stringstream ss;
+
+      for(const auto& pair : ext->get_point().contents())
+         {
+         ss << pair.first << ": " << pair.second << " ";
+         }
+      data->m_issuing_distribution_point = ss.str();
+      }
+
+   return data;
+   }
+
+}
+
+void X509_CRL::force_decode()
+   {
+   m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
+   }
+
+const CRL_Data& X509_CRL::data() const
+   {
+   if(!m_data)
+      {
+      throw Invalid_State("X509_CRL uninitialized");
+      }
+   return *m_data.get();
+   }
+
+const Extensions& X509_CRL::extensions() const
+   {
+   return data().m_extensions;
+   }
+
+/*
+* Return the list of revoked certificates
+*/
+const std::vector<CRL_Entry>& X509_CRL::get_revoked() const
+   {
+   return data().m_entries;
+   }
+
+/*
+* Return the distinguished name of the issuer
+*/
+const X509_DN& X509_CRL::issuer_dn() const
+   {
+   return data().m_issuer;
+   }
+
+/*
+* Return the key identifier of the issuer
+*/
+const std::vector<uint8_t>& X509_CRL::authority_key_id() const
+   {
+   return data().m_auth_key_id;
+   }
+
+/*
+* Return the CRL number of this CRL
+*/
+uint32_t X509_CRL::crl_number() const
+   {
+   return static_cast<uint32_t>(data().m_crl_number);
+   }
+
+/*
+* Return the issue data of the CRL
+*/
+const X509_Time& X509_CRL::this_update() const
+   {
+   return data().m_this_update;
+   }
+
+/*
+* Return the date when a new CRL will be issued
+*/
+const X509_Time& X509_CRL::next_update() const
+   {
+   return data().m_next_update;
+   }
+
+/*
+* Return the CRL's distribution point
+*/
+std::string X509_CRL::crl_issuing_distribution_point() const
+   {
+   return data().m_issuing_distribution_point;
+   }
+}
diff --git a/third_party/botan/src/lib/x509/x509_crl.h b/third_party/botan/src/lib/x509/x509_crl.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_crl.h
@@ -0,0 +1,144 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CRL_H_
+#define BOTAN_X509_CRL_H_
+
+#include <botan/x509_obj.h>
+#include <botan/x509_dn.h>
+#include <botan/crl_ent.h>
+#include <vector>
+
+namespace Botan {
+
+class Extensions;
+class X509_Certificate;
+
+struct CRL_Data;
+
+/**
+* This class represents X.509 Certificate Revocation Lists (CRLs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
+   {
+   public:
+      /**
+      * This class represents CRL related errors.
+      *
+      * In a future major release this exception type will be removed and
+      * replaced with Decoding_Error
+      */
+      class BOTAN_PUBLIC_API(2,0) X509_CRL_Error final : public Decoding_Error
+         {
+         public:
+            explicit X509_CRL_Error(const std::string& error) :
+               Decoding_Error("X509_CRL: " + error) {}
+         };
+
+      /**
+      * Check if this particular certificate is listed in the CRL
+      */
+      bool is_revoked(const X509_Certificate& cert) const;
+
+      /**
+      * Get the entries of this CRL in the form of a vector.
+      * @return vector containing the entries of this CRL.
+      */
+      const std::vector<CRL_Entry>& get_revoked() const;
+
+      /**
+      * Get the issuer DN of this CRL.
+      * @return CRLs issuer DN
+      */
+      const X509_DN& issuer_dn() const;
+
+      /**
+      * @return extension data for this CRL
+      */
+      const Extensions& extensions() const;
+
+      /**
+      * Get the AuthorityKeyIdentifier of this CRL.
+      * @return this CRLs AuthorityKeyIdentifier
+      */
+      const std::vector<uint8_t>& authority_key_id() const;
+
+      /**
+      * Get the serial number of this CRL.
+      * @return CRLs serial number
+      */
+      uint32_t crl_number() const;
+
+      /**
+      * Get the CRL's thisUpdate value.
+      * @return CRLs thisUpdate
+      */
+      const X509_Time& this_update() const;
+
+      /**
+      * Get the CRL's nextUpdate value.
+      * @return CRLs nextdUpdate
+      */
+      const X509_Time& next_update() const;
+
+      /**
+      * Get the CRL's distribution point
+      * @return CRL.IssuingDistributionPoint from the CRL's Data_Store
+      */
+      std::string crl_issuing_distribution_point() const;
+
+      /**
+      * Create an uninitialized CRL object. Any attempts to access
+      * this object will throw an exception.
+      */
+      X509_CRL() = default;
+
+      /**
+      * Construct a CRL from a data source.
+      * @param source the data source providing the DER or PEM encoded CRL.
+      */
+      X509_CRL(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+      /**
+      * Construct a CRL from a file containing the DER or PEM encoded CRL.
+      * @param filename the name of the CRL file
+      */
+      X509_CRL(const std::string& filename);
+#endif
+
+      /**
+      * Construct a CRL from a binary vector
+      * @param vec the binary (DER) representation of the CRL
+      */
+      X509_CRL(const std::vector<uint8_t>& vec);
+
+      /**
+      * Construct a CRL
+      * @param issuer issuer of this CRL
+      * @param thisUpdate valid from
+      * @param nextUpdate valid until
+      * @param revoked entries to be included in the CRL
+      */
+      X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+               const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked);
+
+   private:
+      std::string PEM_label() const override;
+
+      std::vector<std::string> alternate_PEM_labels() const override;
+
+      void force_decode() override;
+
+      const CRL_Data& data() const;
+
+      std::shared_ptr<CRL_Data> m_data;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509_dn.cpp b/third_party/botan/src/lib/x509/x509_dn.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_dn.cpp
@@ -0,0 +1,428 @@
+/*
+* X509_DN
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_dn.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/internal/stl_util.h>
+#include <botan/oids.h>
+#include <ostream>
+#include <sstream>
+#include <cctype>
+
+namespace Botan {
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const std::string& type,
+                            const std::string& str)
+   {
+   add_attribute(OID::from_string(type), str);
+   }
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const OID& oid, const ASN1_String& str)
+   {
+   if(str.empty())
+      return;
+
+   m_rdn.push_back(std::make_pair(oid, str));
+   m_dn_bits.clear();
+   }
+
+/*
+* Get the attributes of this X509_DN
+*/
+std::multimap<OID, std::string> X509_DN::get_attributes() const
+   {
+   std::multimap<OID, std::string> retval;
+
+   for(auto& i : m_rdn)
+      multimap_insert(retval, i.first, i.second.value());
+   return retval;
+   }
+
+/*
+* Get the contents of this X.500 Name
+*/
+std::multimap<std::string, std::string> X509_DN::contents() const
+   {
+   std::multimap<std::string, std::string> retval;
+
+   for(auto& i : m_rdn)
+      {
+      multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
+      }
+   return retval;
+   }
+
+bool X509_DN::has_field(const std::string& attr) const
+   {
+   const OID o = OIDS::str2oid_or_empty(deref_info_field(attr));
+   if(o.has_value())
+      return has_field(o);
+   else
+      return false;
+   }
+
+bool X509_DN::has_field(const OID& oid) const
+   {
+   for(auto& i : m_rdn)
+      {
+      if(i.first == oid)
+         return true;
+      }
+
+   return false;
+   }
+
+std::string X509_DN::get_first_attribute(const std::string& attr) const
+   {
+   const OID oid = OID::from_string(deref_info_field(attr));
+   return get_first_attribute(oid).value();
+   }
+
+ASN1_String X509_DN::get_first_attribute(const OID& oid) const
+   {
+   for(auto& i : m_rdn)
+      {
+      if(i.first == oid)
+         {
+         return i.second;
+         }
+      }
+
+   return ASN1_String();
+   }
+
+/*
+* Get a single attribute type
+*/
+std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
+   {
+   const OID oid = OID::from_string(deref_info_field(attr));
+
+   std::vector<std::string> values;
+
+   for(auto& i : m_rdn)
+      {
+      if(i.first == oid)
+         {
+         values.push_back(i.second.value());
+         }
+      }
+
+   return values;
+   }
+
+/*
+* Deref aliases in a subject/issuer info request
+*/
+std::string X509_DN::deref_info_field(const std::string& info)
+   {
+   if(info == "Name" || info == "CommonName" || info == "CN") return "X520.CommonName";
+   if(info == "SerialNumber" || info == "SN")                 return "X520.SerialNumber";
+   if(info == "Country" || info == "C")                       return "X520.Country";
+   if(info == "Organization" || info == "O")                  return "X520.Organization";
+   if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU")
+      return "X520.OrganizationalUnit";
+   if(info == "Locality" || info == "L")                      return "X520.Locality";
+   if(info == "State" || info == "Province" || info == "ST")  return "X520.State";
+   if(info == "Email")                                        return "RFC822";
+   return info;
+   }
+
+/*
+* Compare two X509_DNs for equality
+*/
+bool operator==(const X509_DN& dn1, const X509_DN& dn2)
+   {
+   auto attr1 = dn1.get_attributes();
+   auto attr2 = dn2.get_attributes();
+
+   if(attr1.size() != attr2.size()) return false;
+
+   auto p1 = attr1.begin();
+   auto p2 = attr2.begin();
+
+   while(true)
+      {
+      if(p1 == attr1.end() && p2 == attr2.end())
+         break;
+      if(p1 == attr1.end())      return false;
+      if(p2 == attr2.end())      return false;
+      if(p1->first != p2->first) return false;
+      if(!x500_name_cmp(p1->second, p2->second))
+         return false;
+      ++p1;
+      ++p2;
+      }
+   return true;
+   }
+
+/*
+* Compare two X509_DNs for inequality
+*/
+bool operator!=(const X509_DN& dn1, const X509_DN& dn2)
+   {
+   return !(dn1 == dn2);
+   }
+
+/*
+* Induce an arbitrary ordering on DNs
+*/
+bool operator<(const X509_DN& dn1, const X509_DN& dn2)
+   {
+   auto attr1 = dn1.get_attributes();
+   auto attr2 = dn2.get_attributes();
+
+   // If they are not the same size, choose the smaller as the "lessor"
+   if(attr1.size() < attr2.size())
+      return true;
+   if(attr1.size() > attr2.size())
+      return false;
+
+   // We know they are the same # of elements, now compare the OIDs:
+   auto p1 = attr1.begin();
+   auto p2 = attr2.begin();
+
+   while(p1 != attr1.end() && p2 != attr2.end())
+      {
+      if(p1->first != p2->first)
+         {
+         return (p1->first < p2->first);
+         }
+
+      ++p1;
+      ++p2;
+      }
+
+   // We know this is true because maps have the same size
+   BOTAN_ASSERT_NOMSG(p1 == attr1.end());
+   BOTAN_ASSERT_NOMSG(p2 == attr2.end());
+
+   // Now we know all elements have the same OIDs, compare
+   // their string values:
+
+   p1 = attr1.begin();
+   p2 = attr2.begin();
+   while(p1 != attr1.end() && p2 != attr2.end())
+      {
+      BOTAN_DEBUG_ASSERT(p1->first == p2->first);
+
+      // They may be binary different but same by X.500 rules, check this
+      if(!x500_name_cmp(p1->second, p2->second))
+         {
+         // If they are not (by X.500) the same string, pick the
+         // lexicographic first as the lessor
+         return (p1->second < p2->second);
+         }
+
+      ++p1;
+      ++p2;
+      }
+
+   // if we reach here, then the DNs should be identical
+   BOTAN_DEBUG_ASSERT(dn1 == dn2);
+   return false;
+   }
+
+/*
+* DER encode a DistinguishedName
+*/
+void X509_DN::encode_into(DER_Encoder& der) const
+   {
+   der.start_cons(SEQUENCE);
+
+   if(!m_dn_bits.empty())
+      {
+      /*
+      If we decoded this from somewhere, encode it back exactly as
+      we received it
+      */
+      der.raw_bytes(m_dn_bits);
+      }
+   else
+      {
+      for(const auto& dn : m_rdn)
+         {
+         der.start_cons(SET)
+            .start_cons(SEQUENCE)
+            .encode(dn.first)
+            .encode(dn.second)
+            .end_cons()
+         .end_cons();
+         }
+      }
+
+   der.end_cons();
+   }
+
+/*
+* Decode a BER encoded DistinguishedName
+*/
+void X509_DN::decode_from(BER_Decoder& source)
+   {
+   std::vector<uint8_t> bits;
+
+   source.start_cons(SEQUENCE)
+      .raw_bytes(bits)
+   .end_cons();
+
+   BER_Decoder sequence(bits);
+
+   while(sequence.more_items())
+      {
+      BER_Decoder rdn = sequence.start_cons(SET);
+
+      while(rdn.more_items())
+         {
+         OID oid;
+         ASN1_String str;
+
+         rdn.start_cons(SEQUENCE)
+            .decode(oid)
+            .decode(str) // TODO support Any
+            .end_cons().verify_end("Invalid X509_DN, data follows RDN");
+
+         add_attribute(oid, str);
+         }
+      }
+
+   m_dn_bits = bits;
+   }
+
+namespace {
+
+std::string to_short_form(const OID& oid)
+   {
+   const std::string long_id = oid.to_formatted_string();
+
+   if(long_id == "X520.CommonName")
+      return "CN";
+
+   if(long_id == "X520.Country")
+      return "C";
+
+   if(long_id == "X520.Organization")
+      return "O";
+
+   if(long_id == "X520.OrganizationalUnit")
+      return "OU";
+
+   return long_id;
+   }
+
+}
+
+std::string X509_DN::to_string() const
+   {
+   std::ostringstream out;
+   out << *this;
+   return out.str();
+   }
+
+std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
+   {
+   auto info = dn.dn_info();
+
+   for(size_t i = 0; i != info.size(); ++i)
+      {
+      out << to_short_form(info[i].first) << "=\"";
+      for(char c : info[i].second.value())
+         {
+         if(c == '\\' || c == '\"')
+            {
+            out << "\\";
+            }
+         out << c;
+         }
+      out << "\"";
+
+      if(i + 1 < info.size())
+         {
+         out << ",";
+         }
+      }
+   return out;
+   }
+
+std::istream& operator>>(std::istream& in, X509_DN& dn)
+   {
+   in >> std::noskipws;
+   do
+      {
+      std::string key;
+      std::string val;
+      char c;
+
+      while(in.good())
+         {
+         in >> c;
+
+         if(std::isspace(c) && key.empty())
+            continue;
+         else if(!std::isspace(c))
+            {
+            key.push_back(c);
+            break;
+            }
+         else
+            break;
+         }
+
+      while(in.good())
+         {
+         in >> c;
+
+         if(!std::isspace(c) && c != '=')
+            key.push_back(c);
+         else if(c == '=')
+            break;
+         else
+            throw Invalid_Argument("Ill-formed X.509 DN");
+         }
+
+      bool in_quotes = false;
+      while(in.good())
+         {
+         in >> c;
+
+         if(std::isspace(c))
+            {
+            if(!in_quotes && !val.empty())
+               break;
+            else if(in_quotes)
+               val.push_back(' ');
+            }
+         else if(c == '"')
+            in_quotes = !in_quotes;
+         else if(c == '\\')
+            {
+            if(in.good())
+               in >> c;
+            val.push_back(c);
+            }
+         else if(c == ',' && !in_quotes)
+            break;
+         else
+            val.push_back(c);
+         }
+
+      if(!key.empty() && !val.empty())
+         dn.add_attribute(X509_DN::deref_info_field(key),val);
+      else
+         break;
+      }
+   while(in.good());
+   return in;
+   }
+}
diff --git a/third_party/botan/src/lib/x509/x509_dn.h b/third_party/botan/src/lib/x509/x509_dn.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_dn.h
@@ -0,0 +1,104 @@
+/*
+* X.509 Distinguished Name
+* (C) 1999-2010,2018 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_DN_H_
+#define BOTAN_X509_DN_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <botan/asn1_str.h>
+#include <vector>
+#include <map>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* Distinguished Name
+*/
+class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object
+   {
+   public:
+      X509_DN() = default;
+
+      explicit X509_DN(const std::multimap<OID, std::string>& args)
+         {
+         for(auto i : args)
+            add_attribute(i.first, i.second);
+         }
+
+      explicit X509_DN(const std::multimap<std::string, std::string>& args)
+         {
+         for(auto i : args)
+            add_attribute(i.first, i.second);
+         }
+
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+
+      bool has_field(const OID& oid) const;
+      ASN1_String get_first_attribute(const OID& oid) const;
+
+      /*
+      * Return the BER encoded data, if any
+      */
+      const std::vector<uint8_t>& get_bits() const { return m_dn_bits; }
+
+      bool empty() const { return m_rdn.empty(); }
+
+      std::string to_string() const;
+
+      const std::vector<std::pair<OID,ASN1_String>>& dn_info() const { return m_rdn; }
+
+      std::multimap<OID, std::string> get_attributes() const;
+      std::multimap<std::string, std::string> contents() const;
+
+      bool has_field(const std::string& attr) const;
+      std::vector<std::string> get_attribute(const std::string& attr) const;
+      std::string get_first_attribute(const std::string& attr) const;
+
+      void add_attribute(const std::string& key, const std::string& val);
+
+      void add_attribute(const OID& oid, const std::string& val)
+         {
+         add_attribute(oid, ASN1_String(val));
+         }
+
+      void add_attribute(const OID& oid, const ASN1_String& val);
+
+      static std::string deref_info_field(const std::string& key);
+
+      /**
+      * Lookup upper bounds in characters for the length of distinguished name fields
+      * as given in RFC 5280, Appendix A.
+      *
+      * @param oid the oid of the DN to lookup
+      * @return the upper bound, or zero if no ub is known to Botan
+      */
+      static size_t lookup_ub(const OID& oid);
+
+   private:
+      std::vector<std::pair<OID,ASN1_String>> m_rdn;
+      std::vector<uint8_t> m_dn_bits;
+   };
+
+bool BOTAN_PUBLIC_API(2,0) operator==(const X509_DN& dn1, const X509_DN& dn2);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_DN& dn1, const X509_DN& dn2);
+
+/*
+The ordering here is arbitrary and may change from release to release.
+It is intended for allowing DNs as keys in std::map and similiar containers
+*/
+bool BOTAN_PUBLIC_API(2,0) operator<(const X509_DN& dn1, const X509_DN& dn2);
+
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& out, const X509_DN& dn);
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream& in, X509_DN& dn);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509_dn_ub.cpp b/third_party/botan/src/lib/x509/x509_dn_ub.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_dn_ub.cpp
@@ -0,0 +1,60 @@
+/*
+* DN_UB maps: Upper bounds on the length of DN strings
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_dn.h>
+#include <botan/asn1_oid.h>
+#include <map>
+
+namespace {
+
+/**
+ * Upper bounds for the length of distinguished name fields as given in RFC 5280, Appendix A.
+ * Only OIDS recognized by botan are considered, so far.
+ * Maps OID string representations instead of human readable strings in order
+ * to avoid an additional lookup.
+ */
+static const std::map<Botan::OID, size_t> DN_UB =
+   {
+   { Botan::OID({2,5,4,10}), 64 }, // X520.Organization
+   { Botan::OID({2,5,4,11}), 64 }, // X520.OrganizationalUnit
+   { Botan::OID({2,5,4,12}), 64 }, // X520.Title
+   { Botan::OID({2,5,4,3}), 64 }, // X520.CommonName
+   { Botan::OID({2,5,4,4}), 40 }, // X520.Surname
+   { Botan::OID({2,5,4,42}), 32768 }, // X520.GivenName
+   { Botan::OID({2,5,4,43}), 32768 }, // X520.Initials
+   { Botan::OID({2,5,4,44}), 32768 }, // X520.GenerationalQualifier
+   { Botan::OID({2,5,4,46}), 64 }, // X520.DNQualifier
+   { Botan::OID({2,5,4,5}), 64 }, // X520.SerialNumber
+   { Botan::OID({2,5,4,6}), 3 }, // X520.Country
+   { Botan::OID({2,5,4,65}), 128 }, // X520.Pseudonym
+   { Botan::OID({2,5,4,7}), 128 }, // X520.Locality
+   { Botan::OID({2,5,4,8}), 128 }, // X520.State
+   { Botan::OID({2,5,4,9}), 128 } // X520.StreetAddress
+   };
+}
+
+namespace Botan {
+
+//static
+size_t X509_DN::lookup_ub(const OID& oid)
+   {
+   auto ub_entry = DN_UB.find(oid);
+   if(ub_entry != DN_UB.end())
+      {
+      return ub_entry->second;
+      }
+   else
+      {
+      return 0;
+      }
+   }
+}
+
diff --git a/third_party/botan/src/lib/x509/x509_ext.cpp b/third_party/botan/src/lib/x509/x509_ext.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_ext.cpp
@@ -0,0 +1,1026 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2010,2012 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/datastor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+#include <set>
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create a Certificate_Extension object of some kind to handle
+*/
+std::unique_ptr<Certificate_Extension>
+Extensions::create_extn_obj(const OID& oid,
+                            bool critical,
+                            const std::vector<uint8_t>& body)
+   {
+   const std::string oid_str = oid.to_string();
+
+   std::unique_ptr<Certificate_Extension> extn;
+
+   if(oid == Cert_Extension::Subject_Key_ID::static_oid())
+      {
+      extn.reset(new Cert_Extension::Subject_Key_ID);
+      }
+   else if(oid == Cert_Extension::Key_Usage::static_oid())
+      {
+      extn.reset(new Cert_Extension::Key_Usage);
+      }
+   else if(oid == Cert_Extension::Subject_Alternative_Name::static_oid())
+      {
+      extn.reset(new Cert_Extension::Subject_Alternative_Name);
+      }
+   else if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid())
+      {
+      extn.reset(new Cert_Extension::Issuer_Alternative_Name);
+      }
+   else if(oid == Cert_Extension::Basic_Constraints::static_oid())
+      {
+      extn.reset(new Cert_Extension::Basic_Constraints);
+      }
+   else if(oid == Cert_Extension::CRL_Number::static_oid())
+      {
+      extn.reset(new Cert_Extension::CRL_Number);
+      }
+   else if(oid == Cert_Extension::CRL_ReasonCode::static_oid())
+      {
+      extn.reset(new Cert_Extension::CRL_ReasonCode);
+      }
+   else if(oid == Cert_Extension::Authority_Key_ID::static_oid())
+      {
+      extn.reset(new Cert_Extension::Authority_Key_ID);
+      }
+   else if(oid == Cert_Extension::Name_Constraints::static_oid())
+      {
+      extn.reset(new Cert_Extension::Name_Constraints);
+      }
+   else if(oid == Cert_Extension::CRL_Distribution_Points::static_oid())
+      {
+      extn.reset(new Cert_Extension::CRL_Distribution_Points);
+      }
+   else if(oid == Cert_Extension::CRL_Issuing_Distribution_Point::static_oid())
+      {
+      extn.reset(new Cert_Extension::CRL_Issuing_Distribution_Point);
+      }
+   else if(oid == Cert_Extension::Certificate_Policies::static_oid())
+      {
+      extn.reset(new Cert_Extension::Certificate_Policies);
+      }
+   else if(oid == Cert_Extension::Extended_Key_Usage::static_oid())
+      {
+      extn.reset(new Cert_Extension::Extended_Key_Usage);
+      }
+   else if(oid == Cert_Extension::Authority_Information_Access::static_oid())
+      {
+      extn.reset(new Cert_Extension::Authority_Information_Access);
+      }
+   else
+      {
+      // some other unknown extension type
+      extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+      }
+
+   try
+      {
+      extn->decode_inner(body);
+      }
+   catch(Decoding_Error&)
+      {
+      extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+      extn->decode_inner(body);
+      }
+   return extn;
+   }
+
+/*
+* Validate the extension (the default implementation is a NOP)
+*/
+void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
+      const std::vector<std::shared_ptr<const X509_Certificate>>&,
+      std::vector<std::set<Certificate_Status_Code>>&,
+      size_t)
+   {
+   }
+
+/*
+* Add a new cert
+*/
+void Extensions::add(Certificate_Extension* extn, bool critical)
+   {
+   // sanity check: we don't want to have the same extension more than once
+   if(m_extension_info.count(extn->oid_of()) > 0)
+      {
+      const std::string name = extn->oid_name();
+      delete extn;
+      throw Invalid_Argument("Extension " + name + " already present in Extensions::add");
+      }
+
+   const OID oid = extn->oid_of();
+   Extensions_Info info(critical, extn);
+   m_extension_oids.push_back(oid);
+   m_extension_info.emplace(oid, info);
+   }
+
+bool Extensions::add_new(Certificate_Extension* extn, bool critical)
+   {
+   if(m_extension_info.count(extn->oid_of()) > 0)
+      {
+      delete extn;
+      return false; // already exists
+      }
+
+   const OID oid = extn->oid_of();
+   Extensions_Info info(critical, extn);
+   m_extension_oids.push_back(oid);
+   m_extension_info.emplace(oid, info);
+   return true;
+   }
+
+bool Extensions::remove(const OID& oid)
+   {
+   const bool erased = m_extension_info.erase(oid) > 0;
+
+   if(erased)
+      {
+      m_extension_oids.erase(std::find(m_extension_oids.begin(), m_extension_oids.end(), oid));
+      }
+
+   return erased;
+   }
+
+void Extensions::replace(Certificate_Extension* extn, bool critical)
+   {
+   // Remove it if it existed
+   remove(extn->oid_of());
+
+   const OID oid = extn->oid_of();
+   Extensions_Info info(critical, extn);
+   m_extension_oids.push_back(oid);
+   m_extension_info.emplace(oid, info);
+   }
+
+bool Extensions::extension_set(const OID& oid) const
+   {
+   return (m_extension_info.find(oid) != m_extension_info.end());
+   }
+
+bool Extensions::critical_extension_set(const OID& oid) const
+   {
+   auto i = m_extension_info.find(oid);
+   if(i != m_extension_info.end())
+      return i->second.is_critical();
+   return false;
+   }
+
+std::vector<uint8_t> Extensions::get_extension_bits(const OID& oid) const
+   {
+   auto i = m_extension_info.find(oid);
+   if(i == m_extension_info.end())
+      throw Invalid_Argument("Extensions::get_extension_bits no such extension set");
+
+   return i->second.bits();
+   }
+
+const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const
+   {
+   auto extn = m_extension_info.find(oid);
+   if(extn == m_extension_info.end())
+      return nullptr;
+
+   return &extn->second.obj();
+   }
+
+std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const
+   {
+   if(const Certificate_Extension* ext = this->get_extension_object(oid))
+      {
+      return std::unique_ptr<Certificate_Extension>(ext->copy());
+      }
+   return nullptr;
+   }
+
+std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const
+   {
+   std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts;
+   for(auto&& ext : m_extension_info)
+      {
+      exts.push_back(
+         std::make_pair(
+            std::unique_ptr<Certificate_Extension>(ext.second.obj().copy()),
+            ext.second.is_critical())
+         );
+      }
+   return exts;
+   }
+
+std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw() const
+   {
+   std::map<OID, std::pair<std::vector<uint8_t>, bool>> out;
+   for(auto&& ext : m_extension_info)
+      {
+      out.emplace(ext.first,
+                  std::make_pair(ext.second.bits(),
+                                 ext.second.is_critical()));
+      }
+   return out;
+   }
+
+/*
+* Encode an Extensions list
+*/
+void Extensions::encode_into(DER_Encoder& to_object) const
+   {
+   for(auto ext_info : m_extension_info)
+      {
+      const OID& oid = ext_info.first;
+      const bool should_encode = ext_info.second.obj().should_encode();
+
+      if(should_encode)
+         {
+         const bool is_critical = ext_info.second.is_critical();
+         const std::vector<uint8_t>& ext_value = ext_info.second.bits();
+
+         to_object.start_cons(SEQUENCE)
+               .encode(oid)
+               .encode_optional(is_critical, false)
+               .encode(ext_value, OCTET_STRING)
+            .end_cons();
+         }
+      }
+   }
+
+/*
+* Decode a list of Extensions
+*/
+void Extensions::decode_from(BER_Decoder& from_source)
+   {
+   m_extension_oids.clear();
+   m_extension_info.clear();
+
+   BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+
+   while(sequence.more_items())
+      {
+      OID oid;
+      bool critical;
+      std::vector<uint8_t> bits;
+
+      sequence.start_cons(SEQUENCE)
+         .decode(oid)
+         .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+         .decode(bits, OCTET_STRING)
+      .end_cons();
+
+      std::unique_ptr<Certificate_Extension> obj = create_extn_obj(oid, critical, bits);
+      Extensions_Info info(critical, bits, obj.release());
+
+      m_extension_oids.push_back(oid);
+      m_extension_info.emplace(oid, info);
+      }
+   sequence.verify_end();
+   }
+
+/*
+* Write the extensions to an info store
+*/
+void Extensions::contents_to(Data_Store& subject_info,
+                             Data_Store& issuer_info) const
+   {
+   for(auto&& m_extn_info : m_extension_info)
+      {
+      m_extn_info.second.obj().contents_to(subject_info, issuer_info);
+      subject_info.add(m_extn_info.second.obj().oid_name() + ".is_critical",
+                       m_extn_info.second.is_critical());
+      }
+   }
+
+namespace Cert_Extension {
+
+/*
+* Checked accessor for the path_limit member
+*/
+size_t Basic_Constraints::get_path_limit() const
+   {
+   if(!m_is_ca)
+      throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA");
+   return m_path_limit;
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Basic_Constraints::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+      .encode_if(m_is_ca,
+                 DER_Encoder()
+                    .encode(m_is_ca)
+                    .encode_optional(m_path_limit, NO_CERT_PATH_LIMIT)
+         )
+      .end_cons();
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in)
+      .start_cons(SEQUENCE)
+         .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false)
+         .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+      .end_cons();
+
+   if(m_is_ca == false)
+      m_path_limit = 0;
+   }
+
+/*
+* Return a textual representation
+*/
+void Basic_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   subject.add("X509v3.BasicConstraints.is_ca", (m_is_ca ? 1 : 0));
+   subject.add("X509v3.BasicConstraints.path_constraint", static_cast<uint32_t>(m_path_limit));
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Key_Usage::encode_inner() const
+   {
+   if(m_constraints == NO_CONSTRAINTS)
+      throw Encoding_Error("Cannot encode zero usage constraints");
+
+   const size_t unused_bits = ctz(static_cast<uint32_t>(m_constraints));
+
+   std::vector<uint8_t> der;
+   der.push_back(BIT_STRING);
+   der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
+   der.push_back(unused_bits % 8);
+   der.push_back((m_constraints >> 8) & 0xFF);
+   if(m_constraints & 0xFF)
+      der.push_back(m_constraints & 0xFF);
+
+   return der;
+   }
+
+/*
+* Decode the extension
+*/
+void Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder ber(in);
+
+   BER_Object obj = ber.get_next_object();
+
+   obj.assert_is_a(BIT_STRING, UNIVERSAL, "usage constraint");
+
+   if(obj.length() != 2 && obj.length() != 3)
+      throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint");
+
+   uint16_t usage = 0;
+
+      const uint8_t* bits = obj.bits();
+
+   if(bits[0] >= 8)
+      throw BER_Decoding_Error("Invalid unused bits in usage constraint");
+
+   const uint8_t mask = static_cast<uint8_t>(0xFF << bits[0]);
+
+   if(obj.length() == 2)
+      {
+      usage = make_uint16(bits[1] & mask, 0);
+      }
+   else if(obj.length() == 3)
+      {
+      usage = make_uint16(bits[1], bits[2] & mask);
+      }
+
+   m_constraints = Key_Constraints(usage);
+   }
+
+/*
+* Return a textual representation
+*/
+void Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   subject.add("X509v3.KeyUsage", m_constraints);
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Key_ID::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(m_key_id, OCTET_STRING);
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Subject_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in).decode(m_key_id, OCTET_STRING).verify_end();
+   }
+
+/*
+* Return a textual representation
+*/
+void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   subject.add("X509v3.SubjectKeyIdentifier", m_key_id);
+   }
+
+/*
+* Subject_Key_ID Constructor
+*/
+Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key, const std::string& hash_name)
+   {
+   std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+   m_key_id.resize(hash->output_length());
+
+   hash->update(pub_key);
+   hash->final(m_key_id.data());
+
+   // Truncate longer hashes, 192 bits here seems plenty
+   const size_t max_skid_len = (192 / 8);
+   if(m_key_id.size() > max_skid_len)
+      m_key_id.resize(max_skid_len);
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Authority_Key_ID::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .encode(m_key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+      .end_cons();
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Authority_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in)
+      .start_cons(SEQUENCE)
+      .decode_optional_string(m_key_id, OCTET_STRING, 0);
+   }
+
+/*
+* Return a textual representation
+*/
+void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
+   {
+   if(m_key_id.size())
+      issuer.add("X509v3.AuthorityKeyIdentifier", m_key_id);
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(m_alt_name);
+   return output;
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(m_alt_name);
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in).decode(m_alt_name);
+   }
+
+/*
+* Decode the extension
+*/
+void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in).decode(m_alt_name);
+   }
+
+/*
+* Return a textual representation
+*/
+void Subject_Alternative_Name::contents_to(Data_Store& subject_info,
+                                           Data_Store&) const
+   {
+   subject_info.add(get_alt_name().contents());
+   }
+
+/*
+* Return a textual representation
+*/
+void Issuer_Alternative_Name::contents_to(Data_Store&, Data_Store& issuer_info) const
+   {
+   issuer_info.add(get_alt_name().contents());
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Extended_Key_Usage::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .encode_list(m_oids)
+      .end_cons();
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Extended_Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in).decode_list(m_oids);
+   }
+
+/*
+* Return a textual representation
+*/
+void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   for(size_t i = 0; i != m_oids.size(); ++i)
+      subject.add("X509v3.ExtendedKeyUsage", m_oids[i].to_string());
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Name_Constraints::encode_inner() const
+   {
+   throw Not_Implemented("Name_Constraints encoding");
+   }
+
+
+/*
+* Decode the extension
+*/
+void Name_Constraints::decode_inner(const std::vector<uint8_t>& in)
+   {
+   std::vector<GeneralSubtree> permit, exclude;
+   BER_Decoder ber(in);
+   BER_Decoder ext = ber.start_cons(SEQUENCE);
+   BER_Object per = ext.get_next_object();
+
+   ext.push_back(per);
+   if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+      {
+      ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+      if(permit.empty())
+         throw Encoding_Error("Empty Name Contraint list");
+      }
+
+   BER_Object exc = ext.get_next_object();
+   ext.push_back(exc);
+   if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+      {
+      ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+      if(exclude.empty())
+         throw Encoding_Error("Empty Name Contraint list");
+      }
+
+   ext.end_cons();
+
+   if(permit.empty() && exclude.empty())
+      throw Encoding_Error("Empty Name Contraint extension");
+
+   m_name_constraints = NameConstraints(std::move(permit),std::move(exclude));
+   }
+
+/*
+* Return a textual representation
+*/
+void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   std::stringstream ss;
+
+   for(const GeneralSubtree& gs: m_name_constraints.permitted())
+      {
+      ss << gs;
+      subject.add("X509v3.NameConstraints.permitted", ss.str());
+      ss.str(std::string());
+      }
+   for(const GeneralSubtree& gs: m_name_constraints.excluded())
+      {
+      ss << gs;
+      subject.add("X509v3.NameConstraints.excluded", ss.str());
+      ss.str(std::string());
+      }
+   }
+
+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+      const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+      std::vector<std::set<Certificate_Status_Code>>& cert_status,
+      size_t pos)
+   {
+   if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+      {
+      if(!subject.is_CA_cert() || !subject.is_critical("X509v3.NameConstraints"))
+         cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+
+      const bool issuer_name_constraint_critical =
+         issuer.is_critical("X509v3.NameConstraints");
+
+      const bool at_self_signed_root = (pos == cert_path.size() - 1);
+
+      // Check that all subordinate certs pass the name constraint
+      for(size_t j = 0; j <= pos; ++j)
+         {
+         if(pos == j && at_self_signed_root)
+            continue;
+
+         bool permitted = m_name_constraints.permitted().empty();
+         bool failed = false;
+
+         for(auto c: m_name_constraints.permitted())
+            {
+            switch(c.base().matches(*cert_path.at(j)))
+               {
+               case GeneralName::MatchResult::NotFound:
+               case GeneralName::MatchResult::All:
+                  permitted = true;
+                  break;
+               case GeneralName::MatchResult::UnknownType:
+                  failed = issuer_name_constraint_critical;
+                  permitted = true;
+                  break;
+               default:
+                  break;
+               }
+            }
+
+         for(auto c: m_name_constraints.excluded())
+            {
+            switch(c.base().matches(*cert_path.at(j)))
+               {
+               case GeneralName::MatchResult::All:
+               case GeneralName::MatchResult::Some:
+                  failed = true;
+                  break;
+               case GeneralName::MatchResult::UnknownType:
+                  failed = issuer_name_constraint_critical;
+                  break;
+               default:
+                  break;
+               }
+            }
+
+         if(failed || !permitted)
+            {
+            cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+            }
+         }
+      }
+   }
+
+namespace {
+
+/*
+* A policy specifier
+*/
+class Policy_Information final : public ASN1_Object
+   {
+   public:
+      Policy_Information() = default;
+      explicit Policy_Information(const OID& oid) : m_oid(oid) {}
+
+      const OID& oid() const { return m_oid; }
+
+      void encode_into(DER_Encoder& codec) const override
+         {
+         codec.start_cons(SEQUENCE)
+            .encode(m_oid)
+            .end_cons();
+         }
+
+      void decode_from(BER_Decoder& codec) override
+         {
+         codec.start_cons(SEQUENCE)
+            .decode(m_oid)
+            .discard_remaining()
+            .end_cons();
+         }
+
+   private:
+      OID m_oid;
+   };
+
+}
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Certificate_Policies::encode_inner() const
+   {
+   std::vector<Policy_Information> policies;
+
+   for(size_t i = 0; i != m_oids.size(); ++i)
+      policies.push_back(Policy_Information(m_oids[i]));
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .encode_list(policies)
+      .end_cons();
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void Certificate_Policies::decode_inner(const std::vector<uint8_t>& in)
+   {
+   std::vector<Policy_Information> policies;
+
+   BER_Decoder(in).decode_list(policies);
+   m_oids.clear();
+   for(size_t i = 0; i != policies.size(); ++i)
+      m_oids.push_back(policies[i].oid());
+   }
+
+/*
+* Return a textual representation
+*/
+void Certificate_Policies::contents_to(Data_Store& info, Data_Store&) const
+   {
+   for(size_t i = 0; i != m_oids.size(); ++i)
+      info.add("X509v3.CertificatePolicies", m_oids[i].to_string());
+   }
+
+void Certificate_Policies::validate(
+   const X509_Certificate& /*subject*/,
+   const X509_Certificate& /*issuer*/,
+   const std::vector<std::shared_ptr<const X509_Certificate>>& /*cert_path*/,
+   std::vector<std::set<Certificate_Status_Code>>& cert_status,
+   size_t pos)
+   {
+   std::set<OID> oid_set(m_oids.begin(), m_oids.end());
+   if(oid_set.size() != m_oids.size())
+      {
+      cert_status.at(pos).insert(Certificate_Status_Code::DUPLICATE_CERT_POLICY);
+      }
+   }
+
+std::vector<uint8_t> Authority_Information_Access::encode_inner() const
+   {
+   ASN1_String url(m_ocsp_responder, IA5_STRING);
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+      .start_cons(SEQUENCE)
+      .encode(OID::from_string("PKIX.OCSP"))
+      .add_object(ASN1_Tag(6), CONTEXT_SPECIFIC, url.value())
+      .end_cons()
+      .end_cons();
+   return output;
+   }
+
+void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder ber = BER_Decoder(in).start_cons(SEQUENCE);
+
+   while(ber.more_items())
+      {
+      OID oid;
+
+      BER_Decoder info = ber.start_cons(SEQUENCE);
+
+      info.decode(oid);
+
+      if(oid == OID::from_string("PKIX.OCSP"))
+         {
+         BER_Object name = info.get_next_object();
+
+         if(name.is_a(6, CONTEXT_SPECIFIC))
+            {
+            m_ocsp_responder = ASN1::to_string(name);
+            }
+
+         }
+      if(oid == OID::from_string("PKIX.CertificateAuthorityIssuers"))
+         {
+         BER_Object name = info.get_next_object();
+
+         if(name.is_a(6, CONTEXT_SPECIFIC))
+            {
+            m_ca_issuers.push_back(ASN1::to_string(name));
+            }
+         }
+      }
+   }
+
+void Authority_Information_Access::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   if(!m_ocsp_responder.empty())
+      subject.add("OCSP.responder", m_ocsp_responder);
+   for(const std::string& ca_issuer : m_ca_issuers)
+      subject.add("PKIX.CertificateAuthorityIssuers", ca_issuer);
+   }
+
+/*
+* Checked accessor for the crl_number member
+*/
+size_t CRL_Number::get_crl_number() const
+   {
+   if(!m_has_value)
+      throw Invalid_State("CRL_Number::get_crl_number: Not set");
+   return m_crl_number;
+   }
+
+/*
+* Copy a CRL_Number extension
+*/
+CRL_Number* CRL_Number::copy() const
+   {
+   if(!m_has_value)
+      throw Invalid_State("CRL_Number::copy: Not set");
+   return new CRL_Number(m_crl_number);
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_Number::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(m_crl_number);
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void CRL_Number::decode_inner(const std::vector<uint8_t>& in)
+   {
+   BER_Decoder(in).decode(m_crl_number);
+   m_has_value = true;
+   }
+
+/*
+* Return a textual representation
+*/
+void CRL_Number::contents_to(Data_Store& info, Data_Store&) const
+   {
+   info.add("X509v3.CRLNumber", static_cast<uint32_t>(m_crl_number));
+   }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_ReasonCode::encode_inner() const
+   {
+   std::vector<uint8_t> output;
+   DER_Encoder(output).encode(static_cast<size_t>(m_reason), ENUMERATED, UNIVERSAL);
+   return output;
+   }
+
+/*
+* Decode the extension
+*/
+void CRL_ReasonCode::decode_inner(const std::vector<uint8_t>& in)
+   {
+   size_t reason_code = 0;
+   BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+   m_reason = static_cast<CRL_Code>(reason_code);
+   }
+
+/*
+* Return a textual representation
+*/
+void CRL_ReasonCode::contents_to(Data_Store& info, Data_Store&) const
+   {
+   info.add("X509v3.CRLReasonCode", m_reason);
+   }
+
+std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const
+   {
+   throw Not_Implemented("CRL_Distribution_Points encoding");
+   }
+
+void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf)
+   {
+   BER_Decoder(buf)
+      .decode_list(m_distribution_points)
+      .verify_end();
+
+   std::stringstream ss;
+
+   for(size_t i = 0; i != m_distribution_points.size(); ++i)
+      {
+      auto contents = m_distribution_points[i].point().contents();
+
+      for(const auto& pair : contents)
+         {
+         ss << pair.first << ": " << pair.second << " ";
+         }
+      }
+
+   m_crl_distribution_urls.push_back(ss.str());
+   }
+
+void CRL_Distribution_Points::contents_to(Data_Store& subject, Data_Store&) const
+   {
+   for(const std::string& crl_url : m_crl_distribution_urls)
+      subject.add("CRL.DistributionPoint", crl_url);
+   }
+
+void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const
+   {
+   throw Not_Implemented("CRL_Distribution_Points encoding");
+   }
+
+void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber)
+   {
+   ber.start_cons(SEQUENCE)
+      .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC)
+        .decode_optional_implicit(m_point, ASN1_Tag(0),
+                                  ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED),
+                                  SEQUENCE, CONSTRUCTED)
+      .end_cons().end_cons();
+   }
+
+std::vector<uint8_t> CRL_Issuing_Distribution_Point::encode_inner() const
+   {
+   throw Not_Implemented("CRL_Issuing_Distribution_Point encoding");
+   }
+
+void CRL_Issuing_Distribution_Point::decode_inner(const std::vector<uint8_t>& buf)
+   {
+   BER_Decoder(buf).decode(m_distribution_point).verify_end();
+   }
+
+void CRL_Issuing_Distribution_Point::contents_to(Data_Store& info, Data_Store&) const
+   {
+   auto contents = m_distribution_point.point().contents();
+   std::stringstream ss;
+
+   for(const auto& pair : contents)
+      {
+      ss << pair.first << ": " << pair.second << " ";
+      }
+
+   info.add("X509v3.CRLIssuingDistributionPoint", ss.str());
+   }
+
+std::vector<uint8_t> Unknown_Extension::encode_inner() const
+   {
+   return m_bytes;
+   }
+
+void Unknown_Extension::decode_inner(const std::vector<uint8_t>& bytes)
+   {
+   // Just treat as an opaque blob at this level
+   m_bytes = bytes;
+   }
+
+void Unknown_Extension::contents_to(Data_Store&, Data_Store&) const
+   {
+   // No information store
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/x509/x509_ext.h b/third_party/botan/src/lib/x509/x509_ext.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_ext.h
@@ -0,0 +1,799 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_EXTENSIONS_H_
+#define BOTAN_X509_EXTENSIONS_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <botan/asn1_alt_name.h>
+#include <botan/cert_status.h>
+#include <botan/name_constraint.h>
+#include <botan/key_constraint.h>
+#include <botan/crl_ent.h>
+#include <set>
+
+namespace Botan {
+
+class Data_Store;
+class X509_Certificate;
+
+/**
+* X.509 Certificate Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Extension
+   {
+   public:
+      /**
+      * @return OID representing this extension
+      */
+      virtual OID oid_of() const = 0;
+
+      /*
+      * @return specific OID name
+      * If possible OIDS table should match oid_name to OIDS, ie
+      * OID::from_string(ext->oid_name()) == ext->oid_of()
+      * Should return empty string if OID is not known
+      */
+      virtual std::string oid_name() const = 0;
+
+      /**
+      * Make a copy of this extension
+      * @return copy of this
+      */
+      virtual Certificate_Extension* copy() const = 0;
+
+      /*
+      * Add the contents of this extension into the information
+      * for the subject and/or issuer, as necessary.
+      * @param subject the subject info
+      * @param issuer the issuer info
+      */
+      virtual void contents_to(Data_Store& subject,
+                               Data_Store& issuer) const = 0;
+
+      /*
+      * Callback visited during path validation.
+      *
+      * An extension can implement this callback to inspect
+      * the path during path validation.
+      *
+      * If an error occurs during validation of this extension,
+      * an appropriate status code shall be added to cert_status.
+      *
+      * @param subject Subject certificate that contains this extension
+      * @param issuer Issuer certificate
+      * @param status Certificate validation status codes for subject certificate
+      * @param cert_path Certificate path which is currently validated
+      * @param pos Position of subject certificate in cert_path
+      */
+      virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+            const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+            std::vector<std::set<Certificate_Status_Code>>& cert_status,
+            size_t pos);
+
+      virtual ~Certificate_Extension() = default;
+   protected:
+      friend class Extensions;
+      virtual bool should_encode() const { return true; }
+      virtual std::vector<uint8_t> encode_inner() const = 0;
+      virtual void decode_inner(const std::vector<uint8_t>&) = 0;
+   };
+
+/**
+* X.509 Certificate Extension List
+*/
+class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
+   {
+   public:
+      /**
+      * Look up an object in the extensions, based on OID Returns
+      * nullptr if not set, if the extension was either absent or not
+      * handled. The pointer returned is owned by the Extensions
+      * object.
+      * This would be better with an optional<T> return value
+      */
+      const Certificate_Extension* get_extension_object(const OID& oid) const;
+
+      template<typename T>
+      const T* get_extension_object_as(const OID& oid = T::static_oid()) const
+         {
+         if(const Certificate_Extension* extn = get_extension_object(oid))
+            {
+            // Unknown_Extension oid_name is empty
+            if(extn->oid_name().empty())
+               {
+               return nullptr;
+               }
+            else if(const T* extn_as_T = dynamic_cast<const T*>(extn))
+               {
+               return extn_as_T;
+               }
+            else
+               {
+               throw Decoding_Error("Exception::get_extension_object_as dynamic_cast failed");
+               }
+            }
+
+         return nullptr;
+         }
+
+      /**
+      * Return the set of extensions in the order they appeared in the certificate
+      * (or as they were added, if constructed)
+      */
+      const std::vector<OID>& get_extension_oids() const
+         {
+         return m_extension_oids;
+         }
+
+      /**
+      * Return true if an extension was set
+      */
+      bool extension_set(const OID& oid) const;
+
+      /**
+      * Return true if an extesion was set and marked critical
+      */
+      bool critical_extension_set(const OID& oid) const;
+
+      /**
+      * Return the raw bytes of the extension
+      * Will throw if OID was not set as an extension.
+      */
+      std::vector<uint8_t> get_extension_bits(const OID& oid) const;
+
+      void encode_into(class DER_Encoder&) const override;
+      void decode_from(class BER_Decoder&) override;
+      void contents_to(Data_Store&, Data_Store&) const;
+
+      /**
+      * Adds a new extension to the list.
+      * @param extn pointer to the certificate extension (Extensions takes ownership)
+      * @param critical whether this extension should be marked as critical
+      * @throw Invalid_Argument if the extension is already present in the list
+      */
+      void add(Certificate_Extension* extn, bool critical = false);
+
+      /**
+      * Adds a new extension to the list unless it already exists. If the extension
+      * already exists within the Extensions object, the extn pointer will be deleted.
+      *
+      * @param extn pointer to the certificate extension (Extensions takes ownership)
+      * @param critical whether this extension should be marked as critical
+      * @return true if the object was added false if the extension was already used
+      */
+      bool add_new(Certificate_Extension* extn, bool critical = false);
+
+      /**
+      * Adds an extension to the list or replaces it.
+      * @param extn the certificate extension
+      * @param critical whether this extension should be marked as critical
+      */
+      void replace(Certificate_Extension* extn, bool critical = false);
+
+      /**
+      * Remove an extension from the list. Returns true if the
+      * extension had been set, false otherwise.
+      */
+      bool remove(const OID& oid);
+
+      /**
+      * Searches for an extension by OID and returns the result.
+      * Only the known extensions types declared in this header
+      * are searched for by this function.
+      * @return Copy of extension with oid, nullptr if not found.
+      * Can avoid creating a copy by using get_extension_object function
+      */
+      std::unique_ptr<Certificate_Extension> get(const OID& oid) const;
+
+      /**
+      * Searches for an extension by OID and returns the result decoding
+      * it to some arbitrary extension type chosen by the application.
+      *
+      * Only the unknown extensions, that is, extensions types that
+      * are not declared in this header, are searched for by this
+      * function.
+      *
+      * @return Pointer to new extension with oid, nullptr if not found.
+      */
+      template<typename T>
+      std::unique_ptr<T> get_raw(const OID& oid) const
+         {
+         auto extn_info = m_extension_info.find(oid);
+
+         if(extn_info != m_extension_info.end())
+            {
+            // Unknown_Extension oid_name is empty
+            if(extn_info->second.obj().oid_name() == "")
+               {
+               std::unique_ptr<T> ext(new T);
+               ext->decode_inner(extn_info->second.bits());
+               return ext;
+               }
+            }
+         return nullptr;
+         }
+
+      /**
+      * Returns a copy of the list of extensions together with the corresponding
+      * criticality flag. All extensions are encoded as some object, falling back
+      * to Unknown_Extension class which simply allows reading the bytes as well
+      * as the criticality flag.
+      */
+      std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
+
+      /**
+      * Returns the list of extensions as raw, encoded bytes
+      * together with the corresponding criticality flag.
+      * Contains all extensions, including any extensions encoded as Unknown_Extension
+      */
+      std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const;
+
+      Extensions() {}
+
+      Extensions(const Extensions&) = default;
+      Extensions& operator=(const Extensions&) = default;
+
+      Extensions(Extensions&&) = default;
+      Extensions& operator=(Extensions&&) = default;
+
+   private:
+      static std::unique_ptr<Certificate_Extension>
+         create_extn_obj(const OID& oid,
+                         bool critical,
+                         const std::vector<uint8_t>& body);
+
+      class Extensions_Info
+         {
+         public:
+            Extensions_Info(bool critical,
+                            Certificate_Extension* ext) :
+               m_obj(ext),
+               m_bits(m_obj->encode_inner()),
+               m_critical(critical)
+               {
+               }
+
+            Extensions_Info(bool critical,
+                            const std::vector<uint8_t>& encoding,
+                            Certificate_Extension* ext) :
+               m_obj(ext),
+               m_bits(encoding),
+               m_critical(critical)
+               {
+               }
+
+            bool is_critical() const { return m_critical; }
+            const std::vector<uint8_t>& bits() const { return m_bits; }
+            const Certificate_Extension& obj() const
+               {
+               BOTAN_ASSERT_NONNULL(m_obj.get());
+               return *m_obj.get();
+               }
+
+         private:
+            std::shared_ptr<Certificate_Extension> m_obj;
+            std::vector<uint8_t> m_bits;
+            bool m_critical = false;
+         };
+
+      std::vector<OID> m_extension_oids;
+      std::map<OID, Extensions_Info> m_extension_info;
+   };
+
+namespace Cert_Extension {
+
+static const size_t NO_CERT_PATH_LIMIT = 0xFFFFFFF0;
+
+/**
+* Basic Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Basic_Constraints final : public Certificate_Extension
+   {
+   public:
+      Basic_Constraints* copy() const override
+         { return new Basic_Constraints(m_is_ca, m_path_limit); }
+
+      Basic_Constraints(bool ca = false, size_t limit = 0) :
+         m_is_ca(ca), m_path_limit(limit) {}
+
+      bool get_is_ca() const { return m_is_ca; }
+      size_t get_path_limit() const;
+
+      static OID static_oid() { return OID("2.5.29.19"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override
+         { return "X509v3.BasicConstraints"; }
+
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      bool m_is_ca;
+      size_t m_path_limit;
+   };
+
+/**
+* Key Usage Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
+   {
+   public:
+      Key_Usage* copy() const override { return new Key_Usage(m_constraints); }
+
+      explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
+
+      Key_Constraints get_constraints() const { return m_constraints; }
+
+      static OID static_oid() { return OID("2.5.29.15"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override { return "X509v3.KeyUsage"; }
+
+      bool should_encode() const override
+         { return (m_constraints != NO_CONSTRAINTS); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      Key_Constraints m_constraints;
+   };
+
+/**
+* Subject Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Subject_Key_ID final : public Certificate_Extension
+   {
+   public:
+      Subject_Key_ID() = default;
+
+      explicit Subject_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+      Subject_Key_ID(const std::vector<uint8_t>& public_key,
+                     const std::string& hash_fn);
+
+      Subject_Key_ID* copy() const override
+         { return new Subject_Key_ID(m_key_id); }
+
+      const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+      static OID static_oid() { return OID("2.5.29.14"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+
+      std::string oid_name() const override
+         { return "X509v3.SubjectKeyIdentifier"; }
+
+      bool should_encode() const override { return (m_key_id.size() > 0); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::vector<uint8_t> m_key_id;
+   };
+
+/**
+* Authority Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extension
+   {
+   public:
+      Authority_Key_ID* copy() const override
+         { return new Authority_Key_ID(m_key_id); }
+
+      Authority_Key_ID() = default;
+      explicit Authority_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+      const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+      static OID static_oid() { return OID("2.5.29.35"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override
+         { return "X509v3.AuthorityKeyIdentifier"; }
+
+      bool should_encode() const override { return (m_key_id.size() > 0); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::vector<uint8_t> m_key_id;
+   };
+
+/**
+* Subject Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,4) Subject_Alternative_Name final : public Certificate_Extension
+   {
+   public:
+      const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+      static OID static_oid() { return OID("2.5.29.17"); }
+      OID oid_of() const override { return static_oid(); }
+
+      Subject_Alternative_Name* copy() const override
+         { return new Subject_Alternative_Name(get_alt_name()); }
+
+      explicit Subject_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+         m_alt_name(name) {}
+
+   private:
+      std::string oid_name() const override { return "X509v3.SubjectAlternativeName"; }
+
+      bool should_encode() const override { return m_alt_name.has_items(); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      AlternativeName m_alt_name;
+   };
+
+/**
+* Issuer Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Certificate_Extension
+   {
+   public:
+      const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+      static OID static_oid() { return OID("2.5.29.18"); }
+      OID oid_of() const override { return static_oid(); }
+
+      Issuer_Alternative_Name* copy() const override
+         { return new Issuer_Alternative_Name(get_alt_name()); }
+
+      explicit Issuer_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+         m_alt_name(name) {}
+
+   private:
+      std::string oid_name() const override { return "X509v3.IssuerAlternativeName"; }
+
+      bool should_encode() const override { return m_alt_name.has_items(); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      AlternativeName m_alt_name;
+   };
+
+/**
+* Extended Key Usage Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extension
+   {
+   public:
+      Extended_Key_Usage* copy() const override
+         { return new Extended_Key_Usage(m_oids); }
+
+      Extended_Key_Usage() = default;
+      explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
+
+      const std::vector<OID>& get_oids() const { return m_oids; }
+
+      static OID static_oid() { return OID("2.5.29.37"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override { return "X509v3.ExtendedKeyUsage"; }
+
+      bool should_encode() const override { return (m_oids.size() > 0); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::vector<OID> m_oids;
+   };
+
+/**
+* Name Constraints
+*/
+class BOTAN_PUBLIC_API(2,0) Name_Constraints final : public Certificate_Extension
+   {
+   public:
+      Name_Constraints* copy() const override
+         { return new Name_Constraints(m_name_constraints); }
+
+      Name_Constraints() = default;
+      Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
+
+      void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+            const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+            std::vector<std::set<Certificate_Status_Code>>& cert_status,
+            size_t pos) override;
+
+      const NameConstraints& get_name_constraints() const { return m_name_constraints; }
+
+      static OID static_oid() { return OID("2.5.29.30"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override
+         { return "X509v3.NameConstraints"; }
+
+      bool should_encode() const override { return true; }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      NameConstraints m_name_constraints;
+   };
+
+/**
+* Certificate Policies Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Policies final : public Certificate_Extension
+   {
+   public:
+      Certificate_Policies* copy() const override
+         { return new Certificate_Policies(m_oids); }
+
+      Certificate_Policies() = default;
+      explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
+
+      BOTAN_DEPRECATED("Use get_policy_oids")
+      std::vector<OID> get_oids() const { return m_oids; }
+
+      const std::vector<OID>& get_policy_oids() const { return m_oids; }
+
+      static OID static_oid() { return OID("2.5.29.32"); }
+      OID oid_of() const override { return static_oid(); }
+
+      void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+            const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+            std::vector<std::set<Certificate_Status_Code>>& cert_status,
+            size_t pos) override;
+   private:
+      std::string oid_name() const override
+         { return "X509v3.CertificatePolicies"; }
+
+      bool should_encode() const override { return (m_oids.size() > 0); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::vector<OID> m_oids;
+   };
+
+/**
+* Authority Information Access Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Information_Access final : public Certificate_Extension
+   {
+   public:
+      Authority_Information_Access* copy() const override
+         { return new Authority_Information_Access(m_ocsp_responder, m_ca_issuers); }
+
+      Authority_Information_Access() = default;
+
+      explicit Authority_Information_Access(const std::string& ocsp, const std::vector<std::string>& ca_issuers = std::vector<std::string>()) :
+         m_ocsp_responder(ocsp), m_ca_issuers(ca_issuers) {}
+
+      std::string ocsp_responder() const { return m_ocsp_responder; }
+
+      static OID static_oid() { return OID("1.3.6.1.5.5.7.1.1"); }
+      OID oid_of() const override { return static_oid(); }
+      const std::vector<std::string> ca_issuers() const { return m_ca_issuers; }
+
+   private:
+      std::string oid_name() const override
+         { return "PKIX.AuthorityInformationAccess"; }
+
+      bool should_encode() const override { return (!m_ocsp_responder.empty()); }
+
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::string m_ocsp_responder;
+      std::vector<std::string> m_ca_issuers;
+   };
+
+/**
+* CRL Number Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Number final : public Certificate_Extension
+   {
+   public:
+      CRL_Number* copy() const override;
+
+      CRL_Number() : m_has_value(false), m_crl_number(0) {}
+      CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {}
+
+      size_t get_crl_number() const;
+
+      static OID static_oid() { return OID("2.5.29.20"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override { return "X509v3.CRLNumber"; }
+
+      bool should_encode() const override { return m_has_value; }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      bool m_has_value;
+      size_t m_crl_number;
+   };
+
+/**
+* CRL Entry Reason Code Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_ReasonCode final : public Certificate_Extension
+   {
+   public:
+      CRL_ReasonCode* copy() const override
+         { return new CRL_ReasonCode(m_reason); }
+
+      explicit CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : m_reason(r) {}
+
+      CRL_Code get_reason() const { return m_reason; }
+
+      static OID static_oid() { return OID("2.5.29.21"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override { return "X509v3.ReasonCode"; }
+
+      bool should_encode() const override { return (m_reason != UNSPECIFIED); }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      CRL_Code m_reason;
+   };
+
+/**
+* CRL Distribution Points Extension
+* todo enforce restrictions from RFC 5280 4.2.1.13
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_Extension
+   {
+   public:
+      class BOTAN_PUBLIC_API(2,0) Distribution_Point final : public ASN1_Object
+         {
+         public:
+            void encode_into(class DER_Encoder&) const override;
+            void decode_from(class BER_Decoder&) override;
+
+            const AlternativeName& point() const { return m_point; }
+         private:
+            AlternativeName m_point;
+         };
+
+      CRL_Distribution_Points* copy() const override
+         { return new CRL_Distribution_Points(m_distribution_points); }
+
+      CRL_Distribution_Points() = default;
+
+      explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) :
+         m_distribution_points(points) {}
+
+      const std::vector<Distribution_Point>& distribution_points() const
+         { return m_distribution_points; }
+
+      const std::vector<std::string>& crl_distribution_urls() const
+         { return m_crl_distribution_urls; }
+
+      static OID static_oid() { return OID("2.5.29.31"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override
+         { return "X509v3.CRLDistributionPoints"; }
+
+      bool should_encode() const override
+         { return !m_distribution_points.empty(); }
+
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      std::vector<Distribution_Point> m_distribution_points;
+      std::vector<std::string> m_crl_distribution_urls;
+   };
+
+/**
+* CRL Issuing Distribution Point Extension
+* todo enforce restrictions from RFC 5280 5.2.5
+*/
+class CRL_Issuing_Distribution_Point final : public Certificate_Extension
+   {
+   public:
+      CRL_Issuing_Distribution_Point() = default;
+
+      explicit CRL_Issuing_Distribution_Point(const CRL_Distribution_Points::Distribution_Point& distribution_point) :
+         m_distribution_point(distribution_point) {}
+
+      CRL_Issuing_Distribution_Point* copy() const override
+         { return new CRL_Issuing_Distribution_Point(m_distribution_point); }
+
+      const AlternativeName& get_point() const
+         { return m_distribution_point.point(); }
+
+      static OID static_oid() { return OID("2.5.29.28"); }
+      OID oid_of() const override { return static_oid(); }
+
+   private:
+      std::string oid_name() const override
+         { return "X509v3.CRLIssuingDistributionPoint"; }
+
+      bool should_encode() const override { return true; }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      CRL_Distribution_Points::Distribution_Point m_distribution_point;
+   };
+
+/**
+* An unknown X.509 extension
+* Will add a failure to the path validation result, if critical
+*/
+class BOTAN_PUBLIC_API(2,4) Unknown_Extension final : public Certificate_Extension
+   {
+   public:
+      Unknown_Extension(const OID& oid, bool critical) :
+         m_oid(oid), m_critical(critical) {}
+
+      Unknown_Extension* copy() const override
+         { return new Unknown_Extension(m_oid, m_critical); }
+
+      /**
+      * Return the OID of this unknown extension
+      */
+      OID oid_of() const override
+         { return m_oid; }
+
+      //static_oid not defined for Unknown_Extension
+
+      /**
+      * Return the extension contents
+      */
+      const std::vector<uint8_t>& extension_contents() const { return m_bytes; }
+
+      /**
+      * Return if this extension was marked critical
+      */
+      bool is_critical_extension() const { return m_critical; }
+
+      void validate(const X509_Certificate&, const X509_Certificate&,
+            const std::vector<std::shared_ptr<const X509_Certificate>>&,
+            std::vector<std::set<Certificate_Status_Code>>& cert_status,
+            size_t pos) override
+         {
+         if(m_critical)
+            {
+            cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+            }
+         }
+
+   private:
+      std::string oid_name() const override { return ""; }
+
+      bool should_encode() const override { return true; }
+      std::vector<uint8_t> encode_inner() const override;
+      void decode_inner(const std::vector<uint8_t>&) override;
+      void contents_to(Data_Store&, Data_Store&) const override;
+
+      OID m_oid;
+      bool m_critical;
+      std::vector<uint8_t> m_bytes;
+   };
+
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509_obj.cpp b/third_party/botan/src/lib/x509/x509_obj.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_obj.cpp
@@ -0,0 +1,424 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_obj.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/pem.h>
+#include <botan/emsa.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+struct Pss_params
+   {
+   AlgorithmIdentifier hash_algo;
+   AlgorithmIdentifier mask_gen_algo;
+   AlgorithmIdentifier mask_gen_hash;  // redundant: decoded mask_gen_algo.parameters
+   size_t salt_len;
+   size_t trailer_field;
+   };
+
+Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params)
+   {
+   const AlgorithmIdentifier default_hash("SHA-160", AlgorithmIdentifier::USE_NULL_PARAM);
+   const AlgorithmIdentifier default_mgf("MGF1", default_hash.BER_encode());
+
+   Pss_params pss_parameter;
+   BER_Decoder(encoded_pss_params)
+      .start_cons(SEQUENCE)
+         .decode_optional(pss_parameter.hash_algo, ASN1_Tag(0), PRIVATE, default_hash)
+         .decode_optional(pss_parameter.mask_gen_algo, ASN1_Tag(1), PRIVATE, default_mgf)
+         .decode_optional(pss_parameter.salt_len, ASN1_Tag(2), PRIVATE, size_t(20))
+         .decode_optional(pss_parameter.trailer_field, ASN1_Tag(3), PRIVATE, size_t(1))
+      .end_cons();
+
+   BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash);
+
+   return pss_parameter;
+   }
+}
+
+/*
+* Read a PEM or BER X.509 object
+*/
+void X509_Object::load_data(DataSource& in)
+   {
+   try {
+      if(ASN1::maybe_BER(in) && !PEM_Code::matches(in))
+         {
+         BER_Decoder dec(in);
+         decode_from(dec);
+         }
+      else
+         {
+         std::string got_label;
+         DataSource_Memory ber(PEM_Code::decode(in, got_label));
+
+         if(got_label != PEM_label())
+            {
+            bool is_alternate = false;
+            for(std::string alt_label : alternate_PEM_labels())
+               {
+               if(got_label == alt_label)
+                  {
+                  is_alternate = true;
+                  break;
+                  }
+               }
+
+            if(!is_alternate)
+               throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label);
+            }
+
+         BER_Decoder dec(ber);
+         decode_from(dec);
+         }
+      }
+   catch(Decoding_Error& e)
+      {
+      throw Decoding_Error(PEM_label() + " decoding", e);
+      }
+   }
+
+
+void X509_Object::encode_into(DER_Encoder& to) const
+   {
+   to.start_cons(SEQUENCE)
+         .start_cons(SEQUENCE)
+            .raw_bytes(signed_body())
+         .end_cons()
+         .encode(signature_algorithm())
+         .encode(signature(), BIT_STRING)
+      .end_cons();
+   }
+
+/*
+* Read a BER encoded X.509 object
+*/
+void X509_Object::decode_from(BER_Decoder& from)
+   {
+   from.start_cons(SEQUENCE)
+         .start_cons(SEQUENCE)
+            .raw_bytes(m_tbs_bits)
+         .end_cons()
+         .decode(m_sig_algo)
+         .decode(m_sig, BIT_STRING)
+      .end_cons();
+
+   force_decode();
+   }
+
+/*
+* Return a PEM encoded X.509 object
+*/
+std::string X509_Object::PEM_encode() const
+   {
+   return PEM_Code::encode(BER_encode(), PEM_label());
+   }
+
+/*
+* Return the TBS data
+*/
+std::vector<uint8_t> X509_Object::tbs_data() const
+   {
+   return ASN1::put_in_sequence(m_tbs_bits);
+   }
+
+/*
+* Return the hash used in generating the signature
+*/
+std::string X509_Object::hash_used_for_signature() const
+   {
+   const OID& oid = m_sig_algo.get_oid();
+   const std::vector<std::string> sig_info = split_on(oid.to_formatted_string(), '/');
+
+   if(sig_info.size() == 1 && sig_info[0] == "Ed25519")
+      return "SHA-512";
+   else if(sig_info.size() != 2)
+      throw Internal_Error("Invalid name format found for " + oid.to_string());
+
+   if(sig_info[1] == "EMSA4")
+      {
+      const OID hash_oid = decode_pss_params(signature_algorithm().get_parameters()).hash_algo.get_oid();
+      return hash_oid.to_formatted_string();
+      }
+   else
+      {
+      const std::vector<std::string> pad_and_hash =
+         parse_algorithm_name(sig_info[1]);
+
+      if(pad_and_hash.size() != 2)
+         {
+         throw Internal_Error("Invalid name format " + sig_info[1]);
+         }
+
+      return pad_and_hash[1];
+      }
+   }
+
+/*
+* Check the signature on an object
+*/
+bool X509_Object::check_signature(const Public_Key* pub_key) const
+   {
+   if(!pub_key)
+      throw Invalid_Argument("No key provided for " + PEM_label() + " signature check");
+   std::unique_ptr<const Public_Key> key(pub_key);
+   return check_signature(*key);
+   }
+
+bool X509_Object::check_signature(const Public_Key& pub_key) const
+   {
+   const Certificate_Status_Code code = verify_signature(pub_key);
+   return (code == Certificate_Status_Code::VERIFIED);
+   }
+
+Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key) const
+   {
+   const std::vector<std::string> sig_info =
+      split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+   if(sig_info.size() < 1 || sig_info.size() > 2 || sig_info[0] != pub_key.algo_name())
+      return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+   const std::string pub_key_algo = sig_info[0];
+   std::string padding;
+   if(sig_info.size() == 2)
+      padding = sig_info[1];
+   else if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+      padding = "Pure";
+   else
+      return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+   const Signature_Format format = pub_key.default_x509_signature_format();
+
+   if(padding == "EMSA4")
+      {
+      // "MUST contain RSASSA-PSS-params"
+      if(signature_algorithm().get_parameters().empty())
+         {
+         return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+         }
+
+      Pss_params pss_parameter = decode_pss_params(signature_algorithm().get_parameters());
+
+      // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+      const std::string hash_algo = pss_parameter.hash_algo.get_oid().to_formatted_string();
+      if(hash_algo != "SHA-160" &&
+         hash_algo != "SHA-224" &&
+         hash_algo != "SHA-256" &&
+         hash_algo != "SHA-384" &&
+         hash_algo != "SHA-512")
+         {
+         return Certificate_Status_Code::UNTRUSTED_HASH;
+         }
+
+      const std::string mgf_algo = pss_parameter.mask_gen_algo.get_oid().to_formatted_string();
+      if(mgf_algo != "MGF1")
+         {
+         return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+         }
+
+      // For MGF1, it is strongly RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm
+      // Must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+      if(pss_parameter.mask_gen_hash.get_oid() != pss_parameter.hash_algo.get_oid())
+         {
+         return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+         }
+
+      if(pss_parameter.trailer_field != 1)
+         {
+         return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+         }
+
+      padding += "(" + hash_algo + "," + mgf_algo + "," + std::to_string(pss_parameter.salt_len) + ")";
+      }
+   else
+      {
+      /*
+      * For all other signature types the signature parameters should
+      * be either NULL or empty. In theory there is some distinction between
+      * these but in practice they seem to be used somewhat interchangeably.
+      *
+      * The various RFCs all have prescriptions of what is allowed:
+      * RSA - NULL (RFC 3279)
+      * DSA - empty (RFC 3279)
+      * ECDSA - empty (RFC 3279)
+      * GOST - empty (RFC 4491)
+      * Ed25519 - empty (RFC 8410)
+      * XMSS - empty (draft-vangeest-x509-hash-sigs)
+      *
+      * But in practice we find RSA with empty and ECDSA will NULL all
+      * over the place so it's not really possible to enforce. For Ed25519
+      * and XMSS because they are new we attempt to enforce.
+      */
+      if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+         {
+         if(!signature_algorithm().parameters_are_empty())
+            {
+            return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+            }
+         }
+      else
+         {
+         if(!signature_algorithm().parameters_are_null_or_empty())
+            {
+            return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+            }
+         }
+      }
+
+   try
+      {
+      PK_Verifier verifier(pub_key, padding, format);
+      const bool valid = verifier.verify_message(tbs_data(), signature());
+
+      if(valid)
+         return Certificate_Status_Code::VERIFIED;
+      else
+         return Certificate_Status_Code::SIGNATURE_ERROR;
+      }
+   catch(Algorithm_Not_Found&)
+      {
+      return Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN;
+      }
+   catch(...)
+      {
+      // This shouldn't happen, fallback to generic signature error
+      return Certificate_Status_Code::SIGNATURE_ERROR;
+      }
+   }
+
+/*
+* Apply the X.509 SIGNED macro
+*/
+std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer,
+                                            RandomNumberGenerator& rng,
+                                            const AlgorithmIdentifier& algo,
+                                            const secure_vector<uint8_t>& tbs_bits)
+   {
+   const std::vector<uint8_t> signature = signer->sign_message(tbs_bits, rng);
+
+   std::vector<uint8_t> output;
+   DER_Encoder(output)
+      .start_cons(SEQUENCE)
+         .raw_bytes(tbs_bits)
+         .encode(algo)
+         .encode(signature, BIT_STRING)
+      .end_cons();
+
+   return output;
+   }
+
+namespace {
+
+std::string choose_sig_algo(AlgorithmIdentifier& sig_algo,
+                            const Private_Key& key,
+                            const std::string& hash_fn,
+                            const std::string& user_specified)
+   {
+   const std::string algo_name = key.algo_name();
+   std::string padding;
+
+   // check algo_name and set default
+   if(algo_name == "RSA")
+      {
+      // set to EMSA3 for compatibility reasons, originally it was the only option
+      padding = "EMSA3(" + hash_fn + ")";
+      }
+   else if(algo_name == "DSA" ||
+           algo_name == "ECDSA" ||
+           algo_name == "ECGDSA" ||
+           algo_name == "ECKCDSA" ||
+           algo_name == "GOST-34.10" ||
+           algo_name == "GOST-34.10-2012-256" ||
+           algo_name == "GOST-34.10-2012-512")
+      {
+      padding = "EMSA1(" + hash_fn + ")";
+      }
+   else if(algo_name == "Ed25519")
+      {
+      padding = "Pure";
+      }
+   else if(algo_name == "XMSS")
+      {
+      if(user_specified.empty() == true)
+         {
+         throw Invalid_Argument("XMSS requires padding scheme");
+         }
+      padding = user_specified;
+      sig_algo = AlgorithmIdentifier(OID::from_string("XMSS"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+      return padding;
+      }
+   else
+      {
+      throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
+      }
+
+   if(user_specified.empty() == false)
+      {
+      padding = user_specified;
+      }
+
+   if(padding != "Pure")
+      {
+      // try to construct an EMSA object from the padding options or default
+      std::unique_ptr<EMSA> emsa;
+      try
+         {
+         emsa.reset(get_emsa(padding));
+         }
+      /*
+      * get_emsa will throw if opts contains {"padding",<valid_padding>} but
+      * <valid_padding> does not specify a hash function.
+      * Omitting it is valid since it needs to be identical to hash_fn.
+      * If it still throws, something happened that we cannot repair here,
+      * e.g. the algorithm/padding combination is not supported.
+      */
+      catch(...)
+         {
+         emsa.reset(get_emsa(padding + "(" + hash_fn + ")"));
+         }
+
+      if(!emsa)
+         {
+         throw Invalid_Argument("Could not parse padding scheme " + padding);
+         }
+
+      sig_algo = emsa->config_for_x509(key, hash_fn);
+      return emsa->name();
+      }
+   else
+      {
+      sig_algo = AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+      return "Pure";
+      }
+   }
+
+}
+
+/*
+* Choose a signing format for the key
+*/
+std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(AlgorithmIdentifier& sig_algo,
+                                                          const Private_Key& key,
+                                                          RandomNumberGenerator& rng,
+                                                          const std::string& hash_fn,
+                                                          const std::string& padding_algo)
+   {
+   const Signature_Format format = key.default_x509_signature_format();
+
+   const std::string emsa = choose_sig_algo(sig_algo, key, hash_fn, padding_algo);
+
+   return std::unique_ptr<PK_Signer>(new PK_Signer(key, rng, emsa, format));
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/x509_obj.h b/third_party/botan/src/lib/x509/x509_obj.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509_obj.h
@@ -0,0 +1,145 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_OBJECT_H_
+#define BOTAN_X509_OBJECT_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/alg_id.h>
+#include <botan/cert_status.h>
+#include <vector>
+
+namespace Botan {
+
+class Public_Key;
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+* This class represents abstract X.509 signed objects as in the X.500
+* SIGNED macro
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Object : public ASN1_Object
+   {
+   public:
+      /**
+      * The underlying data that is to be or was signed
+      * @return data that is or was signed
+      */
+      std::vector<uint8_t> tbs_data() const;
+
+      /**
+      * @return signature on tbs_data()
+      */
+      const std::vector<uint8_t>& signature() const { return m_sig; }
+
+      /**
+      * @return signed body
+      */
+      const std::vector<uint8_t>& signed_body() const { return m_tbs_bits; }
+
+      /**
+      * @return signature algorithm that was used to generate signature
+      */
+      const AlgorithmIdentifier& signature_algorithm() const { return m_sig_algo; }
+
+      /**
+      * @return hash algorithm that was used to generate signature
+      */
+      std::string hash_used_for_signature() const;
+
+      /**
+      * Create a signed X509 object.
+      * @param signer the signer used to sign the object
+      * @param rng the random number generator to use
+      * @param alg_id the algorithm identifier of the signature scheme
+      * @param tbs the tbs bits to be signed
+      * @return signed X509 object
+      */
+      static std::vector<uint8_t> make_signed(class PK_Signer* signer,
+                                              RandomNumberGenerator& rng,
+                                              const AlgorithmIdentifier& alg_id,
+                                              const secure_vector<uint8_t>& tbs);
+
+      /**
+      * Check the signature on this data
+      * @param key the public key purportedly used to sign this data
+      * @return status of the signature - OK if verified or otherwise an indicator of
+      *         the problem preventing verification.
+      */
+      Certificate_Status_Code verify_signature(const Public_Key& key) const;
+
+      /**
+      * Check the signature on this data
+      * @param key the public key purportedly used to sign this data
+      * @return true if the signature is valid, otherwise false
+      */
+      bool check_signature(const Public_Key& key) const;
+
+      /**
+      * Check the signature on this data
+      * @param key the public key purportedly used to sign this data
+      *        the object will be deleted after use (this should have
+      *        been a std::unique_ptr<Public_Key>)
+      * @return true if the signature is valid, otherwise false
+      */
+      bool check_signature(const Public_Key* key) const;
+
+      /**
+      * DER encode an X509_Object
+      * See @ref ASN1_Object::encode_into()
+      */
+      void encode_into(class DER_Encoder& to) const override;
+
+      /**
+      * Decode a BER encoded X509_Object
+      * See @ref ASN1_Object::decode_from()
+      */
+      void decode_from(class BER_Decoder& from) override;
+
+      /**
+      * @return PEM encoding of this
+      */
+      std::string PEM_encode() const;
+
+      X509_Object(const X509_Object&) = default;
+      X509_Object& operator=(const X509_Object&) = default;
+
+      virtual std::string PEM_label() const = 0;
+
+      virtual std::vector<std::string> alternate_PEM_labels() const
+         { return std::vector<std::string>(); }
+
+      virtual ~X509_Object() = default;
+
+      static std::unique_ptr<PK_Signer>
+         choose_sig_format(AlgorithmIdentifier& sig_algo,
+                           const Private_Key& key,
+                           RandomNumberGenerator& rng,
+                           const std::string& hash_fn,
+                           const std::string& padding_algo);
+
+   protected:
+
+      X509_Object() = default;
+
+      /**
+      * Decodes from src as either DER or PEM data, then calls force_decode()
+      */
+      void load_data(DataSource& src);
+
+   private:
+      virtual void force_decode() = 0;
+
+      AlgorithmIdentifier m_sig_algo;
+      std::vector<uint8_t> m_tbs_bits;
+      std::vector<uint8_t> m_sig;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509cert.cpp b/third_party/botan/src/lib/x509/x509cert.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509cert.cpp
@@ -0,0 +1,950 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509cert.h>
+#include <botan/datastor.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_ext.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+struct X509_Certificate_Data
+   {
+   std::vector<uint8_t> m_serial;
+   AlgorithmIdentifier m_sig_algo_inner;
+   X509_DN m_issuer_dn;
+   X509_DN m_subject_dn;
+   std::vector<uint8_t> m_issuer_dn_bits;
+   std::vector<uint8_t> m_subject_dn_bits;
+   X509_Time m_not_before;
+   X509_Time m_not_after;
+   std::vector<uint8_t> m_subject_public_key_bits;
+   std::vector<uint8_t> m_subject_public_key_bits_seq;
+   std::vector<uint8_t> m_subject_public_key_bitstring;
+   std::vector<uint8_t> m_subject_public_key_bitstring_sha1;
+   AlgorithmIdentifier m_subject_public_key_algid;
+
+   std::vector<uint8_t> m_v2_issuer_key_id;
+   std::vector<uint8_t> m_v2_subject_key_id;
+   Extensions m_v3_extensions;
+
+   std::vector<OID> m_extended_key_usage;
+   std::vector<uint8_t> m_authority_key_id;
+   std::vector<uint8_t> m_subject_key_id;
+   std::vector<OID> m_cert_policies;
+
+   std::vector<std::string> m_crl_distribution_points;
+   std::string m_ocsp_responder;
+   std::vector<std::string> m_ca_issuers;
+
+   std::vector<uint8_t> m_issuer_dn_bits_sha256;
+   std::vector<uint8_t> m_subject_dn_bits_sha256;
+
+   std::string m_fingerprint_sha1;
+   std::string m_fingerprint_sha256;
+
+   AlternativeName m_subject_alt_name;
+   AlternativeName m_issuer_alt_name;
+   NameConstraints m_name_constraints;
+
+   Data_Store m_subject_ds;
+   Data_Store m_issuer_ds;
+
+   size_t m_version = 0;
+   size_t m_path_len_constraint = 0;
+   Key_Constraints m_key_constraints = NO_CONSTRAINTS;
+   bool m_self_signed = false;
+   bool m_is_ca_certificate = false;
+   bool m_serial_negative = false;
+   };
+
+std::string X509_Certificate::PEM_label() const
+   {
+   return "CERTIFICATE";
+   }
+
+std::vector<std::string> X509_Certificate::alternate_PEM_labels() const
+   {
+   return { "X509 CERTIFICATE" };
+   }
+
+X509_Certificate::X509_Certificate(DataSource& src)
+   {
+   load_data(src);
+   }
+
+X509_Certificate::X509_Certificate(const std::vector<uint8_t>& vec)
+   {
+   DataSource_Memory src(vec.data(), vec.size());
+   load_data(src);
+   }
+
+X509_Certificate::X509_Certificate(const uint8_t data[], size_t len)
+   {
+   DataSource_Memory src(data, len);
+   load_data(src);
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_Certificate::X509_Certificate(const std::string& fsname)
+   {
+   DataSource_Stream src(fsname, true);
+   load_data(src);
+   }
+#endif
+
+namespace {
+
+std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& obj)
+   {
+   std::unique_ptr<X509_Certificate_Data> data(new X509_Certificate_Data);
+
+   BigInt serial_bn;
+   BER_Object public_key;
+   BER_Object v3_exts_data;
+
+   BER_Decoder(obj.signed_body())
+      .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+      .decode(serial_bn)
+      .decode(data->m_sig_algo_inner)
+      .decode(data->m_issuer_dn)
+      .start_cons(SEQUENCE)
+         .decode(data->m_not_before)
+         .decode(data->m_not_after)
+      .end_cons()
+      .decode(data->m_subject_dn)
+      .get_next(public_key)
+      .decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1)
+      .decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2)
+      .get_next(v3_exts_data)
+      .verify_end("TBSCertificate has extra data after extensions block");
+
+   if(data->m_version > 2)
+      throw Decoding_Error("Unknown X.509 cert version " + std::to_string(data->m_version));
+   if(obj.signature_algorithm() != data->m_sig_algo_inner)
+      throw Decoding_Error("X.509 Certificate had differing algorithm identifers in inner and outer ID fields");
+
+   public_key.assert_is_a(SEQUENCE, CONSTRUCTED, "X.509 certificate public key");
+
+   // crude method to save the serial's sign; will get lost during decoding, otherwise
+   data->m_serial_negative = serial_bn.is_negative();
+
+   // for general sanity convert wire version (0 based) to standards version (v1 .. v3)
+   data->m_version += 1;
+
+   data->m_serial = BigInt::encode(serial_bn);
+   data->m_subject_dn_bits = ASN1::put_in_sequence(data->m_subject_dn.get_bits());
+   data->m_issuer_dn_bits = ASN1::put_in_sequence(data->m_issuer_dn.get_bits());
+
+   // validate_public_key_params(public_key.value);
+   AlgorithmIdentifier public_key_alg_id;
+   BER_Decoder(public_key).decode(public_key_alg_id).discard_remaining();
+
+   const std::vector<std::string> public_key_info =
+      split_on(OIDS::oid2str_or_empty(public_key_alg_id.get_oid()), '/');
+
+   if(!public_key_info.empty() && public_key_info[0] == "RSA")
+      {
+      // RFC4055: If PublicKeyAlgo = PSS or OAEP: limit the use of the public key exclusively to either RSASSA - PSS or RSAES - OAEP
+      if(public_key_info.size() >= 2)
+         {
+         if(public_key_info[1] == "EMSA4")
+            {
+            /*
+            When the RSA private key owner wishes to limit the use of the public
+            key exclusively to RSASSA-PSS, then the id-RSASSA-PSS object
+            identifier MUST be used in the algorithm field within the subject
+            public key information, and, if present, the parameters field MUST
+            contain RSASSA-PSS-params.
+
+            All parameters in the signature structure algorithm identifier MUST
+            match the parameters in the key structure algorithm identifier
+            except the saltLength field. The saltLength field in the signature parameters
+            MUST be greater or equal to that in the key parameters field.
+
+            ToDo: Allow salt length to be greater
+            */
+            if(public_key_alg_id != obj.signature_algorithm())
+               {
+               throw Decoding_Error("Algorithm identifier mismatch");
+               }
+            }
+         }
+      else
+         {
+         // oid = rsaEncryption -> parameters field MUST contain NULL
+         if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.get_oid(), AlgorithmIdentifier::USE_NULL_PARAM))
+            {
+            throw Decoding_Error("RSA algorithm parameters field MUST contain NULL");
+            }
+         }
+      }
+
+   data->m_subject_public_key_bits.assign(public_key.bits(), public_key.bits() + public_key.length());
+
+   data->m_subject_public_key_bits_seq = ASN1::put_in_sequence(data->m_subject_public_key_bits);
+
+   BER_Decoder(data->m_subject_public_key_bits)
+      .decode(data->m_subject_public_key_algid)
+      .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+
+   if(v3_exts_data.is_a(3, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+      {
+      // Path validation will reject a v1/v2 cert with v3 extensions
+      BER_Decoder(v3_exts_data).decode(data->m_v3_extensions).verify_end();
+      }
+   else if(v3_exts_data.is_set())
+      {
+      throw BER_Bad_Tag("Unknown tag in X.509 cert", v3_exts_data.tagging());
+      }
+
+   // Now cache some fields from the extensions
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Key_Usage>())
+      {
+      data->m_key_constraints = ext->get_constraints();
+      /*
+      RFC 5280: When the keyUsage extension appears in a certificate,
+      at least one of the bits MUST be set to 1.
+      */
+      if(data->m_key_constraints == NO_CONSTRAINTS)
+         {
+         throw Decoding_Error("Certificate has invalid encoding for KeyUsage");
+         }
+      }
+   else
+      {
+      data->m_key_constraints = NO_CONSTRAINTS;
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Key_ID>())
+      {
+      data->m_subject_key_id = ext->get_key_id();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+      {
+      data->m_authority_key_id = ext->get_key_id();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Name_Constraints>())
+      {
+      data->m_name_constraints = ext->get_name_constraints();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Basic_Constraints>())
+      {
+      if(ext->get_is_ca() == true)
+         {
+         /*
+         * RFC 5280 section 4.2.1.3 requires that CAs include KeyUsage in all
+         * intermediate CA certificates they issue. Currently we accept it being
+         * missing, as do most other implementations. But it may be worth
+         * removing this entirely, or alternately adding a warning level
+         * validation failure for it.
+         */
+         if(data->m_key_constraints == NO_CONSTRAINTS ||
+            (data->m_key_constraints & KEY_CERT_SIGN))
+            {
+            data->m_is_ca_certificate = true;
+            data->m_path_len_constraint = ext->get_path_limit();
+            }
+         }
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Issuer_Alternative_Name>())
+      {
+      data->m_issuer_alt_name = ext->get_alt_name();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+      {
+      data->m_subject_alt_name = ext->get_alt_name();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
+      {
+      data->m_extended_key_usage = ext->get_oids();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Certificate_Policies>())
+      {
+      data->m_cert_policies = ext->get_policy_oids();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>())
+      {
+      data->m_ocsp_responder = ext->ocsp_responder();
+      data->m_ca_issuers = ext->ca_issuers();
+      }
+
+   if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::CRL_Distribution_Points>())
+      {
+      data->m_crl_distribution_points = ext->crl_distribution_urls();
+      }
+
+   // Check for self-signed vs self-issued certificates
+   if(data->m_subject_dn == data->m_issuer_dn)
+      {
+      if(data->m_subject_key_id.empty() == false && data->m_authority_key_id.empty() == false)
+         {
+         data->m_self_signed = (data->m_subject_key_id == data->m_authority_key_id);
+         }
+      else
+         {
+         /*
+         If a parse error or unknown algorithm is encountered, default
+         to assuming it is self signed. We have no way of being certain but
+         that is usually the default case (self-issued is rare in practice).
+         */
+         data->m_self_signed = true;
+
+         try
+            {
+            std::unique_ptr<Public_Key> pub_key(X509::load_key(data->m_subject_public_key_bits_seq));
+
+            Certificate_Status_Code sig_status = obj.verify_signature(*pub_key);
+
+            if(sig_status == Certificate_Status_Code::OK ||
+               sig_status == Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN)
+               {
+               data->m_self_signed = true;
+               }
+            else
+               {
+               data->m_self_signed = false;
+               }
+            }
+         catch(...)
+            {
+            // ignore errors here to allow parsing to continue
+            }
+         }
+      }
+
+   const std::vector<uint8_t> full_encoding = obj.BER_encode();
+
+   std::unique_ptr<HashFunction> sha1(HashFunction::create("SHA-1"));
+   if(sha1)
+      {
+      sha1->update(data->m_subject_public_key_bitstring);
+      data->m_subject_public_key_bitstring_sha1 = sha1->final_stdvec();
+      // otherwise left as empty, and we will throw if subject_public_key_bitstring_sha1 is called
+
+      data->m_fingerprint_sha1 = create_hex_fingerprint(full_encoding, "SHA-1");
+      }
+
+   std::unique_ptr<HashFunction> sha256(HashFunction::create("SHA-256"));
+   if(sha256)
+      {
+      sha256->update(data->m_issuer_dn_bits);
+      data->m_issuer_dn_bits_sha256 = sha256->final_stdvec();
+
+      sha256->update(data->m_subject_dn_bits);
+      data->m_subject_dn_bits_sha256 = sha256->final_stdvec();
+
+      data->m_fingerprint_sha256 = create_hex_fingerprint(full_encoding, "SHA-256");
+      }
+
+   data->m_subject_ds.add(data->m_subject_dn.contents());
+   data->m_issuer_ds.add(data->m_issuer_dn.contents());
+   data->m_v3_extensions.contents_to(data->m_subject_ds, data->m_issuer_ds);
+
+   return data;
+   }
+
+}
+
+/*
+* Decode the TBSCertificate data
+*/
+void X509_Certificate::force_decode()
+   {
+   m_data.reset();
+
+   std::unique_ptr<X509_Certificate_Data> data = parse_x509_cert_body(*this);
+
+   m_data.reset(data.release());
+   }
+
+const X509_Certificate_Data& X509_Certificate::data() const
+   {
+   if(m_data == nullptr)
+      {
+      throw Invalid_State("X509_Certificate uninitialized");
+      }
+   return *m_data.get();
+   }
+
+uint32_t X509_Certificate::x509_version() const
+   {
+   return static_cast<uint32_t>(data().m_version);
+   }
+
+bool X509_Certificate::is_self_signed() const
+   {
+   return data().m_self_signed;
+   }
+
+const X509_Time& X509_Certificate::not_before() const
+   {
+   return data().m_not_before;
+   }
+
+const X509_Time& X509_Certificate::not_after() const
+   {
+   return data().m_not_after;
+   }
+
+const AlgorithmIdentifier& X509_Certificate::subject_public_key_algo() const
+   {
+   return data().m_subject_public_key_algid;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::v2_issuer_key_id() const
+   {
+   return data().m_v2_issuer_key_id;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::v2_subject_key_id() const
+   {
+   return data().m_v2_subject_key_id;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bits() const
+   {
+   return data().m_subject_public_key_bits;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_info() const
+   {
+   return data().m_subject_public_key_bits_seq;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring() const
+   {
+   return data().m_subject_public_key_bitstring;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring_sha1() const
+   {
+   if(data().m_subject_public_key_bitstring_sha1.empty())
+      throw Encoding_Error("X509_Certificate::subject_public_key_bitstring_sha1 called but SHA-1 disabled in build");
+
+   return data().m_subject_public_key_bitstring_sha1;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::authority_key_id() const
+   {
+   return data().m_authority_key_id;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::subject_key_id() const
+   {
+   return data().m_subject_key_id;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::serial_number() const
+   {
+   return data().m_serial;
+   }
+
+bool X509_Certificate::is_serial_negative() const
+   {
+   return data().m_serial_negative;
+   }
+
+
+const X509_DN& X509_Certificate::issuer_dn() const
+   {
+   return data().m_issuer_dn;
+   }
+
+const X509_DN& X509_Certificate::subject_dn() const
+   {
+   return data().m_subject_dn;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::raw_issuer_dn() const
+   {
+   return data().m_issuer_dn_bits;
+   }
+
+const std::vector<uint8_t>& X509_Certificate::raw_subject_dn() const
+   {
+   return data().m_subject_dn_bits;
+   }
+
+bool X509_Certificate::is_CA_cert() const
+   {
+   if(data().m_version < 3 && data().m_self_signed)
+      return true;
+
+   return data().m_is_ca_certificate;
+   }
+
+uint32_t X509_Certificate::path_limit() const
+   {
+   if(data().m_version < 3 && data().m_self_signed)
+      return 32; // in theory infinite, but this is more than enough
+
+   return static_cast<uint32_t>(data().m_path_len_constraint);
+   }
+
+Key_Constraints X509_Certificate::constraints() const
+   {
+   return data().m_key_constraints;
+   }
+
+const std::vector<OID>& X509_Certificate::extended_key_usage() const
+   {
+   return data().m_extended_key_usage;
+   }
+
+const std::vector<OID>& X509_Certificate::certificate_policy_oids() const
+   {
+   return data().m_cert_policies;
+   }
+
+const NameConstraints& X509_Certificate::name_constraints() const
+   {
+   return data().m_name_constraints;
+   }
+
+const Extensions& X509_Certificate::v3_extensions() const
+   {
+   return data().m_v3_extensions;
+   }
+
+bool X509_Certificate::allowed_usage(Key_Constraints usage) const
+   {
+   if(constraints() == NO_CONSTRAINTS)
+      return true;
+   return ((constraints() & usage) == usage);
+   }
+
+bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
+   {
+   return allowed_extended_usage(OID::from_string(usage));
+   }
+
+bool X509_Certificate::allowed_extended_usage(const OID& usage) const
+   {
+   const std::vector<OID>& ex = extended_key_usage();
+   if(ex.empty())
+      return true;
+
+   if(std::find(ex.begin(), ex.end(), usage) != ex.end())
+      return true;
+
+   return false;
+   }
+
+bool X509_Certificate::allowed_usage(Usage_Type usage) const
+   {
+   // These follow suggestions in RFC 5280 4.2.1.12
+
+   switch(usage)
+      {
+      case Usage_Type::UNSPECIFIED:
+         return true;
+
+      case Usage_Type::TLS_SERVER_AUTH:
+         return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+
+      case Usage_Type::TLS_CLIENT_AUTH:
+         return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+
+      case Usage_Type::OCSP_RESPONDER:
+         return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning");
+
+      case Usage_Type::CERTIFICATE_AUTHORITY:
+         return is_CA_cert();
+
+      case Usage_Type::ENCRYPTION:
+         return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT));
+      }
+
+   return false;
+   }
+
+bool X509_Certificate::has_constraints(Key_Constraints constraints) const
+   {
+   if(this->constraints() == NO_CONSTRAINTS)
+      {
+      return false;
+      }
+
+   return ((this->constraints() & constraints) != 0);
+   }
+
+bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
+   {
+   return has_ex_constraint(OID::from_string(ex_constraint));
+   }
+
+bool X509_Certificate::has_ex_constraint(const OID& usage) const
+   {
+   const std::vector<OID>& ex = extended_key_usage();
+   return (std::find(ex.begin(), ex.end(), usage) != ex.end());
+   }
+
+/*
+* Return if a certificate extension is marked critical
+*/
+bool X509_Certificate::is_critical(const std::string& ex_name) const
+   {
+   return v3_extensions().critical_extension_set(OID::from_string(ex_name));
+   }
+
+std::string X509_Certificate::ocsp_responder() const
+   {
+   return data().m_ocsp_responder;
+   }
+
+std::vector<std::string> X509_Certificate::ca_issuers() const
+   {
+   return data().m_ca_issuers;
+   }
+
+std::string X509_Certificate::crl_distribution_point() const
+   {
+   // just returns the first (arbitrarily)
+   if(data().m_crl_distribution_points.size() > 0)
+      return data().m_crl_distribution_points[0];
+   return "";
+   }
+
+const AlternativeName& X509_Certificate::subject_alt_name() const
+   {
+   return data().m_subject_alt_name;
+   }
+
+const AlternativeName& X509_Certificate::issuer_alt_name() const
+   {
+   return data().m_issuer_alt_name;
+   }
+
+/*
+* Return information about the subject
+*/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& req) const
+   {
+   if(req == "Email")
+      return this->subject_info("RFC822");
+
+   if(subject_dn().has_field(req))
+      return subject_dn().get_attribute(req);
+
+   if(subject_alt_name().has_field(req))
+      return subject_alt_name().get_attribute(req);
+
+   // These will be removed later:
+   if(req == "X509.Certificate.v2.key_id")
+      return {hex_encode(this->v2_subject_key_id())};
+   if(req == "X509v3.SubjectKeyIdentifier")
+      return {hex_encode(this->subject_key_id())};
+   if(req == "X509.Certificate.dn_bits")
+      return {hex_encode(this->raw_subject_dn())};
+   if(req == "X509.Certificate.start")
+      return {not_before().to_string()};
+   if(req == "X509.Certificate.end")
+      return {not_after().to_string()};
+
+   if(req == "X509.Certificate.version")
+      return {std::to_string(x509_version())};
+   if(req == "X509.Certificate.serial")
+      return {hex_encode(serial_number())};
+
+   return data().m_subject_ds.get(req);
+   }
+
+/*
+* Return information about the issuer
+*/
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& req) const
+   {
+   if(issuer_dn().has_field(req))
+      return issuer_dn().get_attribute(req);
+
+   if(issuer_alt_name().has_field(req))
+      return issuer_alt_name().get_attribute(req);
+
+   // These will be removed later:
+   if(req == "X509.Certificate.v2.key_id")
+      return {hex_encode(this->v2_issuer_key_id())};
+   if(req == "X509v3.AuthorityKeyIdentifier")
+      return {hex_encode(this->authority_key_id())};
+   if(req == "X509.Certificate.dn_bits")
+      return {hex_encode(this->raw_issuer_dn())};
+
+   return data().m_issuer_ds.get(req);
+   }
+
+/*
+* Return the public key in this certificate
+*/
+std::unique_ptr<Public_Key> X509_Certificate::load_subject_public_key() const
+   {
+   try
+      {
+      return std::unique_ptr<Public_Key>(X509::load_key(subject_public_key_info()));
+      }
+   catch(std::exception& e)
+      {
+      throw Decoding_Error("X509_Certificate::load_subject_public_key", e);
+      }
+   }
+
+std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const
+   {
+   if(data().m_issuer_dn_bits_sha256.empty())
+      throw Encoding_Error("X509_Certificate::raw_issuer_dn_sha256 called but SHA-256 disabled in build");
+   return data().m_issuer_dn_bits_sha256;
+   }
+
+std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
+   {
+   if(data().m_subject_dn_bits_sha256.empty())
+      throw Encoding_Error("X509_Certificate::raw_subject_dn_sha256 called but SHA-256 disabled in build");
+   return data().m_subject_dn_bits_sha256;
+   }
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<OID>& oids)
+   {
+   std::vector<std::string> out;
+
+   for(const OID& oid : oids)
+      {
+      out.push_back(oid.to_formatted_string());
+      }
+   return out;
+   }
+
+}
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
+   {
+   return lookup_oids(extended_key_usage());
+   }
+
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
+   {
+   return lookup_oids(certificate_policy_oids());
+   }
+
+std::string X509_Certificate::fingerprint(const std::string& hash_name) const
+   {
+   /*
+   * The SHA-1 and SHA-256 fingerprints are precomputed since these
+   * are the most commonly used. Especially, SHA-256 fingerprints are
+   * used for cycle detection during path construction.
+   *
+   * If SHA-1 or SHA-256 was missing at parsing time the vectors are
+   * left empty in which case we fall back to create_hex_fingerprint
+   * which will throw if the hash is unavailable.
+   */
+   if(hash_name == "SHA-256" && data().m_fingerprint_sha256.size() > 0)
+      return data().m_fingerprint_sha256;
+   else if(hash_name == "SHA-1" && data().m_fingerprint_sha1.size() > 0)
+      return data().m_fingerprint_sha1;
+   else
+      return create_hex_fingerprint(this->BER_encode(), hash_name);
+   }
+
+bool X509_Certificate::matches_dns_name(const std::string& name) const
+   {
+   if(name.empty())
+      return false;
+
+   std::vector<std::string> issued_names = subject_info("DNS");
+
+   // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+   if(issued_names.empty())
+      issued_names = subject_info("Name");
+
+   for(size_t i = 0; i != issued_names.size(); ++i)
+      {
+      if(host_wildcard_match(issued_names[i], name))
+         return true;
+      }
+
+   return false;
+   }
+
+/*
+* Compare two certificates for equality
+*/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+   {
+   return (this->signature() == other.signature() &&
+           this->signature_algorithm() == other.signature_algorithm() &&
+           this->signed_body() == other.signed_body());
+   }
+
+bool X509_Certificate::operator<(const X509_Certificate& other) const
+   {
+   /* If signature values are not equal, sort by lexicographic ordering of that */
+   if(this->signature() != other.signature())
+      {
+      return (this->signature() < other.signature());
+      }
+
+   // Then compare the signed contents
+   return this->signed_body() < other.signed_body();
+   }
+
+/*
+* X.509 Certificate Comparison
+*/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+   {
+   return !(cert1 == cert2);
+   }
+
+std::string X509_Certificate::to_string() const
+   {
+   std::ostringstream out;
+
+   out << "Version: " << this->x509_version() << "\n";
+   out << "Subject: " << subject_dn() << "\n";
+   out << "Issuer: " << issuer_dn() << "\n";
+   out << "Issued: " << this->not_before().readable_string() << "\n";
+   out << "Expires: " << this->not_after().readable_string() << "\n";
+
+   out << "Constraints:\n";
+   Key_Constraints constraints = this->constraints();
+   if(constraints == NO_CONSTRAINTS)
+      out << " None\n";
+   else
+      {
+      if(constraints & DIGITAL_SIGNATURE)
+         out << "   Digital Signature\n";
+      if(constraints & NON_REPUDIATION)
+         out << "   Non-Repudiation\n";
+      if(constraints & KEY_ENCIPHERMENT)
+         out << "   Key Encipherment\n";
+      if(constraints & DATA_ENCIPHERMENT)
+         out << "   Data Encipherment\n";
+      if(constraints & KEY_AGREEMENT)
+         out << "   Key Agreement\n";
+      if(constraints & KEY_CERT_SIGN)
+         out << "   Cert Sign\n";
+      if(constraints & CRL_SIGN)
+         out << "   CRL Sign\n";
+      if(constraints & ENCIPHER_ONLY)
+         out << "   Encipher Only\n";
+      if(constraints & DECIPHER_ONLY)
+         out << "   Decipher Only\n";
+      }
+
+   const std::vector<OID>& policies = this->certificate_policy_oids();
+   if(!policies.empty())
+      {
+      out << "Policies: " << "\n";
+      for(auto oid : policies)
+         out << "   " << oid.to_string() << "\n";
+      }
+
+   const std::vector<OID>& ex_constraints = this->extended_key_usage();
+   if(!ex_constraints.empty())
+      {
+      out << "Extended Constraints:\n";
+      for(auto&& oid : ex_constraints)
+         {
+         out << "   " << oid.to_formatted_string() << "\n";
+         }
+      }
+
+   const NameConstraints& name_constraints = this->name_constraints();
+
+   if(!name_constraints.permitted().empty() || !name_constraints.excluded().empty())
+      {
+      out << "Name Constraints:\n";
+
+      if(!name_constraints.permitted().empty())
+         {
+         out << "   Permit";
+         for(auto st: name_constraints.permitted())
+            {
+            out << " " << st.base();
+            }
+         out << "\n";
+         }
+
+      if(!name_constraints.excluded().empty())
+         {
+         out << "   Exclude";
+         for(auto st: name_constraints.excluded())
+            {
+            out << " " << st.base();
+            }
+         out << "\n";
+         }
+      }
+
+   if(!ocsp_responder().empty())
+      out << "OCSP responder " << ocsp_responder() << "\n";
+
+   const std::vector<std::string> ca_issuers = this->ca_issuers();
+   if(!ca_issuers.empty())
+      {
+      out << "CA Issuers:\n";
+      for(size_t i = 0; i != ca_issuers.size(); i++)
+         out << "   URI: " << ca_issuers[i] << "\n";
+      }
+
+   if(!crl_distribution_point().empty())
+      out << "CRL " << crl_distribution_point() << "\n";
+
+   out << "Signature algorithm: " << this->signature_algorithm().get_oid().to_formatted_string() << "\n";
+
+   out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+   if(this->authority_key_id().size())
+     out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+   if(this->subject_key_id().size())
+     out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+   try
+      {
+      std::unique_ptr<Public_Key> pubkey(this->subject_public_key());
+      out << "Public Key [" << pubkey->algo_name() << "-" << pubkey->key_length() << "]\n\n";
+      out << X509::PEM_encode(*pubkey);
+      }
+   catch(Decoding_Error&)
+      {
+      const AlgorithmIdentifier& alg_id = this->subject_public_key_algo();
+      out << "Failed to decode key with oid " << alg_id.get_oid().to_string() << "\n";
+      }
+
+   return out.str();
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/x509cert.h b/third_party/botan/src/lib/x509/x509cert.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509cert.h
@@ -0,0 +1,465 @@
+/*
+* X.509 Certificates
+* (C) 1999-2007,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERTS_H_
+#define BOTAN_X509_CERTS_H_
+
+#include <botan/x509_obj.h>
+#include <botan/x509_key.h>
+#include <botan/asn1_time.h>
+#include <botan/key_constraint.h>
+#include <botan/name_constraint.h>
+#include <memory>
+
+namespace Botan {
+
+class Public_Key;
+class X509_DN;
+class AlternativeName;
+class Extensions;
+
+enum class Usage_Type
+   {
+   UNSPECIFIED, // no restrictions
+   TLS_SERVER_AUTH,
+   TLS_CLIENT_AUTH,
+   CERTIFICATE_AUTHORITY,
+   OCSP_RESPONDER,
+   ENCRYPTION
+   };
+
+struct X509_Certificate_Data;
+
+/**
+* This class represents an X.509 Certificate
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
+   {
+   public:
+      /**
+      * Return a newly allocated copy of the public key associated
+      * with the subject of this certificate. This object is owned
+      * by the caller.
+      *
+      * @return public key
+      */
+      Public_Key* subject_public_key() const
+         {
+         return load_subject_public_key().release();
+         }
+
+      /**
+      * Create a public key object associated with the public key bits in this
+      * certificate. If the public key bits was valid for X.509 encoding
+      * purposes but invalid algorithmically (for example, RSA with an even
+      * modulus) that will be detected at this point, and an exception will be
+      * thrown.
+      *
+      * @return subject public key of this certificate
+      */
+      std::unique_ptr<Public_Key> load_subject_public_key() const;
+
+      /**
+      * Get the public key associated with this certificate. This includes the
+      * outer AlgorithmIdentifier
+      * @return subject public key of this certificate
+      */
+      const std::vector<uint8_t>& subject_public_key_bits() const;
+
+      /**
+      * Get the SubjectPublicKeyInfo associated with this certificate.
+      * @return subject public key info of this certificate
+      */
+      const std::vector<uint8_t>& subject_public_key_info() const;
+
+      /**
+      * Return the algorithm identifier of the public key
+      */
+      const AlgorithmIdentifier& subject_public_key_algo() const;
+
+      /**
+      * Get the bit string of the public key associated with this certificate
+      * @return public key bits
+      */
+      const std::vector<uint8_t>& subject_public_key_bitstring() const;
+
+      /**
+      * Get the SHA-1 bit string of the public key associated with this certificate.
+      * This is used for OCSP among other protocols.
+      * This function will throw if SHA-1 is not available.
+      * @return hash of subject public key of this certificate
+      */
+      const std::vector<uint8_t>& subject_public_key_bitstring_sha1() const;
+
+      /**
+      * Get the certificate's issuer distinguished name (DN).
+      * @return issuer DN of this certificate
+      */
+      const X509_DN& issuer_dn() const;
+
+      /**
+      * Get the certificate's subject distinguished name (DN).
+      * @return subject DN of this certificate
+      */
+      const X509_DN& subject_dn() const;
+
+      /**
+      * Get a value for a specific subject_info parameter name.
+      * @param name the name of the parameter to look up. Possible names include
+      * "X509.Certificate.version", "X509.Certificate.serial",
+      * "X509.Certificate.start", "X509.Certificate.end",
+      * "X509.Certificate.v2.key_id", "X509.Certificate.public_key",
+      * "X509v3.BasicConstraints.path_constraint",
+      * "X509v3.BasicConstraints.is_ca", "X509v3.NameConstraints",
+      * "X509v3.ExtendedKeyUsage", "X509v3.CertificatePolicies",
+      * "X509v3.SubjectKeyIdentifier", "X509.Certificate.serial",
+      * "X520.CommonName", "X520.Organization", "X520.Country",
+      * "RFC822" (Email in SAN) or "PKCS9.EmailAddress" (Email in DN).
+      * @return value(s) of the specified parameter
+      */
+      std::vector<std::string> subject_info(const std::string& name) const;
+
+      /**
+      * Get a value for a specific subject_info parameter name.
+      * @param name the name of the parameter to look up. Possible names are
+      * "X509.Certificate.v2.key_id" or "X509v3.AuthorityKeyIdentifier".
+      * @return value(s) of the specified parameter
+      */
+      std::vector<std::string> issuer_info(const std::string& name) const;
+
+      /**
+      * Raw issuer DN bits
+      */
+      const std::vector<uint8_t>& raw_issuer_dn() const;
+
+      /**
+      * SHA-256 of Raw issuer DN
+      */
+      std::vector<uint8_t> raw_issuer_dn_sha256() const;
+
+      /**
+      * Raw subject DN
+      */
+      const std::vector<uint8_t>& raw_subject_dn() const;
+
+      /**
+      * SHA-256 of Raw subject DN
+      */
+      std::vector<uint8_t> raw_subject_dn_sha256() const;
+
+      /**
+      * Get the notBefore of the certificate as a string
+      * @return notBefore of the certificate
+      */
+      std::string BOTAN_DEPRECATED("Use not_before().to_string()") start_time() const
+         {
+         return not_before().to_string();
+         }
+
+      /**
+      * Get the notAfter of the certificate as a string
+      * @return notAfter of the certificate
+      */
+      std::string BOTAN_DEPRECATED("Use not_after().to_string()") end_time() const
+         {
+         return not_after().to_string();
+         }
+
+      /**
+      * Get the notBefore of the certificate as X509_Time
+      * @return notBefore of the certificate
+      */
+      const X509_Time& not_before() const;
+
+      /**
+      * Get the notAfter of the certificate as X509_Time
+      * @return notAfter of the certificate
+      */
+      const X509_Time& not_after() const;
+
+      /**
+      * Get the X509 version of this certificate object.
+      * @return X509 version
+      */
+      uint32_t x509_version() const;
+
+      /**
+      * Get the serial number of this certificate.
+      * @return certificates serial number
+      */
+      const std::vector<uint8_t>& serial_number() const;
+
+      /**
+      * Get the serial number's sign
+      * @return 1 iff the serial is negative.
+      */
+      bool is_serial_negative() const;
+
+      /**
+      * Get the DER encoded AuthorityKeyIdentifier of this certificate.
+      * @return DER encoded AuthorityKeyIdentifier
+      */
+      const std::vector<uint8_t>& authority_key_id() const;
+
+      /**
+      * Get the DER encoded SubjectKeyIdentifier of this certificate.
+      * @return DER encoded SubjectKeyIdentifier
+      */
+      const std::vector<uint8_t>& subject_key_id() const;
+
+      /**
+      * Check whether this certificate is self signed.
+      * If the DN issuer and subject agree,
+      * @return true if this certificate is self signed
+      */
+      bool is_self_signed() const;
+
+      /**
+      * Check whether this certificate is a CA certificate.
+      * @return true if this certificate is a CA certificate
+      */
+      bool is_CA_cert() const;
+
+      /**
+      * Returns true if the specified @param usage is set in the key usage extension
+      * or if no key usage constraints are set at all.
+      * To check if a certain key constraint is set in the certificate
+      * use @see X509_Certificate#has_constraints.
+      */
+      bool allowed_usage(Key_Constraints usage) const;
+
+      /**
+      * Returns true if the specified @param usage is set in the extended key usage extension
+      * or if no extended key usage constraints are set at all.
+      * To check if a certain extended key constraint is set in the certificate
+      * use @see X509_Certificate#has_ex_constraint.
+      */
+      bool allowed_extended_usage(const std::string& usage) const;
+
+      /**
+      * Returns true if the specified usage is set in the extended key usage extension,
+      * or if no extended key usage constraints are set at all.
+      * To check if a certain extended key constraint is set in the certificate
+      * use @see X509_Certificate#has_ex_constraint.
+      */
+      bool allowed_extended_usage(const OID& usage) const;
+
+      /**
+      * Returns true if the required key and extended key constraints are set in the certificate
+      * for the specified @param usage or if no key constraints are set in both the key usage
+      * and extended key usage extension.
+      */
+      bool allowed_usage(Usage_Type usage) const;
+
+      /**
+      * Returns true if the specified @param constraints are included in the key
+      * usage extension.
+      */
+      bool has_constraints(Key_Constraints constraints) const;
+
+      /**
+      * Returns true if and only if @param ex_constraint (referring to an
+      * extended key constraint, eg "PKIX.ServerAuth") is included in the
+      * extended key extension.
+      */
+      bool BOTAN_DEPRECATED("Use version taking an OID")
+         has_ex_constraint(const std::string& ex_constraint) const;
+
+      /**
+      * Returns true if and only if OID @param ex_constraint is
+      * included in the extended key extension.
+      */
+      bool has_ex_constraint(const OID& ex_constraint) const;
+
+      /**
+      * Get the path limit as defined in the BasicConstraints extension of
+      * this certificate.
+      * @return path limit
+      */
+      uint32_t path_limit() const;
+
+      /**
+      * Check whenever a given X509 Extension is marked critical in this
+      * certificate.
+      */
+      bool is_critical(const std::string& ex_name) const;
+
+      /**
+      * Get the key constraints as defined in the KeyUsage extension of this
+      * certificate.
+      * @return key constraints
+      */
+      Key_Constraints constraints() const;
+
+      /**
+      * Get the key constraints as defined in the ExtendedKeyUsage
+      * extension of this certificate.
+      * @return key constraints
+      */
+      std::vector<std::string>
+         BOTAN_DEPRECATED("Use extended_key_usage") ex_constraints() const;
+
+      /**
+      * Get the key usage as defined in the ExtendedKeyUsage extension
+      * of this certificate, or else an empty vector.
+      * @return key usage
+      */
+      const std::vector<OID>& extended_key_usage() const;
+
+      /**
+      * Get the name constraints as defined in the NameConstraints
+      * extension of this certificate.
+      * @return name constraints
+      */
+      const NameConstraints& name_constraints() const;
+
+      /**
+      * Get the policies as defined in the CertificatePolicies extension
+      * of this certificate.
+      * @return certificate policies
+      */
+      std::vector<std::string> BOTAN_DEPRECATED("Use certificate_policy_oids") policies() const;
+
+      const std::vector<OID>& certificate_policy_oids() const;
+
+      /**
+      * Get all extensions of this certificate.
+      * @return certificate extensions
+      */
+      const Extensions& v3_extensions() const;
+
+      /**
+      * Return the v2 issuer key ID. v2 key IDs are almost never used,
+      * instead see v3_subject_key_id.
+      */
+      const std::vector<uint8_t>& v2_issuer_key_id() const;
+
+      /**
+      * Return the v2 subject key ID. v2 key IDs are almost never used,
+      * instead see v3_subject_key_id.
+      */
+      const std::vector<uint8_t>& v2_subject_key_id() const;
+
+      /**
+      * Return the subject alternative names (DNS, IP, ...)
+      */
+      const AlternativeName& subject_alt_name() const;
+
+      /**
+      * Return the issuer alternative names (DNS, IP, ...)
+      */
+      const AlternativeName& issuer_alt_name() const;
+
+      /**
+      * Return the listed address of an OCSP responder, or empty if not set
+      */
+      std::string ocsp_responder() const;
+
+      /**
+      * Return the listed addresses of ca issuers, or empty if not set
+      */
+      std::vector<std::string> ca_issuers() const;
+
+      /**
+      * Return the CRL distribution point, or empty if not set
+      */
+      std::string crl_distribution_point() const;
+
+      /**
+      * @return a free-form string describing the certificate
+      */
+      std::string to_string() const;
+
+      /**
+      * @return a fingerprint of the certificate
+      * @param hash_name hash function used to calculate the fingerprint
+      */
+      std::string fingerprint(const std::string& hash_name = "SHA-1") const;
+
+      /**
+      * Check if a certain DNS name matches up with the information in
+      * the cert
+      * @param name DNS name to match
+      */
+      bool matches_dns_name(const std::string& name) const;
+
+      /**
+      * Check to certificates for equality.
+      * @return true both certificates are (binary) equal
+      */
+      bool operator==(const X509_Certificate& other) const;
+
+      /**
+      * Impose an arbitrary (but consistent) ordering, eg to allow sorting
+      * a container of certificate objects.
+      * @return true if this is less than other by some unspecified criteria
+      */
+      bool operator<(const X509_Certificate& other) const;
+
+      /**
+      * Create a certificate from a data source providing the DER or
+      * PEM encoded certificate.
+      * @param source the data source
+      */
+      explicit X509_Certificate(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+      /**
+      * Create a certificate from a file containing the DER or PEM
+      * encoded certificate.
+      * @param filename the name of the certificate file
+      */
+      explicit X509_Certificate(const std::string& filename);
+#endif
+
+      /**
+      * Create a certificate from a buffer
+      * @param in the buffer containing the DER-encoded certificate
+      */
+      explicit X509_Certificate(const std::vector<uint8_t>& in);
+
+      /**
+      * Create a certificate from a buffer
+      * @param data the buffer containing the DER-encoded certificate
+      * @param length length of data in bytes
+      */
+      X509_Certificate(const uint8_t data[], size_t length);
+
+      /**
+      * Create an uninitialized certificate object. Any attempts to
+      * access this object will throw an exception.
+      */
+      X509_Certificate() = default;
+
+      X509_Certificate(const X509_Certificate& other) = default;
+
+      X509_Certificate& operator=(const X509_Certificate& other) = default;
+
+   private:
+      std::string PEM_label() const override;
+
+      std::vector<std::string> alternate_PEM_labels() const override;
+
+      void force_decode() override;
+
+      const X509_Certificate_Data& data() const;
+
+      std::shared_ptr<X509_Certificate_Data> m_data;
+   };
+
+/**
+* Check two certificates for inequality
+* @param cert1 The first certificate
+* @param cert2 The second certificate
+* @return true if the arguments represent different certificates,
+* false if they are binary identical
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2);
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509opt.cpp b/third_party/botan/src/lib/x509/x509opt.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509opt.cpp
@@ -0,0 +1,100 @@
+/*
+* X.509 Certificate Options
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/parsing.h>
+#include <chrono>
+
+namespace Botan {
+
+/*
+* Set when the certificate should become valid
+*/
+void X509_Cert_Options::not_before(const std::string& time_string)
+   {
+   start = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+   }
+
+/*
+* Set when the certificate should expire
+*/
+void X509_Cert_Options::not_after(const std::string& time_string)
+   {
+   end = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+   }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_constraints(Key_Constraints usage)
+   {
+   constraints = usage;
+   }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const OID& oid)
+   {
+   ex_constraints.push_back(oid);
+   }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const std::string& oid_str)
+   {
+   ex_constraints.push_back(OID::from_string(oid_str));
+   }
+
+/*
+* Mark this certificate for CA usage
+*/
+void X509_Cert_Options::CA_key(size_t limit)
+   {
+   is_CA = true;
+   path_limit = limit;
+   }
+
+void X509_Cert_Options::set_padding_scheme(const std::string& scheme)
+   {
+      padding_scheme = scheme;
+   }
+
+/*
+* Initialize the certificate options
+*/
+X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
+                                     uint32_t expiration_time)
+   {
+   is_CA = false;
+   path_limit = 0;
+   constraints = NO_CONSTRAINTS;
+   // use default for chosen algorithm
+   padding_scheme = "";
+
+   auto now = std::chrono::system_clock::now();
+
+   start = X509_Time(now);
+   end = X509_Time(now + std::chrono::seconds(expiration_time));
+
+   if(initial_opts.empty())
+      return;
+
+   std::vector<std::string> parsed = split_on(initial_opts, '/');
+
+   if(parsed.size() > 4)
+      throw Invalid_Argument("X.509 cert options: Too many names: "
+                             + initial_opts);
+
+   if(parsed.size() >= 1) common_name  = parsed[0];
+   if(parsed.size() >= 2) country      = parsed[1];
+   if(parsed.size() >= 3) organization = parsed[2];
+   if(parsed.size() == 4) org_unit     = parsed[3];
+   }
+
+}
diff --git a/third_party/botan/src/lib/x509/x509path.cpp b/third_party/botan/src/lib/x509/x509path.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509path.cpp
@@ -0,0 +1,1088 @@
+/*
+* X.509 Certificate Path Validation
+* (C) 2010,2011,2012,2014,2016 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509path.h>
+#include <botan/x509_ext.h>
+#include <botan/pk_keys.h>
+#include <botan/ocsp.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <chrono>
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+  #include <future>
+  #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+/*
+* PKIX path validation
+*/
+CertificatePathStatusCodes
+PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                  std::chrono::system_clock::time_point ref_time,
+                  const std::string& hostname,
+                  Usage_Type usage,
+                  size_t min_signature_algo_strength,
+                  const std::set<std::string>& trusted_hashes)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_chain cert_path empty");
+
+   const bool self_signed_ee_cert = (cert_path.size() == 1);
+
+   X509_Time validation_time(ref_time);
+
+   CertificatePathStatusCodes cert_status(cert_path.size());
+
+   if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
+      cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
+
+   if(!cert_path[0]->allowed_usage(usage))
+      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+
+   if(cert_path[0]->is_CA_cert() == false &&
+      cert_path[0]->has_constraints(KEY_CERT_SIGN))
+      {
+      /*
+      "If the keyCertSign bit is asserted, then the cA bit in the
+      basic constraints extension (Section 4.2.1.9) MUST also be
+      asserted." - RFC 5280
+
+      We don't bother doing this check on the rest of the path since they
+      must have the cA bit asserted or the validation will fail anyway.
+      */
+      cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+      }
+
+   for(size_t i = 0; i != cert_path.size(); ++i)
+      {
+      std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+      const bool at_self_signed_root = (i == cert_path.size() - 1);
+
+      const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+      const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+
+      if(at_self_signed_root && (issuer->is_self_signed() == false))
+         {
+         status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
+         }
+
+      if(subject->issuer_dn() != issuer->subject_dn())
+         {
+         status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
+         }
+
+      // Check the serial number
+      if(subject->is_serial_negative())
+         {
+         status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
+         }
+
+      // Check the subject's DN components' length
+
+      for(const auto& dn_pair : subject->subject_dn().dn_info())
+         {
+         const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
+         // dn_pair = <OID,str>
+         if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
+            {
+            status.insert(Certificate_Status_Code::DN_TOO_LONG);
+            }
+         }
+
+      // Check all certs for valid time range
+      if(validation_time < subject->not_before())
+         status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
+
+      if(validation_time > subject->not_after())
+         status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
+
+      // Check issuer constraints
+      if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+         status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+
+      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+
+      // Check the signature algorithm is known
+      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
+         {
+         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
+         }
+      else
+         {
+         // only perform the following checks if the signature algorithm is known
+         if(!issuer_key)
+            {
+            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
+            }
+         else
+            {
+            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
+
+            if(sig_status != Certificate_Status_Code::VERIFIED)
+               status.insert(sig_status);
+
+            if(issuer_key->estimated_strength() < min_signature_algo_strength)
+               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+            }
+
+         // Ignore untrusted hashes on self-signed roots
+         if(trusted_hashes.size() > 0 && !at_self_signed_root)
+            {
+            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
+               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+            }
+         }
+
+      // Check cert extensions
+
+      if(subject->x509_version() == 1)
+         {
+         if(subject->v2_issuer_key_id().empty() == false ||
+            subject->v2_subject_key_id().empty() == false)
+            {
+            status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
+            }
+         }
+
+      Extensions extensions = subject->v3_extensions();
+      const auto& extensions_vec = extensions.extensions();
+      if(subject->x509_version() < 3 && !extensions_vec.empty())
+         {
+         status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
+         }
+      for(auto& extension : extensions_vec)
+         {
+         extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
+         }
+      if(extensions.extensions().size() != extensions.get_extension_oids().size())
+         {
+         status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
+         }
+      }
+
+   // path len check
+   size_t max_path_length = cert_path.size();
+   for(size_t i = cert_path.size() - 1; i > 0 ; --i)
+      {
+      std::set<Certificate_Status_Code>& status = cert_status.at(i);
+      const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+      /*
+      * If the certificate was not self-issued, verify that max_path_length is
+      * greater than zero and decrement max_path_length by 1.
+      */
+      if(subject->subject_dn() != subject->issuer_dn())
+         {
+         if(max_path_length > 0)
+            {
+            --max_path_length;
+            }
+         else
+            {
+            status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
+            }
+         }
+
+      /*
+      * If pathLenConstraint is present in the certificate and is less than max_path_length,
+      * set max_path_length to the value of pathLenConstraint.
+      */
+      if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
+         {
+         max_path_length = subject->path_limit();
+         }
+      }
+
+   return cert_status;
+   }
+
+CertificatePathStatusCodes
+PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+                 const std::vector<Certificate_Store*>& trusted_certstores,
+                 std::chrono::system_clock::time_point ref_time,
+                 std::chrono::seconds max_ocsp_age)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
+
+   CertificatePathStatusCodes cert_status(cert_path.size() - 1);
+
+   for(size_t i = 0; i != cert_path.size() - 1; ++i)
+      {
+      std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+      std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+      std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+      if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
+            && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
+         {
+         try
+            {
+            Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
+
+            if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
+               {
+               // Signature ok, so check the claimed status
+               Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
+               status.insert(ocsp_status);
+               }
+            else
+               {
+               // Some signature problem
+               status.insert(ocsp_signature_status);
+               }
+            }
+         catch(Exception&)
+            {
+            status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
+            }
+         }
+      }
+
+   while(cert_status.size() > 0 && cert_status.back().empty())
+      cert_status.pop_back();
+
+   return cert_status;
+   }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+                std::chrono::system_clock::time_point ref_time)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+   CertificatePathStatusCodes cert_status(cert_path.size());
+   const X509_Time validation_time(ref_time);
+
+   for(size_t i = 0; i != cert_path.size() - 1; ++i)
+      {
+      std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+      if(i < crls.size() && crls.at(i))
+         {
+         std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+         std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+         if(!ca->allowed_usage(CRL_SIGN))
+            status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
+
+         if(validation_time < crls[i]->this_update())
+            status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
+
+         if(validation_time > crls[i]->next_update())
+            status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
+
+         if(crls[i]->check_signature(ca->subject_public_key()) == false)
+            status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
+
+         status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
+
+         if(crls[i]->is_revoked(*subject))
+            status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
+
+         std::string dp = subject->crl_distribution_point();
+         if(!dp.empty())
+            {
+            if(dp != crls[i]->crl_issuing_distribution_point())
+               {
+               status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
+               }
+            }
+
+         for(const auto& extension : crls[i]->extensions().extensions())
+            {
+            // XXX this is wrong - the OID might be defined but the extention not full parsed
+            // for example see #1652
+
+            // is the extension critical and unknown?
+            if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
+               {
+               /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
+                * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
+                */
+               status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
+               }
+            }
+
+         }
+      }
+
+   while(cert_status.size() > 0 && cert_status.back().empty())
+      cert_status.pop_back();
+
+   return cert_status;
+   }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                const std::vector<Certificate_Store*>& certstores,
+                std::chrono::system_clock::time_point ref_time)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+   if(certstores.empty())
+      throw Invalid_Argument("PKIX::check_crl certstores empty");
+
+   std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+   for(size_t i = 0; i != cert_path.size(); ++i)
+      {
+      BOTAN_ASSERT_NONNULL(cert_path[i]);
+      for(size_t c = 0; c != certstores.size(); ++c)
+         {
+         crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
+         if(crls[i])
+            break;
+         }
+      }
+
+   return PKIX::check_crl(cert_path, crls, ref_time);
+   }
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+CertificatePathStatusCodes
+PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                        const std::vector<Certificate_Store*>& trusted_certstores,
+                        std::chrono::system_clock::time_point ref_time,
+                        std::chrono::milliseconds timeout,
+                        bool ocsp_check_intermediate_CAs,
+                        std::chrono::seconds max_ocsp_age)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
+
+   std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
+
+   size_t to_ocsp = 1;
+
+   if(ocsp_check_intermediate_CAs)
+      to_ocsp = cert_path.size() - 1;
+   if(cert_path.size() == 1)
+      to_ocsp = 0;
+
+   for(size_t i = 0; i < to_ocsp; ++i)
+      {
+      const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
+      const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
+
+      if(subject->ocsp_responder() == "")
+         {
+         ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
+                  return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
+                  }));
+         }
+      else
+         {
+         ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
+               OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
+
+               HTTP::Response http;
+               try
+                  {
+                  http = HTTP::POST_sync(subject->ocsp_responder(),
+                                                "application/ocsp-request",
+                                                req.BER_encode(),
+                                                /*redirects*/1,
+                                                timeout);
+                  }
+               catch(std::exception&)
+                  {
+                  // log e.what() ?
+                  }
+               if (http.status_code() != 200)
+                  return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
+               // Check the MIME type?
+
+               return std::make_shared<const OCSP::Response>(http.body());
+               }));
+         }
+      }
+
+   std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
+
+   for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
+      {
+      ocsp_responses.push_back(ocsp_response_futures[i].get());
+      }
+
+   return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
+   }
+
+CertificatePathStatusCodes
+PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                       const std::vector<Certificate_Store*>& certstores,
+                       Certificate_Store_In_Memory* crl_store,
+                       std::chrono::system_clock::time_point ref_time,
+                       std::chrono::milliseconds timeout)
+   {
+   if(cert_path.empty())
+      throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
+   if(certstores.empty())
+      throw Invalid_Argument("PKIX::check_crl_online certstores empty");
+
+   std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
+   std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+   for(size_t i = 0; i != cert_path.size(); ++i)
+      {
+      const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
+      for(size_t c = 0; c != certstores.size(); ++c)
+         {
+         crls[i] = certstores[c]->find_crl_for(*cert);
+         if(crls[i])
+            break;
+         }
+
+      // TODO: check if CRL is expired and re-request?
+
+      // Only request if we don't already have a CRL
+      if(crls[i])
+         {
+         /*
+         We already have a CRL, so just insert this empty one to hold a place in the vector
+         so that indexes match up
+         */
+         future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
+         }
+      else if(cert->crl_distribution_point() == "")
+         {
+         // Avoid creating a thread for this case
+         future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
+               throw Not_Implemented("No CRL distribution point for this certificate");
+               }));
+         }
+      else
+         {
+         future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
+               auto http = HTTP::GET_sync(cert->crl_distribution_point(),
+                                          /*redirects*/ 1, timeout);
+
+               http.throw_unless_ok();
+               // check the mime type?
+               return std::make_shared<const X509_CRL>(http.body());
+               }));
+         }
+      }
+
+   for(size_t i = 0; i != future_crls.size(); ++i)
+      {
+      if(future_crls[i].valid())
+         {
+         try
+            {
+            crls[i] = future_crls[i].get();
+            }
+         catch(std::exception&)
+            {
+            // crls[i] left null
+            // todo: log exception e.what() ?
+            }
+         }
+      }
+
+   const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
+
+   if(crl_store)
+      {
+      for(size_t i = 0; i != crl_status.size(); ++i)
+         {
+         if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
+            {
+            // better be non-null, we supposedly validated it
+            BOTAN_ASSERT_NONNULL(crls[i]);
+            crl_store->add_crl(crls[i]);
+            }
+         }
+      }
+
+   return crl_status;
+   }
+
+#endif
+
+Certificate_Status_Code
+PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                             const std::vector<Certificate_Store*>& trusted_certstores,
+                             const std::shared_ptr<const X509_Certificate>& end_entity,
+                             const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+   {
+   if(end_entity->is_self_signed())
+      {
+      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+      }
+
+   /*
+   * This is an inelegant but functional way of preventing path loops
+   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
+   * fingerprints in the path. If there is a duplicate, we error out.
+   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
+   */
+   std::set<std::string> certs_seen;
+
+   cert_path.push_back(end_entity);
+   certs_seen.insert(end_entity->fingerprint("SHA-256"));
+
+   Certificate_Store_In_Memory ee_extras;
+   for(size_t i = 0; i != end_entity_extra.size(); ++i)
+      ee_extras.add_certificate(end_entity_extra[i]);
+
+   // iterate until we reach a root or cannot find the issuer
+   for(;;)
+      {
+      const X509_Certificate& last = *cert_path.back();
+      const X509_DN issuer_dn = last.issuer_dn();
+      const std::vector<uint8_t> auth_key_id = last.authority_key_id();
+
+      std::shared_ptr<const X509_Certificate> issuer;
+      bool trusted_issuer = false;
+
+      for(Certificate_Store* store : trusted_certstores)
+         {
+         issuer = store->find_cert(issuer_dn, auth_key_id);
+         if(issuer)
+            {
+            trusted_issuer = true;
+            break;
+            }
+         }
+
+      if(!issuer)
+         {
+         // fall back to searching supplemental certs
+         issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
+         }
+
+      if(!issuer)
+         return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
+
+      const std::string fprint = issuer->fingerprint("SHA-256");
+
+      if(certs_seen.count(fprint) > 0) // already seen?
+         {
+         return Certificate_Status_Code::CERT_CHAIN_LOOP;
+         }
+
+      certs_seen.insert(fprint);
+      cert_path.push_back(issuer);
+
+      if(issuer->is_self_signed())
+         {
+         if(trusted_issuer)
+            {
+            return Certificate_Status_Code::OK;
+            }
+         else
+            {
+            return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+            }
+         }
+      }
+   }
+
+/**
+ * utilities for PKIX::build_all_certificate_paths
+ */
+namespace
+{
+// <certificate, trusted?>
+using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
+}
+
+/**
+ * Build all possible certificate paths from the end certificate to self-signed trusted roots.
+ *
+ * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
+ * one of the encountered errors is returned arbitrarily.
+ *
+ * todo add a path building function that returns detailed information on errors encountered while building
+ * the potentially numerous path candidates.
+ *
+ * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
+ * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
+ * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
+ * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
+ * authority key id need not be unique among the certificates used for building the path. In such a case,
+ * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
+ *
+ */
+Certificate_Status_Code
+PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
+                                  const std::vector<Certificate_Store*>& trusted_certstores,
+                                  const std::shared_ptr<const X509_Certificate>& end_entity,
+                                  const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+   {
+   if(!cert_paths_out.empty())
+      {
+      throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
+      }
+
+   if(end_entity->is_self_signed())
+      {
+      return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+      }
+
+   /*
+    * Pile up error messages
+    */
+   std::vector<Certificate_Status_Code> stats;
+
+   Certificate_Store_In_Memory ee_extras;
+   for(size_t i = 0; i != end_entity_extra.size(); ++i)
+      {
+      ee_extras.add_certificate(end_entity_extra[i]);
+      }
+
+   /*
+   * This is an inelegant but functional way of preventing path loops
+   * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
+   * fingerprints in the path. If there is a duplicate, we error out.
+   * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
+   */
+   std::set<std::string> certs_seen;
+
+   // new certs are added and removed from the path during the DFS
+   // it is copied into cert_paths_out when we encounter a trusted root
+   std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
+
+   // todo can we assume that the end certificate is not trusted?
+   std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
+
+   while(!stack.empty())
+      {
+      // found a deletion marker that guides the DFS, backtracing
+      if(stack.back().first == nullptr)
+         {
+         stack.pop_back();
+         std::string fprint = path_so_far.back()->fingerprint("SHA-256");
+         certs_seen.erase(fprint);
+         path_so_far.pop_back();
+         }
+      // process next cert on the path
+      else
+         {
+         std::shared_ptr<const X509_Certificate> last = stack.back().first;
+         bool trusted = stack.back().second;
+         stack.pop_back();
+
+         // certificate already seen?
+         const std::string fprint = last->fingerprint("SHA-256");
+         if(certs_seen.count(fprint) == 1)
+            {
+            stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
+            // the current path ended in a loop
+            continue;
+            }
+
+         // the current path ends here
+         if(last->is_self_signed())
+            {
+            // found a trust anchor
+            if(trusted)
+               {
+               cert_paths_out.push_back(path_so_far);
+               cert_paths_out.back().push_back(last);
+
+               continue;
+               }
+            // found an untrustworthy root
+            else
+               {
+               stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+               continue;
+               }
+            }
+
+         const X509_DN issuer_dn = last->issuer_dn();
+         const std::vector<uint8_t> auth_key_id = last->authority_key_id();
+
+         // search for trusted issuers
+         std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
+         for(Certificate_Store* store : trusted_certstores)
+            {
+            auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
+            trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
+            }
+
+         // search the supplemental certs
+         std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
+            ee_extras.find_all_certs(issuer_dn, auth_key_id);
+
+         // if we could not find any issuers, the current path ends here
+         if(trusted_issuers.size() + misc_issuers.size() == 0)
+            {
+            stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+            continue;
+            }
+
+         // push the latest certificate onto the path_so_far
+         path_so_far.push_back(last);
+         certs_seen.emplace(fprint);
+
+         // push a deletion marker on the stack for backtracing later
+         stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
+
+         for(const auto& trusted_cert : trusted_issuers)
+            {
+            stack.push_back({trusted_cert,true});
+            }
+
+         for(const auto& misc : misc_issuers)
+            {
+            stack.push_back({misc,false});
+            }
+         }
+      }
+
+   // could not construct any potentially valid path
+   if(cert_paths_out.empty())
+      {
+      if(stats.empty())
+         throw Internal_Error("X509 path building failed for unknown reasons");
+      else
+         // arbitrarily return the first error
+         return stats[0];
+      }
+   else
+      {
+      return Certificate_Status_Code::OK;
+      }
+   }
+
+
+void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
+                                   const CertificatePathStatusCodes& crl,
+                                   const CertificatePathStatusCodes& ocsp,
+                                   bool require_rev_on_end_entity,
+                                   bool require_rev_on_intermediates)
+   {
+   if(chain_status.empty())
+      throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
+
+   for(size_t i = 0; i != chain_status.size() - 1; ++i)
+      {
+      bool had_crl = false, had_ocsp = false;
+
+      if(i < crl.size() && crl[i].size() > 0)
+         {
+         for(auto&& code : crl[i])
+            {
+            if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
+               {
+               had_crl = true;
+               }
+            chain_status[i].insert(code);
+            }
+         }
+
+      if(i < ocsp.size() && ocsp[i].size() > 0)
+         {
+         for(auto&& code : ocsp[i])
+            {
+            if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+               code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||  // softfail
+               code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
+               {
+               had_ocsp = true;
+               }
+
+            chain_status[i].insert(code);
+            }
+         }
+
+      if(had_crl == false && had_ocsp == false)
+         {
+         if((require_rev_on_end_entity && i == 0) ||
+            (require_rev_on_intermediates && i > 0))
+            {
+            chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
+            }
+         }
+      }
+   }
+
+Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
+   {
+   if(cert_status.empty())
+      throw Invalid_Argument("PKIX::overall_status empty cert status");
+
+   Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
+
+   // take the "worst" error as overall
+   for(const std::set<Certificate_Status_Code>& s : cert_status)
+      {
+      if(!s.empty())
+         {
+         auto worst = *s.rbegin();
+         // Leave informative OCSP/CRL confirmations on cert-level status only
+         if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
+            {
+            overall_status = worst;
+            }
+         }
+      }
+   return overall_status;
+   }
+
+Path_Validation_Result x509_path_validate(
+   const std::vector<X509_Certificate>& end_certs,
+   const Path_Validation_Restrictions& restrictions,
+   const std::vector<Certificate_Store*>& trusted_roots,
+   const std::string& hostname,
+   Usage_Type usage,
+   std::chrono::system_clock::time_point ref_time,
+   std::chrono::milliseconds ocsp_timeout,
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+   {
+   if(end_certs.empty())
+      {
+      throw Invalid_Argument("x509_path_validate called with no subjects");
+      }
+
+   std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
+   std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
+   for(size_t i = 1; i < end_certs.size(); ++i)
+      {
+      end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
+      }
+
+   std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
+   Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
+
+   // If we cannot successfully build a chain to a trusted self-signed root, stop now
+   if(path_building_result != Certificate_Status_Code::OK)
+      {
+      return Path_Validation_Result(path_building_result);
+      }
+
+   std::vector<Path_Validation_Result> error_results;
+   // Try validating all the potentially valid paths and return the first one to validate properly
+   for(auto cert_path : cert_paths)
+      {
+      CertificatePathStatusCodes status =
+         PKIX::check_chain(cert_path, ref_time,
+                           hostname, usage,
+                           restrictions.minimum_key_strength(),
+                           restrictions.trusted_hashes());
+
+      CertificatePathStatusCodes crl_status =
+         PKIX::check_crl(cert_path, trusted_roots, ref_time);
+
+      CertificatePathStatusCodes ocsp_status;
+
+      if(ocsp_resp.size() > 0)
+         {
+         ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
+         }
+
+      if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
+         {
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+         ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
+                                               ocsp_timeout, restrictions.ocsp_all_intermediates());
+#else
+         ocsp_status.resize(1);
+         ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
+#endif
+         }
+
+      PKIX::merge_revocation_status(status, crl_status, ocsp_status,
+                                    restrictions.require_revocation_information(),
+                                    restrictions.ocsp_all_intermediates());
+
+      Path_Validation_Result pvd(status, std::move(cert_path));
+      if(pvd.successful_validation())
+         {
+         return pvd;
+         }
+      else
+         {
+         error_results.push_back(std::move(pvd));
+         }
+      }
+   return error_results[0];
+   }
+
+Path_Validation_Result x509_path_validate(
+   const X509_Certificate& end_cert,
+   const Path_Validation_Restrictions& restrictions,
+   const std::vector<Certificate_Store*>& trusted_roots,
+   const std::string& hostname,
+   Usage_Type usage,
+   std::chrono::system_clock::time_point when,
+   std::chrono::milliseconds ocsp_timeout,
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+   {
+   std::vector<X509_Certificate> certs;
+   certs.push_back(end_cert);
+   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+   }
+
+Path_Validation_Result x509_path_validate(
+   const std::vector<X509_Certificate>& end_certs,
+   const Path_Validation_Restrictions& restrictions,
+   const Certificate_Store& store,
+   const std::string& hostname,
+   Usage_Type usage,
+   std::chrono::system_clock::time_point when,
+   std::chrono::milliseconds ocsp_timeout,
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+   {
+   std::vector<Certificate_Store*> trusted_roots;
+   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+   return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+   }
+
+Path_Validation_Result x509_path_validate(
+   const X509_Certificate& end_cert,
+   const Path_Validation_Restrictions& restrictions,
+   const Certificate_Store& store,
+   const std::string& hostname,
+   Usage_Type usage,
+   std::chrono::system_clock::time_point when,
+   std::chrono::milliseconds ocsp_timeout,
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+   {
+   std::vector<X509_Certificate> certs;
+   certs.push_back(end_cert);
+
+   std::vector<Certificate_Store*> trusted_roots;
+   trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+   return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+   }
+
+Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
+      size_t key_strength,
+      bool ocsp_intermediates,
+      std::chrono::seconds max_ocsp_age) :
+   m_require_revocation_information(require_rev),
+   m_ocsp_all_intermediates(ocsp_intermediates),
+   m_minimum_key_strength(key_strength),
+   m_max_ocsp_age(max_ocsp_age)
+   {
+   if(key_strength <= 80)
+      { m_trusted_hashes.insert("SHA-160"); }
+
+   m_trusted_hashes.insert("SHA-224");
+   m_trusted_hashes.insert("SHA-256");
+   m_trusted_hashes.insert("SHA-384");
+   m_trusted_hashes.insert("SHA-512");
+   }
+
+namespace {
+CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
+   {
+   CertificatePathStatusCodes warnings;
+   for(const auto& status_set_i : all_statuses)
+      {
+      std::set<Certificate_Status_Code> warning_set_i;
+      for(const auto& code : status_set_i)
+         {
+         if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
+            code < Certificate_Status_Code::FIRST_ERROR_STATUS)
+            {
+            warning_set_i.insert(code);
+            }
+         }
+      warnings.push_back(warning_set_i);
+      }
+   return warnings;
+   }
+}
+
+Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
+                                               std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
+   m_all_status(status),
+   m_warnings(find_warnings(m_all_status)),
+   m_cert_path(cert_chain),
+   m_overall(PKIX::overall_status(m_all_status))
+   {
+   }
+
+const X509_Certificate& Path_Validation_Result::trust_root() const
+   {
+   if(m_cert_path.empty())
+      throw Invalid_State("Path_Validation_Result::trust_root no path set");
+   if(result() != Certificate_Status_Code::VERIFIED)
+      throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
+
+   return *m_cert_path[m_cert_path.size()-1];
+   }
+
+std::set<std::string> Path_Validation_Result::trusted_hashes() const
+   {
+   std::set<std::string> hashes;
+   for(size_t i = 0; i != m_cert_path.size(); ++i)
+      hashes.insert(m_cert_path[i]->hash_used_for_signature());
+   return hashes;
+   }
+
+bool Path_Validation_Result::successful_validation() const
+   {
+   return (result() == Certificate_Status_Code::VERIFIED ||
+           result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+           result() == Certificate_Status_Code::VALID_CRL_CHECKED);
+   }
+
+bool Path_Validation_Result::no_warnings() const
+   {
+   for(auto status_set_i : m_warnings)
+      if(!status_set_i.empty())
+         return false;
+   return true;
+   }
+
+CertificatePathStatusCodes Path_Validation_Result::warnings() const
+   {
+   return m_warnings;
+   }
+
+std::string Path_Validation_Result::result_string() const
+   {
+   return status_string(result());
+   }
+
+const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
+   {
+   if(const char* s = to_string(code))
+      return s;
+
+   return "Unknown error";
+   }
+
+std::string Path_Validation_Result::warnings_string() const
+   {
+   const std::string sep(", ");
+   std::string res;
+   for(size_t i = 0; i < m_warnings.size(); i++)
+      {
+      for(auto code : m_warnings[i])
+         res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
+      }
+   // remove last sep
+   if(res.size() >= sep.size())
+      res = res.substr(0, res.size() - sep.size());
+   return res;
+   }
+}
diff --git a/third_party/botan/src/lib/x509/x509path.h b/third_party/botan/src/lib/x509/x509path.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509path.h
@@ -0,0 +1,475 @@
+/*
+* X.509 Cert Path Validation
+* (C) 2010-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERT_PATH_VALIDATION_H_
+#define BOTAN_X509_CERT_PATH_VALIDATION_H_
+
+#include <botan/cert_status.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/ocsp.h>
+#include <functional>
+#include <set>
+#include <chrono>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+  #define BOTAN_HAS_ONLINE_REVOCATION_CHECKS
+#endif
+
+namespace Botan {
+
+/**
+* This type represents the validation status of an entire certificate path.
+* There is one set of status codes for each certificate in the path.
+*/
+typedef std::vector<std::set<Certificate_Status_Code>> CertificatePathStatusCodes;
+
+/**
+* Specifies restrictions on the PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final
+   {
+   public:
+      /**
+      * @param require_rev if true, revocation information is required
+
+      * @param minimum_key_strength is the minimum strength (in terms of
+      *    operations, eg 80 means 2^80) of a signature. Signatures weaker than
+      *    this are rejected. If more than 80, SHA-1 signatures are also
+      *    rejected. If possible use at least setting 110.
+      *
+      *        80 bit strength requires 1024 bit RSA
+      *        110 bit strength requires 2k bit RSA
+      *        128 bit strength requires ~3k bit RSA or P-256
+      * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+      * well as end entity (if OCSP enabled in path validation request)
+      * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+      *        If zero, there is no maximum age
+      */
+      Path_Validation_Restrictions(bool require_rev = false,
+                                   size_t minimum_key_strength = 110,
+                                   bool ocsp_all_intermediates = false,
+                                   std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+      /**
+      * @param require_rev if true, revocation information is required
+      * @param minimum_key_strength is the minimum strength (in terms of
+      *        operations, eg 80 means 2^80) of a signature. Signatures
+      *        weaker than this are rejected.
+      * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+      * well as end entity (if OCSP enabled in path validation request)
+      * @param trusted_hashes a set of trusted hashes. Any signatures
+      *        created using a hash other than one of these will be
+      *        rejected.
+      * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+      *        If zero, there is no maximum age
+      */
+      Path_Validation_Restrictions(bool require_rev,
+                                   size_t minimum_key_strength,
+                                   bool ocsp_all_intermediates,
+                                   const std::set<std::string>& trusted_hashes,
+                                   std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()) :
+         m_require_revocation_information(require_rev),
+         m_ocsp_all_intermediates(ocsp_all_intermediates),
+         m_trusted_hashes(trusted_hashes),
+         m_minimum_key_strength(minimum_key_strength),
+         m_max_ocsp_age(max_ocsp_age) {}
+
+      /**
+      * @return whether revocation information is required
+      */
+      bool require_revocation_information() const
+         { return m_require_revocation_information; }
+
+      /**
+      * @return whether all intermediate CAs should also be OCSPed. If false
+      * then only end entity OCSP is required/requested.
+      */
+      bool ocsp_all_intermediates() const
+         { return m_ocsp_all_intermediates; }
+
+      /**
+      * @return trusted signature hash functions
+      */
+      const std::set<std::string>& trusted_hashes() const
+         { return m_trusted_hashes; }
+
+      /**
+      * @return minimum required key strength
+      */
+      size_t minimum_key_strength() const
+         { return m_minimum_key_strength; }
+
+      /**
+      * @return maximum age of OCSP responses w/o next_update.
+      * If zero, there is no maximum age
+      */
+      std::chrono::seconds max_ocsp_age() const
+         { return m_max_ocsp_age; }
+
+   private:
+      bool m_require_revocation_information;
+      bool m_ocsp_all_intermediates;
+      std::set<std::string> m_trusted_hashes;
+      size_t m_minimum_key_strength;
+      std::chrono::seconds m_max_ocsp_age;
+   };
+
+/**
+* Represents the result of a PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Result final
+   {
+   public:
+      typedef Certificate_Status_Code Code;
+
+      /**
+      * @return the set of hash functions you are implicitly
+      * trusting by trusting this result.
+      */
+      std::set<std::string> trusted_hashes() const;
+
+      /**
+      * @return the trust root of the validation if successful
+      * throws an exception if the validation failed
+      */
+      const X509_Certificate& trust_root() const;
+
+      /**
+      * @return the full path from subject to trust root
+      * This path may be empty
+      */
+      const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; }
+
+      /**
+      * @return true iff the validation was successful
+      */
+      bool successful_validation() const;
+
+      /**
+      * @return true iff no warnings occured during validation
+      */
+      bool no_warnings() const;
+
+      /**
+      * @return overall validation result code
+      */
+      Certificate_Status_Code result() const { return m_overall; }
+
+      /**
+      * @return a set of status codes for each certificate in the chain
+      */
+      const CertificatePathStatusCodes& all_statuses() const
+         { return m_all_status; }
+
+      /**
+      * @return the subset of status codes that are warnings
+      */
+      CertificatePathStatusCodes warnings() const;
+
+      /**
+      * @return string representation of the validation result
+      */
+      std::string result_string() const;
+
+      /**
+      * @return string representation of the warnings
+      */
+      std::string warnings_string() const;
+
+      /**
+      * @param code validation status code
+      * @return corresponding validation status message
+      */
+      static const char* status_string(Certificate_Status_Code code);
+
+      /**
+      * Create a Path_Validation_Result
+      * @param status list of validation status codes
+      * @param cert_chain the certificate chain that was validated
+      */
+      Path_Validation_Result(CertificatePathStatusCodes status,
+                             std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain);
+
+      /**
+      * Create a Path_Validation_Result
+      * @param status validation status code
+      */
+      explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {}
+
+   private:
+      CertificatePathStatusCodes m_all_status;
+      CertificatePathStatusCodes m_warnings;
+      std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path;
+      Certificate_Status_Code m_overall;
+   };
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate (with end entity certificate in end_certs[0])
+* @param restrictions path validation restrictions
+* @param trusted_roots list of certificate stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*   note: when enabled, OCSP check is softfail by default: if the OCSP server is not
+*   reachable, Path_Validation_Result::successful_validation() will return true.
+*   Hardfail OCSP check can be achieve by also calling Path_Validation_Result::no_warnings().
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+   const std::vector<X509_Certificate>& end_certs,
+   const Path_Validation_Restrictions& restrictions,
+   const std::vector<Certificate_Store*>& trusted_roots,
+   const std::string& hostname = "",
+   Usage_Type usage = Usage_Type::UNSPECIFIED,
+   std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+   std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param trusted_roots list of stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+   const X509_Certificate& end_cert,
+   const Path_Validation_Restrictions& restrictions,
+   const std::vector<Certificate_Store*>& trusted_roots,
+   const std::string& hostname = "",
+   Usage_Type usage = Usage_Type::UNSPECIFIED,
+   std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+   std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+   const X509_Certificate& end_cert,
+   const Path_Validation_Restrictions& restrictions,
+   const Certificate_Store& store,
+   const std::string& hostname = "",
+   Usage_Type usage = Usage_Type::UNSPECIFIED,
+   std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+   std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+   const std::vector<X509_Certificate>& end_certs,
+   const Path_Validation_Restrictions& restrictions,
+   const Certificate_Store& store,
+   const std::string& hostname = "",
+   Usage_Type usage = Usage_Type::UNSPECIFIED,
+   std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+   std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+   const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+
+/**
+* namespace PKIX holds the building blocks that are called by x509_path_validate.
+* This allows custom validation logic to be written by applications and makes
+* for easier testing, but unless you're positive you know what you're doing you
+* probably want to just call x509_path_validate instead.
+*/
+namespace PKIX {
+
+Certificate_Status_Code
+build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths,
+                            const std::vector<Certificate_Store*>& trusted_certstores,
+                            const std::shared_ptr<const X509_Certificate>& end_entity,
+                            const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+
+/**
+* Build certificate path
+* @param cert_path_out output parameter, cert_path will be appended to this vector
+* @param trusted_certstores list of certificate stores that contain trusted certificates
+* @param end_entity the cert to be validated
+* @param end_entity_extra optional list of additional untrusted certs for path building
+* @return result of the path building operation (OK or error)
+*/
+Certificate_Status_Code
+BOTAN_PUBLIC_API(2,0) build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path_out,
+                                 const std::vector<Certificate_Store*>& trusted_certstores,
+                                 const std::shared_ptr<const X509_Certificate>& end_entity,
+                                 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+/**
+* Check the certificate chain, but not any revocation data
+*
+* @param cert_path path built by build_certificate_path with OK result
+* @param ref_time whatever time you want to perform the validation
+* against (normally current system clock)
+* @param hostname the hostname
+* @param usage end entity usage checks
+* @param min_signature_algo_strength 80 or 110 typically
+* Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2.
+* Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
+* @param trusted_hashes set of trusted hash functions, empty means accept any
+* hash we have an OID for
+* @return vector of results on per certificate in the path, each containing a set of
+* results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS,
+* then the result for that certificate is successful. If all results are
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                      std::chrono::system_clock::time_point ref_time,
+                      const std::string& hostname,
+                      Usage_Type usage,
+                      size_t min_signature_algo_strength,
+                      const std::set<std::string>& trusted_hashes);
+
+/**
+* Check OCSP responses for revocation information
+* @param cert_path path already validated by check_chain
+* @param ocsp_responses the OCSP responses to consider
+* @param certstores trusted roots
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                                  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+                                  const std::vector<Certificate_Store*>& certstores,
+                                  std::chrono::system_clock::time_point ref_time,
+                                  std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param crls the list of CRLs to check, it is assumed that crls[i] (if not null)
+* is the associated CRL for the subject in cert_path[i].
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                    const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+                    std::chrono::system_clock::time_point ref_time);
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param certstores a list of certificate stores to query for the CRL
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                    const std::vector<Certificate_Store*>& certstores,
+                    std::chrono::system_clock::time_point ref_time);
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+/**
+* Check OCSP using online (HTTP) access. Current version creates a thread and
+* network connection per OCSP request made.
+*
+* @param cert_path path already validated by check_chain
+* @param trusted_certstores a list of certstores with trusted certs
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param timeout for timing out the responses, though actually this function
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @param ocsp_check_intermediate_CAs if true also performs OCSP on any intermediate
+* CA certificates. If false, only does OCSP on the end entity cert.
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+      const std::vector<Certificate_Store*>& trusted_certstores,
+      std::chrono::system_clock::time_point ref_time,
+      std::chrono::milliseconds timeout,
+      bool ocsp_check_intermediate_CAs,
+      std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRL using online (HTTP) access. Current version creates a thread and
+* network connection per CRL access.
+
+* @param cert_path path already validated by check_chain
+* @param trusted_certstores a list of certstores with trusted certs
+* @param certstore_to_recv_crls optional (nullptr to disable), all CRLs
+* retreived will be saved to this cert store.
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param timeout for timing out the responses, though actually this function
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+                           const std::vector<Certificate_Store*>& trusted_certstores,
+                           Certificate_Store_In_Memory* certstore_to_recv_crls,
+                           std::chrono::system_clock::time_point ref_time,
+                           std::chrono::milliseconds timeout);
+
+#endif
+
+/**
+* Find overall status (OK, error) of a validation
+* @param cert_status result of merge_revocation_status or check_chain
+*/
+Certificate_Status_Code BOTAN_PUBLIC_API(2,0) overall_status(const CertificatePathStatusCodes& cert_status);
+
+/**
+* Merge the results from CRL and/or OCSP checks into chain_status
+* @param chain_status the certificate status
+* @param crl_status results from check_crl
+* @param ocsp_status results from check_ocsp
+* @param require_rev_on_end_entity require valid CRL or OCSP on end-entity cert
+* @param require_rev_on_intermediates require valid CRL or OCSP on all intermediate certificates
+*/
+void BOTAN_PUBLIC_API(2,0) merge_revocation_status(CertificatePathStatusCodes& chain_status,
+                                       const CertificatePathStatusCodes& crl_status,
+                                       const CertificatePathStatusCodes& ocsp_status,
+                                       bool require_rev_on_end_entity,
+                                       bool require_rev_on_intermediates);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/lib/x509/x509self.cpp b/third_party/botan/src/lib/x509/x509self.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509self.cpp
@@ -0,0 +1,151 @@
+/*
+* PKCS #10/Self Signed Cert Creation
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/x509_ext.h>
+#include <botan/x509_ca.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Load information from the X509_Cert_Options
+*/
+void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
+               AlternativeName& subject_alt)
+   {
+   subject_dn.add_attribute("X520.CommonName", opts.common_name);
+   subject_dn.add_attribute("X520.Country", opts.country);
+   subject_dn.add_attribute("X520.State", opts.state);
+   subject_dn.add_attribute("X520.Locality", opts.locality);
+   subject_dn.add_attribute("X520.Organization", opts.organization);
+   subject_dn.add_attribute("X520.OrganizationalUnit", opts.org_unit);
+   for(auto extra_ou : opts.more_org_units) {
+      subject_dn.add_attribute("X520.OrganizationalUnit", extra_ou);
+   }
+
+   subject_dn.add_attribute("X520.SerialNumber", opts.serial_number);
+   subject_alt = AlternativeName(opts.email, opts.uri, opts.dns, opts.ip);
+   subject_alt.add_othername(OID::from_string("PKIX.XMPPAddr"),
+                             opts.xmpp, UTF8_STRING);
+
+   for(auto dns : opts.more_dns)
+      subject_alt.add_attribute("DNS", dns);
+   }
+}
+
+namespace X509 {
+
+/*
+* Create a new self-signed X.509 certificate
+*/
+X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
+                                         const Private_Key& key,
+                                         const std::string& hash_fn,
+                                         RandomNumberGenerator& rng)
+   {
+   AlgorithmIdentifier sig_algo;
+   X509_DN subject_dn;
+   AlternativeName subject_alt;
+
+   // for now, only the padding option is used
+   std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} };
+
+   const std::vector<uint8_t> pub_key = X509::BER_encode(key);
+   std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo));
+   BOTAN_ASSERT_NOMSG(sig_algo.get_oid().has_value());
+   load_info(opts, subject_dn, subject_alt);
+
+   Extensions extensions = opts.extensions;
+
+   Key_Constraints constraints;
+   if(opts.is_CA)
+      {
+      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+      }
+   else
+      {
+      verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+      constraints = opts.constraints;
+      }
+
+   extensions.add_new(
+      new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit),
+      true);
+
+   if(constraints != NO_CONSTRAINTS)
+      {
+      extensions.add_new(new Cert_Extension::Key_Usage(constraints), true);
+      }
+
+   std::unique_ptr<Cert_Extension::Subject_Key_ID> skid(new Cert_Extension::Subject_Key_ID(pub_key, hash_fn));
+
+   extensions.add_new(new Cert_Extension::Authority_Key_ID(skid->get_key_id()));
+   extensions.add_new(skid.release());
+
+   extensions.add_new(
+      new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+   extensions.add_new(
+      new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+
+   return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key,
+                             opts.start, opts.end,
+                             subject_dn, subject_dn,
+                             extensions);
+   }
+
+/*
+* Create a PKCS #10 certificate request
+*/
+PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+                               const Private_Key& key,
+                               const std::string& hash_fn,
+                               RandomNumberGenerator& rng)
+   {
+   X509_DN subject_dn;
+   AlternativeName subject_alt;
+   load_info(opts, subject_dn, subject_alt);
+
+   Key_Constraints constraints;
+   if(opts.is_CA)
+      {
+      constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+      }
+   else
+      {
+      verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+      constraints = opts.constraints;
+      }
+
+   Extensions extensions = opts.extensions;
+
+   extensions.add_new(new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+
+   if(constraints != NO_CONSTRAINTS)
+      {
+      extensions.add_new(new Cert_Extension::Key_Usage(constraints));
+      }
+   extensions.add_new(new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+   extensions.add_new(new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+   return PKCS10_Request::create(key,
+                                 subject_dn,
+                                 extensions,
+                                 hash_fn,
+                                 rng,
+                                 opts.padding_scheme,
+                                 opts.challenge);
+   }
+
+}
+
+}
diff --git a/third_party/botan/src/lib/x509/x509self.h b/third_party/botan/src/lib/x509/x509self.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/lib/x509/x509self.h
@@ -0,0 +1,223 @@
+/*
+* X.509 Self-Signed Certificate
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_SELF_H_
+#define BOTAN_X509_SELF_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/pkcs10.h>
+#include <botan/asn1_time.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class Private_Key;
+
+/**
+* Options for X.509 certificates.
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Cert_Options final
+   {
+   public:
+      /**
+      * the subject common name
+      */
+      std::string common_name;
+
+      /**
+      * the subject counry
+      */
+      std::string country;
+
+      /**
+      * the subject organization
+      */
+      std::string organization;
+
+      /**
+      * the subject organizational unit
+      */
+      std::string org_unit;
+
+      /**
+       * additional subject organizational units.
+       */
+      std::vector<std::string> more_org_units;
+
+      /**
+      * the subject locality
+      */
+      std::string locality;
+
+      /**
+      * the subject state
+      */
+      std::string state;
+
+      /**
+      * the subject serial number
+      */
+      std::string serial_number;
+
+      /**
+      * the subject email adress
+      */
+      std::string email;
+
+      /**
+      * the subject URI
+      */
+      std::string uri;
+
+      /**
+      * the subject IPv4 address
+      */
+      std::string ip;
+
+      /**
+      * the subject DNS
+      */
+      std::string dns;
+
+      /**
+       * additional subject DNS entries.
+       */
+      std::vector<std::string> more_dns;
+
+      /**
+      * the subject XMPP
+      */
+      std::string xmpp;
+
+      /**
+      * the subject challenge password
+      */
+      std::string challenge;
+
+      /**
+      * the subject notBefore
+      */
+      X509_Time start;
+      /**
+      * the subject notAfter
+      */
+      X509_Time end;
+
+      /**
+      * Indicates whether the certificate request
+      */
+      bool is_CA;
+
+      /**
+      * Indicates the BasicConstraints path limit
+      */
+      size_t path_limit;
+
+      std::string padding_scheme;
+
+      /**
+      * The key constraints for the subject public key
+      */
+      Key_Constraints constraints;
+
+      /**
+      * The key extended constraints for the subject public key
+      */
+      std::vector<OID> ex_constraints;
+
+      /**
+      * Additional X.509 extensions
+      */
+      Extensions extensions;
+
+      /**
+      * Mark the certificate as a CA certificate and set the path limit.
+      * @param limit the path limit to be set in the BasicConstraints extension.
+      */
+      void CA_key(size_t limit = 1);
+
+      /**
+      * Choose a padding scheme different from the default for the key used.
+      */
+      void set_padding_scheme(const std::string& scheme);
+
+      /**
+      * Set the notBefore of the certificate.
+      * @param time the notBefore value of the certificate
+      */
+      void not_before(const std::string& time);
+
+      /**
+      * Set the notAfter of the certificate.
+      * @param time the notAfter value of the certificate
+      */
+      void not_after(const std::string& time);
+
+      /**
+      * Add the key constraints of the KeyUsage extension.
+      * @param constr the constraints to set
+      */
+      void add_constraints(Key_Constraints constr);
+
+      /**
+      * Add constraints to the ExtendedKeyUsage extension.
+      * @param oid the oid to add
+      */
+      void add_ex_constraint(const OID& oid);
+
+      /**
+      * Add constraints to the ExtendedKeyUsage extension.
+      * @param name the name to look up the oid to add
+      */
+      void add_ex_constraint(const std::string& name);
+
+      /**
+      * Construct a new options object
+      * @param opts define the common name of this object. An example for this
+      * parameter would be "common_name/country/organization/organizational_unit".
+      * @param expire_time the expiration time (from the current clock in seconds)
+      */
+      X509_Cert_Options(const std::string& opts = "",
+                        uint32_t expire_time = 365 * 24 * 60 * 60);
+   };
+
+namespace X509 {
+
+/**
+* Create a self-signed X.509 certificate.
+* @param opts the options defining the certificate to create
+* @param key the private key used for signing, i.e. the key
+* associated with this self-signed certificate
+* @param hash_fn the hash function to use
+* @param rng the rng to use
+* @return newly created self-signed certificate
+*/
+BOTAN_PUBLIC_API(2,0) X509_Certificate
+create_self_signed_cert(const X509_Cert_Options& opts,
+                        const Private_Key& key,
+                        const std::string& hash_fn,
+                        RandomNumberGenerator& rng);
+
+/**
+* Create a PKCS#10 certificate request.
+* @param opts the options defining the request to create
+* @param key the key used to sign this request
+* @param rng the rng to use
+* @param hash_fn the hash function to use
+* @return newly created PKCS#10 request
+*/
+BOTAN_PUBLIC_API(2,0) PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+                                         const Private_Key& key,
+                                         const std::string& hash_fn,
+                                         RandomNumberGenerator& rng);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/main.cpp b/third_party/botan/src/tests/main.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/main.cpp
@@ -0,0 +1,133 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "../cli/argparse.h"
+#include "test_runner.h"
+#include "tests.h"
+#include <iostream>
+#include <vector>
+#include <string>
+#include <sstream>
+
+#include <botan/version.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+   #include <botan/internal/openssl.h>
+#endif
+
+namespace {
+
+std::string help_text(const std::string& spec)
+   {
+   std::ostringstream err;
+
+   err << "Usage: " << spec << "\n\n"
+       << "Available test suites\n"
+       << "----------------\n";
+
+   size_t line_len = 0;
+
+   for(auto const& test : Botan_Tests::Test::registered_tests())
+      {
+      err << test << " ";
+      line_len += test.size() + 1;
+
+      if(line_len > 64)
+         {
+         err << "\n";
+         line_len = 0;
+         }
+      }
+
+   if(line_len > 0)
+      {
+      err << "\n";
+      }
+
+   return err.str();
+   }
+
+}
+
+int main(int argc, char* argv[])
+   {
+   std::cerr << Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
+
+   try
+      {
+      const std::string arg_spec =
+         "botan-test --verbose --help --data-dir= --pkcs11-lib= --provider= "
+         "--log-success --abort-on-first-fail --no-avoid-undefined --skip-tests= "
+         "--test-threads=0 --run-long-tests --run-online-tests --test-runs=1 --drbg-seed= "
+         "*suites";
+
+      Botan_CLI::Argument_Parser parser(arg_spec);
+
+      parser.parse_args(std::vector<std::string>(argv + 1, argv + argc));
+
+      if(parser.flag_set("help"))
+         {
+         std::cout << help_text(arg_spec);
+         return 0;
+         }
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_HAS_THREAD_UTILS)
+      /*
+      The mlock pool becomes a major contention point when many threads are running,
+      so disable it unless it was explicitly asked for via setting the env variable
+      */
+      if(parser.get_arg_sz("test-threads") != 1)
+         {
+         ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", /*overwrite=*/0);
+         }
+#endif
+
+      const Botan_Tests::Test_Options opts(
+         parser.get_arg_list("suites"),
+         parser.get_arg_list("skip-tests"),
+         parser.get_arg_or("data-dir", "src/tests/data"),
+         parser.get_arg("pkcs11-lib"),
+         parser.get_arg("provider"),
+         parser.get_arg("drbg-seed"),
+         parser.get_arg_sz("test-runs"),
+         parser.get_arg_sz("test-threads"),
+         parser.flag_set("verbose"),
+         parser.flag_set("log-success"),
+         parser.flag_set("run-online-tests"),
+         parser.flag_set("run-long-tests"),
+         parser.flag_set("abort-on-first-fail"));
+
+#if defined(BOTAN_HAS_OPENSSL)
+      if(opts.provider().empty() || opts.provider() == "openssl")
+         {
+         ::ERR_load_crypto_strings();
+         }
+#endif
+
+      Botan_Tests::Test_Runner tests(std::cout);
+
+      int rc = tests.run(opts);
+
+#if defined(BOTAN_HAS_OPENSSL) && defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x01010000)
+      if(opts.provider().empty() || opts.provider() == "openssl")
+         {
+         ERR_free_strings();
+         ::ERR_remove_thread_state(nullptr);
+         }
+#endif
+
+      return rc;
+      }
+   catch(std::exception& e)
+      {
+      std::cerr << "Exiting with error: " << e.what() << std::endl;
+      }
+   catch(...)
+      {
+      std::cerr << "Exiting with unknown exception" << std::endl;
+      }
+   return 2;
+   }
diff --git a/third_party/botan/src/tests/test_aead.cpp b/third_party/botan/src/tests/test_aead.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_aead.cpp
@@ -0,0 +1,468 @@
+/*
+* (C) 2014,2015,2016,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+   #include <botan/aead.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+
+class AEAD_Tests final : public Text_Based_Test
+   {
+   public:
+      AEAD_Tests() : Text_Based_Test("aead", "Key,Nonce,In,Out", "AD") {}
+
+      Test::Result test_enc(const std::vector<uint8_t>& key, const std::vector<uint8_t>& nonce,
+                            const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected,
+                            const std::vector<uint8_t>& ad, const std::string& algo)
+         {
+         const bool is_siv = algo.find("/SIV") != std::string::npos;
+
+         Test::Result result(algo);
+
+         std::unique_ptr<Botan::AEAD_Mode> enc(Botan::AEAD_Mode::create(algo, Botan::ENCRYPTION));
+
+         result.test_eq("AEAD encrypt output_length is correct", enc->output_length(input.size()), expected.size());
+
+         result.confirm("AEAD name is not empty", !enc->name().empty());
+         result.confirm("AEAD default nonce size is accepted", enc->valid_nonce_length(enc->default_nonce_length()));
+
+         Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
+
+         if(is_siv == false)
+            {
+            result.test_throws("Unkeyed object throws for encrypt",
+                               [&]() { enc->update(garbage); });
+            }
+
+         result.test_throws("Unkeyed object throws for encrypt",
+                            [&]() { enc->finish(garbage); });
+
+         if(enc->associated_data_requires_key())
+            {
+            result.test_throws("Unkeyed object throws for set AD",
+                               [&]() { enc->set_associated_data(ad.data(), ad.size()); });
+            }
+
+         // Ensure that test resets AD and message state
+         enc->set_key(key);
+
+         if(is_siv == false)
+            {
+            result.test_throws("Cannot process data until nonce is set (enc)",
+                               [&]() { enc->update(garbage); });
+            result.test_throws("Cannot process data until nonce is set (enc)",
+                               [&]() { enc->finish(garbage); });
+            }
+
+         enc->set_ad(mutate_vec(ad));
+         enc->start(mutate_vec(nonce));
+         enc->update(garbage);
+
+         // reset message specific state
+         enc->reset();
+
+         /*
+         Now try to set the AD *after* setting the nonce
+         For some modes this works, for others it does not.
+         */
+         enc->start(nonce);
+
+         try
+            {
+            enc->set_ad(ad);
+            }
+         catch(Botan::Invalid_State&)
+            {
+            // ad after setting nonce rejected, in this case we need to reset
+            enc->reset();
+            enc->set_ad(ad);
+            enc->start(nonce);
+            }
+
+         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+
+         // have to check here first if input is empty if not we can test update() and eventually process()
+         if(buf.empty())
+            {
+            enc->finish(buf);
+            result.test_eq("encrypt with empty input", buf, expected);
+            }
+         else
+            {
+            // test finish() with full input
+            enc->finish(buf);
+            result.test_eq("encrypt full", buf, expected);
+
+            // additionally test update() if possible
+            const size_t update_granularity = enc->update_granularity();
+            if(input.size() > update_granularity)
+               {
+               // reset state first
+               enc->reset();
+
+               enc->set_ad(ad);
+               enc->start(nonce);
+
+               buf.assign(input.begin(), input.end());
+               size_t input_length = buf.size();
+               size_t offset = 0;
+               uint8_t* p = buf.data();
+               Botan::secure_vector<uint8_t> block(update_granularity);
+               Botan::secure_vector<uint8_t> ciphertext(enc->output_length(buf.size()));
+               while(input_length > update_granularity && ((input_length - update_granularity) >= enc->minimum_final_size()))
+                  {
+                  block.assign(p, p + update_granularity);
+                  enc->update(block);
+                  p += update_granularity;
+                  input_length -= update_granularity;
+                  buffer_insert(ciphertext, 0 + offset, block);
+                  offset += block.size();
+                  }
+
+               // encrypt remaining bytes
+               block.assign(p, p + input_length);
+               enc->finish(block);
+               buffer_insert(ciphertext, 0 + offset, block);
+
+               result.test_eq("encrypt update", ciphertext, expected);
+               }
+
+            // additionally test process() if possible
+            size_t min_final_bytes = enc->minimum_final_size();
+            if(input.size() > (update_granularity + min_final_bytes))
+               {
+               // again reset state first
+               enc->reset();
+
+               enc->set_ad(ad);
+               enc->start(nonce);
+
+               buf.assign(input.begin(), input.end());
+
+               // we can process at max input.size()
+               const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity;
+               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+
+               const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
+
+               if(bytes_written == 0)
+                  {
+                  // SIV case
+                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
+                  enc->finish(buf);
+                  }
+               else
+                  {
+                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+                  enc->finish(buf, bytes_written);
+                  }
+
+               result.test_eq("encrypt process", buf, expected);
+               }
+            }
+
+         enc->clear();
+
+         result.test_throws("Unkeyed object throws for encrypt after clear",
+                            [&]() { enc->finish(buf); });
+
+         if(enc->associated_data_requires_key())
+            {
+            result.test_throws("Unkeyed object throws for set AD after clear",
+                               [&]() { enc->set_associated_data(ad.data(), ad.size()); });
+            }
+
+         return result;
+         }
+
+      Test::Result test_dec(const std::vector<uint8_t>& key, const std::vector<uint8_t>& nonce,
+                            const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected,
+                            const std::vector<uint8_t>& ad, const std::string& algo)
+         {
+         const bool is_siv = algo.find("/SIV") != std::string::npos;
+
+         Test::Result result(algo);
+
+         std::unique_ptr<Botan::AEAD_Mode> dec(Botan::AEAD_Mode::create(algo, Botan::DECRYPTION));
+
+         result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size());
+
+         Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(dec->update_granularity());
+
+         if(is_siv == false)
+            {
+            result.test_throws("Unkeyed object throws for decrypt",
+                               [&]() { dec->update(garbage); });
+            }
+
+         result.test_throws("Unkeyed object throws for decrypt",
+                            [&]() { dec->finish(garbage); });
+
+         if(dec->associated_data_requires_key())
+            {
+            result.test_throws("Unkeyed object throws for set AD",
+                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
+            }
+
+         // First some tests for reset() to make sure it resets what we need it to
+         // set garbage values
+         dec->set_key(key);
+         dec->set_ad(mutate_vec(ad));
+
+         if(is_siv == false)
+            {
+            result.test_throws("Cannot process data until nonce is set (dec)",
+                               [&]() { dec->update(garbage); });
+            result.test_throws("Cannot process data until nonce is set (dec)",
+                               [&]() { dec->finish(garbage); });
+            }
+
+         dec->start(mutate_vec(nonce));
+
+         dec->update(garbage);
+
+         // reset message specific state
+         dec->reset();
+
+         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+         try
+            {
+            // now try to decrypt with correct values
+
+            try
+               {
+               dec->start(nonce);
+               dec->set_ad(ad);
+               }
+            catch(Botan::Invalid_State&)
+               {
+               // ad after setting nonce rejected, in this case we need to reset
+               dec->reset();
+               dec->set_ad(ad);
+               dec->start(nonce);
+            }
+
+            // test finish() with full input
+            dec->finish(buf);
+            result.test_eq("decrypt full", buf, expected);
+
+            // additionally test update() if possible
+            const size_t update_granularity = dec->update_granularity();
+            if(input.size() > update_granularity)
+               {
+               // reset state first
+               dec->reset();
+
+               dec->set_ad(ad);
+               dec->start(nonce);
+
+               buf.assign(input.begin(), input.end());
+               size_t input_length = buf.size();
+               size_t offset = 0;
+               uint8_t* p = buf.data();
+               Botan::secure_vector<uint8_t> block(update_granularity);
+               Botan::secure_vector<uint8_t> plaintext(dec->output_length(buf.size()));
+               while((input_length > update_granularity) && ((input_length - update_granularity) >= dec->minimum_final_size()))
+                  {
+                  block.assign(p, p + update_granularity);
+                  dec->update(block);
+                  p += update_granularity;
+                  input_length -= update_granularity;
+                  buffer_insert(plaintext, 0 + offset, block);
+                  offset += block.size();
+                  }
+
+               // decrypt remaining bytes
+               block.assign(p, p + input_length);
+               dec->finish(block);
+               buffer_insert(plaintext, 0 + offset, block);
+
+               result.test_eq("decrypt update", plaintext, expected);
+               }
+
+            // additionally test process() if possible
+            const size_t min_final_size = dec->minimum_final_size();
+            if(input.size() > (update_granularity + min_final_size))
+               {
+               // again reset state first
+               dec->reset();
+
+               dec->set_ad(ad);
+               dec->start(nonce);
+
+               buf.assign(input.begin(), input.end());
+
+               // we can process at max input.size()
+               const size_t max_blocks_to_process = (input.size() - min_final_size) / update_granularity;
+               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+
+               const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
+
+               if(bytes_written == 0)
+                  {
+                  // SIV case
+                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
+                  dec->finish(buf);
+                  }
+               else
+                  {
+                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+                  dec->finish(buf, bytes_to_process);
+                  }
+
+               result.test_eq("decrypt process", buf, expected);
+               }
+
+            }
+         catch(Botan::Exception& e)
+            {
+            result.test_failure("Failure processing AEAD ciphertext", e.what());
+            }
+
+         // test decryption with modified ciphertext
+         const std::vector<uint8_t> mutated_input = mutate_vec(input, true);
+         buf.assign(mutated_input.begin(), mutated_input.end());
+
+         dec->reset();
+
+         dec->set_ad(ad);
+         dec->start(nonce);
+
+         try
+            {
+            dec->finish(buf);
+            result.test_failure("accepted modified message", mutated_input);
+            }
+         catch(Botan::Integrity_Failure&)
+            {
+            result.test_success("correctly rejected modified message");
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("unexpected error while rejecting modified message", e.what());
+            }
+
+         // test decryption with modified nonce
+         if(nonce.size() > 0)
+            {
+            buf.assign(input.begin(), input.end());
+            std::vector<uint8_t> bad_nonce = mutate_vec(nonce);
+
+            dec->reset();
+            dec->set_ad(ad);
+            dec->start(bad_nonce);
+
+            try
+               {
+               dec->finish(buf);
+               result.test_failure("accepted message with modified nonce", bad_nonce);
+               }
+            catch(Botan::Integrity_Failure&)
+               {
+               result.test_success("correctly rejected modified nonce");
+               }
+            catch(std::exception& e)
+               {
+               result.test_failure("unexpected error while rejecting modified nonce", e.what());
+               }
+            }
+
+         // test decryption with modified associated_data
+         const std::vector<uint8_t> bad_ad = mutate_vec(ad, true);
+
+         dec->reset();
+         dec->set_ad(bad_ad);
+
+         dec->start(nonce);
+
+         try
+            {
+            buf.assign(input.begin(), input.end());
+            dec->finish(buf);
+            result.test_failure("accepted message with modified ad", bad_ad);
+            }
+         catch(Botan::Integrity_Failure&)
+            {
+            result.test_success("correctly rejected modified ad");
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("unexpected error while rejecting modified nonce", e.what());
+            }
+
+         dec->clear();
+
+         result.test_throws("Unkeyed object throws for decrypt",
+                            [&]() { dec->finish(buf); });
+
+         if(dec->associated_data_requires_key())
+            {
+            result.test_throws("Unkeyed object throws for set AD",
+                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
+            }
+
+         return result;
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> nonce    = vars.get_opt_bin("Nonce");
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+         const std::vector<uint8_t> ad       = vars.get_opt_bin("AD");
+
+         Test::Result result(algo);
+
+         std::unique_ptr<Botan::AEAD_Mode> enc(Botan::AEAD_Mode::create(algo, Botan::ENCRYPTION));
+         std::unique_ptr<Botan::AEAD_Mode> dec(Botan::AEAD_Mode::create(algo, Botan::DECRYPTION));
+
+         if(!enc || !dec)
+            {
+            result.note_missing(algo);
+            return result;
+            }
+
+         // must be authenticated
+         result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true);
+         result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true);
+
+         const std::string enc_provider = enc->provider();
+         result.test_is_nonempty("enc provider", enc_provider);
+         const std::string dec_provider = enc->provider();
+         result.test_is_nonempty("dec provider", dec_provider);
+
+         result.test_eq("same provider", enc_provider, dec_provider);
+
+         // FFI currently requires this, so assure it is true for all modes
+         result.test_gt("enc buffer sizes ok", enc->update_granularity(), enc->minimum_final_size());
+         result.test_gt("dec buffer sizes ok", dec->update_granularity(), dec->minimum_final_size());
+
+         // test enc
+         result.merge(test_enc(key, nonce, input, expected, ad, algo));
+
+         // test dec
+         result.merge(test_dec(key, nonce, expected, input, ad, algo));
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("aead", AEAD_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_asn1.cpp b/third_party/botan/src/tests/test_asn1.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_asn1.cpp
@@ -0,0 +1,366 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ASN1)
+   #include <botan/der_enc.h>
+   #include <botan/ber_dec.h>
+   #include <botan/asn1_str.h>
+   #include <botan/asn1_print.h>
+   #include <botan/asn1_time.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_ASN1)
+
+namespace {
+
+Test::Result test_ber_stack_recursion()
+   {
+   Test::Result result("BER stack recursion");
+
+   // OSS-Fuzz #813 GitHub #989
+
+   try
+      {
+      const std::vector<uint8_t> in(10000000, 0);
+      Botan::DataSource_Memory input(in.data(), in.size());
+      Botan::BER_Decoder dec(input);
+
+      while(dec.more_items())
+         {
+         Botan::BER_Object obj;
+         dec.get_next(obj);
+         }
+      }
+   catch(Botan::Decoding_Error&)
+      {
+      }
+
+   result.test_success("No crash");
+
+   return result;
+   }
+
+Test::Result test_ber_eoc_decoding_limits()
+   {
+   Test::Result result("BER nested indefinite length");
+
+   // OSS-Fuzz #4353
+
+   Botan::ASN1_Pretty_Printer printer;
+
+   size_t max_eoc_allowed = 0;
+
+   for(size_t len = 1; len < 1024; ++len)
+      {
+      std::vector<uint8_t> buf(4*len);
+
+      /*
+      This constructs a len deep sequence of SEQUENCES each with
+      an indefinite length
+      */
+      for(size_t i = 0; i != 2*len; i += 2)
+         {
+         buf[i  ] = 0x30;
+         buf[i+1] = 0x80;
+         }
+      // remainder of values left as zeros (EOC markers)
+
+      try
+         {
+         printer.print(buf);
+         }
+      catch(Botan::BER_Decoding_Error&)
+         {
+         max_eoc_allowed = len - 1;
+         break;
+         }
+      }
+
+   result.test_eq("EOC limited to prevent stack exhaustion", max_eoc_allowed, 16);
+
+   return result;
+   }
+
+Test::Result test_asn1_utf8_ascii_parsing()
+   {
+   Test::Result result("ASN.1 ASCII parsing");
+
+   try
+      {
+      // \x13 - ASN1 tag for 'printable string'
+      // \x06 - 6 characters of payload
+      // ...  - UTF-8 encoded (ASCII chars only) word 'Moscow'
+      const std::string moscow =
+         "\x13\x06\x4D\x6F\x73\x63\x6F\x77";
+      const std::string moscow_plain = "Moscow";
+      Botan::DataSource_Memory input(moscow.data());
+      Botan::BER_Decoder dec(input);
+
+      Botan::ASN1_String str;
+      str.decode_from(dec);
+
+      result.test_eq("value()", str.value(), moscow_plain);
+      }
+   catch(const Botan::Decoding_Error &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_asn1_utf8_parsing()
+   {
+   Test::Result result("ASN.1 UTF-8 parsing");
+
+   try
+      {
+      // \x0C - ASN1 tag for 'UTF8 string'
+      // \x0C - 12 characters of payload
+      // ...  - UTF-8 encoded russian word for Moscow in cyrillic script
+      const std::string moscow =
+         "\x0C\x0C\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+      const std::string moscow_plain =
+         "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+      Botan::DataSource_Memory input(moscow.data());
+      Botan::BER_Decoder dec(input);
+
+      Botan::ASN1_String str;
+      str.decode_from(dec);
+
+      result.test_eq("value()", str.value(), moscow_plain);
+      }
+   catch(const Botan::Decoding_Error &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_asn1_ucs2_parsing()
+   {
+   Test::Result result("ASN.1 BMP string (UCS-2) parsing");
+
+   try
+      {
+      // \x1E     - ASN1 tag for 'BMP (UCS-2) string'
+      // \x0C     - 12 characters of payload
+      // ...      - UCS-2 encoding for Moscow in cyrillic script
+      const std::string moscow =
+         "\x1E\x0C\x04\x1C\x04\x3E\x04\x41\x04\x3A\x04\x32\x04\x30";
+      const std::string moscow_plain =
+         "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+
+      Botan::DataSource_Memory input(moscow.data());
+      Botan::BER_Decoder dec(input);
+
+      Botan::ASN1_String str;
+      str.decode_from(dec);
+
+      result.test_eq("value()", str.value(), moscow_plain);
+      }
+   catch(const Botan::Decoding_Error &ex)
+      {
+         result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_asn1_ucs4_parsing()
+   {
+   Test::Result result("ASN.1 universal string (UCS-4) parsing");
+
+   try
+      {
+      // \x1C - ASN1 tag for 'universal string'
+      // \x18 - 24 characters of payload
+      // ...  - UCS-4 encoding for Moscow in cyrillic script
+      const Botan::byte moscow[] =
+         "\x1C\x18\x00\x00\x04\x1C\x00\x00\x04\x3E\x00\x00\x04\x41\x00\x00\x04\x3A\x00\x00\x04\x32\x00\x00\x04\x30";
+      const std::string moscow_plain =
+         "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+      Botan::DataSource_Memory input(moscow, sizeof(moscow));
+      Botan::BER_Decoder dec(input);
+
+      Botan::ASN1_String str;
+      str.decode_from(dec);
+
+      result.test_eq("value()", str.value(), moscow_plain);
+      }
+   catch(const Botan::Decoding_Error &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_asn1_ascii_encoding()
+   {
+   Test::Result result("ASN.1 ASCII encoding");
+
+   try
+      {
+      // UTF-8 encoded (ASCII chars only) word 'Moscow'
+      const std::string moscow =
+         "\x4D\x6F\x73\x63\x6F\x77";
+      Botan::ASN1_String str(moscow);
+
+      Botan::DER_Encoder enc;
+
+      str.encode_into(enc);
+      auto encodingResult = enc.get_contents();
+
+      // \x13 - ASN1 tag for 'printable string'
+      // \x06 - 6 characters of payload
+      const auto moscowEncoded = Botan::hex_decode("13064D6F73636F77");
+      result.test_eq("encoding result", encodingResult, moscowEncoded);
+
+      result.test_success("No crash");
+      }
+   catch(const std::exception &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_asn1_utf8_encoding()
+   {
+   Test::Result result("ASN.1 UTF-8 encoding");
+
+   try
+      {
+      // UTF-8 encoded russian word for Moscow in cyrillic script
+      const std::string moscow =
+         "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+      Botan::ASN1_String str(moscow);
+
+      Botan::DER_Encoder enc;
+
+      str.encode_into(enc);
+      auto encodingResult = enc.get_contents();
+
+      // \x0C - ASN1 tag for 'UTF8 string'
+      // \x0C - 12 characters of payload
+      const auto moscowEncoded =
+         Botan::hex_decode("0C0CD09CD0BED181D0BAD0B2D0B0");
+      result.test_eq("encoding result", encodingResult, moscowEncoded);
+
+      result.test_success("No crash");
+      }
+   catch(const std::exception &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+}
+
+class ASN1_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_ber_stack_recursion());
+         results.push_back(test_ber_eoc_decoding_limits());
+         results.push_back(test_asn1_utf8_ascii_parsing());
+         results.push_back(test_asn1_utf8_parsing());
+         results.push_back(test_asn1_ucs2_parsing());
+         results.push_back(test_asn1_ucs4_parsing());
+         results.push_back(test_asn1_ascii_encoding());
+         results.push_back(test_asn1_utf8_encoding());
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("asn1", ASN1_Tests);
+
+class ASN1_Time_Parsing_Tests final : public Text_Based_Test
+   {
+   public:
+      ASN1_Time_Parsing_Tests() :
+         Text_Based_Test("asn1_time.vec", "Tspec") {}
+
+      Test::Result run_one_test(const std::string& tag_str, const VarMap& vars) override
+         {
+         Test::Result result("ASN.1 date parsing");
+
+         const std::string tspec = vars.get_req_str("Tspec");
+
+         if(tag_str != "UTC" &&
+            tag_str != "UTC.invalid" &&
+            tag_str != "Generalized" &&
+            tag_str != "Generalized.invalid")
+            {
+            throw Test_Error("Invalid tag value in ASN1 date parsing test");
+            }
+
+         const Botan::ASN1_Tag tag =
+            (tag_str == "UTC" || tag_str == "UTC.invalid") ? Botan::UTC_TIME : Botan::GENERALIZED_TIME;
+
+         const bool valid = tag_str.find(".invalid") == std::string::npos;
+
+         if(valid)
+            {
+            Botan::ASN1_Time time(tspec, tag);
+            result.test_success("Accepted valid time");
+            }
+         else
+            {
+            result.test_throws("Invalid time rejected", [=]() {
+               Botan::ASN1_Time time(tspec, tag);
+               });
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("asn1_time", ASN1_Time_Parsing_Tests);
+
+class ASN1_Printer_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("ASN1_Pretty_Printer");
+
+         Botan::ASN1_Pretty_Printer printer;
+
+         const size_t num_tests = 6;
+
+         for(size_t i = 1; i <= num_tests; ++i)
+            {
+            std::string i_str = std::to_string(i);
+            const std::vector<uint8_t> input1 = Test::read_binary_data_file("asn1_print/input" + i_str + ".der");
+            const std::string expected1 = Test::read_data_file("asn1_print/output" + i_str + ".txt");
+
+            result.test_eq("Test " + i_str, printer.print(input1), expected1);
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("asn1_printer", ASN1_Printer_Tests);
+
+#endif
+
+}
+
diff --git a/third_party/botan/src/tests/test_bigint.cpp b/third_party/botan/src/tests/test_bigint.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_bigint.cpp
@@ -0,0 +1,807 @@
+/*
+* (C) 2009,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+   #include <botan/bigint.h>
+   #include <botan/numthry.h>
+   #include <botan/divide.h>
+   #include <botan/internal/primality.h>
+   #include <botan/reducer.h>
+   #include <botan/pow_mod.h>
+   #include <botan/parsing.h>
+   #include "test_rng.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+using Botan::BigInt;
+
+class BigInt_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_bigint_sizes());
+         results.push_back(test_random_prime());
+         results.push_back(test_encode());
+         results.push_back(test_bigint_io());
+         results.push_back(test_get_substring());
+
+         return results;
+         }
+   private:
+      Test::Result test_bigint_sizes()
+         {
+         Test::Result result("BigInt size functions");
+
+         for(size_t bit : { 1, 8, 16, 31, 32, 64, 97, 128, 179, 192, 512, 521 })
+            {
+            BigInt a;
+
+            a.set_bit(bit);
+
+            // Test 2^n and 2^n-1
+            for(size_t i = 0; i != 2; ++i)
+               {
+               const size_t exp_bits = bit + 1 - i;
+               result.test_eq("BigInt::bits", a.bits(), exp_bits);
+               result.test_eq("BigInt::bytes", a.bytes(),
+                              (exp_bits % 8 == 0) ? (exp_bits / 8) : (exp_bits + 8 - exp_bits % 8) / 8);
+
+               if(bit == 1 && i == 1)
+                  {
+                  result.test_is_eq("BigInt::to_u32bit zero", a.to_u32bit(), static_cast<uint32_t>(1));
+                  }
+               else if(bit <= 31 || (bit == 32 && i == 1))
+                  {
+                  result.test_is_eq("BigInt::to_u32bit", a.to_u32bit(), static_cast<uint32_t>((uint64_t(1) << bit) - i));
+                  }
+               else
+                  {
+                  try
+                     {
+                     a.to_u32bit();
+                     result.test_failure("BigInt::to_u32bit roundtripped out of range value");
+                     }
+                  catch(std::exception&)
+                     {
+                     result.test_success("BigInt::to_u32bit rejected out of range");
+                     }
+                  }
+
+               a--;
+               }
+            }
+
+         return result;
+         }
+
+      Test::Result test_random_prime()
+         {
+         Test::Result result("BigInt prime generation");
+
+         result.test_throws("Invalid bit size",
+                            "random_prime: Can't make a prime of 0 bits",
+                            []() { Botan::random_prime(Test::rng(), 0); });
+         result.test_throws("Invalid bit size",
+                            "random_prime: Can't make a prime of 1 bits",
+                            []() { Botan::random_prime(Test::rng(), 1); });
+         result.test_throws("Invalid arg",
+                            "random_prime Invalid value for equiv/modulo",
+                            []() { Botan::random_prime(Test::rng(), 2, 1, 0, 2); });
+
+         BigInt p = Botan::random_prime(Test::rng(), 2);
+         result.confirm("Only two 2-bit primes", p == 2 || p == 3);
+
+         p = Botan::random_prime(Test::rng(), 3);
+         result.confirm("Only two 3-bit primes", p == 5 || p == 7);
+
+         p = Botan::random_prime(Test::rng(), 4);
+         result.confirm("Only two 4-bit primes", p == 11 || p == 13);
+
+         for(size_t bits = 5; bits <= 32; ++bits)
+            {
+            p = Botan::random_prime(Test::rng(), bits);
+            result.test_eq("Expected bit size", p.bits(), bits);
+            result.test_eq("P is prime", Botan::is_prime(p, Test::rng()), true);
+            }
+
+         const size_t safe_prime_bits = 65;
+         const BigInt safe_prime = Botan::random_safe_prime(Test::rng(), safe_prime_bits);
+         result.test_eq("Safe prime size", safe_prime.bits(), safe_prime_bits);
+         result.confirm("P is prime", Botan::is_prime(safe_prime, Test::rng()));
+         result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, Test::rng()));
+
+         return result;
+         }
+
+      Test::Result test_encode()
+         {
+         Test::Result result("BigInt encoding functions");
+
+         const BigInt n1(0xffff);
+         const BigInt n2(1023);
+
+         Botan::secure_vector<uint8_t> encoded_n1 = BigInt::encode_1363(n1, 256);
+         Botan::secure_vector<uint8_t> encoded_n2 = BigInt::encode_1363(n2, 256);
+         Botan::secure_vector<uint8_t> expected = encoded_n1;
+         expected += encoded_n2;
+
+         Botan::secure_vector<uint8_t> encoded_n1_n2 = BigInt::encode_fixed_length_int_pair(n1, n2, 256);
+         result.test_eq("encode_fixed_length_int_pair", encoded_n1_n2, expected);
+
+         for(size_t i = 0; i < 256 - n1.bytes(); ++i)
+            {
+            if(encoded_n1[i] != 0)
+               {
+               result.test_failure("encode_1363", "no zero byte");
+               }
+            }
+
+         return result;
+         }
+
+      Test::Result test_get_substring()
+         {
+         Test::Result result("BigInt get_substring");
+
+         const size_t rbits = 1024;
+
+         const Botan::BigInt r(Test::rng(), rbits);
+
+         for(size_t wlen = 1; wlen <= 32; ++wlen)
+            {
+            for(size_t offset = 0; offset != rbits + 64; ++offset)
+               {
+               const uint32_t val = r.get_substring(offset, wlen);
+
+               Botan::BigInt t = r >> offset;
+               t.mask_bits(wlen);
+
+               const uint32_t cmp = t.to_u32bit();
+
+               result.test_eq("Same value", size_t(val), size_t(cmp));
+               }
+            }
+
+         return result;
+         }
+
+      Test::Result test_bigint_io()
+         {
+         Test::Result result("BigInt IO operators");
+
+         const std::map<std::string, Botan::BigInt> str_to_val =
+            {
+               { "-13", -Botan::BigInt(13) },
+               { "0", Botan::BigInt(0) },
+               { "0x13", Botan::BigInt(0x13) },
+               { "1", Botan::BigInt(1) },
+               { "4294967297", Botan::BigInt(2147483648) * 2 + 1 }
+            };
+
+         for(auto vec : str_to_val)
+            {
+            Botan::BigInt n;
+            std::istringstream iss;
+
+            iss.str(vec.first);
+            iss >> n;
+            result.test_eq("input '" + vec.first + "'", n, vec.second);
+            }
+
+         BigInt n = 33;
+
+         std::ostringstream oss;
+         oss << n;
+         result.test_eq("output 33 dec", oss.str(), "33");
+
+         oss.str("");
+         oss << std::hex << n;
+         result.test_eq("output 33 hex", oss.str(), "21");
+
+         result.test_throws("octal output not supported", [&]() { oss << std::oct << n; });
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bigint_unit", BigInt_Unit_Tests);
+
+class BigInt_Add_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Add_Test() : Text_Based_Test("bn/add.vec", "In1,In2,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Addition");
+
+         using Botan::BigInt;
+
+         const BigInt a = vars.get_req_bn("In1");
+         const BigInt b = vars.get_req_bn("In2");
+         const BigInt c = vars.get_req_bn("Output");
+
+         result.test_eq("a + b", a + b, c);
+         result.test_eq("b + a", b + a, c);
+
+         BigInt e = a;
+         e += b;
+         result.test_eq("a += b", e, c);
+
+         e = b;
+         e += a;
+         result.test_eq("b += a", e, c);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("bn_add", BigInt_Add_Test);
+
+class BigInt_Sub_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Sub_Test() : Text_Based_Test("bn/sub.vec", "In1,In2,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Subtraction");
+
+         const BigInt a = vars.get_req_bn("In1");
+         const BigInt b = vars.get_req_bn("In2");
+         const BigInt c = vars.get_req_bn("Output");
+
+         result.test_eq("a - b", a - b, c);
+
+         BigInt e = a;
+         e -= b;
+         result.test_eq("a -= b", e, c);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_sub", BigInt_Sub_Test);
+
+class BigInt_Mul_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Mul_Test() : Text_Based_Test("bn/mul.vec", "In1,In2,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Multiply");
+
+         const BigInt a = vars.get_req_bn("In1");
+         const BigInt b = vars.get_req_bn("In2");
+         const BigInt c = vars.get_req_bn("Output");
+
+         result.test_eq("a * b", a * b, c);
+         result.test_eq("b * a", b * a, c);
+
+         BigInt e = a;
+         e *= b;
+         result.test_eq("a *= b", e, c);
+
+         e = b;
+         e *= a;
+         result.test_eq("b *= a", e, c);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_mul", BigInt_Mul_Test);
+
+class BigInt_Sqr_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Sqr_Test() : Text_Based_Test("bn/sqr.vec", "Input,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Square");
+
+         const BigInt input = vars.get_req_bn("Input");
+         const BigInt output = vars.get_req_bn("Output");
+
+         result.test_eq("a * a", input * input, output);
+         result.test_eq("sqr(a)", square(input), output);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_sqr", BigInt_Sqr_Test);
+
+class BigInt_Div_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Div_Test() : Text_Based_Test("bn/divide.vec", "In1,In2,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Divide");
+
+         const BigInt a = vars.get_req_bn("In1");
+         const BigInt b = vars.get_req_bn("In2");
+         const BigInt c = vars.get_req_bn("Output");
+
+         result.test_eq("a / b", a / b, c);
+
+         BigInt e = a;
+         e /= b;
+         result.test_eq("a /= b", e, c);
+
+         if(b.bytes() == 1)
+            {
+            const uint8_t b8 = b.byte_at(0);
+
+            Botan::BigInt ct_q;
+            uint8_t ct_r;
+            Botan::ct_divide_u8(a, b8, ct_q, ct_r);
+            result.test_eq("ct_divide_u8 q", ct_q, c);
+            }
+
+         Botan::BigInt ct_q, ct_r;
+         Botan::ct_divide(a, b, ct_q, ct_r);
+         result.test_eq("ct_divide q", ct_q, c);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_div", BigInt_Div_Test);
+
+class BigInt_Mod_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Mod_Test() : Text_Based_Test("bn/mod.vec", "In1,In2,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Mod");
+
+         const BigInt a = vars.get_req_bn("In1");
+         const BigInt b = vars.get_req_bn("In2");
+         const BigInt expected = vars.get_req_bn("Output");
+
+         result.test_eq("a % b", a % b, expected);
+
+         BigInt e = a;
+         e %= b;
+         result.test_eq("a %= b", e, expected);
+
+         const Botan::Modular_Reducer mod_b(b);
+         result.test_eq("Barrett", mod_b.reduce(a), expected);
+
+         // if b fits into a Botan::word test %= operator for words
+         if(b.sig_words() == 1)
+            {
+            const Botan::word b_word = b.word_at(0);
+
+            e = a;
+            e %= b_word;
+            result.test_eq("a %= b (as word)", e, expected);
+
+            result.test_eq("a % b (as word)", a % b_word, expected);
+            }
+
+         if(b.bytes() == 1)
+            {
+            Botan::BigInt ct_q;
+            Botan::uint8_t ct_r;
+            Botan::ct_divide_u8(a, b.byte_at(0), ct_q, ct_r);
+            result.test_eq("ct_divide_u8 r", ct_r, expected);
+            }
+
+         Botan::BigInt ct_q, ct_r;
+         Botan::ct_divide(a, b, ct_q, ct_r);
+         result.test_eq("ct_divide r", ct_r, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_mod", BigInt_Mod_Test);
+
+class BigInt_GCD_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_GCD_Test() : Text_Based_Test("bn/gcd.vec", "X,Y,GCD") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt GCD");
+
+         const BigInt x = vars.get_req_bn("X");
+         const BigInt y = vars.get_req_bn("Y");
+         const BigInt expected = vars.get_req_bn("GCD");
+
+         const BigInt g = Botan::gcd(x, y);
+
+         result.test_eq("gcd", g, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_gcd", BigInt_GCD_Test);
+
+class BigInt_Jacobi_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Jacobi_Test() : Text_Based_Test("bn/jacobi.vec", "A,N,J") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Jacobi");
+
+         const BigInt a = vars.get_req_bn("A");
+         const BigInt n = vars.get_req_bn("N");
+         const std::string expected = vars.get_req_str("J");
+
+         const int32_t j = Botan::jacobi(a, n);
+
+         if(j == 0)
+            result.test_eq("jacobi", expected, "0");
+         else if(j == -1)
+            result.test_eq("jacobi", expected, "-1");
+         else
+            result.test_eq("jacobi", expected, "1");
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_jacobi", BigInt_Jacobi_Test);
+
+class BigInt_Lshift_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Lshift_Test() : Text_Based_Test("bn/lshift.vec", "Value,Shift,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Lshift");
+
+         const BigInt value = vars.get_req_bn("Value");
+         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
+         const BigInt output = vars.get_req_bn("Output");
+
+         result.test_eq("a << s", value << shift, output);
+
+         BigInt e = value;
+         e <<= shift;
+         result.test_eq("a <<= s", e, output);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_lshift", BigInt_Lshift_Test);
+
+class BigInt_Rshift_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Rshift_Test() : Text_Based_Test("bn/rshift.vec", "Value,Shift,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Rshift");
+
+         const BigInt value = vars.get_req_bn("Value");
+         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
+         const BigInt output = vars.get_req_bn("Output");
+
+         result.test_eq("a >> s", value >> shift, output);
+
+         BigInt e = value;
+         e >>= shift;
+         result.test_eq("a >>= s", e, output);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_rshift", BigInt_Rshift_Test);
+
+class BigInt_Powmod_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Powmod_Test() : Text_Based_Test("bn/powmod.vec", "Base,Exponent,Modulus,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Powmod");
+
+         const BigInt base = vars.get_req_bn("Base");
+         const BigInt exponent = vars.get_req_bn("Exponent");
+         const BigInt modulus = vars.get_req_bn("Modulus");
+         const BigInt expected = vars.get_req_bn("Output");
+
+         result.test_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected);
+
+         /*
+         * Only the basic power_mod interface supports negative base
+         */
+         if(base.is_negative())
+            return result;
+
+         Botan::Power_Mod pow_mod1(modulus);
+
+         pow_mod1.set_base(base);
+         pow_mod1.set_exponent(exponent);
+         result.test_eq("pow_mod1", pow_mod1.execute(), expected);
+
+         Botan::Power_Mod pow_mod2(modulus);
+
+         // Reverses ordering which affects window size
+         pow_mod2.set_exponent(exponent);
+         pow_mod2.set_base(base);
+         result.test_eq("pow_mod2", pow_mod2.execute(), expected);
+         result.test_eq("pow_mod2 #2", pow_mod2.execute(), expected);
+
+         if(modulus.is_odd())
+            {
+            // TODO: test different hints
+            // also TODO: remove bogus hinting arguments :)
+            Botan::Power_Mod pow_mod3(modulus, Botan::Power_Mod::NO_HINTS, /*disable_montgomery=*/true);
+
+            pow_mod3.set_exponent(exponent);
+            pow_mod3.set_base(base);
+            result.test_eq("pow_mod_fixed_window", pow_mod3.execute(), expected);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_powmod", BigInt_Powmod_Test);
+
+class BigInt_IsPrime_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_IsPrime_Test() : Text_Based_Test("bn/isprime.vec", "X") {}
+
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override
+         {
+         if(header != "Prime" && header != "NonPrime")
+            {
+            throw Test_Error("Bad header for prime test " + header);
+            }
+
+         const BigInt value = vars.get_req_bn("X");
+         const bool is_prime = (header == "Prime");
+
+         Test::Result result("BigInt Test " + header);
+         result.test_eq("is_prime", Botan::is_prime(value, Test::rng()), is_prime);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_isprime", BigInt_IsPrime_Test);
+
+class BigInt_IsSquare_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_IsSquare_Test() : Text_Based_Test("bn/perfect_square.vec", "X,R") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const BigInt value = vars.get_req_bn("X");
+         const BigInt expected = vars.get_req_bn("R");
+         const BigInt computed = Botan::is_perfect_square(value);
+
+         Test::Result result("BigInt IsSquare");
+         result.test_eq("is_perfect_square", computed, expected);
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_issquare", BigInt_IsSquare_Test);
+
+class BigInt_Ressol_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Ressol_Test() : Text_Based_Test("bn/ressol.vec", "Input,Modulus,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Ressol");
+
+         const Botan::BigInt a = vars.get_req_bn("Input");
+         const Botan::BigInt p = vars.get_req_bn("Modulus");
+         const Botan::BigInt exp = vars.get_req_bn("Output");
+
+         const Botan::BigInt a_sqrt = Botan::ressol(a, p);
+
+         result.test_eq("ressol", a_sqrt, exp);
+
+         if(a_sqrt > 1)
+            {
+            const Botan::BigInt a_sqrt2 = (a_sqrt * a_sqrt) % p;
+            result.test_eq("square correct", a_sqrt2, a);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_ressol", BigInt_Ressol_Test);
+
+class BigInt_InvMod_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_InvMod_Test() : Text_Based_Test("bn/invmod.vec", "Input,Modulus,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt InvMod");
+
+         const Botan::BigInt a = vars.get_req_bn("Input");
+         const Botan::BigInt mod = vars.get_req_bn("Modulus");
+         const Botan::BigInt expected = vars.get_req_bn("Output");
+
+         const Botan::BigInt a_inv = Botan::inverse_mod(a, mod);
+
+         result.test_eq("inverse_mod", a_inv, expected);
+
+         if(a_inv > 1)
+            {
+            result.test_eq("inverse ok", (a * a_inv) % mod, 1);
+            }
+         /*
+         else if((a % mod) > 0)
+            {
+            result.confirm("no inverse with gcd > 1", gcd(a, mod) > 1);
+            }
+         */
+
+         if(mod.is_odd() && a_inv != 0)
+            {
+            result.test_eq("normalized_montgomery_inverse",
+                           normalized_montgomery_inverse(a, mod),
+                           expected);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_invmod", BigInt_InvMod_Test);
+
+class BigInt_Rand_Test final : public Text_Based_Test
+   {
+   public:
+      BigInt_Rand_Test() : Text_Based_Test("bn/random.vec", "Seed,Min,Max,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("BigInt Random");
+
+         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
+         const Botan::BigInt min = vars.get_req_bn("Min");
+         const Botan::BigInt max = vars.get_req_bn("Max");
+         const Botan::BigInt expected = vars.get_req_bn("Output");
+
+         Fixed_Output_RNG rng(seed);
+         Botan::BigInt generated = BigInt::random_integer(rng, min, max);
+
+         result.test_eq("random_integer KAT", generated, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_rand", BigInt_Rand_Test);
+
+class Lucas_Primality_Test final : public Test
+   {
+   public:
+
+      std::vector<Test::Result> run() override
+         {
+         const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 6000);
+
+         // OEIS A217120
+         std::set<uint32_t> lucas_pp{
+            323, 377, 1159, 1829, 3827, 5459, 5777, 9071, 9179,
+            10877, 11419, 11663, 13919, 14839, 16109, 16211, 18407, 18971,
+            19043, 22499, 23407, 24569, 25199, 25877, 26069, 27323, 32759,
+            34943, 35207, 39059, 39203, 39689, 40309, 44099, 46979, 47879,
+            50183, 51983, 53663, 56279, 58519, 60377, 63881, 69509, 72389,
+            73919, 75077, 77219, 79547, 79799, 82983, 84419, 86063, 90287,
+            94667, 97019, 97439,
+         };
+
+         Test::Result result("Lucas primality test");
+
+         for(uint32_t i = 3; i <= lucas_max; i += 2)
+            {
+            Botan::Modular_Reducer mod_i(i);
+            const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i);
+            const bool is_prime = Botan::is_prime(i, Test::rng());
+
+            const bool is_lucas_pp = (is_prime == false && passes_lucas == true);
+
+            if(is_lucas_pp)
+               result.confirm("Lucas pseudoprime is in list", lucas_pp.count(i) == 1);
+            else
+               result.confirm("Lucas non-pseudoprime is not in list", lucas_pp.count(i) == 0);
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("bn_lucas", Lucas_Primality_Test);
+
+class DSA_ParamGen_Test final : public Text_Based_Test
+   {
+   public:
+      DSA_ParamGen_Test() : Text_Based_Test("bn/dsa_gen.vec", "P,Q,Counter,Seed") {}
+
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
+         const size_t offset = vars.get_req_sz("Counter");
+
+         const Botan::BigInt exp_P = vars.get_req_bn("P");
+         const Botan::BigInt exp_Q = vars.get_req_bn("Q");
+
+         const std::vector<std::string> header_parts = Botan::split_on(header, ',');
+
+         if(header_parts.size() != 2)
+            {
+            throw Test_Error("Unexpected header '" + header + "' in DSA param gen test");
+            }
+
+         const size_t p_bits = Botan::to_u32bit(header_parts[1]);
+         const size_t q_bits = Botan::to_u32bit(header_parts[0]);
+
+         Test::Result result("DSA Parameter Generation");
+
+         try
+            {
+            Botan::BigInt gen_P, gen_Q;
+            if(Botan::generate_dsa_primes(Test::rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset))
+               {
+               result.test_eq("P", gen_P, exp_P);
+               result.test_eq("Q", gen_Q, exp_Q);
+               }
+            else
+               {
+               result.test_failure("Seed did not generate a DSA parameter");
+               }
+            }
+         catch(Botan::Lookup_Error&)
+            {
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("dsa_param", DSA_ParamGen_Test);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_block.cpp b/third_party/botan/src/tests/test_block.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_block.cpp
@@ -0,0 +1,203 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+#include <botan/block_cipher.h>
+
+namespace Botan_Tests {
+
+class Block_Cipher_Tests final : public Text_Based_Test
+   {
+   public:
+      Block_Cipher_Tests() : Text_Based_Test("block", "Key,In,Out", "Tweak,Iterations") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::BlockCipher::providers(algo));
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+         const std::vector<uint8_t> tweak    = vars.get_opt_bin("Tweak");
+         const size_t iterations             = vars.get_opt_sz("Iterations", 1);
+
+         Test::Result result(algo);
+
+         if(iterations > 1 && run_long_tests() == false)
+            {
+            return result;
+            }
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(providers.empty())
+            {
+            result.note_missing("block cipher " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create(algo, provider_ask));
+
+            if(!cipher)
+               {
+               result.test_failure("Cipher " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            const std::string provider(cipher->provider());
+            result.test_is_nonempty("provider", provider);
+            result.test_eq(provider, cipher->name(), algo);
+            result.test_gte(provider, cipher->parallelism(), 1);
+            result.test_gte(provider, cipher->block_size(), 8);
+            result.test_gte(provider, cipher->parallel_bytes(), cipher->block_size() * cipher->parallelism());
+
+            // Test that trying to encrypt or decrypt with now key set throws Botan::Invalid_State
+            try
+               {
+               std::vector<uint8_t> block(cipher->block_size());
+               cipher->encrypt(block);
+               result.test_failure("Was able to encrypt without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to encrypt with no key set fails");
+               }
+
+            try
+               {
+               std::vector<uint8_t> block(cipher->block_size());
+               cipher->decrypt(block);
+               result.test_failure("Was able to decrypt without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to encrypt with no key set fails");
+               }
+
+            // Test to make sure clear() resets what we need it to
+            cipher->set_key(Test::rng().random_vec(cipher->key_spec().maximum_keylength()));
+            Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(cipher->block_size());
+            cipher->encrypt(garbage);
+            cipher->clear();
+
+            /*
+            * Different providers may have additional restrictions on key sizes.
+            * Avoid testing the cipher with a key size that it does not natively support.
+            */
+            if(!cipher->valid_keylength(key.size()))
+               {
+               result.test_note("Skipping test with provider " + provider +
+                                " as it does not support key length " + std::to_string(key.size()));
+               continue;
+               }
+
+            cipher->set_key(key);
+
+            if(tweak.size() > 0)
+               {
+               Botan::Tweakable_Block_Cipher* tbc = dynamic_cast<Botan::Tweakable_Block_Cipher*>(cipher.get());
+               if(tbc == nullptr)
+                  result.test_failure("Tweak set in test data but cipher is not a Tweakable_Block_Cipher");
+               else
+                  tbc->set_tweak(tweak.data(), tweak.size());
+               }
+
+            // Test that clone works and does not affect parent object
+            std::unique_ptr<Botan::BlockCipher> clone(cipher->clone());
+            result.confirm("Clone has different pointer", cipher.get() != clone.get());
+            result.test_eq("Clone has same name", cipher->name(), clone->name());
+            clone->set_key(Test::rng().random_vec(cipher->maximum_keylength()));
+
+            // have called set_key on clone: process input values
+            std::vector<uint8_t> buf = input;
+
+            for(size_t i = 0; i != iterations; ++i)
+               {
+               cipher->encrypt(buf);
+               }
+
+            result.test_eq(provider, "encrypt", buf, expected);
+
+            // always decrypt expected ciphertext vs what we produced above
+            buf = expected;
+
+            for(size_t i = 0; i != iterations; ++i)
+               {
+               cipher->decrypt(buf);
+               }
+
+            result.test_eq(provider, "decrypt", buf, input);
+
+            // Now test misaligned buffers
+            const size_t blocks = input.size() / cipher->block_size();
+            buf.resize(input.size() + 1);
+            Botan::copy_mem(buf.data() + 1, input.data(), input.size());
+
+            for(size_t i = 0; i != iterations; ++i)
+               {
+               cipher->encrypt_n(buf.data() + 1, buf.data() + 1, blocks);
+               }
+
+            result.test_eq(provider.c_str(), "encrypt misaligned",
+                           buf.data() + 1, buf.size() - 1,
+                           expected.data(), expected.size());
+
+            // always decrypt expected ciphertext vs what we produced above
+            Botan::copy_mem(buf.data() + 1, expected.data(), expected.size());
+
+            for(size_t i = 0; i != iterations; ++i)
+               {
+               cipher->decrypt_n(buf.data() + 1, buf.data() + 1, blocks);
+               }
+
+            result.test_eq(provider.c_str(), "decrypt misaligned",
+                           buf.data() + 1, buf.size() - 1,
+                           input.data(), input.size());
+
+            cipher->clear();
+
+            try
+               {
+               std::vector<uint8_t> block(cipher->block_size());
+               cipher->encrypt(block);
+               result.test_failure("Was able to encrypt without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to encrypt with no key set (after clear) fails");
+               }
+
+            try
+               {
+               std::vector<uint8_t> block(cipher->block_size());
+               cipher->decrypt(block);
+               result.test_failure("Was able to decrypt without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to decrypt with no key set (after clear) fails");
+               }
+
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("block", Block_Cipher_Tests);
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_blowfish.cpp b/third_party/botan/src/tests/test_blowfish.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_blowfish.cpp
@@ -0,0 +1,46 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_BLOWFISH)
+
+#include <botan/blowfish.h>
+
+namespace Botan_Tests {
+
+class Blowfish_Salted_Tests final : public Text_Based_Test
+   {
+   public:
+      Blowfish_Salted_Tests() : Text_Based_Test("salted_blowfish.vec", "Key,Salt,Out") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("Blowfish salted key schedule");
+
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> salt     = vars.get_req_bin("Salt");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+
+         Botan::Blowfish blowfish;
+
+         blowfish.salted_set_key(key.data(), key.size(),
+                                 salt.data(), salt.size(), 0);
+
+         std::vector<uint8_t> block(8);
+         blowfish.encrypt(block);
+
+         result.test_eq("Expected output", block, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("blowfish_salted", Blowfish_Salted_Tests);
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_c25519.cpp b/third_party/botan/src/tests/test_c25519.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_c25519.cpp
@@ -0,0 +1,172 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_CURVE_25519)
+   #include "test_pubkey.h"
+   #include <botan/curve25519.h>
+   #include <botan/x509_key.h>
+   #include <botan/pkcs8.h>
+   #include <botan/data_src.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_CURVE_25519)
+
+class Curve25519_Sclarmult_Tests final : public Text_Based_Test
+   {
+   public:
+      Curve25519_Sclarmult_Tests() : Text_Based_Test(
+            "pubkey/c25519_scalar.vec",
+            "Secret,Basepoint,Out") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> secret    = vars.get_req_bin("Secret");
+         const std::vector<uint8_t> basepoint = vars.get_req_bin("Basepoint");
+         const std::vector<uint8_t> expected  = vars.get_req_bin("Out");
+
+         std::vector<uint8_t> got(32);
+         Botan::curve25519_donna(got.data(), secret.data(), basepoint.data());
+
+         Test::Result result("Curve25519 scalarmult");
+         result.test_eq("basemult", got, expected);
+         return result;
+         }
+   };
+BOTAN_REGISTER_TEST("curve25519_scalar", Curve25519_Sclarmult_Tests);
+
+class Curve25519_Agreement_Tests final : public PK_Key_Agreement_Test
+   {
+   public:
+      Curve25519_Agreement_Tests() : PK_Key_Agreement_Test(
+         "X25519",
+         "pubkey/x25519.vec",
+         "Secret,CounterKey,K") {}
+
+      std::string default_kdf(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_our_key(const std::string&,
+                                                       const VarMap& vars) override
+         {
+         const std::vector<uint8_t> secret_vec = vars.get_req_bin("Secret");
+         Botan::secure_vector<uint8_t> secret(secret_vec.begin(), secret_vec.end());
+         return std::unique_ptr<Botan::Private_Key>(new Botan::Curve25519_PrivateKey(secret));
+         }
+
+      std::vector<uint8_t> load_their_key(const std::string&, const VarMap& vars) override
+         {
+         return vars.get_req_bin("CounterKey");
+         }
+   };
+BOTAN_REGISTER_TEST("curve25519_agreement", Curve25519_Agreement_Tests);
+
+class Curve25519_Roundtrip_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         for(size_t i = 0; i < 10; ++i)
+            {
+            Test::Result result("Curve25519 roundtrip");
+
+            Botan::Curve25519_PrivateKey a_priv_gen(Test::rng());
+            Botan::Curve25519_PrivateKey b_priv_gen(Test::rng());
+
+#if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32)
+            // Then serialize to encrypted storage
+
+            const std::string a_pass = "alice pass";
+            const std::string b_pass = "bob pass";
+            const auto pbe_time = std::chrono::milliseconds(10);
+            const std::string a_priv_pem = Botan::PKCS8::PEM_encode(a_priv_gen, Test::rng(), a_pass, pbe_time);
+            const std::string b_priv_pem = Botan::PKCS8::PEM_encode(b_priv_gen, Test::rng(), b_pass, pbe_time);
+
+            // Reload back into memory
+            Botan::DataSource_Memory a_priv_ds(a_priv_pem);
+            Botan::DataSource_Memory b_priv_ds(b_priv_pem);
+
+            std::unique_ptr<Botan::Private_Key> a_priv(Botan::PKCS8::load_key(a_priv_ds, Test::rng(), [a_pass]() { return a_pass; }));
+            std::unique_ptr<Botan::Private_Key> b_priv(Botan::PKCS8::load_key(b_priv_ds, Test::rng(), b_pass));
+#else
+            const std::string a_priv_pem = Botan::PKCS8::PEM_encode(a_priv_gen);
+            const std::string b_priv_pem = Botan::PKCS8::PEM_encode(b_priv_gen);
+
+            // Reload back into memory
+            Botan::DataSource_Memory a_priv_ds(a_priv_pem);
+            Botan::DataSource_Memory b_priv_ds(b_priv_pem);
+
+            std::unique_ptr<Botan::Private_Key> a_priv(Botan::PKCS8::load_key(a_priv_ds, Test::rng()));
+            std::unique_ptr<Botan::Private_Key> b_priv(Botan::PKCS8::load_key(b_priv_ds, Test::rng()));
+#endif
+
+            // Export public keys as PEM
+            const std::string a_pub_pem = Botan::X509::PEM_encode(*a_priv);
+            const std::string b_pub_pem = Botan::X509::PEM_encode(*b_priv);
+
+            Botan::DataSource_Memory a_pub_ds(a_pub_pem);
+            Botan::DataSource_Memory b_pub_ds(b_pub_pem);
+
+            std::unique_ptr<Botan::Public_Key> a_pub(Botan::X509::load_key(a_pub_ds));
+            std::unique_ptr<Botan::Public_Key> b_pub(Botan::X509::load_key(b_pub_ds));
+
+            Botan::Curve25519_PublicKey* a_pub_key = dynamic_cast<Botan::Curve25519_PublicKey*>(a_pub.get());
+            Botan::Curve25519_PublicKey* b_pub_key = dynamic_cast<Botan::Curve25519_PublicKey*>(b_pub.get());
+
+            if(a_pub_key && b_pub_key)
+               {
+               Botan::PK_Key_Agreement a_ka(*a_priv, Test::rng(), "Raw");
+               Botan::PK_Key_Agreement b_ka(*b_priv, Test::rng(), "Raw");
+
+               const std::string context = "shared context value";
+               Botan::SymmetricKey a_key = a_ka.derive_key(32, b_pub_key->public_value(), context);
+               Botan::SymmetricKey b_key = b_ka.derive_key(32, a_pub_key->public_value(), context);
+
+               if(!result.test_eq("key agreement", a_key.bits_of(), b_key.bits_of()))
+                  {
+                  result.test_note(a_priv_pem);
+                  result.test_note(b_priv_pem);
+                  }
+               }
+            else
+               {
+               result.test_failure("Cast back to Curve25519 failed");
+               }
+
+            results.push_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("curve25519_rt", Curve25519_Roundtrip_Test);
+
+class Curve25519_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "" };
+         }
+      std::string algo_name() const override
+         {
+         return "Curve25519";
+         }
+   };
+
+BOTAN_REGISTER_TEST("curve25519_keygen", Curve25519_Keygen_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_certstor.cpp b/third_party/botan/src/tests/test_certstor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_certstor.cpp
@@ -0,0 +1,433 @@
+/*
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include <sstream>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509cert.h>
+   #include <botan/certstor.h>
+   #include <botan/internal/filesystem.h>
+   #include <botan/pkcs8.h>
+   #include <botan/pk_keys.h>
+
+   #if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+      #include <botan/certstor_sqlite.h>
+      #include <botan/sqlite3.h>
+   #endif
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+struct CertificateAndKey
+   {
+   const Botan::X509_Certificate certificate;
+   const std::shared_ptr<Botan::Private_Key> private_key;
+   bool operator!=(const CertificateAndKey& rhs) const
+      {
+      return std::tie(certificate, private_key) != std::tie(rhs.certificate, rhs.private_key);
+      }
+   };
+
+#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+Test::Result test_certstor_sqlite3_insert_find_remove_test(const std::vector<CertificateAndKey>& certsandkeys)
+   {
+   Test::Result result("Certificate Store SQLITE3 - Insert, Find, Remove");
+
+   try
+      {
+      auto& rng = Test::rng();
+      const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
+
+      for(const auto& a : certsandkeys)
+         {
+         store.insert_key(a.certificate, *a.private_key);
+         }
+
+      for(const auto& certandkey : certsandkeys)
+         {
+         const auto cert = certandkey.certificate;
+         const auto key = certandkey.private_key;
+         const auto wo_keyid = store.find_cert(cert.subject_dn(), {});
+         const auto w_keyid = store.find_cert(cert.subject_dn(), cert.subject_key_id());
+
+         if(!wo_keyid || !w_keyid)
+            {
+            result.test_failure("Can't retrieve certificate");
+            return result;
+            }
+
+         const auto priv = store.find_key(cert);
+         if(!priv && (certsandkeys[1] != certandkey && certsandkeys[0] != certandkey))
+            {
+            result.test_failure("Can't retrieve private key for " + cert.fingerprint("SHA1"));
+            return result;
+            }
+
+         result.test_eq("Got wrong certificate", cert.fingerprint(), w_keyid->fingerprint());
+
+         if(priv)
+            {
+            result.test_eq("Got wrong private key", key->private_key_bits(), priv->private_key_bits());
+
+            const auto rev_certs = store.find_certs_for_key(*priv);
+
+            if(rev_certs.empty())
+               {
+               result.test_failure("No certificate");
+               }
+            else
+               {
+               const bool found = std::any_of(rev_certs.begin(),
+               rev_certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c) { return c->fingerprint() == cert.fingerprint(); });
+
+               result.test_eq("Got wrong/no certificate", found, true);
+               }
+            }
+
+         if(certsandkeys[4] != certandkey && certsandkeys[5] != certandkey)
+            {
+            result.test_eq("Got wrong certificate", cert.fingerprint(), wo_keyid->fingerprint());
+            }
+
+         result.test_eq("Can't remove certificate", store.remove_cert(cert), true);
+         result.test_eq("Can't remove certificate", !store.find_cert(cert.subject_dn(), cert.subject_key_id()), true);
+
+         if(priv)
+            {
+            store.remove_key(*key);
+            }
+
+         result.test_eq("Can't remove key", !store.find_key(cert), true);
+         }
+
+      return result;
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   }
+
+Test::Result test_certstor_sqlite3_crl_test(const std::vector<CertificateAndKey>& certsandkeys)
+   {
+   Test::Result result("Certificate Store SQLITE3 - CRL");
+   try
+      {
+      auto& rng = Test::rng();
+      const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
+
+      for(const auto& a : certsandkeys)
+         {
+         store.insert_cert(a.certificate);
+         }
+
+      store.revoke_cert(certsandkeys[0].certificate, Botan::CA_COMPROMISE);
+      store.revoke_cert(certsandkeys[3].certificate, Botan::CA_COMPROMISE);
+      store.revoke_cert(certsandkeys[3].certificate, Botan::CA_COMPROMISE);
+
+         {
+         const auto crls = store.generate_crls();
+
+         result.test_eq("Can't revoke certificate", crls.size(), 2);
+         result.test_eq("Can't revoke certificate",
+                        crls[0].is_revoked(certsandkeys[0].certificate) ^ crls[1].is_revoked(certsandkeys[0].certificate), true);
+         result.test_eq("Can't revoke certificate",
+                        crls[0].is_revoked(certsandkeys[3].certificate) ^ crls[1].is_revoked(certsandkeys[3].certificate), true);
+         }
+
+      store.affirm_cert(certsandkeys[3].certificate);
+
+         {
+         const auto crls = store.generate_crls();
+
+         result.test_eq("Can't revoke certificate, wrong crl size", crls.size(), 1);
+         result.test_eq("Can't revoke certificate, cert 0 not revoked", crls[0].is_revoked(certsandkeys[0].certificate), true);
+         }
+
+      const auto cert0_crl = store.find_crl_for(certsandkeys[0].certificate);
+
+      result.test_eq("Can't revoke certificate, crl for cert 0", !cert0_crl, false);
+      result.test_eq("Can't revoke certificate, crl for cert 0 size check", cert0_crl->get_revoked().size(), 1);
+      result.test_eq("Can't revoke certificate, no crl for cert 0", cert0_crl->is_revoked(certsandkeys[0].certificate), true);
+
+      const auto cert3_crl = store.find_crl_for(certsandkeys[3].certificate);
+
+      result.test_eq("Can't revoke certificate, crl for cert 3", !cert3_crl, true);
+
+      return result;
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   }
+
+Test::Result test_certstor_sqlite3_all_subjects_test(const std::vector<CertificateAndKey>& certsandkeys)
+   {
+   Test::Result result("Certificate Store SQLITE3 - All subjects");
+   try
+      {
+      auto& rng = Test::rng();
+      const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
+
+      for(const auto& a : certsandkeys)
+         {
+         store.insert_cert(a.certificate);
+         }
+
+      const auto subjects = store.all_subjects();
+
+      result.test_eq("Check subject list length", subjects.size(), 6);
+
+      for(const auto& sub : subjects)
+         {
+         std::stringstream ss;
+
+         ss << sub;
+         result.test_eq("Check subject " + ss.str(),
+                        certsandkeys[0].certificate.subject_dn() == sub ||
+                        certsandkeys[1].certificate.subject_dn() == sub ||
+                        certsandkeys[2].certificate.subject_dn() == sub ||
+                        certsandkeys[3].certificate.subject_dn() == sub ||
+                        certsandkeys[4].certificate.subject_dn() == sub ||
+                        certsandkeys[5].certificate.subject_dn() == sub,
+                        true);
+         }
+      return result;
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   }
+
+Test::Result test_certstor_sqlite3_find_all_certs_test(const std::vector<CertificateAndKey>& certsandkeys)
+   {
+   Test::Result result("Certificate Store SQLITE3 - Find all certs");
+   try
+      {
+      auto& rng = Test::rng();
+      const std::string passwd(reinterpret_cast<const char*>(rng.random_vec(8).data()), 8);
+      // Just create a database in memory for testing (https://sqlite.org/inmemorydb.html)
+      Botan::Certificate_Store_In_SQLite store(":memory:", passwd, rng);
+
+      for(const auto& a : certsandkeys)
+         {
+         store.insert_cert(a.certificate);
+         }
+
+      for(const auto& a : certsandkeys)
+         {
+         auto res_vec = store.find_all_certs(a.certificate.subject_dn(), a.certificate.subject_key_id());
+         if(res_vec.size() != 1)
+            {
+            result.test_failure("SQLITE all lookup error");
+            return result;
+            }
+         else
+            {
+            std::stringstream a_ss;
+            a_ss << a.certificate.subject_dn();
+            std::stringstream res_ss;
+            res_ss << res_vec.at(0)->subject_dn();
+            result.test_eq("Check subject " + a_ss.str(), a_ss.str(), res_ss.str());
+            }
+         }
+
+      Botan::X509_Certificate same_dn_1 = Botan::X509_Certificate(
+         Test::data_file("x509/bsi/cert_path_common_14/cert_path_common_14_sub_ca.ca.pem.crt"));
+      Botan::X509_Certificate same_dn_2 = Botan::X509_Certificate(
+         Test::data_file("x509/bsi/cert_path_common_14/cert_path_common_14_wrong_sub_ca.ca.pem.crt"));
+
+      store.insert_cert(same_dn_1);
+      store.insert_cert(same_dn_2);
+      auto res_vec = store.find_all_certs(same_dn_1.subject_dn(), {});
+
+      if(res_vec.size() != 2)
+         {
+         result.test_failure("SQLITE all lookup error (duplicate) " + std::to_string(res_vec.size()));
+         return result;
+         }
+      else
+         {
+         std::stringstream cert_ss;
+         cert_ss << same_dn_1.subject_dn();
+         std::stringstream res_ss;
+         res_ss << res_vec.at(0)->subject_dn();
+         result.test_eq("Check subject " + cert_ss.str(), cert_ss.str(), res_ss.str());
+         res_ss.str("");
+         res_ss << res_vec.at(1)->subject_dn();
+         result.test_eq("Check subject " + cert_ss.str(), cert_ss.str(), res_ss.str());
+         }
+      }
+   catch(const std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   return result;
+   }
+
+#endif
+
+Test::Result test_certstor_find_hash_subject(const std::vector<CertificateAndKey>& certsandkeys)
+   {
+   Test::Result result("Certificate Store - Find by subject hash");
+
+   try
+      {
+      Botan::Certificate_Store_In_Memory store;
+
+      for(const auto& a : certsandkeys)
+         {
+         store.add_certificate(a.certificate);
+         }
+
+      for(const auto& certandkey : certsandkeys)
+         {
+         const auto cert = certandkey.certificate;
+         const auto hash = cert.raw_subject_dn_sha256();
+
+         const auto found = store.find_cert_by_raw_subject_dn_sha256(hash);
+         if(!found)
+            {
+            result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA1"));
+            return result;
+            }
+
+         result.test_eq("Got wrong certificate", hash, found->raw_subject_dn_sha256());
+         }
+
+      const auto found = store.find_cert_by_raw_subject_dn_sha256(std::vector<uint8_t>(32, 0));
+      if(found)
+         {
+         result.test_failure("Certificate found for dummy hash");
+         return result;
+         }
+
+      return result;
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   }
+
+Test::Result test_certstor_load_allcert()
+   {
+   Test::Result result("Certificate Store - Load every cert of every files");
+   // test_dir_bundled dir should contain only one file with 2 certificates
+   // concatenated (ValidCert and root)
+   const std::string test_dir_bundled = Test::data_dir() + "/x509/misc/bundledcertdir";
+
+   try
+      {
+      result.test_note("load certs from dir: " + test_dir_bundled);
+      // Certificate_Store_In_Memory constructor loads every cert of every files of the dir.
+      Botan::Certificate_Store_In_Memory store(test_dir_bundled);
+
+      // X509_Certificate constructor loads only the first certificate found in the file.
+      Botan::X509_Certificate root_cert(Test::data_dir() + "/x509/x509test/root.pem");
+      Botan::X509_Certificate valid_cert(Test::data_dir() + "/x509/x509test/ValidCert.pem");
+      std::vector<uint8_t> key_id;
+      result.confirm("Root cert found", store.find_cert(root_cert.subject_dn(), key_id) != nullptr);
+      result.confirm("ValidCert found", store.find_cert(valid_cert.subject_dn(), key_id) != nullptr);
+      return result;
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      return result;
+      }
+   }
+
+class Certstor_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         if(Botan::has_filesystem_impl() == false)
+            {
+            return {Test::Result::Note("Certificate Store",
+                                       "Skipping due to missing filesystem access")};
+            }
+
+         const std::string test_dir = Test::data_dir() + "/x509/certstor";
+         struct CertificateAndKeyFilenames
+            {
+            const std::string certificate;
+            const std::string private_key;
+            } const certsandkeys_filenames[]
+            {
+               {"cert1.crt", "key01.pem"},
+               {"cert2.crt", "key01.pem"},
+               {"cert3.crt", "key03.pem"},
+               {"cert4.crt", "key04.pem"},
+               {"cert5a.crt", "key05.pem"},
+               {"cert5b.crt", "key06.pem"},
+            };
+
+         const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
+
+         if(all_files.empty())
+            {
+            Test::Result result("Certificate Store");
+            result.test_failure("No test files found in " + test_dir);
+            return {result};
+            }
+
+         auto& rng = Test::rng();
+         std::vector<CertificateAndKey> certsandkeys;
+
+         for(const auto& certandkey_filenames : certsandkeys_filenames)
+            {
+            const Botan::X509_Certificate certificate(test_dir + "/" + certandkey_filenames.certificate);
+            std::shared_ptr<Botan::Private_Key> private_key(Botan::PKCS8::load_key(test_dir + "/" +
+                  certandkey_filenames.private_key, rng));
+
+            if(!private_key)
+               {
+               Test::Result result("Certificate Store");
+               result.test_failure("Failed to load key from disk at path: " + test_dir + "/" + certandkey_filenames.private_key);
+               return {result};
+               }
+
+            certsandkeys.push_back({certificate, private_key});
+            }
+
+         std::vector<Test::Result> results;
+
+         results.push_back(test_certstor_find_hash_subject(certsandkeys));
+         results.push_back(test_certstor_load_allcert());
+#if defined(BOTAN_HAS_CERTSTOR_SQLITE3)
+         results.push_back(test_certstor_sqlite3_insert_find_remove_test(certsandkeys));
+         results.push_back(test_certstor_sqlite3_crl_test(certsandkeys));
+         results.push_back(test_certstor_sqlite3_all_subjects_test(certsandkeys));
+         results.push_back(test_certstor_sqlite3_find_all_certs_test(certsandkeys));
+#endif
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("certstor", Certstor_Tests);
+#endif
+}
+}
diff --git a/third_party/botan/src/tests/test_certstor_flatfile.cpp b/third_party/botan/src/tests/test_certstor_flatfile.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_certstor_flatfile.cpp
@@ -0,0 +1,308 @@
+/*
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_CERTSTOR_FLATFILE)
+
+#include "test_certstor_utils.h"
+#include <botan/certstor_flatfile.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/hex.h>
+
+namespace Botan_Tests {
+
+namespace {
+
+std::string get_valid_ca_bundle_path()
+   {
+   return Test::data_file("x509/misc/certstor/valid_ca_bundle.pem");
+   }
+
+std::string get_ca_bundle_containing_user_cert()
+   {
+   return Test::data_file("x509/misc/certstor/ca_bundle_containing_non_ca.pem");
+   }
+
+Test::Result open_certificate_store()
+   {
+   Test::Result result("Flatfile Certificate Store - Open Store");
+
+   try
+      {
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store unused(get_valid_ca_bundle_path());
+      result.end_timer();
+      result.test_gt("found some certificates", unused.all_subjects().size(), 0);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   result.test_success();
+
+   return result;
+   }
+
+Test::Result find_certificate_by_pubkey_sha1()
+   {
+   Test::Result result("Flatfile Certificate Store - Find Certificate by SHA1(pubkey)");
+
+   try
+      {
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto cert = certstore.find_cert_by_pubkey_sha1(get_key_id());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_int_eq("exactly one CN", cns.size(), 1);
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   result.test_throws("on invalid SHA1 hash data", [&]
+      {
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      certstore.find_cert_by_pubkey_sha1({});
+      });
+
+   return result;
+   }
+
+Test::Result find_cert_by_subject_dn()
+   {
+   Test::Result result("Flatfile Certificate Store - Find Certificate by subject DN");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_int_eq("exactly one CN", cns.size(), 1);
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_cert_by_utf8_subject_dn()
+   {
+   Test::Result result("Flatfile Certificate Store - Find Certificate by UTF8 subject DN");
+
+   try
+      {
+      auto dn = get_utf8_dn();
+
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "D-TRUST Root Class 3 CA 2 EV 2009");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_cert_by_subject_dn_and_key_id()
+   {
+   Test::Result result("Flatfile Certificate Store - Find Certificate by subject DN and key ID");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto cert = certstore.find_cert(dn, get_key_id());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_int_eq("exactly one CN", cns.size(), 1);
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_certs_by_subject_dn_and_key_id()
+   {
+   Test::Result result("Flatfile Certificate Store - Find Certificates by subject DN and key ID");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto certs = certstore.find_all_certs(dn, get_key_id());
+      result.end_timer();
+
+      if(result.confirm("result not empty", !certs.empty()) &&
+            result.test_eq("exactly one certificate", certs.size(), 1))
+         {
+         auto cns = certs.front()->subject_dn().get_attribute("CN");
+         result.test_int_eq("exactly one CN", cns.size(), 1);
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_all_subjects()
+   {
+   Test::Result result("Flatfile Certificate Store - Find all Certificate Subjects");
+
+   try
+      {
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+      auto subjects = certstore.all_subjects();
+      result.end_timer();
+
+      if(result.confirm("result not empty", !subjects.empty()))
+         {
+         auto dn = get_dn();
+         auto needle = std::find_if(subjects.cbegin(),
+                                    subjects.cend(),
+                                    [=](const Botan::X509_DN &subject)
+            {
+            return subject == dn;
+            });
+
+         if(result.confirm("found expected certificate", needle != subjects.end()))
+            {
+            result.confirm("expected certificate", *needle == dn);
+            }
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result no_certificate_matches()
+   {
+   Test::Result result("Flatfile Certificate Store - can deal with no matches (regression test)");
+
+   try
+      {
+      auto dn  = get_unknown_dn();
+      auto kid = get_unknown_key_id();
+
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path());
+
+      auto certs = certstore.find_all_certs(dn, kid);
+      auto cert = certstore.find_cert(dn, kid);
+      auto pubk_cert = certstore.find_cert_by_pubkey_sha1(kid);
+      result.end_timer();
+
+      result.confirm("find_all_certs did not find the dummy", certs.empty());
+      result.confirm("find_cert did not find the dummy", !cert);
+      result.confirm("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result certstore_contains_user_certificate()
+   {
+   Test::Result result("Flatfile Certificate Store - rejects bundles with non-CA certs");
+
+   try
+      {
+      result.start_timer();
+      Botan::Flatfile_Certificate_Store certstore(get_ca_bundle_containing_user_cert());
+      result.test_failure("CA bundle with non-CA certs should be rejected");
+      }
+   catch(Botan::Invalid_Argument&)
+      {
+      result.test_success();
+      }
+
+   return result;
+   }
+
+class Certstor_Flatfile_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(open_certificate_store());
+         results.push_back(find_certificate_by_pubkey_sha1());
+         results.push_back(find_cert_by_subject_dn());
+         results.push_back(find_cert_by_utf8_subject_dn());
+         results.push_back(find_cert_by_subject_dn_and_key_id());
+         results.push_back(find_certs_by_subject_dn_and_key_id());
+         results.push_back(find_all_subjects());
+         results.push_back(no_certificate_matches());
+         results.push_back(certstore_contains_user_certificate());
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("certstor_flatfile", Certstor_Flatfile_Tests);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_certstor_system.cpp b/third_party/botan/src/tests/test_certstor_system.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_certstor_system.cpp
@@ -0,0 +1,354 @@
+/*
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+
+#include "test_certstor_utils.h"
+#include <botan/certstor_system.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/hex.h>
+
+namespace Botan_Tests {
+
+namespace {
+
+Test::Result find_certificate_by_pubkey_sha1(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificate by SHA1(pubkey)");
+
+   try
+      {
+      result.start_timer();
+      auto cert = certstore.find_cert_by_pubkey_sha1(get_key_id());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   result.test_throws("on invalid SHA1 hash data", [&]
+      {
+      certstore.find_cert_by_pubkey_sha1({});
+      });
+
+   return result;
+   }
+
+Test::Result find_cert_by_subject_dn(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificate by subject DN");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+/*
+ * test case conditionally disabled for now, see:
+ * https://github.com/randombit/botan/pull/1931
+ * https://github.com/randombit/botan/pull/2280
+*/
+#if !defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+Test::Result find_cert_by_utf8_subject_dn(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificate by UTF8 subject DN");
+
+   try
+      {
+      auto dn = get_utf8_dn();
+
+      result.start_timer();
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "D-TRUST Root Class 3 CA 2 EV 2009");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+#endif
+
+Test::Result find_cert_by_subject_dn_and_key_id(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificate by subject DN and key ID");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      auto cert = certstore.find_cert(dn, get_key_id());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_certs_by_subject_dn_and_key_id(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificates by subject DN and key ID");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      auto certs = certstore.find_all_certs(dn, get_key_id());
+      result.end_timer();
+
+      if(result.confirm("result not empty", !certs.empty()) &&
+            result.test_eq("exactly one certificate", certs.size(), 1))
+         {
+         auto cns = certs.front()->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_all_certs_by_subject_dn(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find all Certificates by subject DN");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      auto certs = certstore.find_all_certs(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      // check for duplications
+      sort(certs.begin(), certs.end());
+      for(size_t i = 1; i < certs.size(); ++i)
+         {
+         if(certs[i-1] == certs[i])
+            {
+            result.test_failure("find_all_certs produced duplicated result");
+            }
+         }
+
+      if(result.confirm("result not empty", !certs.empty()))
+         {
+         auto cns = certs.front()->subject_dn().get_attribute("CN");
+         result.test_gte("at least one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result find_all_subjects(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find all Certificate Subjects");
+
+   try
+      {
+      result.start_timer();
+      auto subjects = certstore.all_subjects();
+      result.end_timer();
+
+      if(result.confirm("result not empty", !subjects.empty()))
+         {
+         auto dn = get_dn();
+         auto needle = std::find_if(subjects.cbegin(),
+                                    subjects.cend(),
+                                    [=](const Botan::X509_DN &subject)
+            {
+            return subject == dn;
+            });
+
+         if(result.confirm("found expected certificate", needle != subjects.end()))
+            {
+            result.confirm("expected certificate", *needle == dn);
+            }
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+Test::Result no_certificate_matches(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - can deal with no matches (regression test)");
+
+   try
+      {
+      auto dn  = get_unknown_dn();
+      auto kid = get_unknown_key_id();
+
+      result.start_timer();
+      auto certs = certstore.find_all_certs(dn, kid);
+      auto cert = certstore.find_cert(dn, kid);
+      auto pubk_cert = certstore.find_cert_by_pubkey_sha1(kid);
+      result.end_timer();
+
+      result.confirm("find_all_certs did not find the dummy", certs.empty());
+      result.confirm("find_cert did not find the dummy", !cert);
+      result.confirm("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+
+Test::Result certificate_matching_with_dn_normalization(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - normalization of X.509 DN (regression test)");
+
+   try
+      {
+      auto dn = get_skewed_dn();
+
+      result.start_timer();
+      auto certs = certstore.find_all_certs(dn, std::vector<uint8_t>());
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      if(result.confirm("find_all_certs did find the skewed DN", !certs.empty()) &&
+            result.confirm("find_cert did find the skewed DN", cert != nullptr))
+         {
+         result.test_eq("it is the correct cert", certs.front()->subject_dn().get_first_attribute("CN"), "DST Root CA X3");
+         result.test_eq("it is the correct cert", cert->subject_dn().get_first_attribute("CN"), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
+#endif
+
+class Certstor_System_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result open_result("System Certificate Store - Open Keychain");
+
+         std::unique_ptr<Botan::Certificate_Store> system;
+
+         try
+            {
+            open_result.start_timer();
+            system.reset(new Botan::System_Certificate_Store);
+            open_result.end_timer();
+            }
+         catch(Botan::Not_Implemented& e)
+            {
+            BOTAN_UNUSED(e);
+            open_result.test_note("Skipping due to not available in current build");
+            return {open_result};
+            }
+         catch(std::exception& e)
+            {
+            open_result.test_failure(e.what());
+            return {open_result};
+            }
+
+         open_result.test_success();
+
+         std::vector<Test::Result> results;
+         results.push_back(open_result);
+
+         results.push_back(find_certificate_by_pubkey_sha1(*system));
+         results.push_back(find_cert_by_subject_dn(*system));
+         results.push_back(find_cert_by_subject_dn_and_key_id(*system));
+         results.push_back(find_all_certs_by_subject_dn(*system));
+         results.push_back(find_certs_by_subject_dn_and_key_id(*system));
+         results.push_back(find_all_subjects(*system));
+         results.push_back(no_certificate_matches(*system));
+#if !defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+         results.push_back(find_cert_by_utf8_subject_dn(*system));
+#endif
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+         results.push_back(certificate_matching_with_dn_normalization(*system));
+#endif
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("certstor_system", Certstor_System_Tests);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_certstor_utils.cpp b/third_party/botan/src/tests/test_certstor_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_certstor_utils.cpp
@@ -0,0 +1,81 @@
+/*
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "test_certstor_utils.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/ber_dec.h>
+#include <botan/hex.h>
+
+namespace Botan_Tests {
+
+Botan::X509_DN read_dn(const std::string hex)
+   {
+   Botan::X509_DN dn;
+   Botan::BER_Decoder decoder(Botan::hex_decode(hex));
+   dn.decode_from(decoder);
+   return dn;
+   }
+
+Botan::X509_DN get_dn()
+   {
+   // ASN.1 encoded subject DN of "DST Root CA X3"
+   // This certificate is in the standard "System Roots" of any macOS setup,
+   // serves as the trust root of botan.randombit.net and expires on
+   // Thursday, 30. September 2021 at 16:01:15 Central European Summer Time
+   return read_dn("303f31243022060355040a131b4469676974616c205369676e6174757265"
+                  "20547275737420436f2e311730150603550403130e44535420526f6f7420"
+                  "4341205833");
+   }
+
+Botan::X509_DN get_utf8_dn()
+   {
+   // ASN.1 encoded subject DN of "D-TRUST Root Class 3 CA 2 EV 2009"
+   // This DN contains UTF8-encoded strings
+   // expires on 05. November 2029 at 8:50:46 UTC
+   return read_dn("3050310B300906035504061302444531153013060355040A0C0C442D54727"
+                  "5737420476D6248312A302806035504030C21442D545255535420526F6F74"
+                  "20436C617373203320434120322045562032303039");
+   }
+
+std::vector<uint8_t> get_key_id()
+   {
+   // this is the same as the public key SHA1
+   return Botan::hex_decode("c4a7b1a47b2c71fadbe14b9075ffc41560858910");
+   }
+
+Botan::X509_DN get_unknown_dn()
+   {
+   // thats a D-Trust "Test Certificate". It should be fairly likely that
+   // _nobody_ will _ever_ have that in their system keychain
+   // CN: D-TRUST Limited Basic Test PU CA 1-4 2016
+   return read_dn("305b310b300906035504061302444531153013060355040a0c0c442d5472"
+                  "75737420476d62483135303306035504030c2c442d5452555354204c696d"
+                  "6974656420426173696320526f6f74205465737420505520434120312032"
+                  "303135");
+   }
+
+Botan::X509_DN get_skewed_dn()
+   {
+   // This DN contains ASN.1 PrintableString fields that are not 'normalized'
+   // according to Apple's idea of a normalized PrintableString field:
+   //   (1) It has leading and trailing white space
+   //   (2) It contains multiple spaces between 'words'
+   return read_dn("304b312a3028060355040a132120204469676974616c2020205369676e61"
+                  "7475726520547275737420436f2e2020311d301b06035504031314202044"
+                  "5354202020526f6f742043412058332020");
+   }
+
+std::vector<uint8_t> get_unknown_key_id()
+   {
+   // this is the same as the public key SHA1
+   return Botan::hex_decode("785c0b67b536eeacbb2b27cf9123301abe7ab09a");
+   }
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_certstor_utils.h b/third_party/botan/src/tests/test_certstor_utils.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_certstor_utils.h
@@ -0,0 +1,39 @@
+/*
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TEST_CERT_STORE_UTILS_H_
+#define BOTAN_TEST_CERT_STORE_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include <botan/build.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/x509_dn.h>
+
+namespace Botan_Tests {
+
+Botan::X509_DN read_dn(const std::string hex);
+
+Botan::X509_DN get_dn();
+
+Botan::X509_DN get_utf8_dn();
+
+std::vector<uint8_t> get_key_id();
+
+Botan::X509_DN get_unknown_dn();
+
+Botan::X509_DN get_skewed_dn();
+
+std::vector<uint8_t> get_unknown_key_id();
+}
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/tests/test_clang_bug.cpp b/third_party/botan/src/tests/test_clang_bug.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_clang_bug.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2020 Jack Lloyd, René Meusel, Hannes Rantzsch
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/*
+ * This test case ensures that we avoid crashing in PKCS8::load_key due to a bug in Clang 8.
+ *
+ * A detailed description of the issue can be found here: https://github.com/randombit/botan/issues/2255.
+ * In short, Clang 8 performs a double-free on captured objects if an exception is thrown within a lambda. This case can
+ * occur when PKCS8::load_key fails to load the key due to a wrong password. The password needs to be long enough to not
+ * be small buffer optimized.
+ *
+ * The Clang bug is tracked here and apparently won't be fixed in Clang 8: https://bugs.llvm.org/show_bug.cgi?id=41810.
+ * Other Clang versions seem not to be affected.
+ */
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+   #include <botan/pkcs8.h>
+   #include <botan/data_src.h>
+#endif // BOTAN_HAS_PUBLIC_KEY_CRYPTO
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+namespace {
+
+// The key needs to be a PKCS #8 encoded and encrypted private key.
+std::string getPrivateKey()
+   {
+   return
+      "-----BEGIN ENCRYPTED PRIVATE KEY-----"
+      "MIIHdDBeBgkqhkiG9w0BBQ0wUTAwBgkqhkiG9w0BBQwwIwQMswcJBnmZ9FwM4hbe"
+      "AgJlkAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQKpgftrEQL/IcEN93"
+      "xUnTdASCBxBRWf7m9CHhUmCjr/gWA+L8D2oqf/L2R4/zlL7N2NvCwbjWS9V+bDYQ"
+      "J1dixs1eW0A3WolNUoZRkTvTGEufZW92L2afHRSkOvLlupqiTHFOf67XTgPbchZd"
+      "teTMixkIDw3wHtN5zBApL3UZ2h5lNOhfocWxh1AddotMRsY4zXoTmKzdl8DC3bfQ"
+      "0zBME6I4rMFjJnKFYupe1GdP9TlJ1/ioE0KUr9f5IGzSPy9ayEW8H+BmdpfsEAt4"
+      "s0AG4HbjhZ6n3BFn2jXhezVu4Vd6f+qMmdkLMKG+TyNkP1PFWuJMV+F5ftQi6xdm"
+      "LP19idEojEnGzk7yWNnCXzDagBMQlR0m9RJfoG2JRYlc3qlI8QPEUb+0WcaLh8Us"
+      "U16Y7EW1WlUkPlvKuTOmNSsiAjBStJkWkPNHwKV4gjNEn755JjeFvxLEqA8e812N"
+      "phsSRpx9+xAqwLZHX+5aSlodUr740LOf23t6UOMbOq6Oe3cFAV85USmZV4JAYYIQ"
+      "CgKVBIUxJO5b6+8+B6Nfqy1HVu+/p1NtqGRT93qxRz78u+N07bld0yrelwlkCyBn"
+      "eLuqcGUuWdVNIRNKM3r8TqseW7xu5p3kRXg9BRE6lXSpIPZnBs8vRVDsjXCsXI/f"
+      "JrW3rkPtx7s3CtgTqjxoKdBI/W9jnBAvhDz+UYiYrlqVOZ4nq7puylbNvezL+P4e"
+      "2y8oDjX6OUXNT6MwM2MP/73bvoekOmhT2tPcXWCMNfLN56y/aDMxJ2yoWaxdpSLP"
+      "X6eoyP76GcI+hI9TWkXIFFuR/18+aaovlkR6Vb4H+SJaOCLLceUDsJ2WOqT5fArc"
+      "E0He2+DDlvuBiv2GSIhbA6ae1qNtADcmevhxqrXHuOYw914dXIidNkaAAL7OeJlI"
+      "I0+SQIxfhbiwZObxKCa0BHmYOEKixa3suALWSSCjbOTVzWfPc5OL6y2PysJ0I82X"
+      "Ql6rRkhiH/ZCZcC7P152Yd/PsoH3PRh3vJQGHa7ijQVtqvzGiaSelsCIwidmgvr3"
+      "35Lt1EBYI1aN9j0Op/KCwfuyGgH/sa12s8WVRXWVLBxmtfka6p92RGSHYnuqSse3"
+      "+Wtzn48njYTpBmpBGZMYMBGJDyt8XuOOYHhPjRqkSjWMWHCiWLT+4HPdTuuSCz7c"
+      "ososirxutINPxpMHihT1l/uOLJS1ZK6o+4VHecFrINLr3xTwNLBCK12P7PhG2acY"
+      "yV6HZD3WU+eIdvFTfXTlZblOhoJFMynwdZnsPPktFoFewsHaanZHm5jBcZMncA2f"
+      "sVeZfssV+A2W1ZtC9PQZ8PAT83qNHwAKBg6ZGL46Q9gRPauIeEG9pfyqwREDH92+"
+      "oouyE8m5gcxNdfY3y8C2Mam03xQDvfwfflJInEShoBWLGv5nstOIOptjwoV/wYyn"
+      "Nw7jOezGqHoOZBWtuyEpdWO/4rmdVGPuGwe5s8IHkjVIUQwQqwTLm9pXKFjvaEM9"
+      "RyDLDMg3PNmAUafrEzR9CDU93tr1zebcr7ZMOAPlV0VGi3NbAiPie/62/t7E0RLU"
+      "NIptJGKCOhKvG7wy0IXaLbEtAowTgEK0OrQ5oiZD8d8J+T7ZYIRQf3ppWFobFd5n"
+      "DL+w3AJ75yvpsSO9M3lbkjzFrwyqbNG1dWoWZmRwu8aAiSMeqJiBhGpZMwm5qljj"
+      "4uCCIG5+XEWxwg+THs9s3pPfEiKvosQbJeja7y6JcAeJqO6guNJ03A1qTqUcJY9h"
+      "hcVORQXc8FK9ReN1v52TI19vbyUGaGpJGinagCpKL/+MeznqdKrT0DqoMA+x/U5V"
+      "Qzm5+2Wg6FnqNHu/3TkfnP7eUjIAZ4dyGHffScacrf1ZXEz7Tmur3T168z4SPHF7"
+      "usIOQ2RgaHQTilDl0m1deosq/5eX/J193nt56urXQ+nRj7GcdjNYYJdv0vttThnh"
+      "jamndRcMQ9Pso8WTgNlMJjYKGmJV3pZ652AZo4jQPearrEnAC/YEJuj5rxYT2RSL"
+      "MkBEiZbRQR2yDfg3IbnN0uMSVGiP2qd04uKvQ45RIDJ9PbEDsB0a9R/W/Uc2X9Eo"
+      "ptlKgHNgTtslIj+GU6r1p0sUkW2iBI6rg3z7uDpnHveuTJXITPyim9W9fjluuqh+"
+      "ApgH0RqSu9vxdVM7mLd6wRyeH0ST7mUNcjElBdDW+bXcvKpqGUizc3Nq7iosRo3p"
+      "wGjmcjNwvuac1+guKFDMskeVrBRDE1Ulbo//AGxoGcoRKz81vmhEdaaq0s7xmkvI"
+      "zxWxdqGmyw+K7rAvbJuCURrdr+vvdJMsGt9PXDBogJPGqkrytL+8S3DXFMw83Mm5"
+      "qAA7A4H2qdXURUfFdWgcrY5Nxo1PLtKztRrGZ4lAf+xAuDJmf5E/fQvZJwlx+cVu"
+      "BhLslycbfcv1iKw0/uzS4B+M8bomW7AAWAla1+A7zOpGRbDNAZeCLA=="
+      "-----END ENCRYPTED PRIVATE KEY-----";
+   }
+
+}  // namespace
+
+class Clang_Bug_41810 final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("PKCS8::load_key does not crash when compiled with Clang 8");
+
+         auto pem = getPrivateKey();
+
+         Botan::DataSource_Memory ds(reinterpret_cast<const uint8_t*>(pem.data()), pem.size());
+
+         std::string pw = "01234567890123456789"; // sufficiently long, wrong password
+         // Note: the correct password for this key is "smallbufferoptimization"
+
+         try
+            {
+            Botan::PKCS8::load_key(ds, Test::rng(), pw);
+            result.test_failure("load_key should have thrown due to wrong password");
+            }
+         catch(const std::exception&)
+            {
+            result.test_success("load_key doesn't crash");
+            }
+
+         return {result};
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("clang_bug", Clang_Bug_41810);
+
+#endif // BOTAN_HAS_PUBLIC_KEY_CRYPTO
+
+}  // namespace Botan_Tests
diff --git a/third_party/botan/src/tests/test_compression.cpp b/third_party/botan/src/tests/test_compression.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_compression.cpp
@@ -0,0 +1,238 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_COMPRESSION)
+   #include <botan/compression.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+namespace {
+
+const char* text_str =
+   "'Twas brillig, and the slithy toves"
+   "Did gyre and gimble in the wabe:"
+   "All mimsy were the borogoves,"
+   "And the mome raths outgrabe."
+
+   "'Beware the Jabberwock, my son!"
+   "The jaws that bite, the claws that catch!"
+   "Beware the Jubjub bird, and shun"
+   "The frumious Bandersnatch!'"
+
+   "He took his vorpal sword in hand;"
+   "Long time the manxome foe he sought-"
+   "So rested he by the Tumtum tree"
+   "And stood awhile in thought."
+
+   "And, as in uffish thought he stood,"
+   "The Jabberwock, with eyes of flame,"
+   "Came whiffling through the tulgey wood,"
+   "And burbled as it came!"
+
+   "One, two! One, two! And through and through"
+   "The vorpal blade went snicker-snack!"
+   "He left it dead, and with its head"
+   "He went galumphing back."
+
+   "'And hast thou slain the Jabberwock?"
+   "Come to my arms, my beamish boy!"
+   "O frabjous day! Callooh! Callay!'"
+   "He chortled in his joy."
+
+   "'Twas brillig, and the slithy toves"
+   "Did gyre and gimble in the wabe:"
+   "All mimsy were the borogoves,"
+   "And the mome raths outgrabe.";
+
+class Compression_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         const size_t text_len = std::strlen(text_str);
+
+         for(std::string algo : { "zlib", "deflate", "gzip", "bz2", "lzma" })
+            {
+            try
+               {
+               Test::Result result(algo + " compression");
+
+               std::unique_ptr<Botan::Compression_Algorithm> c(Botan::make_compressor(algo));
+               std::unique_ptr<Botan::Decompression_Algorithm> d(Botan::make_decompressor(algo));
+
+               if(!c || !d)
+                  {
+                  result.note_missing(algo);
+                  continue;
+                  }
+
+               result.test_ne("Not the same name", c->name(), d->name());
+
+               const Botan::secure_vector<uint8_t> empty;
+               const Botan::secure_vector<uint8_t> all_zeros(text_len, 0);
+               const Botan::secure_vector<uint8_t> random_binary = Test::rng().random_vec(text_len);
+               const Botan::secure_vector<uint8_t> short_text = { 'f', 'o', 'o', '\n' };
+
+               const uint8_t* textb = reinterpret_cast<const uint8_t*>(text_str);
+               const Botan::secure_vector<uint8_t> text(textb, textb + text_len);
+
+               const size_t c1_e = run_compression(result, 1, *c, *d, empty);
+               const size_t c9_e = run_compression(result, 9, *c, *d, empty);
+               const size_t c1_z = run_compression(result, 1, *c, *d, all_zeros);
+               const size_t c9_z = run_compression(result, 9, *c, *d, all_zeros);
+               const size_t c1_r = run_compression(result, 1, *c, *d, random_binary);
+               const size_t c9_r = run_compression(result, 9, *c, *d, random_binary);
+               const size_t c1_t = run_compression(result, 1, *c, *d, text);
+               const size_t c9_t = run_compression(result, 9, *c, *d, text);
+               const size_t c1_s = run_compression(result, 1, *c, *d, short_text);
+               const size_t c9_s = run_compression(result, 9, *c, *d, short_text);
+
+               result.test_gte("Empty input L1 compresses to non-empty output", c1_e, 1);
+               result.test_gte("Empty input L9 compresses to non-empty output", c9_e, 1);
+
+               result.test_gte("Level 9 compresses empty at least as well as level 1", c1_e, c9_e);
+               result.test_gte("Level 9 compresses zeros at least as well as level 1", c1_z, c9_z);
+               result.test_gte("Level 9 compresses random at least as well as level 1", c1_r, c9_r);
+               result.test_gte("Level 9 compresses text at least as well as level 1", c1_t, c9_t);
+               result.test_gte("Level 9 compresses short text at least as well as level 1", c1_s, c9_s);
+
+               result.test_lt("Zeros compresses much better than text", c1_z / 8, c1_t);
+               result.test_lt("Text compresses much better than random", c1_t / 2, c1_r);
+
+               results.emplace_back(result);
+               }
+            catch(std::exception& e)
+               {
+               results.emplace_back(Test::Result::Failure("testing " + algo, e.what()));
+               }
+            }
+
+         return results;
+         }
+
+   private:
+
+      // Returns # of bytes of compressed message
+      size_t run_compression(Test::Result& result,
+                             size_t level,
+                             Botan::Compression_Algorithm& c,
+                             Botan::Decompression_Algorithm& d,
+                             const Botan::secure_vector<uint8_t>& msg)
+         {
+         Botan::secure_vector<uint8_t> compressed(2*msg.size());
+
+         for(bool with_flush : { true, false })
+            {
+            try
+               {
+               compressed = msg;
+
+               c.start(level);
+               c.update(compressed, 0, false);
+
+               if(with_flush)
+                  {
+                  Botan::secure_vector<uint8_t> flush_bits;
+                  c.update(flush_bits, 0, true);
+                  compressed += flush_bits;
+                  }
+
+               Botan::secure_vector<uint8_t> final_bits;
+               c.finish(final_bits);
+               compressed += final_bits;
+
+               Botan::secure_vector<uint8_t> decompressed = compressed;
+               d.start();
+               d.update(decompressed);
+
+               Botan::secure_vector<uint8_t> final_outputs;
+               d.finish(final_outputs);
+
+               decompressed += final_outputs;
+
+               result.test_eq("compression round tripped", msg, decompressed);
+               }
+            catch(Botan::Exception& e)
+               {
+               result.test_failure(e.what());
+               }
+            }
+
+         return compressed.size();
+         }
+   };
+
+BOTAN_REGISTER_TEST("compression", Compression_Tests);
+
+class CompressionCreate_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         for(std::string algo : { "zlib", "deflate", "gzip", "bz2", "lzma" })
+            {
+            try
+               {
+               Test::Result result(algo + " create compression");
+
+               std::unique_ptr<Botan::Compression_Algorithm> c1(Botan::Compression_Algorithm::create(algo));
+               std::unique_ptr<Botan::Decompression_Algorithm> d1(Botan::Decompression_Algorithm::create(algo));
+
+               if(!c1 || !d1)
+                  {
+                  result.note_missing(algo);
+                  continue;
+                  }
+               result.test_ne("Not the same name after create", c1->name(), d1->name());
+
+               std::unique_ptr<Botan::Compression_Algorithm> c2(Botan::Compression_Algorithm::create_or_throw(algo));
+               std::unique_ptr<Botan::Decompression_Algorithm> d2(Botan::Decompression_Algorithm::create_or_throw(algo));
+
+               if(!c2 || !d2)
+                  {
+                  result.note_missing(algo);
+                  continue;
+                  }
+               result.test_ne("Not the same name after create_or_throw", c2->name(), d2->name());
+
+               results.emplace_back(result);
+               }
+            catch(std::exception& e)
+               {
+               results.emplace_back(Test::Result::Failure("testing " + algo, e.what()));
+               }
+            }
+
+            {
+            Test::Result result("create invalid compression");
+            result.test_throws("lookup error",
+                               "Unavailable Compression bogocompress",
+                               [&]() { Botan::Compression_Algorithm::create_or_throw("bogocompress"); });
+            result.test_throws("lookup error",
+                               "Unavailable Decompression bogocompress",
+                               [&]() { Botan::Decompression_Algorithm::create_or_throw("bogocompress"); });
+            results.emplace_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("create_compression", CompressionCreate_Tests);
+
+}
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_cryptobox.cpp b/third_party/botan/src/tests/test_cryptobox.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_cryptobox.cpp
@@ -0,0 +1,75 @@
+/*
+* (C) 2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_CRYPTO_BOX)
+   #include <botan/cryptobox.h>
+   #include <botan/pem.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_CRYPTO_BOX)
+
+class Cryptobox_KAT final : public Text_Based_Test
+   {
+   public:
+      Cryptobox_KAT() : Text_Based_Test("cryptobox.vec", "Input,Passphrase,Salt,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("Cryptobox");
+
+         const std::string password = vars.get_req_str("Passphrase");
+         const std::vector<uint8_t> input    = vars.get_req_bin("Input");
+         const std::vector<uint8_t> salt     = vars.get_req_bin("Salt");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         const std::string expected_pem = Botan::PEM_Code::encode(expected, "BOTAN CRYPTOBOX MESSAGE");
+
+         Fixed_Output_RNG salt_rng(salt);
+
+         const std::string ciphertext =
+            Botan::CryptoBox::encrypt(input.data(), input.size(), password, salt_rng);
+
+         result.test_eq("encryption is expected value", ciphertext, expected_pem);
+
+         result.test_eq("decryption works", Botan::CryptoBox::decrypt_bin(ciphertext, password), input);
+
+         // Now corrupt a bit and ensure it fails
+         try
+            {
+            const std::vector<uint8_t> corrupted = Test::mutate_vec(expected);
+            const std::string corrupted_pem = Botan::PEM_Code::encode(corrupted, "BOTAN CRYPTOBOX MESSAGE");
+
+            Botan::CryptoBox::decrypt(corrupted_pem, password);
+            result.test_failure("Decrypted corrupted cryptobox message", corrupted);
+            }
+         catch(Botan::Decoding_Error&)
+            {
+            result.test_success("Rejected corrupted cryptobox message");
+            }
+         catch(Botan::Invalid_Argument&)
+            {
+            result.test_success("Rejected corrupted cryptobox message");
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("cryptobox", Cryptobox_KAT);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_datastore.cpp b/third_party/botan/src/tests/test_datastore.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_datastore.cpp
@@ -0,0 +1,71 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/datastor.h>
+
+namespace Botan_Tests {
+
+class Datastore_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("Data_Store");
+
+         Botan::Data_Store ds1;
+         Botan::Data_Store ds2;
+
+         result.confirm("equality", ds1 == ds2);
+
+         result.test_eq("has_value", ds1.has_value("key"), false);
+         result.test_eq("get1 with default", ds1.get1("key", "default"), "default");
+
+         result.test_throws("get1", "Data_Store::get1: No values set for missing_key",
+                            [&ds1]() { ds1.get1("missing_key"); });
+
+         result.test_eq("search_for", ds1.search_for([](std::string, std::string) { return true; }).size(), 0);
+
+         ds1.add("key", "value");
+
+         result.test_eq("search_for", ds1.search_for([](std::string, std::string) { return true; }).size(), 1);
+
+         result.test_eq("equality", ds1 == ds2, false);
+
+         result.test_eq("has_value", ds1.has_value("key"), true);
+         result.test_eq("get1 with default", ds1.get1("key", "default"), "value");
+         result.test_eq("get1", ds1.get1("key"), "value");
+
+         result.test_eq("get1_memvec", ds1.get1_memvec("memvec").size(), 0);
+
+         const std::vector<uint8_t> memvec = { 9, 1, 1, 4 };
+         ds1.add("memvec", memvec);
+
+         result.test_eq("get1_memvec", ds1.get1_memvec("memvec"), memvec);
+
+         result.test_eq("get1_uint32", ds1.get1_uint32("memvec"), size_t(9010104));
+
+         result.test_eq("get1_uint32", ds1.get1_uint32("missing", 999), size_t(999));
+
+         ds1.add("key", "value2");
+
+         result.test_throws("get1", "Data_Store::get1: More than one value for key",
+                            [&ds1]() { ds1.get1("key"); });
+
+         result.test_eq("get multiple", ds1.get("key").size(), 2);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("x509_datastore", Datastore_Tests);
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_dh.cpp b/third_party/botan/src/tests/test_dh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_dh.cpp
@@ -0,0 +1,161 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   #include "test_pubkey.h"
+   #include <botan/pubkey.h>
+   #include <botan/dh.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+
+class Diffie_Hellman_KAT_Tests final : public PK_Key_Agreement_Test
+   {
+   public:
+      Diffie_Hellman_KAT_Tests()
+         : PK_Key_Agreement_Test(
+              "Diffie-Hellman",
+              "pubkey/dh.vec",
+              "P,G,X,Y,Msg,OutLen,K",
+              "Q,KDF") {}
+
+      std::string default_kdf(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_our_key(const std::string&, const VarMap& vars) override
+         {
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt q = vars.get_opt_bn("Q", 0);
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt x = vars.get_req_bn("X");
+
+         Botan::DL_Group grp;
+         if(q == 0)
+            {
+            grp = Botan::DL_Group(p, g);
+            }
+         else
+            {
+            grp = Botan::DL_Group(p, q, g);
+            }
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::DH_PrivateKey(Test::rng(), grp, x));
+         return key;
+         }
+
+      std::vector<uint8_t> load_their_key(const std::string&, const VarMap& vars) override
+         {
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt q = vars.get_opt_bn("Q", 0);
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt y = vars.get_req_bn("Y");
+
+         Botan::DL_Group grp;
+         if(q == 0)
+            {
+            grp = Botan::DL_Group(p, g);
+            }
+         else
+            {
+            grp = Botan::DL_Group(p, q, g);
+            }
+
+         Botan::DH_PublicKey key(grp, y);
+         return key.public_value();
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("DH negative tests");
+
+         const BigInt g("2");
+         const BigInt p("58458002095536094658683755258523362961421200751439456159756164191494576279467");
+         const Botan::DL_Group grp(p, g);
+
+         const Botan::BigInt x("46205663093589612668746163860870963912226379131190812163519349848291472898748");
+         std::unique_ptr<Botan::Private_Key> privkey(new Botan::DH_PrivateKey(Test::rng(), grp, x));
+
+         std::unique_ptr<Botan::PK_Key_Agreement> kas(new Botan::PK_Key_Agreement(*privkey, rng(), "Raw"));
+
+         result.test_throws("agreement input too big",
+                            "DH agreement - invalid key provided",
+                            [&kas]()
+            {
+            const BigInt too_big("584580020955360946586837552585233629614212007514394561597561641914945762794672");
+            kas->derive_key(16, BigInt::encode(too_big));
+            });
+
+         result.test_throws("agreement input too small",
+                            "DH agreement - invalid key provided",
+                            [&kas]()
+            {
+            const BigInt too_small("1");
+            kas->derive_key(16, BigInt::encode(too_small));
+            });
+
+         return{result};
+         }
+
+   };
+
+class DH_Invalid_Key_Tests final : public Text_Based_Test
+   {
+   public:
+      DH_Invalid_Key_Tests() :
+         Text_Based_Test("pubkey/dh_invalid.vec", "P,Q,G,InvalidKey") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("DH invalid keys");
+
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt q = vars.get_req_bn("Q");
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt pubkey = vars.get_req_bn("InvalidKey");
+
+         Botan::DL_Group grp(p, q, g);
+         std::unique_ptr<Botan::Public_Key> key(new Botan::DH_PublicKey(grp, pubkey));
+         result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
+         return result;
+         }
+   };
+
+class Diffie_Hellman_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "modp/ietf/1024" };
+         }
+      std::string algo_name() const override
+         {
+         return "DH";
+         }
+   };
+
+BOTAN_REGISTER_TEST("dh_kat", Diffie_Hellman_KAT_Tests);
+BOTAN_REGISTER_TEST("dh_invalid", DH_Invalid_Key_Tests);
+
+BOTAN_REGISTER_TEST("dh_keygen", Diffie_Hellman_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_dl_group.cpp b/third_party/botan/src/tests/test_dl_group.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_dl_group.cpp
@@ -0,0 +1,247 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_DL_GROUP)
+   #include <botan/dl_group.h>
+   #include <botan/workfactor.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+namespace {
+
+class DL_Group_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_dl_encoding());
+         results.push_back(test_dl_errors());
+
+         return results;
+         }
+
+   private:
+      Test::Result test_dl_errors()
+         {
+         Test::Result result("DL_Group errors");
+         result.test_throws("Uninitialized",
+                            "DL_Group uninitialized",
+                            []() { Botan::DL_Group dl; dl.get_p(); });
+
+#if !defined(BOTAN_HAS_SANITIZER_UNDEFINED)
+         result.test_throws("Bad generator param",
+                            "DL_Group unknown PrimeType",
+                            []() {
+                            auto invalid_type = static_cast<Botan::DL_Group::PrimeType>(9);
+                            Botan::DL_Group dl(Test::rng(), invalid_type, 1024);
+             });
+#endif
+
+         return result;
+         }
+
+      Test::Result test_dl_encoding()
+         {
+         Test::Result result("DL_Group encoding");
+
+         const Botan::DL_Group orig("modp/ietf/1024");
+
+         const std::string pem1 = orig.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+         const std::string pem2 = orig.PEM_encode(Botan::DL_Group::ANSI_X9_57);
+         const std::string pem3 = orig.PEM_encode(Botan::DL_Group::PKCS_3);
+
+         Botan::DL_Group group1(pem1);
+
+         result.test_eq("Same p in X9.42 decoding", group1.get_p(), orig.get_p());
+         result.test_eq("Same q in X9.42 decoding", group1.get_q(), orig.get_q());
+         result.test_eq("Same g in X9.42 decoding", group1.get_g(), orig.get_g());
+
+         result.test_eq("PEM encodings match",
+                        group1.PEM_encode(Botan::DL_Group::ANSI_X9_42),
+                        Botan::DL_Group::PEM_for_named_group("modp/ietf/1024"));
+
+         Botan::DL_Group group2(pem2);
+
+         result.test_eq("Same p in X9.57 decoding", group2.get_p(), orig.get_p());
+         result.test_eq("Same q in X9.57 decoding", group2.get_q(), orig.get_q());
+         result.test_eq("Same g in X9.57 decoding", group2.get_g(), orig.get_g());
+
+         Botan::DL_Group group3(pem3);
+
+         result.test_eq("Same p in X9.57 decoding", group3.get_p(), orig.get_p());
+         // no q in PKCS #3 format
+         result.test_eq("Same g in X9.57 decoding", group3.get_g(), orig.get_g());
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("dl_group", DL_Group_Tests);
+
+class DL_Generate_Group_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("DL_Group generate");
+
+         result.start_timer();
+
+         auto& rng = Test::rng();
+
+         Botan::DL_Group dh1050(rng, Botan::DL_Group::Prime_Subgroup, 1050, 175);
+         result.test_eq("DH p size", dh1050.get_p().bits(), 1050);
+         result.test_eq("DH q size", dh1050.get_q().bits(), 175);
+         result.test_lte("DH g size", dh1050.get_g().bits(), 1050);
+         result.test_eq("DH group verifies", dh1050.verify_group(rng, true), true);
+
+         Botan::DL_Group dh_implicit_q(rng, Botan::DL_Group::Prime_Subgroup, 1040);
+         result.test_eq("DH p size", dh_implicit_q.get_p().bits(), 1040);
+         result.test_eq("DH q size", dh_implicit_q.get_q().bits(), Botan::dl_exponent_size(1040));
+         result.test_eq("DH group verifies", dh_implicit_q.verify_group(rng, true), true);
+
+         if(Test::run_long_tests())
+            {
+            Botan::DL_Group dh_strong(rng, Botan::DL_Group::Strong, 1025);
+            result.test_eq("DH p size", dh_strong.get_p().bits(), 1025);
+            result.test_eq("DH q size", dh_strong.get_q().bits(), 1024);
+            result.test_eq("DH group verifies", dh_strong.verify_group(rng, true), true);
+            }
+
+#if defined(BOTAN_HAS_SHA1)
+         Botan::DL_Group dsa1024(rng, Botan::DL_Group::DSA_Kosherizer, 1024);
+         result.test_eq("DSA p size", dsa1024.get_p().bits(), 1024);
+         result.test_eq("DSA q size", dsa1024.get_q().bits(), 160);
+         result.test_lte("DSA g size", dsa1024.get_g().bits(), 1024);
+         result.test_eq("DSA group verifies", dsa1024.verify_group(rng, true), true);
+
+         const std::vector<uint8_t> short_seed(16);
+         const std::vector<uint8_t> invalid_seed(20);
+         const std::vector<uint8_t> working_seed = Botan::hex_decode("0000000000000000000000000000000000000021");
+
+         result.test_throws("DSA seed does not generate group",
+                            "DL_Group: The seed given does not generate a DSA group",
+                            [&rng,&invalid_seed]() { Botan::DL_Group dsa(rng, invalid_seed, 1024, 160); });
+
+         result.test_throws("DSA seed is too short",
+                            "Generating a DSA parameter set with a 160 bit long q requires a seed at least as many bits long",
+                            [&rng,&short_seed]() { Botan::DL_Group dsa(rng, short_seed, 1024, 160); });
+
+         // From FIPS 186-3 test data
+         const std::vector<uint8_t> seed = Botan::hex_decode("1F5DA0AF598EEADEE6E6665BF880E63D8B609BA2");
+
+         result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 1024, 224); });
+         result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 3072, 224); });
+         result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 2048, 256); });
+
+         Botan::DL_Group dsa_from_seed(rng, seed, 1024, 160);
+
+         result.test_eq("DSA q from seed", dsa_from_seed.get_q(),
+                        Botan::BigInt("0xAB1A788BCE3C557A965A5BFA6908FAA665FDEB7D"));
+
+         // Modulo just to avoid embedding entire 1024-bit P in src file
+         result.test_eq("DSA p from seed", static_cast<size_t>(dsa_from_seed.get_p() % 4294967291), size_t(2513712339));
+
+         result.test_eq("DSA group from seed verifies", dsa_from_seed.verify_group(rng, true), true);
+#endif
+
+         result.end_timer();
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("dl_group_gen", DL_Generate_Group_Tests);
+
+class DL_Named_Group_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         const std::vector<std::string> dl_named =
+            {
+            "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",
+
+            "ffdhe/ietf/2048",
+            "ffdhe/ietf/3072",
+            "ffdhe/ietf/4096",
+            "ffdhe/ietf/6144",
+            "ffdhe/ietf/8192",
+            };
+
+         Test::Result result("DL_Group named");
+         result.start_timer();
+
+         for(std::string name : dl_named)
+            {
+            // Confirm we can load every group we expect
+            Botan::DL_Group group(name);
+
+            result.test_ne("DL_Group p is set", group.get_p(), 0);
+            result.test_ne("DL_Group g is set", group.get_g(), 0);
+
+            const size_t strength = group.estimated_strength();
+
+            // 8192 bit ~~ 2**202 strength
+            result.confirm("Plausible strength", strength >= 80 && strength < 210);
+
+            result.confirm("Expected source", group.source() == Botan::DL_Group_Source::Builtin);
+
+            if(name.find("modp/srp/") == std::string::npos)
+               {
+               result.test_ne("DL_Group q is set", group.get_q(), 0);
+               }
+            else
+               {
+               result.test_eq("DL_Group q is not set for SRP groups", group.get_q(), 0);
+               }
+
+            if(group.p_bits() <= 1536 || Test::run_long_tests())
+               {
+               result.test_eq(name + " strong verifies", group.verify_group(Test::rng(), true), true);
+               }
+
+            }
+         result.end_timer();
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("dl_group_named", DL_Named_Group_Tests);
+
+}
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_dlies.cpp b/third_party/botan/src/tests/test_dlies.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_dlies.cpp
@@ -0,0 +1,207 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_DLIES) && defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   #include "test_pubkey.h"
+   #include <botan/dlies.h>
+   #include <botan/dh.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_DLIES) && defined(BOTAN_HAS_DIFFIE_HELLMAN)
+
+class DLIES_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      DLIES_KAT_Tests()
+         : Text_Based_Test(
+              "pubkey/dlies.vec",
+              "Kdf,Mac,MacKeyLen,IV,Group,X1,X2,Msg,Ciphertext") {}
+
+      Test::Result run_one_test(const std::string& cipher_algo, const VarMap& vars) override
+         {
+         const Botan::BigInt x1 = vars.get_req_bn("X1");
+         const Botan::BigInt x2 = vars.get_req_bn("X2");
+
+         const std::vector<uint8_t> input    = vars.get_req_bin("Msg");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Ciphertext");
+
+         const std::string kdf_algo = vars.get_req_str("Kdf");
+         const std::string mac_algo = vars.get_req_str("Mac");
+         const size_t mac_key_len = vars.get_req_sz("MacKeyLen");
+         const std::string group_name = vars.get_req_str("Group");
+
+         const std::vector<uint8_t> iv = vars.get_opt_bin("IV");
+
+         Test::Result result("DLIES " + cipher_algo);
+
+         std::unique_ptr<Botan::KDF> kdf(Botan::KDF::create(kdf_algo));
+         if(!kdf)
+            {
+            result.test_note("Skipping due to missing KDF:  " + kdf_algo);
+            return result;
+            }
+
+         std::unique_ptr<Botan::MAC> mac(Botan::MAC::create(mac_algo));
+         if(!mac)
+            {
+            result.test_note("Skipping due to missing MAC:  " + mac_algo);
+            return result;
+            }
+
+         std::unique_ptr<Botan::Cipher_Mode> enc;
+         std::unique_ptr<Botan::Cipher_Mode> dec;
+         size_t cipher_key_len = 0;
+
+         if(cipher_algo != "XOR")
+            {
+            enc = Botan::Cipher_Mode::create(cipher_algo, Botan::ENCRYPTION);
+            dec = Botan::Cipher_Mode::create(cipher_algo, Botan::DECRYPTION);
+
+            if(!enc || !dec)
+               {
+               result.test_note("Skipping due to missing cipher:  " + mac_algo);
+               return result;
+               }
+
+            cipher_key_len = enc->key_spec().maximum_keylength();
+            }
+
+         Botan::DL_Group domain(group_name);
+
+         Botan::DH_PrivateKey from(Test::rng(), domain, x1);
+         Botan::DH_PrivateKey to(Test::rng(), domain, x2);
+
+         Botan::DLIES_Encryptor encryptor(from, Test::rng(), kdf->clone(), enc.release(), cipher_key_len, mac->clone(),
+                                          mac_key_len);
+         Botan::DLIES_Decryptor decryptor(to, Test::rng(), kdf.release(), dec.release(), cipher_key_len, mac.release(),
+                                          mac_key_len);
+
+         if(!iv.empty())
+            {
+            encryptor.set_initialization_vector(iv);
+            decryptor.set_initialization_vector(iv);
+            }
+
+         encryptor.set_other_key(to.public_value());
+
+         result.test_eq("encryption", encryptor.encrypt(input, Test::rng()), expected);
+         result.test_eq("decryption", decryptor.decrypt(expected), input);
+
+         check_invalid_ciphertexts(result, decryptor, input, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("dlies", DLIES_KAT_Tests);
+
+Test::Result test_xor()
+   {
+   Test::Result result("DLIES XOR");
+
+   std::vector<std::string> kdfs = { "KDF2(SHA-512)", "KDF1-18033(SHA-512)" };
+   std::vector<std::string> macs = { "HMAC(SHA-512)", "CMAC(AES-128)" };
+
+   const size_t mac_key_len = 16;
+
+   std::unique_ptr<Botan::KDF> kdf;
+   std::unique_ptr<Botan::MAC> mac;
+
+   Botan::DH_PrivateKey alice(Test::rng(), Botan::DL_Group("modp/ietf/2048"));
+   Botan::DH_PrivateKey bob(Test::rng(), Botan::DL_Group("modp/ietf/2048"));
+
+   for(const auto& kfunc : kdfs)
+      {
+      kdf = Botan::KDF::create(kfunc);
+
+      if(!kdf)
+         {
+         result.test_note("Skipping due to missing KDF: " + kfunc);
+         continue;
+         }
+
+      for(const auto& mfunc : macs)
+         {
+         mac = Botan::MAC::create(mfunc);
+
+         if(!mac)
+            {
+            result.test_note("Skipping due to missing MAC: " + mfunc);
+            continue;
+            }
+
+         Botan::DLIES_Encryptor encryptor(alice, Test::rng(), kdf->clone(), mac->clone(), mac_key_len);
+
+         // negative test: other pub key not set
+         Botan::secure_vector<uint8_t> plaintext = Test::rng().random_vec(32);
+
+         result.test_throws("encrypt not possible without setting other public key", [&encryptor, &plaintext]()
+            {
+            encryptor.encrypt(plaintext, Test::rng());
+            });
+
+         encryptor.set_other_key(bob.public_value());
+         std::vector<uint8_t> ciphertext = encryptor.encrypt(plaintext, Test::rng());
+
+         Botan::DLIES_Decryptor decryptor(bob, Test::rng(), kdf->clone(), mac->clone(), mac_key_len);
+
+         // negative test: ciphertext too short
+         result.test_throws("ciphertext too short", [ &decryptor ]()
+            {
+            decryptor.decrypt(std::vector<uint8_t>(2));
+            });
+
+         result.test_eq("decryption", decryptor.decrypt(ciphertext), plaintext);
+
+         check_invalid_ciphertexts(result, decryptor, unlock(plaintext), ciphertext);
+         }
+      }
+
+   return result;
+   }
+
+class DLIES_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         std::vector<std::function<Test::Result()>> fns =
+            {
+            test_xor
+            };
+
+         for(size_t i = 0; i != fns.size(); ++i)
+            {
+            try
+               {
+               results.push_back(fns[ i ]());
+               }
+            catch(std::exception& e)
+               {
+               results.push_back(Test::Result::Failure("DLIES unit tests " + std::to_string(i), e.what()));
+               }
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("dlies_unit", DLIES_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_dsa.cpp b/third_party/botan/src/tests/test_dsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_dsa.cpp
@@ -0,0 +1,111 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_DSA)
+   #include <botan/dsa.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_DSA)
+
+class DSA_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      DSA_KAT_Tests() : PK_Signature_Generation_Test(
+            "DSA",
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+            "pubkey/dsa_rfc6979.vec",
+            "P,Q,G,X,Hash,Msg,Signature",
+#else
+            "pubkey/dsa_prob.vec",
+            "P,Q,G,X,Hash,Msg,Nonce,Signature",
+#endif
+            "") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt q = vars.get_req_bn("Q");
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt x = vars.get_req_bn("X");
+
+         const Botan::DL_Group grp(p, q, g);
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::DSA_PrivateKey(Test::rng(), grp, x));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return "EMSA1(" + vars.get_req_str("Hash") + ")";
+         }
+   };
+
+class DSA_Verification_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      DSA_Verification_Tests() : PK_Signature_Verification_Test(
+            "DSA",
+            "pubkey/dsa_verify.vec",
+            "P,Q,G,Y,Msg,Signature") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt q = vars.get_req_bn("Q");
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt y = vars.get_req_bn("Y");
+
+         const Botan::DL_Group grp(p, q, g);
+
+         std::unique_ptr<Botan::Public_Key> key(new Botan::DSA_PublicKey(grp, y));
+         return key;
+         }
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+   };
+
+class DSA_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "dsa/jce/1024" };
+         }
+      std::string algo_name() const override
+         {
+         return "DSA";
+         }
+   };
+
+BOTAN_REGISTER_TEST("dsa_sign", DSA_KAT_Tests);
+BOTAN_REGISTER_TEST("dsa_verify", DSA_Verification_Tests);
+BOTAN_REGISTER_TEST("dsa_keygen", DSA_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ecc_pointmul.cpp b/third_party/botan/src/tests/test_ecc_pointmul.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ecc_pointmul.cpp
@@ -0,0 +1,97 @@
+/*
+* (C) 2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   #include <botan/ec_group.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+class ECC_Basepoint_Mul_Tests final : public Text_Based_Test
+   {
+   public:
+      ECC_Basepoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_base_point_mul.vec", "m,X,Y") {}
+
+      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override
+         {
+         Test::Result result("ECC base point multiply " + group_id);
+
+         const Botan::BigInt m = vars.get_req_bn("m");
+         const Botan::BigInt X = vars.get_req_bn("X");
+         const Botan::BigInt Y = vars.get_req_bn("Y");
+
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         const Botan::PointGFp& base_point = group.get_base_point();
+
+         const Botan::PointGFp p1 = base_point * m;
+         result.test_eq("p1 affine X", p1.get_affine_x(), X);
+         result.test_eq("p1 affine Y", p1.get_affine_y(), Y);
+
+         std::vector<Botan::BigInt> ws;
+         const Botan::PointGFp p2 = group.blinded_base_point_multiply(m, Test::rng(), ws);
+         result.test_eq("p2 affine X", p2.get_affine_x(), X);
+         result.test_eq("p2 affine Y", p2.get_affine_y(), Y);
+
+         const Botan::PointGFp p3 = group.blinded_var_point_multiply(base_point, m, Test::rng(), ws);
+         result.test_eq("p3 affine X", p3.get_affine_x(), X);
+         result.test_eq("p3 affine Y", p3.get_affine_y(), Y);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecc_basemul", ECC_Basepoint_Mul_Tests);
+
+class ECC_Varpoint_Mul_Tests final : public Text_Based_Test
+   {
+   public:
+      ECC_Varpoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_var_point_mul.vec", "X,Y,k,kX,kY") {}
+
+      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override
+         {
+         Test::Result result("ECC var point multiply " + group_id);
+
+         const Botan::BigInt X = vars.get_req_bn("X");
+         const Botan::BigInt Y = vars.get_req_bn("Y");
+         const Botan::BigInt k = vars.get_req_bn("k");
+         const Botan::BigInt kX = vars.get_req_bn("kX");
+         const Botan::BigInt kY = vars.get_req_bn("kY");
+
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         const Botan::PointGFp pt = group.point(X, Y);
+
+         result.confirm("Input point is on the curve", pt.on_the_curve());
+
+         const Botan::PointGFp p1 = pt * k;
+         result.test_eq("p1 affine X", p1.get_affine_x(), kX);
+         result.test_eq("p1 affine Y", p1.get_affine_y(), kY);
+
+         result.confirm("Output point is on the curve", p1.on_the_curve());
+
+         std::vector<Botan::BigInt> ws;
+         const Botan::PointGFp p2 = group.blinded_var_point_multiply(pt, k, Test::rng(), ws);
+         result.test_eq("p2 affine X", p2.get_affine_x(), kX);
+         result.test_eq("p2 affine Y", p2.get_affine_y(), kY);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecc_varmul", ECC_Varpoint_Mul_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ecdh.cpp b/third_party/botan/src/tests/test_ecdh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ecdh.cpp
@@ -0,0 +1,72 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ECDH)
+   #include "test_pubkey.h"
+   #include <botan/ecdh.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECDH)
+
+class ECDH_KAT_Tests final : public PK_Key_Agreement_Test
+   {
+   public:
+      ECDH_KAT_Tests()
+         : PK_Key_Agreement_Test(
+              "ECDH",
+              "pubkey/ecdh.vec",
+              "Group,Secret,CounterKey,K",
+              "KDF") {}
+
+      std::string default_kdf(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_our_key(const std::string& group_id,
+            const VarMap& vars) override
+         {
+         Botan::EC_Group group(group_id);
+         const Botan::BigInt secret = vars.get_req_bn("Secret");
+         std::unique_ptr<Botan::Private_Key> key(new Botan::ECDH_PrivateKey(Test::rng(), group, secret));
+         return key;
+         }
+
+      std::vector<uint8_t> load_their_key(const std::string&, const VarMap& vars) override
+         {
+         return vars.get_req_bin("CounterKey");
+         }
+   };
+
+class ECDH_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "secp256r1", "secp384r1", "secp521r1",
+                  "brainpool256r1", "brainpool384r1", "brainpool512r1", "frp256v1" };
+         }
+      std::string algo_name() const override
+         {
+         return "ECDH";
+         }
+   };
+
+
+BOTAN_REGISTER_TEST("ecdh_kat", ECDH_KAT_Tests);
+BOTAN_REGISTER_TEST("ecdh_keygen", ECDH_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ecdsa.cpp b/third_party/botan/src/tests/test_ecdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ecdsa.cpp
@@ -0,0 +1,269 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_ECDSA)
+   #include "test_pubkey.h"
+   #include <botan/ecdsa.h>
+   #include <botan/pk_algs.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_Verification_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      ECDSA_Verification_Tests() : PK_Signature_Verification_Test(
+            "ECDSA",
+            "pubkey/ecdsa_verify.vec",
+            "Group,Px,Py,Hash,Msg,Signature") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const std::string group_id = vars.get_req_str("Group");
+         const BigInt px = vars.get_req_bn("Px");
+         const BigInt py = vars.get_req_bn("Py");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         const Botan::PointGFp public_point = group.point(px, py);
+
+         std::unique_ptr<Botan::Public_Key> key(new Botan::ECDSA_PublicKey(group, public_point));
+         return key;
+         }
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+   };
+
+class ECDSA_Wycheproof_Verification_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      ECDSA_Wycheproof_Verification_Tests() : PK_Signature_Verification_Test(
+            "ECDSA",
+            "pubkey/ecdsa_wycheproof.vec",
+            "Group,Px,Py,Hash,Msg,Signature",
+            "Valid") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      Botan::Signature_Format sig_format() const override
+         {
+         return Botan::DER_SEQUENCE;
+         }
+
+      bool test_random_invalid_sigs() const override { return false; }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const std::string group_id = vars.get_req_str("Group");
+         const BigInt px = vars.get_req_bn("Px");
+         const BigInt py = vars.get_req_bn("Py");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         const Botan::PointGFp public_point = group.point(px, py);
+
+         std::unique_ptr<Botan::Public_Key> key(new Botan::ECDSA_PublicKey(group, public_point));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return "EMSA1(" + vars.get_req_str("Hash") + ")";
+         }
+   };
+
+class ECDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      ECDSA_Signature_KAT_Tests() : PK_Signature_Generation_Test(
+            "ECDSA",
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+            "pubkey/ecdsa_rfc6979.vec",
+            "Group,X,Hash,Msg,Signature") {}
+#else
+            "pubkey/ecdsa_prob.vec",
+            "Group,X,Hash,Msg,Nonce,Signature") {}
+#endif
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const std::string group_id = vars.get_req_str("Group");
+         const BigInt x = vars.get_req_bn("X");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::ECDSA_PrivateKey(Test::rng(), group, x));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         const std::string hash = vars.get_req_str("Hash");
+         if(hash.substr(0,3) == "Raw")
+            return hash;
+         return "EMSA1(" + hash + ")";
+         }
+
+#if !defined(BOTAN_HAS_RFC6979_GENERATOR)
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         // probabilistic ecdsa signature generation extracts more random than just the nonce,
+         // but the nonce is extracted first
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+#endif
+   };
+
+class ECDSA_Sign_Verify_DER_Test final : public PK_Sign_Verify_DER_Test
+   {
+   public:
+      ECDSA_Sign_Verify_DER_Test() :
+         PK_Sign_Verify_DER_Test("ECDSA", "EMSA1(SHA-512)") {}
+
+      std::unique_ptr<Botan::Private_Key> key() const override
+         {
+         return Botan::create_private_key( "ECDSA",  Test::rng(), "secp256r1" );
+         }
+   };
+
+class ECDSA_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "secp256r1", "secp384r1", "secp521r1", "frp256v1" };
+         }
+      std::string algo_name() const override
+         {
+         return "ECDSA";
+         }
+   };
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+
+class ECDSA_Key_Recovery_Tests final : public Text_Based_Test
+   {
+   public:
+      ECDSA_Key_Recovery_Tests() :
+         Text_Based_Test("pubkey/ecdsa_key_recovery.vec", "Group,Msg,R,S,V,PubkeyX,PubkeyY") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("ECDSA key recovery");
+
+         const std::string group_id = vars.get_req_str("Group");
+         Botan::EC_Group group(group_id);
+
+         const BigInt R = vars.get_req_bn("R");
+         const BigInt S = vars.get_req_bn("S");
+         const uint8_t V = vars.get_req_u8("V");
+         const std::vector<uint8_t> msg = vars.get_req_bin("Msg");
+         const BigInt pubkey_x = vars.get_req_bn("PubkeyX");
+         const BigInt pubkey_y = vars.get_req_bn("PubkeyY");
+
+         try
+            {
+            Botan::ECDSA_PublicKey pubkey(group, msg, R, S, V);
+            result.test_eq("Pubkey X coordinate", pubkey.public_point().get_affine_x(), pubkey_x);
+            result.test_eq("Pubkey Y coordinate", pubkey.public_point().get_affine_y(), pubkey_y);
+
+            const uint8_t computed_V = pubkey.recovery_param(msg, R, S);
+            result.test_eq("Recovery param is correct", static_cast<size_t>(computed_V), static_cast<size_t>(V));
+
+            Botan::PK_Verifier verifier(pubkey, "Raw");
+
+            auto sig = Botan::BigInt::encode_fixed_length_int_pair(R, S, group.get_order_bytes());
+
+            result.confirm("Signature verifies", verifier.verify_message(msg, sig));
+            }
+         catch(Botan::Exception& e)
+            {
+            result.test_failure("Failed to recover ECDSA public key", e.what());
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecdsa_key_recovery", ECDSA_Key_Recovery_Tests);
+
+#endif
+
+class ECDSA_Invalid_Key_Tests final : public Text_Based_Test
+   {
+   public:
+      ECDSA_Invalid_Key_Tests() :
+         Text_Based_Test("pubkey/ecdsa_invalid.vec", "Group,InvalidKeyX,InvalidKeyY") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("ECDSA invalid keys");
+
+         const std::string group_id = vars.get_req_str("Group");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+         const Botan::BigInt x = vars.get_req_bn("InvalidKeyX");
+         const Botan::BigInt y = vars.get_req_bn("InvalidKeyY");
+
+         std::unique_ptr<Botan::PointGFp> public_point;
+
+         try
+            {
+            public_point.reset(new Botan::PointGFp(group.point(x, y)));
+            }
+         catch(Botan::Invalid_Argument&)
+            {
+            // PointGFp() performs a range check on x, y in [0, p−1],
+            // which is also part of the EC public key checks, e.g.,
+            // in NIST SP800-56A rev2, sec. 5.6.2.3.2
+            result.test_success("public key fails check");
+            return result;
+            }
+
+         std::unique_ptr<Botan::Public_Key> key(new Botan::ECDSA_PublicKey(group, *public_point));
+         result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecdsa_verify", ECDSA_Verification_Tests);
+BOTAN_REGISTER_TEST("ecdsa_verify_wycheproof", ECDSA_Wycheproof_Verification_Tests);
+BOTAN_REGISTER_TEST("ecdsa_sign", ECDSA_Signature_KAT_Tests);
+BOTAN_REGISTER_TEST("ecdsa_sign_verify_der", ECDSA_Sign_Verify_DER_Test);
+BOTAN_REGISTER_TEST("ecdsa_keygen", ECDSA_Keygen_Tests);
+BOTAN_REGISTER_TEST("ecdsa_invalid", ECDSA_Invalid_Key_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ecgdsa.cpp b/third_party/botan/src/tests/test_ecgdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ecgdsa.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2016 René Korthaus, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_ECGDSA)
+   #include "test_pubkey.h"
+   #include <botan/ecgdsa.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECGDSA)
+
+class ECGDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      ECGDSA_Signature_KAT_Tests() : PK_Signature_Generation_Test(
+            "ECGDSA",
+            "pubkey/ecgdsa.vec",
+            "Group,X,Hash,Msg,Nonce,Signature") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const std::string group_id = vars.get_req_str("Group");
+         const BigInt x = vars.get_req_bn("X");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::ECGDSA_PrivateKey(Test::rng(), group, x));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return "EMSA1(" + vars.get_req_str("Hash") + ")";
+         }
+
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         // ecgdsa signature generation extracts more random than just the nonce,
+         // but the nonce is extracted first
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+   };
+
+class ECGDSA_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "secp256r1", "secp384r1", "secp521r1" };
+         }
+      std::string algo_name() const override
+         {
+         return "ECGDSA";
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecgdsa_sign", ECGDSA_Signature_KAT_Tests);
+BOTAN_REGISTER_TEST("ecgdsa_keygen", ECGDSA_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ecies.cpp b/third_party/botan/src/tests/test_ecies.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ecies.cpp
@@ -0,0 +1,449 @@
+/*
+* (C) 2016 Philipp Weber
+* (C) 2016 Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ECIES)
+   #include <botan/ecies.h>
+   #include <botan/ecdh.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECIES) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC)
+
+using Flags = Botan::ECIES_Flags;
+
+Botan::PointGFp::Compression_Type get_compression_type(const std::string& format)
+   {
+   if(format == "uncompressed")
+      {
+      return Botan::PointGFp::UNCOMPRESSED;
+      }
+   else if(format == "compressed")
+      {
+      return Botan::PointGFp::COMPRESSED;
+      }
+   else if(format == "hybrid")
+      {
+      return Botan::PointGFp::HYBRID;
+      }
+   throw Botan::Invalid_Argument("invalid compression format");
+   }
+
+Flags ecies_flags(bool cofactor_mode, bool old_cofactor_mode, bool check_mode, bool single_hash_mode)
+   {
+   return (cofactor_mode ? Flags::COFACTOR_MODE : Flags::NONE)
+          | (single_hash_mode ? Flags::SINGLE_HASH_MODE : Flags::NONE)
+          | (old_cofactor_mode ? Flags::OLD_COFACTOR_MODE : Flags::NONE)
+          | (check_mode ? Flags::CHECK_MODE : Flags::NONE);
+   }
+
+void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key,
+                           const Botan::ECDH_PrivateKey& other_private_key,
+                           const Botan::ECIES_System_Params& ecies_params,
+                           const Botan::InitializationVector& iv, const std::string& label,
+                           const std::vector<uint8_t>& plaintext, const std::vector<uint8_t>& ciphertext)
+   {
+   try
+      {
+      Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
+      ecies_enc.set_other_key(other_private_key.public_point());
+      Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng());
+      if(!iv.bits_of().empty())
+         {
+         ecies_enc.set_initialization_vector(iv);
+         ecies_dec.set_initialization_vector(iv);
+         }
+      if(!label.empty())
+         {
+         ecies_enc.set_label(label);
+         ecies_dec.set_label(label);
+         }
+
+      const std::vector<uint8_t> encrypted = ecies_enc.encrypt(plaintext, Test::rng());
+      if(!ciphertext.empty())
+         {
+         result.test_eq("encrypted data", encrypted, ciphertext);
+         }
+      const Botan::secure_vector<uint8_t> decrypted = ecies_dec.decrypt(encrypted);
+      result.test_eq("decrypted data equals plaintext", decrypted, plaintext);
+
+      std::vector<uint8_t> invalid_encrypted = encrypted;
+      uint8_t& last_byte = invalid_encrypted[invalid_encrypted.size() - 1];
+      last_byte = ~last_byte;
+      result.test_throws("throw on invalid ciphertext", [&ecies_dec, &invalid_encrypted]
+         {
+         ecies_dec.decrypt(invalid_encrypted);
+         });
+      }
+   catch(Botan::Lookup_Error& e)
+      {
+      result.test_note(std::string("Test not executed: ") + e.what());
+      }
+   }
+
+void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key,
+                           const Botan::ECDH_PrivateKey& other_private_key,
+                           const Botan::ECIES_System_Params& ecies_params, size_t iv_length = 0)
+   {
+   const std::vector<uint8_t> plaintext { 1, 2, 3 };
+   check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, std::vector<uint8_t>(iv_length, 0), "",
+                         plaintext, std::vector<uint8_t>());
+   }
+
+#if defined(BOTAN_HAS_KDF1_18033) && defined(BOTAN_HAS_SHA1)
+
+class ECIES_ISO_Tests final : public Text_Based_Test
+   {
+   public:
+      ECIES_ISO_Tests() : Text_Based_Test(
+            "pubkey/ecies-18033.vec",
+            "format,p,a,b,mu,nu,gx,gy,hx,hy,x,r,C0,K") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("ECIES-ISO");
+
+         // get test vectors defined by ISO 18033
+         const Botan::PointGFp::Compression_Type compression_type = get_compression_type(vars.get_req_str("format"));
+         const Botan::BigInt p = vars.get_req_bn("p");
+         const Botan::BigInt a = vars.get_req_bn("a");
+         const Botan::BigInt b = vars.get_req_bn("b");
+         const Botan::BigInt mu = vars.get_req_bn("mu");   // order
+         const Botan::BigInt nu = vars.get_req_bn("nu");   // cofactor
+         const Botan::BigInt gx = vars.get_req_bn("gx");   // base point x
+         const Botan::BigInt gy = vars.get_req_bn("gy");   // base point y
+         const Botan::BigInt hx = vars.get_req_bn("hx");   // x of public point of bob
+         const Botan::BigInt hy = vars.get_req_bn("hy");   // y of public point of bob
+         const Botan::BigInt x = vars.get_req_bn("x");   // private key of bob
+         const Botan::BigInt r = vars.get_req_bn("r");   // (ephemeral) private key of alice
+         const std::vector<uint8_t> c0 = vars.get_req_bin("C0");   // expected encoded (ephemeral) public key
+         const std::vector<uint8_t> k = vars.get_req_bin("K");   // expected derived secret
+
+         const Botan::EC_Group domain(p, a, b, gx, gy, mu, nu);
+
+         // keys of bob
+         const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, x);
+         const Botan::PointGFp other_public_key_point = domain.point(hx, hy);
+         const Botan::ECDH_PublicKey other_public_key(domain, other_public_key_point);
+
+         // (ephemeral) keys of alice
+         const Botan::ECDH_PrivateKey eph_private_key(Test::rng(), domain, r);
+         const Botan::PointGFp eph_public_key_point = eph_private_key.public_point();
+         const std::vector<uint8_t> eph_public_key_bin = eph_public_key_point.encode(compression_type);
+         result.test_eq("encoded (ephemeral) public key", eph_public_key_bin, c0);
+
+         // test secret derivation: ISO 18033 test vectors use KDF1 from ISO 18033
+         // no cofactor-/oldcofactor-/singlehash-/check-mode and 128 byte secret length
+         Botan::ECIES_KA_Params ka_params(eph_private_key.domain(), "KDF1-18033(SHA-1)", 128, compression_type, Flags::NONE);
+         const Botan::ECIES_KA_Operation ka(eph_private_key, ka_params, true, Test::rng());
+         const Botan::SymmetricKey secret_key = ka.derive_secret(eph_public_key_bin, other_public_key_point);
+         result.test_eq("derived secret key", secret_key.bits_of(), k);
+
+         // test encryption / decryption
+
+         for(auto comp_type : { Botan::PointGFp::UNCOMPRESSED, Botan::PointGFp::COMPRESSED, Botan::PointGFp::HYBRID })
+            {
+            for(bool cofactor_mode : { true, false })
+               {
+               for(bool single_hash_mode : { true, false })
+                  {
+                  for(bool old_cofactor_mode : { true, false })
+                     {
+                     for(bool check_mode : { true, false })
+                        {
+                        Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode);
+
+                        if(size_t(cofactor_mode) + size_t(check_mode) + size_t(old_cofactor_mode) > 1)
+                           {
+                           auto onThrow =  [&]()
+                              {
+                              Botan::ECIES_System_Params(eph_private_key.domain(),
+                                 "KDF2(SHA-1)", "AES-256/CBC", 32, "HMAC(SHA-1)", 20,
+                                 comp_type, flags);
+                              };
+                           result.test_throws("throw on invalid ECIES_Flags", onThrow);
+                           continue;
+                           }
+
+                        Botan::ECIES_System_Params ecies_params(eph_private_key.domain(), "KDF2(SHA-1)", "AES-256/CBC",
+                                                                32, "HMAC(SHA-1)", 20, comp_type, flags);
+                        check_encrypt_decrypt(result, eph_private_key, other_private_key, ecies_params, 16);
+                        }
+                     }
+                  }
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecies_iso", ECIES_ISO_Tests);
+
+#endif
+
+class ECIES_Tests final : public Text_Based_Test
+   {
+   public:
+      ECIES_Tests()
+         : Text_Based_Test(
+              "pubkey/ecies.vec",
+              "Curve,PrivateKey,OtherPrivateKey,Kdf,Dem,DemKeyLen,Mac,MacKeyLen,Format,"
+              "CofactorMode,OldCofactorMode,CheckMode,SingleHashMode,Label,Plaintext,Ciphertext",
+              "Iv") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("ECIES");
+
+         const std::string curve = vars.get_req_str("Curve");
+         const Botan::BigInt private_key_value = vars.get_req_bn("PrivateKey");
+         const Botan::BigInt other_private_key_value = vars.get_req_bn("OtherPrivateKey");
+         const std::string kdf = vars.get_req_str("Kdf");
+         const std::string dem = vars.get_req_str("Dem");
+         const size_t dem_key_len = vars.get_req_sz("DemKeyLen");
+         const std::vector<uint8_t> iv = vars.get_opt_bin("Iv");
+         const std::string mac = vars.get_req_str("Mac");
+         const size_t mac_key_len = vars.get_req_sz("MacKeyLen");
+         const Botan::PointGFp::Compression_Type compression_type = get_compression_type(vars.get_req_str("Format"));
+         const bool cofactor_mode = vars.get_req_sz("CofactorMode") != 0;
+         const bool old_cofactor_mode = vars.get_req_sz("OldCofactorMode") != 0;
+         const bool check_mode = vars.get_req_sz("CheckMode") != 0;
+         const bool single_hash_mode = vars.get_req_sz("SingleHashMode") != 0;
+         const std::string label = vars.get_req_str("Label");
+         const std::vector<uint8_t> plaintext = vars.get_req_bin("Plaintext");
+         const std::vector<uint8_t> ciphertext = vars.get_req_bin("Ciphertext");
+
+         const Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode);
+         const Botan::EC_Group domain(curve);
+         const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+         const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
+
+         const Botan::ECIES_System_Params ecies_params(private_key.domain(), kdf, dem, dem_key_len, mac, mac_key_len,
+               compression_type, flags);
+         check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("ecies", ECIES_Tests);
+
+#if defined(BOTAN_HAS_KDF1_18033) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SHA2_64)
+
+Test::Result test_other_key_not_set()
+   {
+   Test::Result result("ECIES other key not set");
+
+   const Flags flags = ecies_flags(false, false, false, true);
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
+         "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
+         flags);
+
+   Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
+
+   result.test_throws("encrypt not possible without setting other public key", [ &ecies_enc ]()
+      {
+      ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
+      });
+
+   return result;
+   }
+
+Test::Result test_kdf_not_found()
+   {
+   Test::Result result("ECIES kdf not found");
+
+   const Flags flags = ecies_flags(false, false, false, true);
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF-XYZ(SHA-512)", "AES-256/CBC", 32,
+         "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
+         flags);
+
+   result.test_throws("kdf not found", [&]()
+      {
+      Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
+      ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
+      });
+
+   return result;
+   }
+
+Test::Result test_mac_not_found()
+   {
+   Test::Result result("ECIES mac not found");
+
+   const Flags flags = ecies_flags(false, false, false, true);
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
+         "XYZMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
+         flags);
+
+   result.test_throws("mac not found", [&]()
+      {
+      Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
+      ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
+      });
+
+   return result;
+   }
+
+Test::Result test_cipher_not_found()
+   {
+   Test::Result result("ECIES cipher not found");
+
+   const Flags flags = ecies_flags(false, false, false, true);
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-XYZ-256/CBC", 32,
+         "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
+         flags);
+
+   result.test_throws("cipher not found", [&]()
+      {
+      Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
+      ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
+      });
+
+   return result;
+   }
+
+Test::Result test_system_params_short_ctor()
+   {
+   Test::Result result("ECIES short system params ctor");
+
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::BigInt other_private_key_value("2294226772740614508941417891614236736606752960073669253551166842"
+         "5866095315090327914760325168219669828915074071456176066304457448"
+         "25404691681749451640151380153");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
+
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
+         "HMAC(SHA-512)", 16);
+
+   const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+   const std::string label = "Test";
+
+   const std::vector<uint8_t> plaintext = Botan::hex_decode("000102030405060708090A0B0C0D0E0F");
+
+   // generated with botan
+   const std::vector<uint8_t> ciphertext = Botan::hex_decode("0401519EAA0489FF9D51E98E4C22349463E2001CD06F8CE47D81D4007A"
+                                           "79ACF98E92C814686477CEA666EFC277DC84E15FC95E38AFF8E16D478A"
+                                           "44CD5C5F1517F8B1F300000591317F261C3D04A7207F01EAE3EC70F2360"
+                                           "0F82C53CC0B85BE7AC9F6CE79EF2AB416E5934D61BA9D346385D7545C57F"
+                                           "77C7EA7C58E18C70CBFB0A24AE1B9943EC5A8D0657522CCDF30BA95674D81"
+                                           "B397635D215178CD13BD9504AE957A9888F4128FFC0F0D3F1CEC646AEC8CE"
+                                           "3F2463D233B22A7A12B679F4C06501F584D4DEFF6D26592A8D873398BD892"
+                                           "B477B3468813C053DA43C4F3D49009F7A12D6EF7");
+
+   check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext);
+
+   return result;
+   }
+
+Test::Result test_ciphertext_too_short()
+   {
+   Test::Result result("ECIES ciphertext too short");
+
+   const Botan::EC_Group domain("secp521r1");
+   const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
+                                         "5002761777100287880684822948852132235484464537021197213998300006"
+                                         "547176718172344447619746779823");
+
+   const Botan::BigInt other_private_key_value("2294226772740614508941417891614236736606752960073669253551166842"
+         "5866095315090327914760325168219669828915074071456176066304457448"
+         "25404691681749451640151380153");
+
+   const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
+   const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
+
+   const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
+         "HMAC(SHA-512)", 16);
+
+   Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng());
+
+   result.test_throws("ciphertext too short", [ &ecies_dec ]()
+      {
+      ecies_dec.decrypt(Botan::hex_decode("0401519EAA0489FF9D51E98E4C22349A"));
+      });
+
+   return result;
+   }
+
+class ECIES_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         std::vector<std::function<Test::Result()>> fns =
+            {
+            test_other_key_not_set,
+            test_kdf_not_found,
+            test_mac_not_found,
+            test_cipher_not_found,
+            test_system_params_short_ctor,
+            test_ciphertext_too_short
+            };
+
+         for(size_t i = 0; i != fns.size(); ++i)
+            {
+            try
+               {
+               results.emplace_back(fns[ i ]());
+               }
+            catch(std::exception& e)
+               {
+               results.emplace_back(Test::Result::Failure("ECIES unit tests " + std::to_string(i), e.what()));
+               }
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecies_unit", ECIES_Unit_Tests);
+
+#endif
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_eckcdsa.cpp b/third_party/botan/src/tests/test_eckcdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_eckcdsa.cpp
@@ -0,0 +1,79 @@
+/*
+* (C) 2016 René Korthaus, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_ECKCDSA)
+   #include "test_pubkey.h"
+   #include <botan/eckcdsa.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECKCDSA)
+
+class ECKCDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      ECKCDSA_Signature_KAT_Tests()
+         : PK_Signature_Generation_Test(
+              "ECKCDSA",
+              "pubkey/eckcdsa.vec",
+              "Group,X,Hash,Msg,Nonce,Signature") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const std::string group_id = vars.get_req_str("Group");
+         const BigInt x = vars.get_req_bn("X");
+         Botan::EC_Group group(Botan::OID::from_string(group_id));
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::ECKCDSA_PrivateKey(Test::rng(), group, x));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return "EMSA1(" + vars.get_req_str("Hash") + ")";
+         }
+
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         // eckcdsa signature generation extracts more random than just the nonce,
+         // but the nonce is extracted first
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+   };
+
+class ECKCDSA_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "secp256r1", "secp384r1", "secp521r1" };
+         }
+      std::string algo_name() const override
+         {
+         return "ECKCDSA";
+         }
+   };
+
+BOTAN_REGISTER_TEST("eckcdsa_sign", ECKCDSA_Signature_KAT_Tests);
+BOTAN_REGISTER_TEST("eckcdsa_keygen", ECKCDSA_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ed25519.cpp b/third_party/botan/src/tests/test_ed25519.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ed25519.cpp
@@ -0,0 +1,122 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ED25519)
+   #include "test_pubkey.h"
+   #include <botan/ed25519.h>
+   #include <botan/pkcs8.h>
+   #include <botan/x509_key.h>
+   #include <botan/data_src.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ED25519)
+
+class Ed25519_Verification_Tests : public PK_Signature_Verification_Test
+   {
+   public:
+      Ed25519_Verification_Tests() : PK_Signature_Verification_Test(
+         "Ed25519",
+         "pubkey/ed25519_verify.vec",
+         "Pubkey,Msg,Signature", "Valid") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const std::vector<uint8_t> pubkey = vars.get_req_bin("Pubkey");
+
+         std::unique_ptr<Botan::Ed25519_PublicKey> key(new Botan::Ed25519_PublicKey(pubkey));
+
+         return std::unique_ptr<Botan::Public_Key>(key.release());
+
+         }
+   };
+
+class Ed25519_Signature_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      Ed25519_Signature_Tests() : PK_Signature_Generation_Test(
+            "Ed25519",
+            "pubkey/ed25519.vec",
+            "Privkey,Pubkey,Msg,Signature") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const std::vector<uint8_t> privkey = vars.get_req_bin("Privkey");
+         const std::vector<uint8_t> pubkey = vars.get_req_bin("Pubkey");
+
+         Botan::secure_vector<uint8_t> seed(privkey.begin(), privkey.end());
+
+         std::unique_ptr<Botan::Ed25519_PrivateKey> key(new Botan::Ed25519_PrivateKey(seed));
+
+         if(key->get_public_key() != pubkey)
+            throw Test_Error("Invalid Ed25519 key in test data");
+
+         return std::unique_ptr<Botan::Private_Key>(key.release());
+         }
+   };
+
+class Ed25519_Curdle_Format_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         // Keys from draft-ietf-curdle-pkix-04.txt
+         const std::string priv_key_str =
+            "-----BEGIN PRIVATE KEY-----\n"
+            "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC\n"
+            "-----END PRIVATE KEY-----\n";
+
+         const std::string pub_key_str =
+            "-----BEGIN PUBLIC KEY-----\n"
+            "MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=\n"
+            "-----END PUBLIC KEY-----\n";
+
+         Test::Result result("Ed25519 CURDLE format");
+
+         Botan::DataSource_Memory priv_data(priv_key_str);
+         std::unique_ptr<Botan::Private_Key> priv_key(Botan::PKCS8::load_key(priv_data, Test::rng()));
+         result.confirm("Private key loaded", priv_key != nullptr);
+
+         Botan::DataSource_Memory pub_data(pub_key_str);
+         std::unique_ptr<Botan::Public_Key> pub_key(Botan::X509::load_key(pub_data));
+         result.confirm("Public key loaded", pub_key != nullptr);
+
+         Botan::PK_Signer signer(*priv_key, Test::rng(), "Pure");
+         signer.update("message");
+         std::vector<uint8_t> sig = signer.signature(Test::rng());
+
+         Botan::PK_Verifier verifier(*pub_key, "Pure");
+         verifier.update("message");
+         result.confirm("Signature valid", verifier.check_signature(sig));
+
+         return std::vector<Test::Result>{result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("ed25519_verify", Ed25519_Verification_Tests);
+BOTAN_REGISTER_TEST("ed25519_sign", Ed25519_Signature_Tests);
+BOTAN_REGISTER_TEST("ed25519_curdle", Ed25519_Curdle_Format_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_elg.cpp b/third_party/botan/src/tests/test_elg.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_elg.cpp
@@ -0,0 +1,63 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ELGAMAL)
+   #include <botan/elgamal.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ELGAMAL)
+
+class ElGamal_KAT_Tests final : public PK_Encryption_Decryption_Test
+   {
+   public:
+      ElGamal_KAT_Tests()
+         : PK_Encryption_Decryption_Test(
+              "ElGamal",
+              "pubkey/elgamal.vec",
+              "P,G,X,Msg,Nonce,Ciphertext",
+              "Padding") {}
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const Botan::BigInt p = vars.get_req_bn("P");
+         const Botan::BigInt g = vars.get_req_bn("G");
+         const Botan::BigInt x = vars.get_req_bn("X");
+
+         const Botan::DL_Group grp(p, g);
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::ElGamal_PrivateKey(Test::rng(), grp, x));
+         return key;
+         }
+   };
+
+class ElGamal_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "modp/ietf/1024" };
+         }
+      std::string algo_name() const override
+         {
+         return "ElGamal";
+         }
+   };
+
+BOTAN_REGISTER_TEST("elgamal_encrypt", ElGamal_KAT_Tests);
+BOTAN_REGISTER_TEST("elgamal_keygen", ElGamal_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_entropy.cpp b/third_party/botan/src/tests/test_entropy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_entropy.cpp
@@ -0,0 +1,135 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+#include <botan/entropy_src.h>
+
+#if defined(BOTAN_HAS_COMPRESSION)
+   #include <botan/compression.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+class Entropy_Source_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
+
+         std::vector<std::string> src_names = srcs.enabled_sources();
+
+         std::vector<Test::Result> results;
+
+         for(auto const& src_name : src_names)
+            {
+            Test::Result result("Entropy source " + src_name);
+
+            result.start_timer();
+
+            try
+               {
+               SeedCapturing_RNG rng;
+
+               size_t bits = srcs.poll_just(rng, src_name);
+
+               result.test_gte("Entropy estimate", rng.seed_material().size() * 8, bits);
+
+               if(rng.samples() > 0)
+                  {
+                  result.test_gte("Seed material bytes", rng.seed_material().size(), 1);
+                  result.test_gte("Samples", rng.samples(), 1);
+                  }
+
+               result.test_note("poll result", rng.seed_material());
+
+#if defined(BOTAN_HAS_COMPRESSION)
+               if(!rng.seed_material().empty())
+                  {
+                  /*
+                  * Skip bzip2 both due to OS X problem (GH #394) and because bzip2's
+                  * large blocks cause the entropy differential test to fail sometimes.
+                  */
+                  for(const std::string comp_algo : { "zlib", "lzma" })
+                     {
+                     std::unique_ptr<Botan::Compression_Algorithm> comp(Botan::make_compressor(comp_algo));
+
+                     if(comp)
+                        {
+                        size_t comp1_size = 0;
+
+                        try
+                           {
+                           Botan::secure_vector<uint8_t> compressed;
+                           compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
+                           comp->start(9);
+                           comp->finish(compressed);
+
+                           comp1_size = compressed.size();
+
+                           result.test_gte(comp_algo + " compressed entropy better than advertised",
+                                           compressed.size() * 8, bits);
+                           }
+                        catch(std::exception& e)
+                           {
+                           result.test_failure(comp_algo + " exception while compressing", e.what());
+                           }
+
+                        SeedCapturing_RNG rng2;
+
+                        size_t bits2 = srcs.poll_just(rng2, src_name);
+
+                        result.test_note("poll 2 result", rng2.seed_material());
+
+                        try
+                           {
+                           Botan::secure_vector<uint8_t> compressed;
+                           compressed.insert(compressed.end(), rng.seed_material().begin(), rng.seed_material().end());
+                           compressed.insert(compressed.end(), rng2.seed_material().begin(), rng2.seed_material().end());
+
+                           comp->start();
+                           comp->finish(compressed);
+
+                           size_t comp2_size = compressed.size();
+
+                           result.test_lt("Two blocks of entropy are larger than one",
+                                          comp1_size, comp2_size);
+
+                           size_t comp_diff = comp2_size - comp1_size;
+
+                           result.test_gte(comp_algo + " diff compressed entropy better than advertised",
+                                           comp_diff * 8, bits2);
+                           }
+                        catch(std::exception& e)
+                           {
+                           result.test_failure(comp_algo + " exception while compressing", e.what());
+                           }
+                        }
+                     }
+                  }
+#endif
+               }
+            catch(std::exception& e)
+               {
+               result.test_failure("during entropy collection test", e.what());
+               }
+
+            result.end_timer();
+            results.push_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("entropy", Entropy_Source_Tests);
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ffi.cpp b/third_party/botan/src/tests/test_ffi.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ffi.cpp
@@ -0,0 +1,2805 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2016 René Korthaus
+* (C) 2018 Ribose Inc, Krzysztof Kwiatkowski
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+#include <botan/version.h>
+
+#if defined(BOTAN_HAS_FFI)
+   #include <botan/hex.h>
+   #include <botan/ffi.h>
+   #include <botan/loadstor.h>
+   #include <set>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_FFI)
+
+#define TEST_FFI_OK(func, args) result.test_rc_ok(#func, func args)
+#define TEST_FFI_FAIL(msg, func, args) result.test_rc_fail(#func, msg, func args)
+#define TEST_FFI_RC(rc, func, args) result.test_rc(#func, rc, func args)
+
+#define REQUIRE_FFI_OK(func, args)                           \
+   if(!TEST_FFI_OK(func, args)) {                            \
+      result.test_note("Exiting test early due to failure"); \
+      return result;                                         \
+   }
+
+class FFI_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("FFI");
+
+         botan_rng_t rng;
+         if(!TEST_FFI_OK(botan_rng_init, (&rng, "user")))
+            {
+            return {result};
+            }
+
+         std::vector<Test::Result> results;
+         results.push_back(ffi_test_rng());
+         results.push_back(ffi_test_utils());
+         results.push_back(ffi_test_errors());
+         results.push_back(ffi_test_hex());
+         results.push_back(ffi_test_base64());
+         results.push_back(ffi_test_hash());
+         results.push_back(ffi_test_mac());
+         results.push_back(ffi_test_kdf(rng));
+         results.push_back(ffi_test_scrypt());
+         results.push_back(ffi_test_mp(rng));
+         results.push_back(ffi_test_pkcs_hash_id());
+         results.push_back(ffi_test_cert_validation());
+         results.push_back(ffi_test_crl());
+
+#if defined(BOTAN_HAS_AES)
+         results.push_back(ffi_test_block_ciphers());
+         results.push_back(ffi_test_ciphers_cbc());
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+         results.push_back(ffi_test_ciphers_aead_gcm());
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+         results.push_back(ffi_test_ciphers_aead_eax());
+#endif
+
+#if defined(BOTAN_HAS_CTR_BE)
+         results.push_back(ffi_test_stream_ciphers());
+#endif
+
+#endif
+
+
+#if defined(BOTAN_HAS_FPE_FE1)
+         results.push_back(ffi_test_fpe());
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+         results.push_back(ffi_test_keywrap());
+#endif
+
+#if defined(BOTAN_HAS_HOTP) && defined(BOTAN_HAS_SHA1)
+         results.push_back(ffi_test_hotp());
+#endif
+
+#if defined(BOTAN_HAS_TOTP) && defined(BOTAN_HAS_SHA1)
+         results.push_back(ffi_test_totp());
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+         results.push_back(ffi_test_rsa(rng));
+         results.push_back(ffi_test_rsa_cert());
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+         results.push_back(ffi_test_dsa(rng));
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+         results.push_back(ffi_test_ecdsa(rng));
+         results.push_back(ffi_test_ecdsa_cert());
+#endif
+
+#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_KDF2) && defined(BOTAN_HAS_SHA2_32)
+         results.push_back(ffi_test_ecdh(rng));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+         results.push_back(ffi_test_sm2(rng));
+         results.push_back(ffi_test_sm2_enc(rng));
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+         results.push_back(ffi_test_mceliece(rng));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL) && defined(BOTAN_HAS_EME_RAW)
+         results.push_back(ffi_test_elgamal(rng));
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+         results.push_back(ffi_test_dh(rng));
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+         results.push_back(ffi_test_ed25519(rng));
+#endif
+
+#if defined(BOTAN_HAS_X25519)
+         results.push_back(ffi_test_x25519());
+#endif
+
+         TEST_FFI_OK(botan_rng_destroy, (rng));
+
+         results.push_back(result);
+         return results;
+         }
+
+   private:
+
+      Test::Result ffi_test_utils()
+         {
+         Test::Result result("FFI");
+         result.test_is_eq("FFI API version", botan_ffi_api_version(), uint32_t(BOTAN_HAS_FFI));
+         result.test_is_eq("Major version", botan_version_major(), Botan::version_major());
+         result.test_is_eq("Minor version", botan_version_minor(), Botan::version_minor());
+         result.test_is_eq("Patch version", botan_version_patch(), Botan::version_patch());
+         result.test_is_eq("Botan version", botan_version_string(), Botan::version_cstr());
+         result.test_is_eq("Botan version datestamp", botan_version_datestamp(), Botan::version_datestamp());
+         result.test_is_eq("FFI supports its own version", botan_ffi_supports_api(botan_ffi_api_version()), 0);
+
+         result.test_is_eq("FFI supports 2.0 version", botan_ffi_supports_api(20150515), 0);
+         result.test_is_eq("FFI supports 2.1 version", botan_ffi_supports_api(20170327), 0);
+         result.test_is_eq("FFI supports 2.3 version", botan_ffi_supports_api(20170815), 0);
+         result.test_is_eq("FFI supports 2.8 version", botan_ffi_supports_api(20180713), 0);
+
+         result.test_is_eq("FFI doesn't support bogus version", botan_ffi_supports_api(20160229), -1);
+
+         const std::vector<uint8_t> mem1 = { 0xFF, 0xAA, 0xFF };
+         const std::vector<uint8_t> mem2 = mem1;
+         const std::vector<uint8_t> mem3 = { 0xFF, 0xA9, 0xFF };
+
+         TEST_FFI_RC(0, botan_same_mem, (mem1.data(), mem2.data(), mem1.size()));
+         TEST_FFI_RC(-1, botan_same_mem, (mem1.data(), mem3.data(), mem1.size()));
+
+         std::vector<uint8_t> to_zero = { 0xFF, 0xA0 };
+         TEST_FFI_OK(botan_scrub_mem, (to_zero.data(), to_zero.size()));
+         result.confirm("scrub_memory zeros", to_zero[0] == 0 && to_zero[1] == 0);
+
+         const std::vector<uint8_t> bin = { 0xAA, 0xDE, 0x01 };
+
+         std::string outstr;
+         std::vector<uint8_t> outbuf;
+
+         outstr.resize(2 * bin.size());
+         TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), &outstr[0], 0));
+         result.test_eq("uppercase hex", outstr, "AADE01");
+
+         TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), &outstr[0], BOTAN_FFI_HEX_LOWER_CASE));
+         result.test_eq("lowercase hex", outstr, "aade01");
+         return result;
+         }
+
+      Test::Result ffi_test_rng()
+         {
+         Test::Result result("FFI RNG");
+
+         // RNG test and initialization
+         botan_rng_t rng;
+         botan_rng_t system_rng;
+         botan_rng_t rdrand_rng = nullptr;
+         botan_rng_t null_rng;
+
+         TEST_FFI_FAIL("invalid rng type", botan_rng_init, (&rng, "invalid_type"));
+
+         REQUIRE_FFI_OK(botan_rng_init, (&system_rng, "system"));
+         REQUIRE_FFI_OK(botan_rng_init, (&null_rng, "null"));
+
+         int rc = botan_rng_init(&rdrand_rng, "rdrand");
+         result.confirm("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED);
+
+         std::vector<uint8_t> outbuf(512);
+
+         rc = botan_rng_init(&rng, "user-threadsafe");
+         result.confirm("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED);
+
+         if(rc != 0)
+            {
+            REQUIRE_FFI_OK(botan_rng_init, (&rng, "user"));
+            REQUIRE_FFI_OK(botan_rng_destroy, (rng));
+            }
+
+         if(rc == 0)
+            {
+            TEST_FFI_OK(botan_rng_get, (rng, outbuf.data(), outbuf.size()));
+            TEST_FFI_OK(botan_rng_reseed, (rng, 256));
+
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, botan_rng_reseed_from_rng, (rng, null_rng, 256));
+            if(rdrand_rng)
+               {
+               TEST_FFI_OK(botan_rng_reseed_from_rng, (rng, rdrand_rng, 256));
+               }
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, botan_rng_get, (null_rng, outbuf.data(), outbuf.size()));
+
+            TEST_FFI_OK(botan_rng_destroy, (rng));
+            }
+
+         if(TEST_FFI_OK(botan_rng_init, (&rng, "user")))
+            {
+            TEST_FFI_OK(botan_rng_get, (rng, outbuf.data(), outbuf.size()));
+            TEST_FFI_OK(botan_rng_reseed, (rng, 256));
+
+            TEST_FFI_OK(botan_rng_reseed_from_rng, (rng, system_rng, 256));
+
+            uint8_t not_really_entropy[32] = { 0 };
+            TEST_FFI_OK(botan_rng_add_entropy, (rng, not_really_entropy, 32));
+            }
+
+         TEST_FFI_OK(botan_rng_destroy, (rng));
+         TEST_FFI_OK(botan_rng_destroy, (null_rng));
+         TEST_FFI_OK(botan_rng_destroy, (system_rng));
+         TEST_FFI_OK(botan_rng_destroy, (rdrand_rng));
+
+         return result;
+         }
+
+      Test::Result ffi_test_rsa_cert()
+         {
+         Test::Result result("FFI RSA cert");
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_X509_CERTIFICATES)
+         botan_x509_cert_t cert;
+         if(TEST_FFI_OK(botan_x509_cert_load_file, (&cert, Test::data_file("x509/ocsp/randombit.pem").c_str())))
+            {
+            TEST_FFI_RC(0, botan_x509_cert_hostname_match, (cert, "randombit.net"));
+            TEST_FFI_RC(0, botan_x509_cert_hostname_match, (cert, "www.randombit.net"));
+            TEST_FFI_RC(-1, botan_x509_cert_hostname_match, (cert, "*.randombit.net"));
+            TEST_FFI_RC(-1, botan_x509_cert_hostname_match, (cert, "flub.randombit.net"));
+            TEST_FFI_RC(-1, botan_x509_cert_hostname_match, (cert, "randombit.net.com"));
+
+            botan_x509_cert_t copy;
+            TEST_FFI_OK(botan_x509_cert_dup, (&copy, cert));
+            TEST_FFI_RC(0, botan_x509_cert_hostname_match, (copy, "randombit.net"));
+
+            TEST_FFI_OK(botan_x509_cert_destroy, (copy));
+            TEST_FFI_OK(botan_x509_cert_destroy, (cert));
+            }
+#endif
+
+         return result;
+         }
+
+
+      Test::Result ffi_test_crl()
+         {
+         Test::Result result("FFI CRL");
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+         const char *crl_string = "-----BEGIN X509 CRL-----\n"
+                                 "MIICoTCCAQkCAQEwDQYJKoZIhvcNAQELBQAwgZQxLTArBgNVBAMTJFVzYWJsZSBj\n"
+                                 "ZXJ0IHZhbGlkYXRpb246IFRlbXBvcmFyeSBDQTE5MDcGA1UECxMwQ2VudHJlIGZv\n"
+                                 "ciBSZXNlYXJjaCBvbiBDcnlwdG9ncmFwaHkgYW5kIFNlY3VyaXR5MRswGQYDVQQK\n"
+                                 "ExJNYXNhcnlrIFVuaXZlcnNpdHkxCzAJBgNVBAYTAkNaGA8yMDUwMDIyNTE1MjE0\n"
+                                 "MloYDzIwNTAwMjI1MTUyNDQxWjAAoDowODAfBgNVHSMEGDAWgBRKzxAvI4+rVVo/\n"
+                                 "JzLigRznREyB+TAVBgNVHRQEDgIMXcr16yNys/gjeuCFMA0GCSqGSIb3DQEBCwUA\n"
+                                 "A4IBgQCfxv/5REM/KUnzeVycph3dJr1Yrtxhc6pZmQ9pMzSW/nawLN3rUHm5oG44\n"
+                                 "ZuQgjvzE4PnbU0/DNRu/4w3H58kgrctJHHXbbvkU3lf2ZZLh2wBl+EUh92+/COow\n"
+                                 "ZyGB+jqj/XwB99hYUhrY6NLEWRz08kpgG6dnNMEU0uFqdQKWk0CQPnmgPRgDb8BW\n"
+                                 "IuMBcjY7aF9XoCZFOqPYdEvUKzAo4QGCf7uJ7fNGS3LqvjaLjAHJseSr5/yR7Q9r\n"
+                                 "nEdI38yKPbRj0tNHe7j+BbYg31C+X+AZZKJtlTg8GxYR3qfQio1kDgpZ3rQLzHY3\n"
+                                 "ea2MLX/Kdx9cPSwh4KwlcDxQmQKoELb4EnZW1CScSBHi9HQyCBNyCkgkOBMGcJqz\n"
+                                 "Ihq1dGeSf8eca9+Avk5kAQ3yjXK1TI2CDEi0msrXLr9XbgowXiOLLzR+rYkhQz+V\n"
+                                 "RnIoBwjnrGoJoz636KS170SZCB9ARNs17WE4IvbJdZrTXNOGaVZCQUUpiLRj4ZSO\n"
+                                 "Na/nobI=\n"
+                                 "-----END X509 CRL-----";
+
+         botan_x509_crl_t bytecrl;
+         REQUIRE_FFI_OK(botan_x509_crl_load, (&bytecrl, reinterpret_cast<const uint8_t*>(crl_string), 966));
+
+         botan_x509_crl_t crl;
+         REQUIRE_FFI_OK(botan_x509_crl_load_file, (&crl, Test::data_file("x509/nist/root.crl").c_str()));
+
+         botan_x509_cert_t cert1;
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&cert1, Test::data_file("x509/nist/test01/end.crt").c_str()));
+         TEST_FFI_RC(-1, botan_x509_is_revoked, (crl, cert1));
+         TEST_FFI_OK(botan_x509_cert_destroy, (cert1));
+
+         botan_x509_cert_t cert2;
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&cert2, Test::data_file("x509/nist/test20/int.crt").c_str()));
+         TEST_FFI_RC(0, botan_x509_is_revoked, (crl, cert2));
+         TEST_FFI_RC(-1, botan_x509_is_revoked, (bytecrl, cert2));
+         TEST_FFI_OK(botan_x509_cert_destroy, (cert2));
+
+         TEST_FFI_OK(botan_x509_crl_destroy, (crl));
+         TEST_FFI_OK(botan_x509_crl_destroy, (bytecrl));
+#endif
+         return result;
+         }
+
+
+      Test::Result ffi_test_cert_validation()
+         {
+         Test::Result result("FFI Cert validation");
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
+
+         botan_x509_cert_t root;
+         int rc;
+
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&root, Test::data_file("x509/nist/root.crt").c_str()));
+
+         botan_x509_cert_t end2;
+         botan_x509_cert_t sub2;
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end2, Test::data_file("x509/nist/test02/end.crt").c_str()));
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub2, Test::data_file("x509/nist/test02/int.crt").c_str()));
+
+         TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end2, &sub2, 1, &root, 1, nullptr, 0, nullptr, 0));
+         result.confirm("Validation failed", rc == 5002);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc), "Signature error");
+
+         TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end2, nullptr, 0, &root, 1, nullptr, 0, nullptr, 0));
+         result.confirm("Validation failed", rc == 3000);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc), "Certificate issuer not found");
+
+         botan_x509_cert_t end7;
+         botan_x509_cert_t sub7;
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end7, Test::data_file("x509/nist/test07/end.crt").c_str()));
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub7, Test::data_file("x509/nist/test07/int.crt").c_str()));
+
+         botan_x509_cert_t subs[2] = {sub2, sub7};
+         TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end7, subs, 2, &root, 1, nullptr, 0, nullptr, 0));
+         result.confirm("Validation failed", rc == 1001);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc),
+                        "Hash function used is considered too weak for security");
+
+         TEST_FFI_RC(0, botan_x509_cert_verify, (&rc, end7, subs, 2, &root, 1, nullptr, 80, nullptr, 0));
+         result.confirm("Validation passed", rc == 0);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc), "Verified");
+
+         TEST_FFI_RC(1, botan_x509_cert_verify_with_crl, (&rc, end7, subs, 2, nullptr, 0, nullptr, 0, "x509/farce", 0, nullptr, 0));
+         result.confirm("Validation failed", rc == 3000);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc), "Certificate issuer not found");
+
+         botan_x509_crl_t rootcrl;
+
+         REQUIRE_FFI_OK(botan_x509_crl_load_file, (&rootcrl, Test::data_file("x509/nist/root.crl").c_str()));
+         TEST_FFI_RC(0, botan_x509_cert_verify_with_crl, (&rc, end7, subs, 2, &root, 1, &rootcrl, 1, nullptr, 80, nullptr, 0));
+         result.confirm("Validation passed", rc == 0);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc), "Verified");
+
+         botan_x509_cert_t end20;
+         botan_x509_cert_t sub20;
+         botan_x509_crl_t sub20crl;
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end20, Test::data_file("x509/nist/test20/end.crt").c_str()));
+         REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub20, Test::data_file("x509/nist/test20/int.crt").c_str()));
+         REQUIRE_FFI_OK(botan_x509_crl_load_file, (&sub20crl, Test::data_file("x509/nist/test20/int.crl").c_str()));
+         botan_x509_crl_t crls[2] = {sub20crl, rootcrl};
+         TEST_FFI_RC(1, botan_x509_cert_verify_with_crl, (&rc, end20, &sub20, 1, &root, 1, crls, 2, nullptr, 80, nullptr, 0));
+         result.confirm("Validation failed", rc == 5000);
+         result.test_eq("Validation status string", botan_x509_cert_validation_status(rc),
+                        "Certificate is revoked");
+
+         TEST_FFI_OK(botan_x509_cert_destroy, (end2));
+         TEST_FFI_OK(botan_x509_cert_destroy, (sub2));
+         TEST_FFI_OK(botan_x509_cert_destroy, (end7));
+         TEST_FFI_OK(botan_x509_cert_destroy, (sub7));
+         TEST_FFI_OK(botan_x509_cert_destroy, (end20));
+         TEST_FFI_OK(botan_x509_cert_destroy, (sub20));
+         TEST_FFI_OK(botan_x509_crl_destroy, (sub20crl));
+         TEST_FFI_OK(botan_x509_cert_destroy, (root));
+         TEST_FFI_OK(botan_x509_crl_destroy, (rootcrl));
+
+#endif
+         return result;
+         }
+
+      Test::Result ffi_test_ecdsa_cert()
+         {
+         Test::Result result("FFI ECDSA cert");
+
+#if defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_X509_CERTIFICATES)
+         botan_x509_cert_t cert;
+         if(TEST_FFI_OK(botan_x509_cert_load_file, (&cert, Test::data_file("x509/ecc/CSCA.CSCA.csca-germany.1.crt").c_str())))
+            {
+            size_t date_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_starts, (cert, nullptr, &date_len));
+
+            date_len = 8;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_starts, (cert, nullptr, &date_len));
+
+            std::string date(date_len - 1, '0');
+            TEST_FFI_OK(botan_x509_cert_get_time_starts, (cert, &date[0], &date_len));
+            result.test_eq("cert valid from", date, "070719152718Z");
+
+            date_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_expires, (cert, nullptr, &date_len));
+
+            date.resize(date_len - 1);
+            TEST_FFI_OK(botan_x509_cert_get_time_expires, (cert, &date[0], &date_len));
+            result.test_eq("cert valid until", date, "280119151800Z");
+
+            uint64_t not_before = 0;
+            TEST_FFI_OK(botan_x509_cert_not_before, (cert, &not_before));
+            result.confirm("cert not before", not_before == 1184858838);
+
+            uint64_t not_after = 0;
+            TEST_FFI_OK(botan_x509_cert_not_after, (cert, &not_after));
+            result.confirm("cert not after", not_after == 1831907880);
+
+            size_t serial_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_serial_number, (cert, nullptr, &serial_len));
+
+            std::vector<uint8_t> serial(serial_len);
+            TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert, serial.data(), &serial_len));
+            result.test_eq("cert serial length", serial.size(), 1);
+            result.test_int_eq(serial[0], 1, "cert serial");
+
+            size_t fingerprint_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_fingerprint,
+                        (cert, "SHA-256", nullptr, &fingerprint_len));
+
+            std::vector<uint8_t> fingerprint(fingerprint_len);
+            TEST_FFI_OK(botan_x509_cert_get_fingerprint, (cert, "SHA-256", fingerprint.data(), &fingerprint_len));
+            result.test_eq("cert fingerprint", reinterpret_cast<const char*>(fingerprint.data()),
+                           "3B:6C:99:1C:D6:5A:51:FC:EB:17:E3:AA:F6:3C:1A:DA:14:1F:82:41:30:6F:64:EE:FF:63:F3:1F:D6:07:14:9F");
+
+            size_t key_id_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_authority_key_id,
+                        (cert, nullptr, &key_id_len));
+
+            std::vector<uint8_t> key_id(key_id_len);
+            TEST_FFI_OK(botan_x509_cert_get_authority_key_id, (cert, key_id.data(), &key_id_len));
+            result.test_eq("cert authority key id", Botan::hex_encode(key_id.data(), key_id.size(), true),
+                           "0096452DE588F966C4CCDF161DD1F3F5341B71E7");
+
+            key_id_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_key_id,
+                        (cert, nullptr, &key_id_len));
+
+            key_id.resize(key_id_len);
+            TEST_FFI_OK(botan_x509_cert_get_subject_key_id, (cert, key_id.data(), &key_id_len));
+            result.test_eq("cert subject key id", Botan::hex_encode(key_id.data(), key_id.size(), true),
+                           "0096452DE588F966C4CCDF161DD1F3F5341B71E7");
+
+            size_t pubkey_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_public_key_bits,
+                        (cert, nullptr, &pubkey_len));
+
+            std::vector<uint8_t> pubkey(pubkey_len);
+            TEST_FFI_OK(botan_x509_cert_get_public_key_bits, (cert, pubkey.data(), &pubkey_len));
+
+            botan_pubkey_t pub;
+            if(TEST_FFI_OK(botan_x509_cert_get_public_key, (cert, &pub)))
+               {
+               TEST_FFI_OK(botan_pubkey_destroy, (pub));
+               }
+
+            size_t dn_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_issuer_dn,
+                        (cert, "Name", 0, nullptr, &dn_len));
+
+            std::vector<uint8_t> dn(dn_len);
+            TEST_FFI_OK(botan_x509_cert_get_issuer_dn, (cert, "Name", 0, dn.data(), &dn_len));
+            result.test_eq("issuer dn", reinterpret_cast<const char*>(dn.data()), "csca-germany");
+
+            dn_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_subject_dn,
+                        (cert, "Name", 0, nullptr, &dn_len));
+
+            dn.resize(dn_len);
+            TEST_FFI_OK(botan_x509_cert_get_subject_dn, (cert, "Name", 0, dn.data(), &dn_len));
+            result.test_eq("subject dn", reinterpret_cast<const char*>(dn.data()), "csca-germany");
+
+            size_t printable_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_to_string,
+                        (cert, nullptr, &printable_len));
+
+            std::string printable(printable_len - 1, '0');
+            TEST_FFI_OK(botan_x509_cert_to_string, (cert, &printable[0], &printable_len));
+
+            TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, KEY_CERT_SIGN));
+            TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, CRL_SIGN));
+            TEST_FFI_RC(1, botan_x509_cert_allowed_usage, (cert, DIGITAL_SIGNATURE));
+
+            TEST_FFI_OK(botan_x509_cert_destroy, (cert));
+            }
+#endif
+         return result;
+         }
+
+      Test::Result ffi_test_pkcs_hash_id()
+         {
+         Test::Result result("FFI PKCS hash id");
+
+#if defined(BOTAN_HAS_HASH_ID)
+         std::vector<uint8_t> hash_id(64);
+         size_t hash_id_len;
+
+         hash_id_len = 3; // too short
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                     botan_pkcs_hash_id, ("SHA-256", hash_id.data(), &hash_id_len));
+
+         result.test_eq("Expected SHA-256 PKCS hash id len", hash_id_len, 19);
+
+         TEST_FFI_OK(botan_pkcs_hash_id, ("SHA-256", hash_id.data(), &hash_id_len));
+
+         result.test_eq("Expected SHA-256 PKCS hash id len", hash_id_len, 19);
+
+         hash_id.resize(hash_id_len);
+         result.test_eq("Expected SHA_256 PKCS hash id",
+                        hash_id, "3031300D060960864801650304020105000420");
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_ciphers_cbc()
+         {
+         Test::Result result("FFI CBC cipher");
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC)
+         botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+         if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC/PKCS7", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+            {
+            size_t min_keylen = 0;
+            size_t max_keylen = 0;
+            TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+            result.test_int_eq(min_keylen, 16, "Min key length");
+            result.test_int_eq(max_keylen, 16, "Max key length");
+
+            // from https://github.com/geertj/bluepass/blob/master/tests/vectors/aes-cbc-pkcs7.txt
+            const std::vector<uint8_t> plaintext =
+               Botan::hex_decode("0397f4f6820b1f9386f14403be5ac16e50213bd473b4874b9bcbf5f318ee686b1d");
+            const std::vector<uint8_t> symkey = Botan::hex_decode("898be9cc5004ed0fa6e117c9a3099d31");
+            const std::vector<uint8_t> nonce = Botan::hex_decode("9dea7621945988f96491083849b068df");
+            const std::vector<uint8_t> exp_ciphertext =
+               Botan::hex_decode("e232cd6ef50047801ee681ec30f61d53cfd6b0bca02fd03c1b234baa10ea82ac9dab8b960926433a19ce6dea08677e34");
+
+            size_t output_written = 0;
+            size_t input_consumed = 0;
+
+            // Test that after clear or final the object can be reused
+            for(size_t r = 0; r != 2; ++r)
+               {
+               size_t ctext_len;
+               TEST_FFI_OK(botan_cipher_output_length, (cipher_encrypt, plaintext.size(), &ctext_len));
+               result.test_eq("Expected size of padded message", ctext_len, plaintext.size() + 15);
+               std::vector<uint8_t> ciphertext(ctext_len);
+
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0, ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+               TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                 ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+
+               ciphertext.resize(output_written);
+               result.test_eq("AES/CBC ciphertext", ciphertext, exp_ciphertext);
+
+               if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+                  {
+                  size_t ptext_len;
+                  TEST_FFI_OK(botan_cipher_output_length, (cipher_decrypt, ciphertext.size(), &ptext_len));
+                  std::vector<uint8_t> decrypted(ptext_len);
+
+                  TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+                  TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+                  TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                    decrypted.data(), decrypted.size(),   &output_written,
+                                                    ciphertext.data(), ciphertext.size(), &input_consumed));
+
+                  decrypted.resize(output_written);
+
+                  result.test_eq("AES/CBC plaintext", decrypted, plaintext);
+
+                  TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+                  }
+               }
+
+            TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+            }
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_ciphers_aead_gcm()
+         {
+         Test::Result result("FFI GCM");
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+
+         botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+         if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+            {
+            char namebuf[18];
+            size_t name_len = 15;
+            TEST_FFI_FAIL("output buffer too short", botan_cipher_name, (cipher_encrypt, namebuf, &name_len));
+            result.test_eq("name len", name_len, 16);
+
+            name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_cipher_name, (cipher_encrypt, namebuf, &name_len)))
+               {
+               result.test_eq("name len", name_len, 16);
+               result.test_eq("name", std::string(namebuf), "AES-128/GCM(16)");
+               }
+
+            size_t min_keylen = 0;
+            size_t max_keylen = 0;
+            size_t nonce_len = 0;
+            size_t tag_len = 0;
+
+            TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+            result.test_int_eq(min_keylen, 16, "Min key length");
+            result.test_int_eq(max_keylen, 16, "Max key length");
+
+            TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len));
+            result.test_int_eq(nonce_len, 12, "Expected default GCM nonce length");
+
+            TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len));
+            result.test_int_eq(tag_len, 16, "Expected GCM tag length");
+
+            TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 12));
+            // GCM accepts any nonce size except zero
+            TEST_FFI_RC(0, botan_cipher_valid_nonce_length, (cipher_encrypt, 0));
+            TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 1));
+            TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 100009));
+
+            // NIST test vector
+            const std::vector<uint8_t> plaintext =
+               Botan::hex_decode("D9313225F88406E5A55909C5AFF5269A86A7A9531534F7DA2E4C303D8A318A721C3C0C95956809532FCF0E2449A6B525B16AEDF5AA0DE657BA637B39");
+
+            const std::vector<uint8_t> symkey = Botan::hex_decode("FEFFE9928665731C6D6A8F9467308308");
+            const std::vector<uint8_t> nonce = Botan::hex_decode("CAFEBABEFACEDBADDECAF888");
+            const std::vector<uint8_t> exp_ciphertext = Botan::hex_decode(
+                     "42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E0915BC94FBC3221A5DB94FAE95AE7121A47");
+            const std::vector<uint8_t> aad = Botan::hex_decode("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2");
+
+            std::vector<uint8_t> ciphertext(tag_len + plaintext.size());
+
+            size_t output_written = 0;
+            size_t input_consumed = 0;
+
+            // Test that after clear or final the object can be reused
+            for(size_t r = 0; r != 2; ++r)
+               {
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+
+               // First use a nonce of the AAD, and ensure reset works
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, aad.data(), aad.size()));
+               TEST_FFI_OK(botan_cipher_reset, (cipher_encrypt));
+
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0,
+                                                 ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+               TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+               TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_encrypt, aad.data(), aad.size()));
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                 ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+
+               ciphertext.resize(output_written);
+               result.test_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext);
+
+               if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+                  {
+                  std::vector<uint8_t> decrypted(plaintext.size());
+
+                  TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+                  TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_decrypt, aad.data(), aad.size()));
+                  TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+                  TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                    decrypted.data(), decrypted.size(), &output_written,
+                                                    ciphertext.data(), ciphertext.size(), &input_consumed));
+
+                  result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed");
+                  result.test_int_eq(output_written, decrypted.size(), "Expected output size produced");
+                  result.test_eq("AES/GCM plaintext", decrypted, plaintext);
+
+                  TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+                  }
+               }
+
+            TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+            }
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_ciphers_aead_eax()
+         {
+         Test::Result result("FFI EAX");
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+
+         botan_cipher_t cipher_encrypt, cipher_decrypt;
+
+         if(TEST_FFI_OK(botan_cipher_init, (&cipher_encrypt, "AES-128/EAX", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+            {
+            size_t min_keylen = 0;
+            size_t max_keylen = 0;
+            size_t mod_keylen = 0;
+            size_t nonce_len = 0;
+            size_t tag_len = 0;
+
+            TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen));
+            result.test_int_eq(min_keylen, 16, "Min key length");
+            result.test_int_eq(max_keylen, 16, "Max key length");
+
+            TEST_FFI_OK(botan_cipher_get_keyspec, (cipher_encrypt, &min_keylen, &max_keylen, &mod_keylen));
+            result.test_int_eq(min_keylen, 16, "Min key length");
+            result.test_int_eq(max_keylen, 16, "Max key length");
+            result.test_int_eq(mod_keylen, 1, "Mod key length");
+
+            TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len));
+            result.test_int_eq(nonce_len, 12, "Expected default EAX nonce length");
+
+            TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len));
+            result.test_int_eq(tag_len, 16, "Expected EAX tag length");
+
+            TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 12));
+            // EAX accepts any nonce size...
+            TEST_FFI_RC(1, botan_cipher_valid_nonce_length, (cipher_encrypt, 0));
+
+            const std::vector<uint8_t> plaintext =
+               Botan::hex_decode("0000000000000000000000000000000011111111111111111111111111111111");
+            const std::vector<uint8_t> symkey = Botan::hex_decode("000102030405060708090a0b0c0d0e0f");
+            const std::vector<uint8_t> nonce = Botan::hex_decode("3c8cc2970a008f75cc5beae2847258c2");
+            const std::vector<uint8_t> exp_ciphertext =
+               Botan::hex_decode("3c441f32ce07822364d7a2990e50bb13d7b02a26969e4a937e5e9073b0d9c968db90bdb3da3d00afd0fc6a83551da95e");
+
+            std::vector<uint8_t> ciphertext(tag_len + plaintext.size());
+
+            size_t output_written = 0;
+            size_t input_consumed = 0;
+
+            // Test that after clear or final the object can be reused
+            for(size_t r = 0; r != 2; ++r)
+               {
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, 0,
+                                                 ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+               TEST_FFI_OK(botan_cipher_clear, (cipher_encrypt));
+
+               TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size()));
+               TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size()));
+               TEST_FFI_OK(botan_cipher_update, (cipher_encrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                 ciphertext.data(), ciphertext.size(), &output_written,
+                                                 plaintext.data(), plaintext.size(), &input_consumed));
+
+               ciphertext.resize(output_written);
+               result.test_eq("AES/EAX ciphertext", ciphertext, exp_ciphertext);
+
+               if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/EAX", BOTAN_CIPHER_INIT_FLAG_DECRYPT)))
+                  {
+                  std::vector<uint8_t> decrypted(plaintext.size());
+
+                  TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size()));
+                  TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size()));
+                  TEST_FFI_OK(botan_cipher_update, (cipher_decrypt, BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+                                                    decrypted.data(), decrypted.size(), &output_written,
+                                                    ciphertext.data(), ciphertext.size(), &input_consumed));
+
+                  result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed");
+                  result.test_int_eq(output_written, decrypted.size(), "Expected output size produced");
+                  result.test_eq("AES/EAX plaintext", decrypted, plaintext);
+
+                  TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt));
+                  }
+               }
+
+            TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt));
+            }
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_stream_ciphers()
+         {
+         Test::Result result("FFI stream ciphers");
+
+#if defined(BOTAN_HAS_CTR_BE)
+
+         const std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF");
+         const std::vector<uint8_t> pt = Botan::hex_decode(
+                                            "AE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710");
+         const std::vector<uint8_t> exp_ct = Botan::hex_decode(
+                                                "9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE");
+
+         botan_cipher_t ctr;
+
+         std::vector<uint8_t> ct(pt.size());
+
+         if(TEST_FFI_OK(botan_cipher_init, (&ctr, "AES-128/CTR-BE", BOTAN_CIPHER_INIT_FLAG_ENCRYPT)))
+            {
+            size_t input_consumed = 0;
+            size_t output_written = 0;
+
+            TEST_FFI_OK(botan_cipher_set_key, (ctr, key.data(), key.size()));
+            TEST_FFI_OK(botan_cipher_start, (ctr, nonce.data(), nonce.size()));
+
+            // Test partial updates...
+            TEST_FFI_OK(botan_cipher_update, (ctr, 0,
+                                              ct.data(), ct.size(), &output_written,
+                                              pt.data(), 5, &input_consumed));
+
+            result.test_int_eq(output_written, 5, "Expected output written");
+            result.test_int_eq(input_consumed, 5, "Expected input consumed");
+
+            TEST_FFI_OK(botan_cipher_update, (ctr, 0,
+                                              &ct[5], ct.size() - 5, &output_written,
+                                              &pt[5], pt.size() - 5, &input_consumed));
+
+            result.test_int_eq(output_written, ct.size() - 5, "Expected output written");
+            result.test_int_eq(input_consumed, pt.size() - 5, "Expected input consumed");
+            result.test_eq("AES-128/CTR ciphertext", ct, exp_ct);
+
+            TEST_FFI_OK(botan_cipher_destroy, (ctr));
+            }
+
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_hash()
+         {
+         Test::Result result("FFI hash");
+
+         const char* input_str = "ABC";
+
+         botan_hash_t hash;
+         TEST_FFI_FAIL("invalid hash name", botan_hash_init, (&hash, "SHA-255", 0));
+         TEST_FFI_FAIL("invalid flags", botan_hash_init, (&hash, "SHA-256", 1));
+
+         if(TEST_FFI_OK(botan_hash_init, (&hash, "SHA-256", 0)))
+            {
+            char namebuf[10];
+            size_t name_len = 7;
+            TEST_FFI_FAIL("output buffer too short", botan_hash_name, (hash, namebuf, &name_len));
+            result.test_eq("name len", name_len, 8);
+
+            name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_hash_name, (hash, namebuf, &name_len)))
+               {
+               result.test_eq("name len", name_len, 8);
+               result.test_eq("name", std::string(namebuf), "SHA-256");
+               }
+
+            size_t block_size;
+            if (TEST_FFI_OK(botan_hash_block_size, (hash, &block_size)))
+               {
+                  result.test_eq("hash block size", block_size, 64);
+               }
+
+            size_t output_len;
+            if(TEST_FFI_OK(botan_hash_output_length, (hash, &output_len)))
+               {
+               result.test_eq("hash output length", output_len, 32);
+
+               std::vector<uint8_t> outbuf(output_len);
+
+               // Test that after clear or final the object can be reused
+               for(size_t r = 0; r != 2; ++r)
+                  {
+                  TEST_FFI_OK(botan_hash_update, (hash, reinterpret_cast<const uint8_t*>(input_str), 1));
+                  TEST_FFI_OK(botan_hash_clear, (hash));
+
+                  TEST_FFI_OK(botan_hash_update, (hash, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+                  TEST_FFI_OK(botan_hash_final, (hash, outbuf.data()));
+
+                  result.test_eq("SHA-256 output", outbuf, "B5D4045C3F466FA91FE2CC6ABE79232A1A57CDF104F7A26E716E0A1E2789DF78");
+                  }
+
+               // Test botan_hash_copy_state
+               const char *msg = "message digest";
+               const char *expected = "F7846F55CF23E14EEBEAB5B4E1550CAD5B509E3348FBC4EFA3A1413D393CB650";
+               TEST_FFI_OK(botan_hash_clear, (hash));
+               TEST_FFI_OK(botan_hash_update, (hash, reinterpret_cast<const uint8_t*>(&msg[0]), 1));
+               botan_hash_t fork;
+               if (TEST_FFI_OK(botan_hash_copy_state, (&fork, hash)))
+                  {
+                  TEST_FFI_OK(botan_hash_update, (fork, reinterpret_cast<const uint8_t*>(&msg[1]), std::strlen(msg) - 2));
+
+                  TEST_FFI_OK(botan_hash_update, (hash, reinterpret_cast<const uint8_t*>(&msg[1]), std::strlen(msg) - 1));
+                  TEST_FFI_OK(botan_hash_final, (hash, outbuf.data()));
+                  result.test_eq("hashing split", outbuf, expected);
+
+                  TEST_FFI_OK(botan_hash_update, (fork, reinterpret_cast<const uint8_t*>(&msg[std::strlen(msg)-1]), 1));
+                  TEST_FFI_OK(botan_hash_final, (fork, outbuf.data()));
+                  result.test_eq("hashing split", outbuf, expected);
+
+                  TEST_FFI_OK(botan_hash_destroy, (fork));
+                  }
+               }
+
+            TEST_FFI_OK(botan_hash_destroy, (hash));
+            }
+         return result;
+         }
+
+      Test::Result ffi_test_mac()
+         {
+         Test::Result result("FFI MAC");
+
+         const char* input_str = "ABC";
+
+         // MAC test
+         botan_mac_t mac;
+         TEST_FFI_FAIL("bad flag", botan_mac_init, (&mac, "HMAC(SHA-256)", 1));
+         TEST_FFI_FAIL("bad name", botan_mac_init, (&mac, "HMAC(SHA-259)", 0));
+
+         if(TEST_FFI_OK(botan_mac_init, (&mac, "HMAC(SHA-256)", 0)))
+            {
+            char namebuf[16];
+            size_t name_len = 13;
+            TEST_FFI_FAIL("output buffer too short", botan_mac_name, (mac, namebuf, &name_len));
+            result.test_eq("name len", name_len, 14);
+
+            name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_mac_name, (mac, namebuf, &name_len)))
+               {
+               result.test_eq("name len", name_len, 14);
+               result.test_eq("name", std::string(namebuf), "HMAC(SHA-256)");
+               }
+
+            size_t min_keylen = 0, max_keylen = 0, mod_keylen = 0;
+            TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, nullptr, nullptr));
+            TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, &min_keylen, nullptr, nullptr));
+            TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, &max_keylen, nullptr));
+            TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, nullptr, &mod_keylen));
+
+            result.test_eq("Expected min keylen", min_keylen, 0);
+            result.test_eq("Expected max keylen", max_keylen, 4096);
+            result.test_eq("Expected mod keylen", mod_keylen, 1);
+
+            size_t output_len;
+            if(TEST_FFI_OK(botan_mac_output_length, (mac, &output_len)))
+               {
+               result.test_eq("MAC output length", output_len, 32);
+
+               const uint8_t mac_key[] = { 0xAA, 0xBB, 0xCC, 0xDD };
+               std::vector<uint8_t> outbuf(output_len);
+
+               // Test that after clear or final the object can be reused
+               for(size_t r = 0; r != 2; ++r)
+                  {
+                  TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key)));
+                  TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+                  TEST_FFI_OK(botan_mac_clear, (mac));
+
+                  TEST_FFI_OK(botan_mac_set_key, (mac, mac_key, sizeof(mac_key)));
+                  TEST_FFI_OK(botan_mac_update, (mac, reinterpret_cast<const uint8_t*>(input_str), std::strlen(input_str)));
+                  TEST_FFI_OK(botan_mac_final, (mac, outbuf.data()));
+
+                  result.test_eq("HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7");
+                  }
+               }
+
+            TEST_FFI_OK(botan_mac_destroy, (mac));
+            }
+
+         return result;
+         }
+
+      Test::Result ffi_test_scrypt()
+         {
+         Test::Result result("FFI Scrypt");
+
+         std::vector<uint8_t> output(24);
+         const uint8_t salt[8] = { 0 };
+         const char* pass = "password";
+
+#if defined(BOTAN_HAS_SCRYPT)
+         TEST_FFI_OK(botan_scrypt, (output.data(), output.size(), pass, salt, sizeof(salt), 8, 1, 1));
+
+         result.test_eq("scrypt output", output, "4B9B888D695288E002CC4F9D90808A4D296A45CE4471AFBB");
+
+         size_t N, r, p;
+         TEST_FFI_OK(botan_pwdhash_timed, ("Scrypt", 50, &r, &p, &N, output.data(), output.size(),
+                                           "bunny", 5, salt, sizeof(salt)));
+
+         std::vector<uint8_t> cmp(output.size());
+
+         TEST_FFI_OK(botan_pwdhash, ("Scrypt", N, r, p, cmp.data(), cmp.size(),
+                                     "bunny", 5, salt, sizeof(salt)));
+         result.test_eq("recomputed scrypt", cmp, output);
+#else
+         TEST_FFI_RC(BOTAN_FFI_ERROR_NOT_IMPLEMENTED,
+                     botan_scrypt, (output.data(), output.size(), pass, salt, sizeof(salt), 8, 1, 1));
+#endif
+         return result;
+         }
+
+      Test::Result ffi_test_kdf(botan_rng_t rng)
+         {
+         Test::Result result("FFI KDF");
+         std::vector<uint8_t> outbuf;
+
+         const std::string passphrase = "ltexmfeyylmlbrsyikaw";
+
+#if defined(BOTAN_HAS_PBKDF2) && defined(BOTAN_HAS_SHA1)
+         const std::vector<uint8_t> pbkdf_salt = Botan::hex_decode("ED1F39A0A7F3889AAF7E60743B3BC1CC2C738E60");
+         const size_t pbkdf_out_len = 10;
+         const size_t pbkdf_iterations = 1000;
+
+         outbuf.resize(pbkdf_out_len);
+
+         if(TEST_FFI_OK(botan_pbkdf, ("PBKDF2(SHA-1)",
+                                      outbuf.data(), outbuf.size(),
+                                      passphrase.c_str(),
+                                      pbkdf_salt.data(), pbkdf_salt.size(),
+                                      pbkdf_iterations)))
+            {
+            result.test_eq("PBKDF output", outbuf, "027AFADD48F4BE8DCC4F");
+            }
+
+         size_t iters_10ms, iters_100ms;
+
+         TEST_FFI_OK(botan_pbkdf_timed, ("PBKDF2(SHA-1)", outbuf.data(), outbuf.size(),
+                                         passphrase.c_str(),
+                                         pbkdf_salt.data(), pbkdf_salt.size(),
+                                         10, &iters_10ms));
+         TEST_FFI_OK(botan_pbkdf_timed, ("PBKDF2(SHA-1)", outbuf.data(), outbuf.size(),
+                                         passphrase.c_str(),
+                                         pbkdf_salt.data(), pbkdf_salt.size(),
+                                         100, &iters_100ms));
+
+         result.test_note("PBKDF timed 10 ms " + std::to_string(iters_10ms) + " iterations " +
+                          "100 ms " + std::to_string(iters_100ms) + " iterations");
+#endif
+
+#if defined(BOTAN_HAS_KDF2) && defined(BOTAN_HAS_SHA1)
+         const std::vector<uint8_t> kdf_secret = Botan::hex_decode("92167440112E");
+         const std::vector<uint8_t> kdf_salt = Botan::hex_decode("45A9BEDED69163123D0348F5185F61ABFB1BF18D6AEA454F");
+         const size_t kdf_out_len = 18;
+         outbuf.resize(kdf_out_len);
+
+         if(TEST_FFI_OK(botan_kdf, ("KDF2(SHA-1)", outbuf.data(), outbuf.size(),
+                                    kdf_secret.data(),
+                                    kdf_secret.size(),
+                                    kdf_salt.data(),
+                                    kdf_salt.size(),
+                                    nullptr,
+                                    0)))
+            {
+            result.test_eq("KDF output", outbuf, "3A5DC9AA1C872B4744515AC2702D6396FC2A");
+            }
+#endif
+
+         size_t out_len = 64;
+         std::string outstr;
+         outstr.resize(out_len);
+
+         int rc = botan_bcrypt_generate(reinterpret_cast<uint8_t*>(&outstr[0]),
+                                        &out_len, passphrase.c_str(), rng, 4, 0);
+
+         if(rc == 0)
+            {
+            result.test_eq("bcrypt output size", out_len, 61);
+
+            TEST_FFI_OK(botan_bcrypt_is_valid, (passphrase.c_str(), outstr.data()));
+            TEST_FFI_FAIL("bad password", botan_bcrypt_is_valid, ("nope", outstr.data()));
+            }
+         return result;
+         }
+
+      Test::Result ffi_test_block_ciphers()
+         {
+         Test::Result result("FFI block ciphers");
+
+         botan_block_cipher_t cipher;
+
+         if(TEST_FFI_OK(botan_block_cipher_init, (&cipher, "AES-128")))
+            {
+            char namebuf[10];
+            size_t name_len = 7;
+            TEST_FFI_FAIL("output buffer too short", botan_block_cipher_name, (cipher, namebuf, &name_len));
+            result.test_eq("name len", name_len, 8);
+
+            name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_block_cipher_name, (cipher, namebuf, &name_len)))
+               {
+               result.test_eq("name len", name_len, 8);
+               result.test_eq("name", std::string(namebuf), "AES-128");
+               }
+
+            const std::vector<uint8_t> zero16(16, 0);
+            std::vector<uint8_t> block(16, 0);
+
+            TEST_FFI_OK(botan_block_cipher_clear, (cipher));
+
+            TEST_FFI_RC(BOTAN_FFI_ERROR_KEY_NOT_SET, botan_block_cipher_encrypt_blocks, (cipher, block.data(), block.data(), 1));
+            TEST_FFI_RC(BOTAN_FFI_ERROR_KEY_NOT_SET, botan_block_cipher_decrypt_blocks, (cipher, block.data(), block.data(), 1));
+
+            TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_block_cipher_encrypt_blocks, (cipher, nullptr, nullptr, 0));
+            TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_block_cipher_decrypt_blocks, (cipher, nullptr, nullptr, 0));
+
+            TEST_FFI_RC(16, botan_block_cipher_block_size, (cipher));
+
+            size_t min_keylen = 0, max_keylen = 0, mod_keylen = 0;
+            TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, nullptr, nullptr));
+            TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, &min_keylen, nullptr, nullptr));
+            TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, &max_keylen, nullptr));
+            TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, nullptr, &mod_keylen));
+
+            result.test_eq("Expected min keylen", min_keylen, 16);
+            result.test_eq("Expected max keylen", max_keylen, 16);
+            result.test_eq("Expected mod keylen", mod_keylen, 1);
+
+            TEST_FFI_OK(botan_block_cipher_set_key, (cipher, zero16.data(), zero16.size()));
+
+            TEST_FFI_OK(botan_block_cipher_encrypt_blocks, (cipher, block.data(), block.data(), 1));
+            result.test_eq("AES-128 encryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E");
+
+            TEST_FFI_OK(botan_block_cipher_encrypt_blocks, (cipher, block.data(), block.data(), 1));
+            result.test_eq("AES-128 encryption works", block, "F795BD4A52E29ED713D313FA20E98DBC");
+
+            TEST_FFI_OK(botan_block_cipher_decrypt_blocks, (cipher, block.data(), block.data(), 1));
+            result.test_eq("AES-128 decryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E");
+
+            TEST_FFI_OK(botan_block_cipher_decrypt_blocks, (cipher, block.data(), block.data(), 1));
+            result.test_eq("AES-128 decryption works", block, "00000000000000000000000000000000");
+
+            TEST_FFI_OK(botan_block_cipher_clear, (cipher));
+            botan_block_cipher_destroy(cipher);
+            }
+
+         return result;
+         }
+
+      Test::Result ffi_test_errors()
+         {
+         // Test some error handling situations
+         Test::Result result("FFI error handling");
+
+         // delete of null is ok/ignored
+         TEST_FFI_RC(0, botan_hash_destroy, (nullptr));
+
+#if !defined(BOTAN_HAS_SANITIZER_UNDEFINED)
+         // Confirm that botan_x_destroy checks the argument type
+         botan_mp_t mp;
+         botan_mp_init(&mp);
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT, botan_hash_destroy, (reinterpret_cast<botan_hash_t>(mp)));
+         TEST_FFI_RC(0, botan_mp_destroy, (mp));
+#endif
+
+         std::set<std::string> errors;
+         for(int i = -100; i != 50; ++i)
+            {
+            const char* err = botan_error_description(i);
+            result.confirm("Never a null pointer", err != nullptr);
+
+            std::string s(err);
+
+            if(s != "Unknown error")
+               {
+               result.confirm("No duplicate messages", errors.count(s) == 0);
+               errors.insert(s);
+               }
+            }
+
+         return result;
+         }
+
+      Test::Result ffi_test_base64()
+         {
+         Test::Result result("FFI base64");
+
+         const uint8_t bin[9] = { 0x16, 0x8a, 0x1f, 0x06, 0xe9, 0xe7, 0xcb, 0xdd, 0x34 };
+         char out_buf[1024] = { 0 };
+
+         size_t out_len = sizeof(out_buf);
+         TEST_FFI_OK(botan_base64_encode, (bin, sizeof(bin), out_buf, &out_len));
+
+         result.test_eq("encoded string", out_buf, "FoofBunny900");
+
+         out_len -= 1;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                     botan_base64_encode,
+                     (bin, sizeof(bin), out_buf, &out_len));
+
+         const char* base64 = "U3VjaCBiYXNlNjQgd293IQ==";
+         uint8_t out_bin[1024] = { 0 };
+
+         out_len = 3;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                     botan_base64_decode,
+                     (base64, strlen(base64), out_bin, &out_len));
+
+         result.test_eq("output length", out_len, 18);
+
+         out_len = sizeof(out_bin);
+         TEST_FFI_OK(botan_base64_decode, (base64, strlen(base64), out_bin, &out_len));
+
+         result.test_eq("decoded string",
+                        std::string(reinterpret_cast<const char*>(out_bin), out_len),
+                        "Such base64 wow!");
+
+         return result;
+         }
+
+      Test::Result ffi_test_hex()
+         {
+         Test::Result result("FFI hex");
+
+         const uint8_t bin[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
+         char hex_buf[16] = { 0 };
+
+         TEST_FFI_OK(botan_hex_encode, (bin, sizeof(bin), hex_buf, 0));
+
+         result.test_eq("encoded string", hex_buf, "DEADBEEF");
+
+         const char* hex = "67657420796572206A756D626F20736872696D70";
+         uint8_t out_bin[1024] = { 0 };
+         size_t out_len = 5;
+
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                     botan_hex_decode,
+                     (hex, strlen(hex), out_bin, &out_len));
+
+         out_len = sizeof(out_bin);
+         TEST_FFI_OK(botan_hex_decode, (hex, strlen(hex), out_bin, &out_len));
+
+         result.test_eq("decoded string",
+                        std::string(reinterpret_cast<const char*>(out_bin), out_len),
+                        "get yer jumbo shrimp");
+
+         return result;
+         }
+
+      Test::Result ffi_test_mp(botan_rng_t rng)
+         {
+         Test::Result result("FFI MP");
+
+         char str_buf[1024] = { 0 };
+         size_t str_len = 0;
+
+         botan_mp_t x;
+         botan_mp_init(&x);
+         TEST_FFI_RC(0, botan_mp_is_odd, (x));
+         TEST_FFI_RC(1, botan_mp_is_even, (x));
+         TEST_FFI_RC(0, botan_mp_is_negative, (x));
+         TEST_FFI_RC(1, botan_mp_is_positive, (x));
+         TEST_FFI_RC(1, botan_mp_is_zero, (x));
+         botan_mp_destroy(x);
+
+         botan_mp_init(&x);
+         size_t bn_bytes = 0;
+         TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+         result.test_eq("Expected size for MP 0", bn_bytes, 0);
+
+         botan_mp_set_from_int(x, 5);
+         TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+         result.test_eq("Expected size for MP 5", bn_bytes, 1);
+
+         botan_mp_add_u32(x, x, 75);
+         TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+         result.test_eq("Expected size for MP 80", bn_bytes, 1);
+
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (x, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_add", std::string(str_buf), "80");
+
+         botan_mp_sub_u32(x, x, 80);
+         TEST_FFI_RC(1, botan_mp_is_zero, (x));
+         botan_mp_add_u32(x, x, 259);
+         TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes));
+         result.test_eq("Expected size for MP 259", bn_bytes, 2);
+
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (x, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_add", std::string(str_buf), "259");
+
+         TEST_FFI_RC(1, botan_mp_is_odd, (x));
+         TEST_FFI_RC(0, botan_mp_is_even, (x));
+         TEST_FFI_RC(0, botan_mp_is_negative, (x));
+         TEST_FFI_RC(1, botan_mp_is_positive, (x));
+         TEST_FFI_RC(0, botan_mp_is_zero, (x));
+
+
+            {
+            botan_mp_t zero;
+            botan_mp_init(&zero);
+            int cmp;
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero));
+            result.confirm("bigint_mp_cmp(+, 0)", cmp == 1);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x));
+            result.confirm("bigint_mp_cmp(0, +)", cmp == -1);
+
+            TEST_FFI_RC(0, botan_mp_is_negative, (x));
+            TEST_FFI_RC(1, botan_mp_is_positive, (x));
+            TEST_FFI_OK(botan_mp_flip_sign, (x));
+            TEST_FFI_RC(1, botan_mp_is_negative, (x));
+            TEST_FFI_RC(0, botan_mp_is_positive, (x));
+
+            // test no negative zero
+            TEST_FFI_RC(0, botan_mp_is_negative, (zero));
+            TEST_FFI_RC(1, botan_mp_is_positive, (zero));
+            TEST_FFI_OK(botan_mp_flip_sign, (zero));
+            TEST_FFI_RC(0, botan_mp_is_negative, (zero));
+            TEST_FFI_RC(1, botan_mp_is_positive, (zero));
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero));
+            result.confirm("bigint_mp_cmp(-, 0)", cmp == -1);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x));
+            result.confirm("bigint_mp_cmp(0, -)", cmp == 1);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, zero));
+            result.confirm("bigint_mp_cmp(0, 0)", cmp == 0);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, x, x));
+            result.confirm("bigint_mp_cmp(x, x)", cmp == 0);
+
+            TEST_FFI_OK(botan_mp_flip_sign, (x));
+
+            botan_mp_destroy(zero);
+            }
+
+         size_t x_bits = 0;
+         TEST_FFI_OK(botan_mp_num_bits, (x, &x_bits));
+         result.test_eq("botan_mp_num_bits", x_bits, 9);
+
+         TEST_FFI_OK(botan_mp_to_hex, (x, str_buf));
+         result.test_eq("botan_mp_to_hex", std::string(str_buf), "0103");
+
+         uint32_t x_32;
+         TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32));
+         result.test_eq("botan_mp_to_uint32", size_t(x_32), size_t(0x103));
+
+         TEST_FFI_RC(1, botan_mp_get_bit, (x, 1));
+         TEST_FFI_RC(0, botan_mp_get_bit, (x, 87));
+         TEST_FFI_OK(botan_mp_set_bit, (x, 87));
+         TEST_FFI_RC(1, botan_mp_get_bit, (x, 87));
+         TEST_FFI_OK(botan_mp_to_hex, (x, str_buf));
+         result.test_eq("botan_mp_set_bit", std::string(str_buf), "8000000000000000000103");
+
+         TEST_FFI_OK(botan_mp_clear_bit, (x, 87));
+         TEST_FFI_OK(botan_mp_to_hex, (x, str_buf));
+         result.test_eq("botan_mp_set_bit", std::string(str_buf), "0103");
+
+         botan_mp_t y;
+         TEST_FFI_OK(botan_mp_init, (&y));
+         TEST_FFI_OK(botan_mp_set_from_int, (y, 0x1234567));
+
+         botan_mp_t r;
+         botan_mp_init(&r);
+
+         TEST_FFI_OK(botan_mp_add, (r, x, y));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_add", std::string(str_buf), "19089002");
+
+         TEST_FFI_OK(botan_mp_mul, (r, x, y));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_mul", std::string(str_buf), "4943984437");
+         TEST_FFI_RC(0, botan_mp_is_negative, (r));
+
+         botan_mp_t q;
+         botan_mp_init(&q);
+         TEST_FFI_OK(botan_mp_div, (q, r, y, x));
+
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (q, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_div_q", std::string(str_buf), "73701");
+
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_div_r", std::string(str_buf), "184");
+
+         TEST_FFI_OK(botan_mp_set_from_str, (y, "4943984437"));
+         TEST_FFI_OK(botan_mp_sub, (r, x, y));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_sub", std::string(str_buf), "4943984178");
+         TEST_FFI_RC(1, botan_mp_is_negative, (r));
+
+         TEST_FFI_OK(botan_mp_lshift, (r, x, 39));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_lshift", std::string(str_buf), "142386755796992");
+
+         TEST_FFI_OK(botan_mp_rshift, (r, r, 3));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_rshift", std::string(str_buf), "17798344474624");
+
+         TEST_FFI_OK(botan_mp_gcd, (r, x, y));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_gcd", std::string(str_buf), "259");
+
+         botan_mp_t p;
+         botan_mp_init(&p);
+         const uint8_t M127[] = { 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+         TEST_FFI_OK(botan_mp_from_bin, (p, M127, sizeof(M127)));
+         TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64));
+
+         size_t p_bits = 0;
+         TEST_FFI_OK(botan_mp_num_bits, (p, &p_bits));
+         result.test_eq("botan_mp_num_bits", p_bits, 127);
+
+         TEST_FFI_OK(botan_mp_mod_inverse, (r, x, p));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_mod_inverse", std::string(str_buf), "40728777507911553541948312086427855425");
+
+         TEST_FFI_OK(botan_mp_powmod, (r, x, r, p));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_powmod", std::string(str_buf), "40550417419160441638948180641668117560");
+
+         TEST_FFI_OK(botan_mp_num_bytes, (r, &bn_bytes));
+         result.test_eq("botan_mp_num_bytes", bn_bytes, 16);
+
+         std::vector<uint8_t> bn_buf;
+         bn_buf.resize(bn_bytes);
+         botan_mp_to_bin(r, bn_buf.data());
+         result.test_eq("botan_mp_to_bin", bn_buf, "1E81B9EFE0BE1902F6D03F9F5E5FB438");
+
+         TEST_FFI_OK(botan_mp_set_from_mp, (y, r));
+         TEST_FFI_OK(botan_mp_mod_mul, (r, x, y, p));
+         str_len = sizeof(str_buf);
+         TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len));
+         result.test_eq("botan_mp_mod_mul", std::string(str_buf), "123945920473931248854653259523111998693");
+
+         str_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mp_to_str, (r, 10, str_buf, &str_len));
+
+         size_t x_bytes;
+         botan_mp_rand_bits(x, rng, 512);
+         TEST_FFI_OK(botan_mp_num_bytes, (x, &x_bytes));
+         result.test_lte("botan_mp_num_bytes", x_bytes, 512 / 8);
+
+         TEST_FFI_OK(botan_mp_set_from_radix_str, (x, "909A", 16));
+         TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32));
+         result.test_eq("botan_mp_set_from_radix_str(16)", x_32, static_cast<size_t>(0x909A));
+
+         TEST_FFI_OK(botan_mp_set_from_radix_str, (x, "9098135", 10));
+         TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32));
+         result.test_eq("botan_mp_set_from_radix_str(10)", x_32, static_cast<size_t>(9098135));
+
+         botan_mp_destroy(p);
+         botan_mp_destroy(x);
+         botan_mp_destroy(y);
+         botan_mp_destroy(r);
+         botan_mp_destroy(q);
+
+         return result;
+         }
+
+      void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng)
+         {
+         // export public key
+         size_t pubkey_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export, (pub, nullptr, &pubkey_len,
+                     BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         std::vector<uint8_t> pubkey(pubkey_len);
+         TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         pubkey_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_export,
+                     (pub, nullptr, &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         pubkey.resize(pubkey_len);
+         TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         // reimport exported public key
+         botan_pubkey_t pub_copy;
+         TEST_FFI_OK(botan_pubkey_load, (&pub_copy, pubkey.data(), pubkey_len));
+         TEST_FFI_OK(botan_pubkey_check_key, (pub_copy, rng, 0));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub_copy));
+
+         // export private key
+         std::vector<uint8_t> privkey;
+         size_t privkey_len = 0;
+
+         // call with nullptr to query the length
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export,
+                     (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         privkey.resize(privkey_len);
+         privkey_len = privkey.size(); // set buffer size
+
+         TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         privkey.resize(privkey_len);
+
+         result.test_gte("Reasonable size", privkey.size(), 32);
+
+         // reimport exported private key
+         botan_privkey_t copy;
+         TEST_FFI_OK(botan_privkey_load, (&copy, rng, privkey.data(), privkey.size(), nullptr));
+         botan_privkey_destroy(copy);
+
+         // Now again for PEM
+         privkey_len = 0;
+
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export,
+                     (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         privkey.resize(privkey_len);
+         TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         TEST_FFI_OK(botan_privkey_load, (&copy, rng, privkey.data(), privkey.size(), nullptr));
+         botan_privkey_destroy(copy);
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_PKCS5_PBES2)
+         const size_t pbkdf_iter = 1000;
+
+         // export private key encrypted
+         privkey_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted_pbkdf_iter, (priv, nullptr,
+                     &privkey_len, rng, "password", pbkdf_iter, "", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         privkey.resize(privkey_len);
+         privkey_len = privkey.size();
+
+         TEST_FFI_OK(botan_privkey_export_encrypted_pbkdf_iter, (priv, privkey.data(), &privkey_len, rng, "password", pbkdf_iter,
+                     "", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+         // reimport encrypted private key
+         botan_privkey_load(&copy, rng, privkey.data(), privkey.size(), "password");
+         botan_privkey_destroy(copy);
+
+         privkey_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted_pbkdf_iter, (priv, nullptr,
+                     &privkey_len, rng, "password", pbkdf_iter, "", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         privkey.resize(privkey_len);
+         TEST_FFI_OK(botan_privkey_export_encrypted_pbkdf_iter, (priv, privkey.data(), &privkey_len, rng, "password", pbkdf_iter,
+                     "", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+         privkey.resize(privkey_len * 2);
+         privkey_len = privkey.size();
+         const uint32_t pbkdf_msec = 100;
+         size_t pbkdf_iters_out = 0;
+
+#if defined(BOTAN_HAS_SCRYPT)
+         const std::string pbe_hash = "Scrypt";
+#else
+         const std::string pbe_hash = "SHA-512";
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+         const std::string pbe_cipher = "AES-256/GCM";
+#else
+         const std::string pbe_cipher = "AES-256/CBC";
+#endif
+
+         TEST_FFI_OK(botan_privkey_export_encrypted_pbkdf_msec,
+                     (priv, privkey.data(), &privkey_len, rng, "password",
+                      pbkdf_msec, &pbkdf_iters_out, pbe_cipher.c_str(), pbe_hash.c_str(), 0));
+
+         if(pbe_hash == "Scrypt")
+            {
+            result.test_eq("Scrypt iters set to zero in this API", pbkdf_iters_out, 0);
+            }
+         else
+            {
+            // PBKDF2 currently always rounds to multiple of 2000
+            result.test_eq("Expected PBKDF2 iters", pbkdf_iters_out % 2000, 0);
+            }
+
+         privkey.resize(privkey_len);
+
+         TEST_FFI_OK(botan_privkey_load, (&copy, rng, privkey.data(), privkey.size(), "password"));
+         botan_privkey_destroy(copy);
+#endif
+
+         // calculate fingerprint
+         size_t strength = 0;
+         TEST_FFI_OK(botan_pubkey_estimated_strength, (pub, &strength));
+         result.test_gte("estimated strength", strength, 1);
+
+         size_t fingerprint_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pubkey_fingerprint,
+                     (pub, "SHA-512", nullptr, &fingerprint_len));
+
+         std::vector<uint8_t> fingerprint(fingerprint_len);
+         TEST_FFI_OK(botan_pubkey_fingerprint, (pub, "SHA-512", fingerprint.data(), &fingerprint_len));
+         }
+
+      Test::Result ffi_test_fpe()
+         {
+         Test::Result result("FFI FPE");
+
+         const uint8_t key[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+         botan_mp_t n, x;
+         botan_fpe_t fpe;
+
+         botan_mp_init(&n);
+         botan_mp_set_from_str(n, "1000000000");
+
+         botan_mp_init(&x);
+         botan_mp_set_from_str(x, "178051120");
+
+         TEST_FFI_OK(botan_fpe_fe1_init, (&fpe, n, key, sizeof(key), 5, 0));
+
+         TEST_FFI_OK(botan_fpe_encrypt, (fpe, x, nullptr, 0));
+
+         uint32_t xval = 0;
+         TEST_FFI_OK(botan_mp_to_uint32, (x, &xval));
+         result.test_eq("Expected FPE ciphertext", xval, size_t(605648666));
+
+         TEST_FFI_OK(botan_fpe_encrypt, (fpe, x, nullptr, 0));
+         TEST_FFI_OK(botan_fpe_decrypt, (fpe, x, nullptr, 0));
+         TEST_FFI_OK(botan_fpe_decrypt, (fpe, x, nullptr, 0));
+
+         TEST_FFI_OK(botan_mp_to_uint32, (x, &xval));
+         result.test_eq("FPE round trip", xval, size_t(178051120));
+
+         TEST_FFI_OK(botan_fpe_destroy, (fpe));
+         TEST_FFI_OK(botan_mp_destroy, (x));
+         TEST_FFI_OK(botan_mp_destroy, (n));
+
+         return result;
+         }
+
+      Test::Result ffi_test_totp()
+         {
+         Test::Result result("FFI TOTP");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("3132333435363738393031323334353637383930");
+         const size_t digits = 8;
+         const size_t timestep = 30;
+         botan_totp_t totp;
+
+         TEST_FFI_OK(botan_totp_init, (&totp, key.data(), key.size(), "SHA-1", digits, timestep));
+
+         uint32_t code;
+         TEST_FFI_OK(botan_totp_generate, (totp, &code, 59));
+         result.confirm("TOTP code", code == 94287082);
+
+         TEST_FFI_OK(botan_totp_generate, (totp, &code, 1111111109));
+         result.confirm("TOTP code 2", code == 7081804);
+
+         TEST_FFI_OK(botan_totp_check, (totp, 94287082, 59+60, 60));
+         TEST_FFI_RC(1, botan_totp_check, (totp, 94287082, 59+31, 1));
+         TEST_FFI_RC(1, botan_totp_check, (totp, 94287082, 59+61, 1));
+
+         TEST_FFI_OK(botan_totp_destroy, (totp));
+
+         return result;
+         }
+
+      Test::Result ffi_test_hotp()
+         {
+         Test::Result result("FFI HOTP");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("3132333435363738393031323334353637383930");
+         const size_t digits = 6;
+
+         botan_hotp_t hotp;
+         uint32_t hotp_val;
+
+         TEST_FFI_OK(botan_hotp_init, (&hotp, key.data(), key.size(), "SHA-1", digits));
+
+         TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 0));
+         result.confirm("Valid value for counter 0", hotp_val == 755224);
+         TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 1));
+         result.confirm("Valid value for counter 0", hotp_val == 287082);
+         TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 2));
+         result.confirm("Valid value for counter 0", hotp_val == 359152);
+         TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 0));
+         result.confirm("Valid value for counter 0", hotp_val == 755224);
+
+         uint64_t next_ctr = 0;
+
+         TEST_FFI_OK(botan_hotp_check, (hotp, &next_ctr, 755224, 0, 0));
+         result.confirm("HOTP resync", next_ctr == 1);
+         TEST_FFI_OK(botan_hotp_check, (hotp, nullptr, 359152, 2, 0));
+         TEST_FFI_RC(1, botan_hotp_check, (hotp, nullptr, 359152, 1, 0));
+         TEST_FFI_OK(botan_hotp_check, (hotp, &next_ctr, 359152, 0, 2));
+         result.confirm("HOTP resync", next_ctr == 3);
+
+         TEST_FFI_OK(botan_hotp_destroy, (hotp));
+
+         return result;
+         }
+
+      Test::Result ffi_test_keywrap()
+         {
+         Test::Result result("FFI keywrap");
+
+         const uint8_t key[16] = { 0 };
+         const uint8_t kek[16] = { 0xFF, 0 };
+
+         const uint8_t expected_wrapped_key[16+8] = {
+            0x04, 0x13, 0x37, 0x39, 0x82, 0xCF, 0xFA, 0x31, 0x81, 0xCA, 0x4F, 0x59,
+            0x74, 0x4D, 0xED, 0x29, 0x1F, 0x3F, 0xE5, 0x24, 0x00, 0x1B, 0x93, 0x20
+         };
+
+         uint8_t wrapped[16 + 8] = { 0 };
+         size_t wrapped_keylen = sizeof(wrapped);
+         TEST_FFI_OK(botan_key_wrap3394, (key, sizeof(key),
+                                          kek, sizeof(kek),
+                                          wrapped, &wrapped_keylen));
+
+         result.test_eq("Expected wrapped keylen size", wrapped_keylen, 16 + 8);
+
+         result.test_eq(nullptr, "Wrapped key", wrapped, wrapped_keylen,
+                        expected_wrapped_key, sizeof(expected_wrapped_key));
+
+         uint8_t dec_key[16] = { 0 };
+         size_t dec_keylen = sizeof(dec_key);
+         TEST_FFI_OK(botan_key_unwrap3394, (wrapped, sizeof(wrapped),
+                                            kek, sizeof(kek),
+                                            dec_key, &dec_keylen));
+
+         result.test_eq(nullptr, "Unwrapped key", dec_key, dec_keylen, key, sizeof(key));
+         return result;
+         }
+
+      Test::Result ffi_test_rsa(botan_rng_t rng)
+         {
+         Test::Result result("FFI RSA");
+
+         botan_privkey_t priv;
+
+         if(TEST_FFI_OK(botan_privkey_create_rsa, (&priv, rng, 1024)))
+            {
+            TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0));
+
+            botan_pubkey_t pub;
+            TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+            TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0));
+
+            ffi_test_pubkey_export(result, pub, priv, rng);
+
+            botan_mp_t p, q, d, n, e;
+            botan_mp_init(&p);
+            botan_mp_init(&q);
+            botan_mp_init(&d);
+            botan_mp_init(&n);
+            botan_mp_init(&e);
+
+            TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_privkey_get_field, (p, priv, "quux"));
+            TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_pubkey_get_field, (p, pub, "quux"));
+
+            TEST_FFI_OK(botan_privkey_rsa_get_p, (p, priv));
+            TEST_FFI_OK(botan_privkey_rsa_get_q, (q, priv));
+            TEST_FFI_OK(botan_privkey_rsa_get_d, (d, priv));
+            TEST_FFI_OK(botan_privkey_rsa_get_e, (e, priv));
+            TEST_FFI_OK(botan_privkey_rsa_get_n, (n, priv));
+
+            // Confirm same (e,n) values in public key
+               {
+               botan_mp_t pub_e, pub_n;
+               botan_mp_init(&pub_e);
+               botan_mp_init(&pub_n);
+               TEST_FFI_OK(botan_pubkey_rsa_get_e, (pub_e, pub));
+               TEST_FFI_OK(botan_pubkey_rsa_get_n, (pub_n, pub));
+
+               TEST_FFI_RC(1, botan_mp_equal, (pub_e, e));
+               TEST_FFI_RC(1, botan_mp_equal, (pub_n, n));
+               botan_mp_destroy(pub_e);
+               botan_mp_destroy(pub_n);
+               }
+
+            TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64));
+            TEST_FFI_RC(1, botan_mp_is_prime, (q, rng, 64));
+
+            // Test p != q
+            TEST_FFI_RC(0, botan_mp_equal, (p, q));
+
+            // Test p * q == n
+            botan_mp_t x;
+            botan_mp_init(&x);
+            TEST_FFI_OK(botan_mp_mul, (x, p, q));
+
+            TEST_FFI_RC(1, botan_mp_equal, (x, n));
+            botan_mp_destroy(x);
+
+            botan_privkey_t loaded_privkey;
+            // First try loading a bogus key and verify check_key fails
+            TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, n, d, q));
+            TEST_FFI_RC(-1, botan_privkey_check_key, (loaded_privkey, rng, 0));
+            botan_privkey_destroy(loaded_privkey);
+
+            TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, p, q, e));
+            TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+
+            botan_pubkey_t loaded_pubkey;
+            TEST_FFI_OK(botan_pubkey_load_rsa, (&loaded_pubkey, n, e));
+            TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+            botan_mp_destroy(p);
+            botan_mp_destroy(q);
+            botan_mp_destroy(d);
+            botan_mp_destroy(e);
+            botan_mp_destroy(n);
+
+            size_t pkcs1_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                  botan_privkey_rsa_get_privkey, (loaded_privkey, nullptr, &pkcs1_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+            std::vector<uint8_t> pkcs1(pkcs1_len);
+            TEST_FFI_OK(botan_privkey_rsa_get_privkey, (loaded_privkey, pkcs1.data(), &pkcs1_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER));
+
+            botan_privkey_t privkey_from_pkcs1;
+            TEST_FFI_OK(botan_privkey_load_rsa_pkcs1, (&privkey_from_pkcs1, pkcs1.data(), pkcs1_len));
+            TEST_FFI_OK(botan_privkey_destroy, (privkey_from_pkcs1));
+
+            pkcs1_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
+                  botan_privkey_rsa_get_privkey, (loaded_privkey, nullptr, &pkcs1_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+            pkcs1.resize(pkcs1_len);
+            TEST_FFI_OK(botan_privkey_rsa_get_privkey, (loaded_privkey, pkcs1.data(), &pkcs1_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM));
+
+            char namebuf[32] = { 0 };
+            size_t name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_pubkey_algo_name, (loaded_pubkey, namebuf, &name_len)))
+               {
+               result.test_eq("algo name", std::string(namebuf), "RSA");
+               }
+
+            name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_privkey_algo_name, (loaded_privkey, namebuf, &name_len)))
+               {
+               result.test_eq("algo name", std::string(namebuf), "RSA");
+               }
+
+#if defined(BOTAN_HAS_EME_OAEP)
+            botan_pk_op_encrypt_t encrypt;
+
+            if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&encrypt, loaded_pubkey, "OAEP(SHA-256)", 0)))
+               {
+               std::vector<uint8_t> plaintext(32);
+               TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size()));
+
+               size_t ctext_len;
+               TEST_FFI_OK(botan_pk_op_encrypt_output_length, (encrypt, plaintext.size(), &ctext_len));
+               std::vector<uint8_t> ciphertext(ctext_len);
+
+               if(TEST_FFI_OK(botan_pk_op_encrypt, (encrypt, rng,
+                                                    ciphertext.data(), &ctext_len,
+                                                    plaintext.data(), plaintext.size())))
+                  {
+                  ciphertext.resize(ctext_len);
+
+                  botan_pk_op_decrypt_t decrypt;
+                  if(TEST_FFI_OK(botan_pk_op_decrypt_create, (&decrypt, priv, "OAEP(SHA-256)", 0)))
+                     {
+                     size_t decrypted_len;
+                     TEST_FFI_OK(botan_pk_op_decrypt_output_length, (decrypt, ciphertext.size(), &decrypted_len));
+                     std::vector<uint8_t> decrypted(decrypted_len);
+                     TEST_FFI_OK(botan_pk_op_decrypt, (decrypt, decrypted.data(), &decrypted_len,
+                                                       ciphertext.data(), ciphertext.size()));
+                     decrypted.resize(decrypted_len);
+
+                     result.test_eq("RSA plaintext", decrypted, plaintext);
+                     }
+
+                  TEST_FFI_OK(botan_pk_op_decrypt_destroy, (decrypt));
+                  }
+
+               TEST_FFI_OK(botan_pk_op_encrypt_destroy, (encrypt));
+               }
+#endif
+
+            TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+            TEST_FFI_OK(botan_pubkey_destroy, (pub));
+            TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+            TEST_FFI_OK(botan_privkey_destroy, (priv));
+            }
+
+         return result;
+         }
+
+         void do_dsa_test(botan_privkey_t priv, botan_rng_t rng, Test::Result &result)
+            {
+            TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0));
+
+            botan_pubkey_t pub;
+            TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+            TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0));
+
+            ffi_test_pubkey_export(result, pub, priv, rng);
+
+            botan_mp_t p, q, g, x, y;
+            botan_mp_init(&p);
+            botan_mp_init(&q);
+            botan_mp_init(&g);
+            botan_mp_init(&x);
+            botan_mp_init(&y);
+
+            TEST_FFI_OK(botan_privkey_dsa_get_x, (x, priv));
+            TEST_FFI_OK(botan_pubkey_dsa_get_g, (g, pub));
+            TEST_FFI_OK(botan_pubkey_dsa_get_p, (p, pub));
+            TEST_FFI_OK(botan_pubkey_dsa_get_q, (q, pub));
+            TEST_FFI_OK(botan_pubkey_dsa_get_y, (y, pub));
+
+            botan_mp_t cmp;
+            botan_mp_init(&cmp);
+            TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_privkey_get_field, (cmp, priv, "quux"));
+
+            TEST_FFI_OK(botan_privkey_get_field, (cmp, priv, "x"));
+            TEST_FFI_RC(1, botan_mp_equal, (cmp, x));
+
+            TEST_FFI_OK(botan_privkey_get_field, (cmp, priv, "y"));
+            TEST_FFI_RC(1, botan_mp_equal, (cmp, y));
+
+            TEST_FFI_OK(botan_privkey_get_field, (cmp, priv, "p"));
+            TEST_FFI_RC(1, botan_mp_equal, (cmp, p));
+            botan_mp_destroy(cmp);
+
+            botan_privkey_t loaded_privkey;
+            TEST_FFI_OK(botan_privkey_load_dsa, (&loaded_privkey, p, q, g, x));
+            TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+
+            botan_pubkey_t loaded_pubkey;
+            TEST_FFI_OK(botan_pubkey_load_dsa, (&loaded_pubkey, p, q, g, y));
+            TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+            botan_mp_destroy(p);
+            botan_mp_destroy(q);
+            botan_mp_destroy(g);
+            botan_mp_destroy(y);
+            botan_mp_destroy(x);
+
+            botan_pk_op_sign_t signer;
+
+            std::vector<uint8_t> message(6, 6);
+            std::vector<uint8_t> signature;
+
+            if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, "EMSA1(SHA-256)", 0)))
+               {
+               // TODO: break input into multiple calls to update
+               TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+
+               size_t sig_len;
+               TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
+               signature.resize(sig_len);
+
+               size_t output_sig_len = sig_len;
+               TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &output_sig_len));
+               result.test_lte("Output length is upper bound", output_sig_len, sig_len);
+               signature.resize(output_sig_len);
+
+               TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+               }
+
+            botan_pk_op_verify_t verifier = nullptr;
+
+            if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-256)", 0)))
+               {
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               // TODO: randomize this
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+               }
+
+            TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+            TEST_FFI_OK(botan_pubkey_destroy, (pub));
+            TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+            TEST_FFI_OK(botan_privkey_destroy, (priv));
+            }
+
+      Test::Result ffi_test_dsa(botan_rng_t rng)
+         {
+         Test::Result result("FFI DSA");
+         botan_privkey_t priv;
+
+         if(TEST_FFI_OK(botan_privkey_create, (&priv, "DSA", "dsa/jce/1024", rng)))
+            {
+               do_dsa_test(priv, rng, result);
+            }
+
+         if(TEST_FFI_OK(botan_privkey_create_dsa, (&priv, rng, 1024, 160)))
+            {
+               do_dsa_test(priv, rng, result);
+            }
+
+         return result;
+         }
+      Test::Result ffi_test_ecdsa(botan_rng_t rng)
+         {
+         Test::Result result("FFI ECDSA");
+         static const char* kCurve = "secp384r1";
+         botan_privkey_t priv;
+         botan_pubkey_t pub;
+         botan_privkey_t loaded_privkey;
+         botan_pubkey_t loaded_pubkey;
+
+         REQUIRE_FFI_OK(botan_privkey_create_ecdsa, (&priv, rng, kCurve));
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+         ffi_test_pubkey_export(result, pub, priv, rng);
+
+         // Check key load functions
+         botan_mp_t private_scalar, public_x, public_y;
+         botan_mp_init(&private_scalar);
+         botan_mp_init(&public_x);
+         botan_mp_init(&public_y);
+
+         TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_privkey_get_field, (private_scalar, priv, "quux"));
+         TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_pubkey_get_field, (private_scalar, pub, "quux"));
+
+         TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y"));
+         TEST_FFI_OK(botan_privkey_load_ecdsa, (&loaded_privkey, private_scalar, kCurve));
+         TEST_FFI_OK(botan_pubkey_load_ecdsa, (&loaded_pubkey, public_x, public_y, kCurve));
+         TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+         TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+         char namebuf[32] = { 0 };
+         size_t name_len = sizeof(namebuf);
+
+         TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len));
+         result.test_eq(namebuf, namebuf, "ECDSA");
+
+         std::vector<uint8_t> message(1280), signature;
+         TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size()));
+
+         for(uint32_t flags = 0; flags <= 1; ++flags)
+            {
+            botan_pk_op_sign_t signer;
+            if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, "EMSA1(SHA-384)", flags)))
+               {
+               // TODO: break input into multiple calls to update
+               TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+
+               size_t sig_len;
+               TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
+
+               signature.resize(sig_len);
+
+               size_t output_sig_len = signature.size();
+               TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &output_sig_len));
+               signature.resize(output_sig_len);
+
+               TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+               }
+
+            botan_pk_op_verify_t verifier = nullptr;
+
+            if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-384)", flags)))
+               {
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               // TODO: randomize this
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+               TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+               }
+            }
+
+         TEST_FFI_OK(botan_mp_destroy, (private_scalar));
+         TEST_FFI_OK(botan_mp_destroy, (public_x));
+         TEST_FFI_OK(botan_mp_destroy, (public_y));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_privkey_destroy, (priv));
+         TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+         TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+
+         return result;
+         }
+
+      Test::Result ffi_test_sm2(botan_rng_t rng)
+         {
+         Test::Result result("FFI SM2 Sig");
+         static const char* kCurve = "sm2p256v1";
+         const std::string sm2_ident = "SM2 Ident Field";
+         botan_privkey_t priv;
+         botan_pubkey_t pub;
+         botan_privkey_t loaded_privkey;
+         botan_pubkey_t loaded_pubkey;
+
+         REQUIRE_FFI_OK(botan_privkey_create, (&priv, "SM2_Sig", kCurve, rng));
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+         ffi_test_pubkey_export(result, pub, priv, rng);
+
+         uint8_t za[32];
+         size_t sizeof_za = sizeof(za);
+         TEST_FFI_OK(botan_pubkey_sm2_compute_za, (za, &sizeof_za, "Ident", "SM3", pub));
+
+         // Check key load functions
+         botan_mp_t private_scalar, public_x, public_y;
+         botan_mp_init(&private_scalar);
+         botan_mp_init(&public_x);
+         botan_mp_init(&public_y);
+
+         TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y"));
+         REQUIRE_FFI_OK(botan_privkey_load_sm2, (&loaded_privkey, private_scalar, kCurve));
+         REQUIRE_FFI_OK(botan_pubkey_load_sm2, (&loaded_pubkey, public_x, public_y, kCurve));
+         TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+         TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+         char namebuf[32] = { 0 };
+         size_t name_len = sizeof(namebuf);
+
+         TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len));
+         result.test_eq(namebuf, namebuf, "SM2");
+
+         std::vector<uint8_t> message(1280), signature;
+         TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size()));
+         botan_pk_op_sign_t signer;
+         if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, sm2_ident.c_str(), 0)))
+            {
+            // TODO: break input into multiple calls to update
+            TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+
+            size_t sig_len;
+            TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
+
+            signature.resize(sig_len);
+
+            TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_len));
+            signature.resize(sig_len);
+
+            TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+            }
+
+         botan_pk_op_verify_t verifier = nullptr;
+
+         if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, sm2_ident.c_str(), 0)))
+            {
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            // TODO: randomize this
+            signature[0] ^= 1;
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            message[0] ^= 1;
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            signature[0] ^= 1;
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            message[0] ^= 1;
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+            }
+
+         TEST_FFI_OK(botan_mp_destroy, (private_scalar));
+         TEST_FFI_OK(botan_mp_destroy, (public_x));
+         TEST_FFI_OK(botan_mp_destroy, (public_y));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_privkey_destroy, (priv));
+         TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+         TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+
+         return result;
+         }
+
+      Test::Result ffi_test_sm2_enc(botan_rng_t rng)
+         {
+         Test::Result result("FFI SM2 Enc");
+         static const char* kCurve = "sm2p256v1";
+         botan_privkey_t priv;
+         botan_pubkey_t pub;
+         botan_privkey_t loaded_privkey;
+         botan_pubkey_t loaded_pubkey;
+
+         REQUIRE_FFI_OK(botan_privkey_create, (&priv, "SM2_Enc", kCurve, rng));
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+         ffi_test_pubkey_export(result, pub, priv, rng);
+
+         uint8_t za[32];
+         size_t sizeof_za = sizeof(za);
+         TEST_FFI_OK(botan_pubkey_sm2_compute_za, (za, &sizeof_za, "Ident", "SM3", pub));
+
+         // Check key load functions
+         botan_mp_t private_scalar, public_x, public_y;
+         botan_mp_init(&private_scalar);
+         botan_mp_init(&public_x);
+         botan_mp_init(&public_y);
+
+         TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y"));
+         REQUIRE_FFI_OK(botan_privkey_load_sm2_enc, (&loaded_privkey, private_scalar, kCurve));
+         REQUIRE_FFI_OK(botan_pubkey_load_sm2_enc, (&loaded_pubkey, public_x, public_y, kCurve));
+         TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+         TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+         char namebuf[32] = { 0 };
+         size_t name_len = sizeof(namebuf);
+
+         TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len));
+         result.test_eq(namebuf, namebuf, "SM2");
+
+         std::vector<uint8_t> message(32);
+
+         std::vector<uint8_t> ciphertext;
+         TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size()));
+
+         botan_pk_op_encrypt_t enc;
+         if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&enc, loaded_pubkey, "", 0)))
+            {
+            size_t ctext_len;
+            TEST_FFI_OK(botan_pk_op_encrypt_output_length, (enc, message.size(), &ctext_len));
+
+            ciphertext.resize(ctext_len);
+            TEST_FFI_OK(botan_pk_op_encrypt, (enc, rng, ciphertext.data(), &ctext_len,
+                                              message.data(), message.size()));
+            ciphertext.resize(ctext_len);
+
+            botan_pk_op_decrypt_t dec;
+            TEST_FFI_OK(botan_pk_op_decrypt_create, (&dec, loaded_privkey, "", 0));
+
+            std::vector<uint8_t> recovered(message.size());
+            size_t recovered_len = recovered.size();
+
+            TEST_FFI_OK(botan_pk_op_decrypt,
+                        (dec, recovered.data(), &recovered_len,
+                         ciphertext.data(), ciphertext.size()));
+
+            botan_pk_op_decrypt_destroy(dec);
+            }
+         botan_pk_op_encrypt_destroy(enc);
+
+         TEST_FFI_OK(botan_mp_destroy, (private_scalar));
+         TEST_FFI_OK(botan_mp_destroy, (public_x));
+         TEST_FFI_OK(botan_mp_destroy, (public_y));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_privkey_destroy, (priv));
+         TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+         TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+
+         return result;
+         }
+
+      Test::Result ffi_test_ecdh(botan_rng_t rng)
+         {
+         Test::Result result("FFI ECDH");
+
+         botan_mp_t private_scalar, public_x, public_y;
+         botan_privkey_t loaded_privkey1;
+         botan_pubkey_t loaded_pubkey1;
+         botan_mp_init(&private_scalar);
+         botan_mp_init(&public_x);
+         botan_mp_init(&public_y);
+
+         botan_privkey_t priv1;
+         REQUIRE_FFI_OK(botan_privkey_create_ecdh, (&priv1, rng, "secp256r1"));
+
+         botan_privkey_t priv2;
+         REQUIRE_FFI_OK(botan_privkey_create_ecdh, (&priv2, rng, "secp256r1"));
+
+         botan_pubkey_t pub1;
+         REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub1, priv1));
+
+         botan_pubkey_t pub2;
+         REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2));
+
+         /* Reload key-pair1 in order to test functions for key loading */
+         TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv1, "x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub1, "public_x"));
+         TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub1, "public_y"));
+         REQUIRE_FFI_OK(botan_privkey_load_ecdh, (&loaded_privkey1, private_scalar, "secp256r1"));
+         REQUIRE_FFI_OK(botan_pubkey_load_ecdh, (&loaded_pubkey1, public_x, public_y, "secp256r1"));
+         TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey1, rng, 0));
+         TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0));
+
+         ffi_test_pubkey_export(result, loaded_pubkey1, priv1, rng);
+         ffi_test_pubkey_export(result, pub2, priv2, rng);
+
+         botan_pk_op_ka_t ka1;
+         REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "KDF2(SHA-256)", 0));
+         botan_pk_op_ka_t ka2;
+         REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, "KDF2(SHA-256)", 0));
+
+         size_t pubkey1_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pk_op_key_agreement_export_public, (priv1, nullptr, &pubkey1_len));
+         std::vector<uint8_t> pubkey1(pubkey1_len);
+         REQUIRE_FFI_OK(botan_pk_op_key_agreement_export_public, (priv1, pubkey1.data(), &pubkey1_len));
+         size_t pubkey2_len = 0;
+         TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pk_op_key_agreement_export_public, (priv2, nullptr, &pubkey2_len));
+         std::vector<uint8_t> pubkey2(pubkey2_len);
+         REQUIRE_FFI_OK(botan_pk_op_key_agreement_export_public, (priv2, pubkey2.data(), &pubkey2_len));
+
+         std::vector<uint8_t> salt(32);
+         TEST_FFI_OK(botan_rng_get, (rng, salt.data(), salt.size()));
+
+         const size_t shared_key_len = 64;
+
+         std::vector<uint8_t> key1(shared_key_len);
+         size_t key1_len = key1.size();
+         TEST_FFI_OK(botan_pk_op_key_agreement, (ka1, key1.data(), &key1_len,
+                                                 pubkey2.data(), pubkey2.size(),
+                                                 salt.data(), salt.size()));
+
+         std::vector<uint8_t> key2(shared_key_len);
+         size_t key2_len = key2.size();
+         TEST_FFI_OK(botan_pk_op_key_agreement, (ka2, key2.data(), &key2_len,
+                                                 pubkey1.data(), pubkey1.size(),
+                                                 salt.data(), salt.size()));
+
+         result.test_eq("shared ECDH key", key1, key2);
+
+         TEST_FFI_OK(botan_mp_destroy, (private_scalar));
+         TEST_FFI_OK(botan_mp_destroy, (public_x));
+         TEST_FFI_OK(botan_mp_destroy, (public_y));
+         TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka1));
+         TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka2));
+         TEST_FFI_OK(botan_privkey_destroy, (priv1));
+         TEST_FFI_OK(botan_privkey_destroy, (priv2));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub1));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub2));
+         TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey1));
+         TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1));
+         return result;
+         }
+
+      Test::Result ffi_test_mceliece(botan_rng_t rng)
+         {
+         Test::Result result("FFI McEliece");
+
+         botan_privkey_t priv;
+#if defined(BOTAN_HAS_MCELIECE)
+         if(TEST_FFI_OK(botan_privkey_create_mceliece, (&priv, rng, 2048, 50)))
+            {
+            botan_pubkey_t pub;
+            TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+
+            ffi_test_pubkey_export(result, pub, priv, rng);
+
+            char namebuf[32] = { 0 };
+            size_t name_len = sizeof(namebuf);
+            if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len)))
+               {
+               result.test_eq("algo name", std::string(namebuf), "McEliece");
+               }
+
+            // TODO test KEM
+
+#if defined(BOTAN_HAS_MCEIES)
+            const uint8_t ad[8] = { 0xAD, 0xAD, 0xAD, 0xAD, 0xBE, 0xEE, 0xEE, 0xFF };
+            const size_t ad_len = sizeof(ad);
+
+            const Botan::secure_vector<uint8_t> plaintext = Test::rng().random_vec(Test::rng().next_byte());
+            size_t plaintext_len = plaintext.size();
+            size_t ciphertext_len = 0;
+
+            // first calculate ciphertext length
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(),
+                        plaintext.size(), ad, ad_len, nullptr, &ciphertext_len));
+            std::vector<uint8_t> ciphertext(ciphertext_len);
+
+            // now encrypt
+            if(TEST_FFI_OK(botan_mceies_encrypt, (pub, rng, "AES-256/OCB", plaintext.data(), plaintext.size(), ad, ad_len,
+                                                  ciphertext.data(), &ciphertext_len)))
+               {
+               std::vector<uint8_t> decrypted(ciphertext.size());
+               size_t decrypted_len = plaintext_len;
+
+               TEST_FFI_OK(botan_mceies_decrypt, (priv, "AES-256/OCB", ciphertext.data(), ciphertext.size(), ad, ad_len,
+                                                  decrypted.data(), &decrypted_len));
+
+               decrypted.resize(decrypted_len);
+
+               result.test_eq("MCIES plaintext", decrypted, plaintext);
+               }
+#endif
+
+            TEST_FFI_OK(botan_pubkey_destroy, (pub));
+            TEST_FFI_OK(botan_privkey_destroy, (priv));
+            }
+#else
+         // Not included, test that calling the FFI function work (and returns an error)
+         TEST_FFI_RC(BOTAN_FFI_ERROR_NOT_IMPLEMENTED, botan_privkey_create_mceliece, (&priv, rng, 2048, 50));
+#endif
+
+         return result;
+         }
+
+      Test::Result ffi_test_ed25519(botan_rng_t rng)
+         {
+         Test::Result result("FFI Ed25519");
+
+         botan_pubkey_t pub;
+         botan_privkey_t priv;
+
+         // From draft-koch-eddsa-for-openpgp-04
+         const std::vector<uint8_t> seed = Botan::hex_decode(
+            "1a8b1ff05ded48e18bf50166c664ab023ea70003d78d9e41f5758a91d850f8d2");
+         const std::vector<uint8_t> pubkey = Botan::hex_decode(
+            "3f098994bdd916ed4053197934e4a87c80733a1280d62f8010992e43ee3b2406");
+         const std::vector<uint8_t> message = Botan::hex_decode(
+            "4f70656e504750040016080006050255f95f9504ff0000000c");
+         const std::vector<uint8_t> exp_sig = Botan::hex_decode(
+            "56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed3366"
+            "d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404");
+
+         TEST_FFI_OK(botan_privkey_load_ed25519, (&priv, seed.data()));
+
+         uint8_t retr_privkey[64];
+         TEST_FFI_OK(botan_privkey_ed25519_get_privkey, (priv, retr_privkey));
+
+         result.test_eq(nullptr, "Public key matches", retr_privkey + 32, 32,
+                        pubkey.data(), pubkey.size());
+
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+
+         uint8_t retr_pubkey[32];
+         TEST_FFI_OK(botan_pubkey_ed25519_get_pubkey, (pub, retr_pubkey));
+         result.test_eq(nullptr, "Public key matches", retr_pubkey, 32,
+                        pubkey.data(), pubkey.size());
+
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_pubkey_load_ed25519, (&pub, pubkey.data()));
+
+         botan_pk_op_sign_t signer;
+         std::vector<uint8_t> signature;
+
+         if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, priv, "SHA-256", 0)))
+            {
+            TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+
+            size_t sig_len;
+            TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
+
+            signature.resize(sig_len);
+
+            TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_len));
+            signature.resize(sig_len);
+
+            TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+            }
+
+         result.test_eq("Expected signature", signature, exp_sig);
+
+         botan_pk_op_verify_t verifier;
+
+         if(TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "SHA-256", 0)))
+            {
+            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+            TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+            TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+            }
+
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_privkey_destroy, (priv));
+
+         return result;
+         }
+
+      Test::Result ffi_test_x25519()
+         {
+         Test::Result result("FFI X25519");
+
+         // From RFC 8037
+
+         const std::vector<uint8_t> a_pub_bits =
+            Botan::hex_decode("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
+         const std::vector<uint8_t> b_priv_bits =
+            Botan::hex_decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
+         const std::vector<uint8_t> b_pub_bits =
+            Botan::hex_decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
+         const std::vector<uint8_t> shared_secret_bits =
+            Botan::hex_decode("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
+
+         botan_privkey_t b_priv;
+         TEST_FFI_OK(botan_privkey_load_x25519, (&b_priv, b_priv_bits.data()));
+
+         std::vector<uint8_t> privkey_read(32);
+         TEST_FFI_OK(botan_privkey_x25519_get_privkey, (b_priv, privkey_read.data()));
+         result.test_eq("X25519 private key", privkey_read, b_priv_bits);
+
+         std::vector<uint8_t> pubkey_read(32);
+
+         botan_pubkey_t b_pub;
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv));
+         TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (b_pub, pubkey_read.data()));
+         result.test_eq("X25519 public key b", pubkey_read, b_pub_bits);
+
+         botan_pubkey_t a_pub;
+         TEST_FFI_OK(botan_pubkey_load_x25519, (&a_pub, a_pub_bits.data()));
+         TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (a_pub, pubkey_read.data()));
+         result.test_eq("X25519 public key a", pubkey_read, a_pub_bits);
+
+         botan_pk_op_ka_t ka;
+         REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka, b_priv, "Raw", 0));
+
+         std::vector<uint8_t> shared_output(32);
+         size_t shared_len = shared_output.size();
+         TEST_FFI_OK(botan_pk_op_key_agreement, (ka,
+                                                 shared_output.data(), &shared_len,
+                                                 a_pub_bits.data(), a_pub_bits.size(),
+                                                 nullptr, 0));
+
+         result.test_eq("Shared secret matches expected", shared_secret_bits, shared_output);
+
+         TEST_FFI_OK(botan_pubkey_destroy, (a_pub));
+         TEST_FFI_OK(botan_pubkey_destroy, (b_pub));
+         TEST_FFI_OK(botan_privkey_destroy, (b_priv));
+         TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka));
+
+         return result;
+         }
+
+      void do_elgamal_test(botan_privkey_t priv, botan_rng_t rng, Test::Result& result)
+         {
+         TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0));
+
+         botan_pubkey_t pub;
+         TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+         TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0));
+
+         ffi_test_pubkey_export(result, pub, priv, rng);
+         botan_mp_t p, g, x, y;
+         botan_mp_init(&p);
+         botan_mp_init(&g);
+         botan_mp_init(&x);
+         botan_mp_init(&y);
+
+         TEST_FFI_OK(botan_pubkey_get_field, (p, pub, "p"));
+         TEST_FFI_OK(botan_pubkey_get_field, (g, pub, "g"));
+         TEST_FFI_OK(botan_pubkey_get_field, (y, pub, "y"));
+         TEST_FFI_OK(botan_privkey_get_field, (x, priv, "x"));
+
+         size_t p_len = 0;
+         TEST_FFI_OK(botan_mp_num_bytes, (p, &p_len));
+
+         botan_privkey_t loaded_privkey;
+         TEST_FFI_OK(botan_privkey_load_elgamal, (&loaded_privkey, p, g, x));
+         TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+
+         botan_pubkey_t loaded_pubkey;
+         TEST_FFI_OK(botan_pubkey_load_elgamal, (&loaded_pubkey, p, g, y));
+         TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+         botan_mp_destroy(p);
+         botan_mp_destroy(g);
+         botan_mp_destroy(y);
+         botan_mp_destroy(x);
+
+         std::vector<uint8_t> plaintext(16, 0xFF);
+         std::vector<uint8_t> ciphertext;
+         std::vector<uint8_t> decryption;
+
+         // Test encryption
+         botan_pk_op_encrypt_t op_enc;
+         if (TEST_FFI_OK(botan_pk_op_encrypt_create, (&op_enc, loaded_pubkey, "Raw", 0)))
+            {
+            size_t ctext_len;
+            TEST_FFI_OK(botan_pk_op_encrypt_output_length, (op_enc, plaintext.size(), &ctext_len));
+            ciphertext.resize(ctext_len);
+            TEST_FFI_OK(botan_pk_op_encrypt, (op_enc, rng, ciphertext.data(), &ctext_len, plaintext.data(), plaintext.size()));
+            ciphertext.resize(ctext_len);
+            TEST_FFI_OK(botan_pk_op_encrypt_destroy, (op_enc));
+            }
+
+         // Test decryption
+         botan_pk_op_decrypt_t op_dec;
+         if (TEST_FFI_OK(botan_pk_op_decrypt_create, (&op_dec, loaded_privkey, "Raw", 0)))
+            {
+            size_t ptext_len;
+            TEST_FFI_OK(botan_pk_op_decrypt_output_length, (op_dec, ciphertext.size(), &ptext_len));
+            decryption.resize(ptext_len);
+            TEST_FFI_OK(botan_pk_op_decrypt, (op_dec, decryption.data(), &ptext_len, ciphertext.data(), ciphertext.size()));
+            decryption.resize(ptext_len);
+            TEST_FFI_OK(botan_pk_op_decrypt_destroy, (op_dec));
+            }
+
+         result.test_eq("decryption worked", decryption, plaintext);
+
+         TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+         TEST_FFI_OK(botan_pubkey_destroy, (pub));
+         TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+         TEST_FFI_OK(botan_privkey_destroy, (priv));
+         }
+
+      Test::Result ffi_test_elgamal(botan_rng_t rng)
+         {
+         Test::Result result("FFI ElGamal");
+
+         botan_privkey_t priv;
+
+         if(TEST_FFI_OK(botan_privkey_create, (&priv, "ElGamal", nullptr, rng)))
+            {
+               do_elgamal_test(priv, rng, result);
+            }
+
+         if(TEST_FFI_OK(botan_privkey_create_elgamal, (&priv, rng, 1024, 160)))
+            {
+               do_elgamal_test(priv, rng, result);
+            }
+
+         return result;
+         }
+
+
+      Test::Result ffi_test_dh(botan_rng_t rng)
+         {
+         Test::Result result("FFI DH");
+
+            botan_mp_t private_x, public_g, public_p, public_y;
+
+            botan_privkey_t loaded_privkey1;
+            botan_pubkey_t loaded_pubkey1;
+
+            botan_mp_init(&private_x);
+
+            botan_mp_init(&public_g);
+            botan_mp_init(&public_p);
+            botan_mp_init(&public_y);
+
+            botan_privkey_t priv1;
+            REQUIRE_FFI_OK(botan_privkey_create_dh, (&priv1, rng, "modp/ietf/2048"));
+
+            botan_privkey_t priv2;
+            REQUIRE_FFI_OK(botan_privkey_create_dh, (&priv2, rng, "modp/ietf/2048"));
+
+            botan_pubkey_t pub1;
+            REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub1, priv1));
+
+            botan_pubkey_t pub2;
+            REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2));
+
+            // Reload key-pair1 in order to test functions for key loading
+            TEST_FFI_OK(botan_privkey_get_field, (private_x, priv1, "x"));
+            TEST_FFI_OK(botan_pubkey_get_field, (public_g, pub1, "g"));
+            TEST_FFI_OK(botan_pubkey_get_field, (public_p, pub1, "p"));
+            TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub1, "y"));
+
+            TEST_FFI_OK(botan_privkey_load_dh, (&loaded_privkey1, public_p, public_g, private_x));
+
+            TEST_FFI_OK(botan_pubkey_load_dh, (&loaded_pubkey1, public_p, public_g, public_y));
+
+            TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey1, rng, 0));
+            TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0));
+
+            botan_mp_t loaded_public_g, loaded_public_p, loaded_public_y;
+            botan_mp_init(&loaded_public_g);
+            botan_mp_init(&loaded_public_p);
+            botan_mp_init(&loaded_public_y);
+
+            TEST_FFI_OK(botan_pubkey_get_field, (loaded_public_g, loaded_pubkey1, "g"));
+            TEST_FFI_OK(botan_pubkey_get_field, (loaded_public_p, loaded_pubkey1, "p"));
+            TEST_FFI_OK(botan_pubkey_get_field, (loaded_public_y, loaded_pubkey1, "y"));
+
+            int cmp;
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_g, public_g));
+            result.confirm("bigint_mp_cmp(g, g)", cmp == 0);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_p, public_p));
+            result.confirm("bigint_mp_cmp(p, p)", cmp == 0);
+
+            TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_y, public_y));
+            result.confirm("bigint_mp_cmp(y, y)", cmp == 0);
+
+            botan_pk_op_ka_t ka1;
+            REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "Raw", 0));
+            botan_pk_op_ka_t ka2;
+            REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, "Raw", 0));
+
+            size_t pubkey1_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pk_op_key_agreement_export_public, (priv1, nullptr, &pubkey1_len));
+            std::vector<uint8_t> pubkey1(pubkey1_len);
+            REQUIRE_FFI_OK(botan_pk_op_key_agreement_export_public, (priv1, pubkey1.data(), &pubkey1_len));
+            size_t pubkey2_len = 0;
+            TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pk_op_key_agreement_export_public, (priv2, nullptr, &pubkey2_len));
+            std::vector<uint8_t> pubkey2(pubkey2_len);
+            REQUIRE_FFI_OK(botan_pk_op_key_agreement_export_public, (priv2, pubkey2.data(), &pubkey2_len));
+
+            std::vector<uint8_t> salt(32);
+
+            TEST_FFI_OK(botan_rng_get, (rng, salt.data(), salt.size()));
+
+            const size_t shared_key_len = 256;
+
+            std::vector<uint8_t> key1(shared_key_len);
+            size_t key1_len = key1.size();
+
+            TEST_FFI_OK(botan_pk_op_key_agreement, (ka1, key1.data(), &key1_len,
+                                                    pubkey2.data(), pubkey2.size(),
+                                                    salt.data(), salt.size()));
+
+            std::vector<uint8_t> key2(shared_key_len);
+            size_t key2_len = key2.size();
+
+            TEST_FFI_OK(botan_pk_op_key_agreement, (ka2, key2.data(), &key2_len,
+                                                    pubkey1.data(), pubkey1.size(),
+                                                    salt.data(), salt.size()));
+
+            result.test_eq("shared DH key", key1, key2);
+
+            TEST_FFI_OK(botan_mp_destroy, (private_x));
+            TEST_FFI_OK(botan_mp_destroy, (public_p));
+            TEST_FFI_OK(botan_mp_destroy, (public_g));
+            TEST_FFI_OK(botan_mp_destroy, (public_y));
+
+            TEST_FFI_OK(botan_mp_destroy, (loaded_public_p));
+            TEST_FFI_OK(botan_mp_destroy, (loaded_public_g));
+            TEST_FFI_OK(botan_mp_destroy, (loaded_public_y));
+
+            TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka1));
+            TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka2));
+            TEST_FFI_OK(botan_privkey_destroy, (priv1));
+            TEST_FFI_OK(botan_privkey_destroy, (priv2));
+            TEST_FFI_OK(botan_pubkey_destroy, (pub1));
+            TEST_FFI_OK(botan_pubkey_destroy, (pub2));
+            TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey1));
+            TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1));
+
+         return result;
+         }
+
+
+   };
+
+BOTAN_REGISTER_TEST("ffi", FFI_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_filters.cpp b/third_party/botan/src/tests/test_filters.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_filters.cpp
@@ -0,0 +1,792 @@
+/*
+* (C) 2016 Daniel Neus
+*     2016,2017 Jack Lloyd
+*     2017 René Korthaus
+*     2017 Philippe Lieser
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_FILTERS)
+   #include <botan/secqueue.h>
+   #include <botan/pipe.h>
+   #include <botan/filters.h>
+   #include <botan/data_snk.h>
+   #include <botan/cipher_filter.h>
+#endif
+
+#if defined(BOTAN_HAS_PIPE_UNIXFD_IO)
+   #include <botan/fd_unix.h>
+   #include <unistd.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+   #include <fstream>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_FILTERS)
+
+class Filter_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_secqueue());
+         results.push_back(test_data_src_sink());
+         results.push_back(test_data_src_sink_flush());
+         results.push_back(test_pipe_io());
+         results.push_back(test_pipe_fd_io());
+         results.push_back(test_pipe_errors());
+         results.push_back(test_pipe_hash());
+         results.push_back(test_pipe_mac());
+         results.push_back(test_pipe_stream());
+         results.push_back(test_pipe_cbc());
+         results.push_back(test_pipe_cfb());
+         results.push_back(test_pipe_compress());
+         results.push_back(test_pipe_compress_bzip2());
+         results.push_back(test_pipe_codec());
+         results.push_back(test_fork());
+         results.push_back(test_chain());
+         results.push_back(test_threaded_fork());
+
+
+         return results;
+         }
+
+   private:
+      Test::Result test_secqueue()
+         {
+         Test::Result result("SecureQueue");
+
+         try
+            {
+            Botan::SecureQueue queue_a;
+            result.test_eq("queue not attachable", queue_a.attachable(), false);
+
+            std::vector<uint8_t> test_data = {0x24, 0xB2, 0xBF, 0xC2, 0xE6, 0xD4, 0x7E, 0x04, 0x67, 0xB3};
+            queue_a.write(test_data.data(), test_data.size());
+
+            result.test_eq("size of SecureQueue is correct", queue_a.size(), test_data.size());
+            result.test_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0);
+
+            uint8_t b;
+            result.test_eq("check_available", queue_a.check_available(1), true);
+            result.test_eq("check_available", queue_a.check_available(50), false);
+            size_t bytes_read = queue_a.read_byte(b);
+            result.test_eq("1 byte read", bytes_read, 1);
+
+            Botan::secure_vector<uint8_t> produced(b);
+            Botan::secure_vector<uint8_t> expected(test_data.at(0));
+            result.test_eq("byte read is correct", produced, expected);
+
+            result.test_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1);
+
+            Botan::SecureQueue queue_b;
+            queue_a = queue_b;
+            result.test_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("SecureQueue", e.what());
+            }
+
+         return result;
+         }
+
+      Test::Result test_data_src_sink()
+         {
+         Test::Result result("DataSink");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+         std::ostringstream oss;
+
+         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(oss));
+
+         Botan::DataSource_Memory input_mem("65666768");
+         pipe.process_msg(input_mem);
+
+         result.test_eq("output string", oss.str(), "efgh");
+
+         Botan::DataSource_Memory input_mem2("41414141");
+         pipe.process_msg(input_mem2);
+
+         result.test_eq("output string", oss.str(), "efghAAAA");
+
+         std::istringstream iss("4343");
+         Botan::DataSource_Stream input_strm(iss);
+         pipe.process_msg(input_strm);
+
+         result.test_eq("output string", oss.str(), "efghAAAACC");
+#endif
+         return result;
+         }
+
+      Test::Result test_data_src_sink_flush()
+         {
+         Test::Result result("DataSinkFlush");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+         const std::string tmp_name = Test::temp_file_name("botan_test_data_src_sink_flush.tmp");
+         if(tmp_name.empty())
+            {
+            result.test_failure("Failed to create temporary file");
+            return result;
+            }
+
+         std::ofstream outfile(tmp_name);
+
+         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(outfile));
+
+         Botan::DataSource_Memory input_mem("65666768");
+         pipe.process_msg(input_mem);
+
+         std::ifstream outfile_read(tmp_name);
+         std::stringstream ss;
+         ss << outfile_read.rdbuf();
+         std::string foo = ss.str();
+
+         result.test_eq("output string", ss.str(), "efgh");
+
+         // ensure files are closed
+         outfile.close();
+         outfile_read.close();
+
+         if(std::remove(tmp_name.c_str()) != 0)
+            {
+            result.test_failure("Failed to remove temporary file at conclusion of test");
+            }
+#endif
+         return result;
+         }
+
+      Test::Result test_pipe_io()
+         {
+         Test::Result result("Pipe I/O operators");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+         Botan::Pipe pipe(new Botan::Hex_Encoder);
+
+         pipe.process_msg("ABCD");
+
+         std::ostringstream oss;
+         oss << pipe;
+         result.test_eq("output string", oss.str(), "41424344");
+
+         std::istringstream iss("AAAA");
+         pipe.start_msg();
+         iss >> pipe;
+         pipe.end_msg();
+
+         pipe.set_default_msg(1);
+         oss << pipe;
+         result.test_eq("output string2", oss.str(), "4142434441414141");
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_errors()
+         {
+         Test::Result result("Pipe");
+
+         Botan::Pipe pipe;
+
+         pipe.append(nullptr); // ignored
+         pipe.append_filter(nullptr); // ignored
+         pipe.prepend(nullptr); // ignored
+         pipe.prepend_filter(nullptr); // ignored
+         pipe.pop(); // empty pipe, so ignored
+
+         std::unique_ptr<Botan::Filter> queue_filter(new Botan::SecureQueue);
+
+         // can't explicitly insert a queue into the pipe because they are implicit
+         result.test_throws("pipe error",
+                            "Pipe::append: SecureQueue cannot be used",
+                            [&]() { pipe.append(queue_filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Pipe::prepend: SecureQueue cannot be used",
+                            [&]() { pipe.prepend(queue_filter.get()); });
+
+         pipe.append_filter(new Botan::BitBucket); // succeeds
+         pipe.pop();
+
+         pipe.prepend_filter(new Botan::BitBucket); // succeeds
+         pipe.pop();
+
+         pipe.start_msg();
+
+         std::unique_ptr<Botan::Filter> filter(new Botan::BitBucket);
+
+         // now inside a message, cannot modify pipe structure
+
+         result.test_throws("pipe error",
+                            "Cannot call Pipe::append_filter after start_msg",
+                            [&]() { pipe.append_filter(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Cannot call Pipe::prepend_filter after start_msg",
+                            [&]() { pipe.prepend_filter(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Cannot append to a Pipe while it is processing",
+                            [&]() { pipe.append(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Cannot prepend to a Pipe while it is processing",
+                            [&]() { pipe.prepend(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Cannot pop off a Pipe while it is processing",
+                            [&]() { pipe.pop(); });
+
+         pipe.end_msg();
+
+         result.test_throws("pipe error",
+                            "Cannot call Pipe::append_filter after start_msg",
+                            [&]() { pipe.append_filter(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Cannot call Pipe::prepend_filter after start_msg",
+                            [&]() { pipe.prepend_filter(filter.get()); });
+
+         result.test_throws("pipe error",
+                            "Pipe::read: Invalid message number 100",
+                            [&]() { uint8_t b; size_t got = pipe.read(&b, 1, 100); BOTAN_UNUSED(got); });
+
+         pipe.append(nullptr); // ignored
+         pipe.prepend(nullptr); // ignored
+         pipe.pop(); // empty pipe, so ignored
+
+         return result;
+         }
+
+      Test::Result test_pipe_mac()
+         {
+         Test::Result result("Pipe");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_SHA2_32)
+         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+         Botan::Keyed_Filter* mac_filter = new Botan::MAC_Filter("HMAC(SHA-256)", key, 12);
+
+         mac_filter->set_iv(Botan::InitializationVector()); // ignored
+
+         result.test_throws("Keyed_Filter::set_iv throws if not implemented",
+                            "IV length 1 is invalid for HMAC(SHA-256)",
+                            [mac_filter]() { mac_filter->set_iv(Botan::InitializationVector("AA")); });
+
+         Botan::Pipe pipe(mac_filter,
+                          new Botan::Base64_Encoder);
+
+         pipe.process_msg("Hi");
+         pipe.process_msg("Bye");
+         pipe.process_msg("Hi");
+
+         result.test_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ");
+         result.test_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y");
+         result.test_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ");
+#endif
+         return result;
+         }
+
+      Test::Result test_pipe_hash()
+         {
+         Test::Result result("Pipe");
+
+#if defined(BOTAN_HAS_SHA2_32)
+         // unrelated test of popping a chain
+         Botan::Pipe pipe(new Botan::Chain(new Botan::Hash_Filter("SHA-224"), new Botan::Hash_Filter("SHA-224")));
+         pipe.pop();
+
+         pipe.append(new Botan::Hash_Filter("SHA-256"));
+
+         result.test_eq("Message count", pipe.message_count(), 0);
+
+         pipe.start_msg();
+         uint8_t inb = 0x41;
+         pipe.write(&inb, 1);
+         pipe.write(std::vector<uint8_t>(6, 0x41));
+         pipe.write(inb);
+         pipe.end_msg();
+
+         result.test_eq("Message count", pipe.message_count(), 1);
+         result.test_eq("Message size", pipe.remaining(), 32);
+
+         std::vector<uint8_t> out(32), last16(16);
+
+         result.test_eq("Bytes read", pipe.get_bytes_read(0), 0);
+         result.test_eq("More to read", pipe.end_of_data(), false);
+         result.test_eq("Expected read count", pipe.read(&out[0], 5), 5);
+         result.test_eq("Bytes read", pipe.get_bytes_read(0), 5);
+         result.test_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16);
+         result.test_eq("Expected read count", pipe.read(&out[5], 17), 17);
+         result.test_eq("Bytes read", pipe.get_bytes_read(0), 22);
+         result.test_eq("Remaining", pipe.remaining(), 10);
+         result.test_eq("Remaining", pipe.remaining(), 10);
+         result.test_eq("Expected read count", pipe.read(&out[22], 12), 10);
+         result.test_eq("Expected read count", pipe.read(&out[0], 1), 0); // no more output
+         result.test_eq("Bytes read", pipe.get_bytes_read(0), 32);
+         result.test_eq("No more to read", pipe.end_of_data(), true);
+
+         result.test_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385");
+         result.test_eq("Expected last16", last16, "D1D575819A8F55CC689510285E212385");
+
+         pipe.reset();
+
+#if defined(BOTAN_HAS_CRC32)
+         pipe.prepend(new Botan::Hash_Filter("CRC32"));
+         pipe.append(new Botan::Hash_Filter("CRC32"));
+         pipe.process_msg(std::vector<uint8_t>(1024, 0));
+         result.test_eq("Expected CRC32d", pipe.read_all(1), "99841F60");
+#endif
+#endif
+         return result;
+         }
+
+      Test::Result test_pipe_cfb()
+         {
+         Test::Result result("Pipe CFB");
+
+#if defined(BOTAN_HAS_BLOWFISH) && defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_CODEC_FILTERS)
+
+         // Generated with Botan 1.10
+
+         const Botan::InitializationVector iv("AABBCCDDEEFF0123");
+         const Botan::SymmetricKey key("AABBCCDDEEFF0123");
+
+         const uint8_t msg_bits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+         const std::string cfb_expected[] = {
+            "A4",
+            "BEA4",
+            "06AD98",
+            "E4AFC5AC",
+            "A9B531559C",
+            "38B60DA66445",
+            "194F5E93199839",
+            "093B6381D2E5D806",
+            "B44FA624226EECF027",
+            "80B8DC3332A835AC11A8",
+            "2C0E910A1E5C38344CC5BB",
+            "3CB6180AE2E189342F681023",
+            "DE0F4B10C7D9CADDB5A9078199",
+            "FAE18B0ED873F234CCD6E1555B2D",
+            "7195FFE735B0A95065BA244C77A11F",
+         };
+
+         Botan::Keyed_Filter* cfb_enc = Botan::get_cipher("Blowfish/CFB", key, iv, Botan::ENCRYPTION);
+         Botan::Pipe enc_pipe(cfb_enc, new Botan::Hex_Encoder);
+
+         Botan::Keyed_Filter* cfb_dec = Botan::get_cipher("Blowfish/CFB", key, Botan::DECRYPTION);
+         cfb_dec->set_iv(iv);
+         Botan::Pipe dec_pipe(new Botan::Hex_Decoder, cfb_dec, new Botan::Hex_Encoder);
+
+         for(size_t i = 1; i != sizeof(msg_bits); ++i)
+            {
+            enc_pipe.start_msg();
+            enc_pipe.write(msg_bits, i);
+            enc_pipe.end_msg();
+
+            dec_pipe.process_msg(cfb_expected[i-1]);
+            }
+
+         result.test_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1);
+         result.test_eq("dec pipe msg count", dec_pipe.message_count(), sizeof(msg_bits) - 1);
+
+         for(size_t i = 0; i != enc_pipe.message_count(); ++i)
+            {
+            result.test_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]);
+            }
+
+         for(size_t i = 0; i != dec_pipe.message_count(); ++i)
+            {
+            result.test_eq("decrypt", dec_pipe.read_all_as_string(i),
+                           Botan::hex_encode(msg_bits, i+1));
+            }
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_cbc()
+         {
+         Test::Result result("Pipe CBC");
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_CIPHER_MODE_PADDING)
+         Botan::Cipher_Mode_Filter* cipher =
+            new Botan::Cipher_Mode_Filter(Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::ENCRYPTION));
+
+         result.test_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7");
+
+         result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(16), true);
+         result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(17), false);
+
+         result.test_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16);
+         result.test_eq("Cipher key length min", cipher->key_spec().minimum_keylength(), 16);
+
+         // takes ownership of cipher
+         Botan::Pipe pipe(cipher);
+
+         cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+
+         pipe.process_msg("Don't use plain CBC mode");
+
+         result.test_eq("Message count", pipe.message_count(), 1);
+         result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
+         auto ciphertext = pipe.read_all();
+         result.test_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size());
+
+         result.test_eq("Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB 6836159C36781246A1ADF50982757F4B");
+
+         pipe.process_msg("IV carryover");
+         auto ciphertext2 = pipe.read_all(1);
+         pipe.process_msg("IV carryover2");
+         auto ciphertext3 = pipe.read_all(2);
+
+         // These values tested against PyCrypto
+         result.test_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2");
+         result.test_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25");
+
+         Botan::Cipher_Mode_Filter* dec_cipher =
+            new Botan::Cipher_Mode_Filter(Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::DECRYPTION));
+         pipe.append(dec_cipher);
+         dec_cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+         dec_cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
+
+         // reset the IV on the encryption filter
+         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
+
+         const std::vector<uint8_t> zeros_in(1024);
+         Botan::DataSource_Memory src(zeros_in);
+         pipe.start_msg();
+         pipe.write(src);
+         pipe.end_msg();
+
+         pipe.set_default_msg(3);
+         result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
+         Botan::secure_vector<uint8_t> zeros_out = pipe.read_all();
+         result.test_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size());
+
+         result.test_eq("Cipher roundtrip", zeros_in, zeros_out);
+#endif
+         return result;
+         }
+
+      Test::Result test_pipe_compress()
+         {
+         Test::Result result("Pipe compress zlib");
+
+#if defined(BOTAN_HAS_ZLIB)
+
+         std::unique_ptr<Botan::Compression_Filter> comp_f(new Botan::Compression_Filter("zlib", 9));
+
+         result.test_eq("Compressor filter name", comp_f->name(), "Zlib_Compression");
+         Botan::Pipe pipe(comp_f.release());
+
+         const std::string input_str = "Hello there HELLO there I said is this thing on?";
+
+         pipe.start_msg();
+         pipe.write(input_str);
+         pipe.end_msg();
+
+         auto compr = pipe.read_all(0);
+         // Can't do equality check on compression because output may differ
+         result.test_lt("Compressed is shorter", compr.size(), input_str.size());
+
+         std::unique_ptr<Botan::Decompression_Filter> decomp_f(new Botan::Decompression_Filter("zlib"));
+         result.test_eq("Decompressor name", decomp_f->name(), "Zlib_Decompression");
+         pipe.append(decomp_f.release());
+         pipe.pop(); // remove compressor
+
+         pipe.process_msg(compr);
+
+         std::string decomp = pipe.read_all_as_string(1);
+         result.test_eq("Decompressed ok", decomp, input_str);
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_compress_bzip2()
+         {
+         Test::Result result("Pipe compress bzip2");
+
+#if defined(BOTAN_HAS_BZIP2)
+
+         std::unique_ptr<Botan::Compression_Filter> comp_f(new Botan::Compression_Filter("bzip2", 9));
+
+         result.test_eq("Compressor filter name", comp_f->name(), "Bzip2_Compression");
+         Botan::Pipe pipe(comp_f.release());
+
+         const std::string input_str = "foo\n";
+
+         pipe.start_msg();
+         pipe.write(input_str);
+         pipe.end_msg();
+
+         auto compr = pipe.read_all(0);
+         // Here the output is actually longer than the input as input is so short
+
+         std::unique_ptr<Botan::Decompression_Filter> decomp_f(new Botan::Decompression_Filter("bzip2"));
+         result.test_eq("Decompressor name", decomp_f->name(), "Bzip2_Decompression");
+         pipe.append(decomp_f.release());
+         pipe.pop(); // remove compressor
+
+         pipe.process_msg(compr);
+
+         std::string decomp = pipe.read_all_as_string(1);
+         result.test_eq("Decompressed ok", decomp, input_str);
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_codec()
+         {
+         Test::Result result("Pipe");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+         Botan::Pipe pipe(new Botan::Base64_Encoder);
+
+         result.test_eq("Message count", pipe.message_count(), 0);
+
+         pipe.process_msg("ABCDX");
+
+         result.test_eq("Message count", pipe.message_count(), 1);
+         result.test_eq("Message size", pipe.remaining(), 8);
+
+         std::string output = pipe.read_all_as_string(0);
+         result.test_eq("Message size", pipe.remaining(0), 0);
+         result.test_eq("Output round tripped", output, "QUJDRFg=");
+
+         pipe.append(new Botan::Base64_Decoder);
+         pipe.process_msg("FOOBAZ");
+
+         result.test_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ");
+
+         pipe.pop();
+         pipe.pop();
+
+         // Pipe is empty of filters, should still pass through
+         pipe.process_msg("surprise plaintext");
+
+         pipe.set_default_msg(2);
+         result.test_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext");
+
+         pipe.append(new Botan::Hex_Decoder);
+
+         pipe.process_msg("F331F00D");
+         Botan::secure_vector<uint8_t> bin = pipe.read_all(3);
+         result.test_eq("hex decoded", bin, "F331F00D");
+
+         pipe.append(new Botan::Hex_Encoder);
+         pipe.process_msg("F331F00D");
+         result.test_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D");
+
+         // Now tests with line wrapping enabled
+
+         pipe.reset();
+         pipe.append(new Botan::Hex_Decoder);
+         pipe.append(new Botan::Base64_Encoder(/*break_lines=*/true,
+                     /*line_length=*/4,
+                     /*trailing_newline=*/true));
+
+         pipe.process_msg("6dab1eeb8a2eb69bad");
+         result.test_eq("base64 with linebreaks and trailing newline", pipe.read_all_as_string(5), "base\n64ou\ntput\n\n");
+
+         pipe.reset();
+         pipe.append(new Botan::Hex_Decoder);
+         pipe.append(new Botan::Base64_Encoder(true, 5, false));
+         pipe.process_msg("6dab1eeb8a2eb69bad");
+         result.test_eq("base64 with linebreaks", pipe.read_all_as_string(6), "base6\n4outp\nut\n");
+
+         pipe.reset();
+         pipe.append(new Botan::Hex_Encoder(true, 13, Botan::Hex_Encoder::Uppercase));
+         pipe.process_msg("hex encoding this string");
+         result.test_eq("hex uppercase with linebreaks", pipe.read_all_as_string(7),
+                        "68657820656E6\n36F64696E6720\n7468697320737\n472696E67\n");
+
+         pipe.reset();
+         pipe.append(new Botan::Hex_Encoder(true, 16, Botan::Hex_Encoder::Lowercase));
+         pipe.process_msg("hex encoding this string");
+         result.test_eq("hex lowercase with linebreaks", pipe.read_all_as_string(8),
+                        "68657820656e636f\n64696e6720746869\n7320737472696e67\n");
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_stream()
+         {
+         Test::Result result("Pipe CTR");
+
+#if defined(BOTAN_HAS_CTR_BE) && defined(BOTAN_HAS_AES)
+         Botan::Keyed_Filter* aes = nullptr;
+         const Botan::SymmetricKey some_other_key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
+         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+         const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+         Botan::Pipe pipe(aes = new Botan::StreamCipher_Filter("CTR-BE(AES-128)", some_other_key));
+
+         aes->set_key(key);
+         aes->set_iv(iv);
+
+         pipe.process_msg("ABCDEF");
+
+         result.test_eq("Message count", pipe.message_count(), 1);
+         result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
+
+         pipe.process_msg("ABCDEF");
+         result.test_eq("Ciphertext", pipe.read_all(1), "8E72F1153514");
+#endif
+         return result;
+         }
+
+      Test::Result test_fork()
+         {
+         Test::Result result("Filter Fork");
+
+#if defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
+         Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"),
+                                          new Botan::Hash_Filter("SHA-512-256")));
+
+         result.test_eq("Message count", pipe.message_count(), 0);
+         pipe.process_msg("OMG");
+         result.test_eq("Message count", pipe.message_count(), 2);
+
+         // Test reading out of order
+         result.test_eq("Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0");
+         result.test_eq("Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
+#endif
+         return result;
+         }
+
+      Test::Result test_chain()
+         {
+         Test::Result result("Filter Chain");
+
+#if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
+
+         Botan::Filter* filters[2] = {
+            new Botan::Hash_Filter("SHA-256"),
+            new Botan::Hex_Encoder
+         };
+
+         std::unique_ptr<Botan::Chain> chain(new Botan::Chain(filters, 2));
+
+         result.test_eq("Chain has a name", chain->name(), "Chain");
+
+         std::unique_ptr<Botan::Fork> fork(
+            new Botan::Fork(
+               chain.release(),
+               new Botan::Chain(new Botan::Hash_Filter("SHA-512-256", 19), new Botan::Hex_Encoder)
+            ));
+
+         result.test_eq("Fork has a name", fork->name(), "Fork");
+         Botan::Pipe pipe(fork.release());
+
+         result.test_eq("Message count", pipe.message_count(), 0);
+         pipe.process_msg("OMG");
+         result.test_eq("Message count", pipe.message_count(), 2);
+
+         result.test_eq("Hash 1", pipe.read_all_as_string(0),
+                        "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
+         result.test_eq("Hash 2", pipe.read_all_as_string(1),
+                        "610480FFA82F24F6926544B976FE387878E3D9");
+#endif
+
+         return result;
+         }
+
+      Test::Result test_pipe_fd_io()
+         {
+         Test::Result result("Pipe file descriptor IO");
+
+#if defined(BOTAN_HAS_PIPE_UNIXFD_IO) && defined(BOTAN_HAS_CODEC_FILTERS)
+         int fd[2];
+         if(::pipe(fd) != 0)
+            return result; // pipe unavailable?
+
+         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
+         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
+
+         hex_enc.process_msg("hi chappy");
+         fd[1] << hex_enc;
+         ::close(fd[1]);
+
+         hex_dec.start_msg();
+         fd[0] >> hex_dec;
+         hex_dec.end_msg();
+         ::close(fd[0]);
+
+         std::string dec = hex_dec.read_all_as_string();
+
+         result.test_eq("IO through Unix pipe works", dec, "hi chappy");
+#endif
+
+         return result;
+         }
+
+      Test::Result test_threaded_fork()
+         {
+         Test::Result result("Threaded_Fork");
+
+#if defined(BOTAN_HAS_THREAD_UTILS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32)
+         Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, new Botan::Base64_Encoder));
+
+         result.test_eq("Message count", pipe.message_count(), 0);
+         pipe.process_msg("woo");
+         result.test_eq("Message count", pipe.message_count(), 2);
+
+         // Test reading out of order
+         result.test_eq("Hash 2", pipe.read_all_as_string(1), "d29v");
+         result.test_eq("Hash 1", pipe.read_all_as_string(0), "776F6F");
+
+         pipe.reset();
+
+         const size_t filter_count = 5;
+         Botan::Filter* filters[filter_count];
+         for(size_t i = 0; i != filter_count; ++i)
+            {
+            filters[i] = new Botan::Hash_Filter("SHA-256");
+            }
+
+         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
+
+         result.test_eq("Message count before start_msg", pipe.message_count(), 2);
+
+         pipe.start_msg();
+         for(size_t i = 0; i != 919; ++i)
+            {
+            std::vector<uint8_t> input(i + 5, static_cast<uint8_t>(i));
+            pipe.write(input);
+            }
+         pipe.end_msg();
+
+         result.test_eq("Message count after end_msg", pipe.message_count(), 2 + filter_count);
+         for(size_t i = 0; i != filter_count; ++i)
+            {
+            result.test_eq("Output " + std::to_string(i), pipe.read_all(2 + i),
+                           "327AD8055223F5926693D8BEA40F7B35BDEEB535647DFB93F464E40EA01939A9");
+            }
+#endif
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("filter", Filter_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_fpe.cpp b/third_party/botan/src/tests/test_fpe.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_fpe.cpp
@@ -0,0 +1,49 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_FPE_FE1)
+   #include <botan/fpe_fe1.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_FPE_FE1)
+
+class FPE_FE1_Tests final : public Text_Based_Test
+   {
+   public:
+      FPE_FE1_Tests() : Text_Based_Test("fpe_fe1.vec", "Mod,In,Out,Key,Tweak") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const Botan::BigInt modulus  = vars.get_req_bn("Mod");
+         const Botan::BigInt input    = vars.get_req_bn("In");
+         const Botan::BigInt expected = vars.get_req_bn("Out");
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> tweak    = vars.get_req_bin("Tweak");
+
+         Test::Result result("FPE_FE1");
+
+         const Botan::BigInt got = Botan::FPE::fe1_encrypt(modulus, input, key, tweak);
+
+         result.test_eq("ciphertext", got, expected);
+
+         const Botan::BigInt decry = Botan::FPE::fe1_decrypt(modulus, got, key, tweak);
+
+         result.test_eq("decrypted", decry, input);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("fpe_fe1", FPE_FE1_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_gf2m.cpp b/third_party/botan/src/tests/test_gf2m.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_gf2m.cpp
@@ -0,0 +1,75 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_MCELIECE)
+   #include <botan/gf2m_small_m.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_MCELIECE)
+
+class GF2m_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_gf_overflow());
+
+         return results;
+         }
+
+   private:
+      Test::Result test_gf_overflow()
+         {
+         Test::Result result("GF2m");
+
+         /*
+         * This is testing one specific case where an overflow
+         * occurred. It should be extended to test more of the interface.
+         */
+         for(size_t degree = 2; degree <= 15; ++degree)
+            {
+            Botan::GF2m_Field field(degree);
+
+            for(size_t i = 0; i <= field.gf_ord(); ++i)
+               {
+               Botan::gf2m a = static_cast<Botan::gf2m>(i);
+
+               result.test_eq("square vs multiply",
+                              static_cast<size_t>(field.gf_square(a)),
+                              static_cast<size_t>(field.gf_mul(a, a)));
+
+               /*
+               * This sequence is from the start of gf2m_decomp_rootfind_state::calc_Fxj_j_neq_0
+               */
+                  {
+                  const Botan::gf2m jl_gray = field.gf_l_from_n(a);
+                  const Botan::gf2m xl_j_tt_5 = field.gf_square_rr(jl_gray);
+                  const Botan::gf2m xl_gray_tt_3 = field.gf_mul_rrr(xl_j_tt_5, jl_gray);
+                  Botan::gf2m s = field.gf_mul_nrr(xl_gray_tt_3, field.gf_ord());
+
+                  result.test_gte("Value less than order", field.gf_ord(), s);
+                  }
+               }
+            }
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("gf2m", GF2m_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_gost_3410.cpp b/third_party/botan/src/tests/test_gost_3410.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_gost_3410.cpp
@@ -0,0 +1,121 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+   #include <botan/gost_3410.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+
+class GOST_3410_2001_Verification_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      GOST_3410_2001_Verification_Tests() : PK_Signature_Verification_Test(
+            "GOST 34.10-2001",
+            "pubkey/gost_3410_verify.vec",
+            "P,A,B,Gx,Gy,Oid,Order,Cofactor,Px,Py,Hash,Msg,Signature") {}
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const BigInt p = vars.get_req_bn("P");
+         const BigInt a = vars.get_req_bn("A");
+         const BigInt b = vars.get_req_bn("B");
+         const BigInt Gx = vars.get_req_bn("Gx");
+         const BigInt Gy = vars.get_req_bn("Gy");
+         const BigInt order = vars.get_req_bn("Order");
+         const BigInt cofactor = vars.get_req_bn("Cofactor");
+         const Botan::OID oid(vars.get_req_str("Oid"));
+
+         Botan::EC_Group group(p, a, b, Gx, Gy, order, cofactor, oid);
+
+         const BigInt Px = vars.get_req_bn("Px");
+         const BigInt Py = vars.get_req_bn("Py");
+
+         const Botan::PointGFp public_point = group.point(Px, Py);
+
+         std::unique_ptr<Botan::Public_Key> key(new Botan::GOST_3410_PublicKey(group, public_point));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         const std::string hash = vars.get_req_str("Hash");
+         if(hash == "Raw")
+            return hash;
+         return "EMSA1(" + hash + ")";
+         }
+   };
+
+class GOST_3410_2001_Signature_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      GOST_3410_2001_Signature_Tests() : PK_Signature_Generation_Test(
+            "GOST 34.10-2001",
+            "pubkey/gost_3410_sign.vec",
+            "P,A,B,Gx,Gy,Oid,Order,X,Cofactor,Hash,Nonce,Msg,Signature") {}
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const BigInt p = vars.get_req_bn("P");
+         const BigInt a = vars.get_req_bn("A");
+         const BigInt b = vars.get_req_bn("B");
+         const BigInt Gx = vars.get_req_bn("Gx");
+         const BigInt Gy = vars.get_req_bn("Gy");
+         const BigInt order = vars.get_req_bn("Order");
+         const BigInt cofactor = vars.get_req_bn("Cofactor");
+         const Botan::OID oid(vars.get_req_str("Oid"));
+
+         Botan::EC_Group group(p, a, b, Gx, Gy, order, cofactor, oid);
+
+         const BigInt x = vars.get_req_bn("X");
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::GOST_3410_PrivateKey(Test::rng(), group, x));
+         return key;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         const std::string hash = vars.get_req_str("Hash");
+         if(hash == "Raw")
+            return hash;
+         return "EMSA1(" + hash + ")";
+         }
+
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+   };
+
+class GOST_3410_2001_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "gost_256A", "secp256r1" };
+         }
+      std::string algo_name() const override
+         {
+         return "GOST-34.10";
+         }
+   };
+
+BOTAN_REGISTER_TEST("gost_3410_verify", GOST_3410_2001_Verification_Tests);
+BOTAN_REGISTER_TEST("gost_3410_sign", GOST_3410_2001_Signature_Tests);
+BOTAN_REGISTER_TEST("gost_3410_keygen", GOST_3410_2001_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_hash.cpp b/third_party/botan/src/tests/test_hash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_hash.cpp
@@ -0,0 +1,332 @@
+/*
+* (C) 2014,2015,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_HASH)
+   #include <botan/hash.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_HASH)
+
+namespace {
+
+class Invalid_Hash_Name_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("Invalid HashFunction names");
+         test_invalid_name(result, "NonExistentHash");
+         test_invalid_name(result, "Blake2b(9)", "Bad output bits size for BLAKE2b");
+         test_invalid_name(result, "Comb4P(MD5,MD5)", "Comb4P: Must use two distinct hashes");
+         test_invalid_name(result, "Comb4P(MD5,SHA-256)", "Comb4P: Incompatible hashes MD5 and SHA-256");
+         test_invalid_name(result, "Tiger(168)", "Tiger: Illegal hash output size: 168");
+         test_invalid_name(result, "Tiger(20,2)", "Tiger: Invalid number of passes: 2");
+         test_invalid_name(result, "Keccak-1600(160)", "Keccak_1600: Invalid output length 160");
+         test_invalid_name(result, "SHA-3(160)", "SHA_3: Invalid output length 160");
+
+         return {result};
+         }
+
+   private:
+      void test_invalid_name(Result& result,
+                             const std::string& name,
+                             const std::string& expected_msg = "") const
+         {
+         try
+            {
+            auto hash = Botan::HashFunction::create_or_throw(name);
+            result.test_failure("Was successfully able to create " + name);
+            }
+         catch(Botan::Invalid_Argument& e)
+            {
+            const std::string msg = e.what();
+            const std::string full_msg = "" + expected_msg;
+            result.test_eq("expected error message", msg, full_msg);
+            }
+         catch(Botan::Lookup_Error& e)
+            {
+            const std::string algo_not_found_msg = "Unavailable Hash " + name;
+            const std::string msg = e.what();
+            result.test_eq("expected error message", msg, algo_not_found_msg);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("some unknown exception", e.what());
+            }
+         catch(...)
+            {
+            result.test_failure("some unknown exception");
+            }
+         }
+   };
+
+BOTAN_REGISTER_TEST("invalid_name_hash", Invalid_Hash_Name_Tests);
+
+class Hash_Function_Tests final : public Text_Based_Test
+   {
+   public:
+      Hash_Function_Tests() : Text_Based_Test("hash", "In,Out") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::HashFunction::providers(algo));
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+
+         Test::Result result(algo);
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(providers.empty())
+            {
+            result.note_missing("hash " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(algo, provider_ask));
+
+            if(!hash)
+               {
+               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            std::unique_ptr<Botan::HashFunction> clone(hash->clone());
+
+            const std::string provider(hash->provider());
+            result.test_is_nonempty("provider", provider);
+            result.test_eq(provider, hash->name(), algo);
+            result.test_eq(provider, hash->name(), clone->name());
+
+            hash->update(input);
+            result.test_eq(provider, "hashing", hash->final(), expected);
+
+            clone->update(input);
+            result.test_eq(provider, "hashing (clone)", clone->final(), expected);
+
+            // Test to make sure clear() resets what we need it to
+            hash->update("some discarded input");
+            hash->clear();
+            hash->update(nullptr, 0); // this should be effectively ignored
+            hash->update(input);
+
+            result.test_eq(provider, "hashing after clear", hash->final(), expected);
+
+            // Test that misaligned inputs work
+
+            if(input.size() > 0)
+               {
+               std::vector<uint8_t> misaligned = input;
+               const size_t current_alignment = reinterpret_cast<uintptr_t>(misaligned.data()) % 16;
+
+               const size_t bytes_to_misalign = 15 - current_alignment;
+
+               for(size_t i = 0; i != bytes_to_misalign; ++i)
+                  misaligned.insert(misaligned.begin(), 0x23);
+
+               hash->update(&misaligned[bytes_to_misalign], input.size());
+               result.test_eq(provider, "hashing misaligned data", hash->final(), expected);
+               }
+
+            if(input.size() > 5)
+               {
+               hash->update(input[0]);
+
+               std::unique_ptr<Botan::HashFunction> fork = hash->copy_state();
+               // verify fork copy doesn't affect original computation
+               fork->update(&input[1], input.size() - 2);
+
+               size_t so_far = 1;
+               while(so_far < input.size())
+                  {
+                  size_t take = Test::rng().next_byte() % (input.size() - so_far);
+
+                  if(input.size() - so_far == 1)
+                     take = 1;
+
+                  hash->update(&input[so_far], take);
+                  so_far += take;
+                  }
+               result.test_eq(provider, "hashing split", hash->final(), expected);
+
+               fork->update(&input[input.size() - 1], 1);
+               result.test_eq(provider, "hashing split", fork->final(), expected);
+               }
+
+            if(hash->hash_block_size() > 0)
+               {
+               // GOST-34.11 uses 32 byte block
+               result.test_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32);
+               }
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("hash", Hash_Function_Tests);
+
+class Hash_NIST_MonteCarlo_Tests final : public Text_Based_Test
+   {
+   public:
+      Hash_NIST_MonteCarlo_Tests() : Text_Based_Test("hash_mc.vec", "Seed,Count,Output") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::HashFunction::providers(algo));
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
+         const size_t count = vars.get_req_sz("Count");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         Test::Result result("NIST Monte Carlo " + algo);
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(providers.empty())
+            {
+            result.note_missing("hash " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(algo, provider_ask));
+
+            if(!hash)
+               {
+               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            std::vector<std::vector<uint8_t>> input;
+            input.push_back(seed);
+            input.push_back(seed);
+            input.push_back(seed);
+
+            std::vector<uint8_t> buf(hash->output_length());
+
+            for(size_t j = 0; j <= count; ++j)
+               {
+               for(size_t i = 3; i != 1003; ++i)
+                  {
+                  hash->update(input[0]);
+                  hash->update(input[1]);
+                  hash->update(input[2]);
+
+                  hash->final(input[0].data());
+                  input[0].swap(input[1]);
+                  input[1].swap(input[2]);
+                  }
+
+               if(j < count)
+                  {
+                  input[0] = input[2];
+                  input[1] = input[2];
+                  }
+               }
+
+            result.test_eq("Output is expected", input[2], expected);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("hash_nist_mc", Hash_NIST_MonteCarlo_Tests);
+
+class Hash_LongRepeat_Tests final : public Text_Based_Test
+   {
+   public:
+      Hash_LongRepeat_Tests() : Text_Based_Test("hash_rep.vec", "Input,TotalLength,Digest") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::HashFunction::providers(algo));
+         }
+
+      // repeating the output several times reduces buffering overhead during processing
+      static std::vector<uint8_t> expand_input(const std::vector<uint8_t>& input, size_t min_len)
+         {
+         std::vector<uint8_t> output;
+         output.reserve(min_len);
+
+         while(output.size() < min_len)
+            output.insert(output.end(), input.begin(), input.end());
+
+         return output;
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> input = expand_input(vars.get_req_bin("Input"), 256);
+         const size_t total_len = vars.get_req_sz("TotalLength");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Digest");
+
+         Test::Result result("Long input " + algo);
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(total_len > 1000000 && Test::run_long_tests() == false)
+            {
+            return result;
+            }
+
+         if(providers.empty())
+            {
+            result.note_missing("hash " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(algo, provider_ask));
+
+            if(!hash)
+               {
+               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            const size_t full_inputs = total_len / input.size();
+            const size_t leftover = total_len % input.size();
+
+            for(size_t i = 0; i != full_inputs; ++i)
+               hash->update(input);
+
+            if(leftover > 0)
+               hash->update(input.data(), leftover);
+
+            std::vector<uint8_t> output(hash->output_length());
+            hash->final(output.data());
+            result.test_eq("Output is expected", output, expected);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("hash_rep", Hash_LongRepeat_Tests);
+
+}
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_hash_id.cpp b/third_party/botan/src/tests/test_hash_id.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_hash_id.cpp
@@ -0,0 +1,93 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_HASH_ID) && defined(BOTAN_HAS_ASN1)
+  #include <botan/hash_id.h>
+  #include <botan/der_enc.h>
+  #include <botan/alg_id.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_HASH_ID) && defined(BOTAN_HAS_ASN1)
+
+class PKCS_HashID_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         const std::vector<std::pair<std::string,size_t>> hash_id_fns = {
+            {"MD5", 16},
+            {"RIPEMD-160", 20},
+            {"SHA-160", 20},
+            {"SHA-224", 28},
+            {"SHA-256", 32},
+            {"SHA-384", 48},
+            {"SHA-512", 64},
+            {"SHA-512-256", 32},
+            {"SHA-3(224)", 28},
+            {"SHA-3(256)", 32},
+            {"SHA-3(384)", 48},
+            {"SHA-3(512)", 64},
+            {"SM3", 32},
+            {"Tiger(24,3)", 24}
+         };
+
+         std::vector<Test::Result> results;
+
+         for(auto hash_info : hash_id_fns)
+            {
+            const std::string hash_fn = hash_info.first;
+            const size_t hash_len = hash_info.second;
+
+            Test::Result result("PKCS hash id for " + hash_fn);
+
+            try
+               {
+               const std::vector<uint8_t> pkcs_id = Botan::pkcs_hash_id(hash_fn);
+
+               const Botan::OID oid = Botan::OID::from_string(hash_fn);
+               const Botan::AlgorithmIdentifier alg(oid, Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+               const std::vector<uint8_t> dummy_hash(hash_len);
+
+               std::vector<uint8_t> bits;
+               Botan::DER_Encoder der(bits);
+               der.start_cons(Botan::SEQUENCE).encode(alg).encode(dummy_hash, Botan::OCTET_STRING).end_cons();
+
+               result.test_eq("Dummy hash is expected size", bits.size() - pkcs_id.size(), dummy_hash.size());
+
+               for(size_t i = pkcs_id.size(); i != bits.size(); ++i)
+                  {
+                  if(bits[i] != 0)
+                     {
+                     result.test_failure("Dummy hash had nonzero value");
+                     break;
+                     }
+                  }
+
+               std::vector<uint8_t> encoded_id(bits.begin(), bits.begin() + pkcs_id.size());
+
+               result.test_eq("Encoded ID matches hardcoded", encoded_id, pkcs_id);
+
+               }
+            catch(Botan::Exception& e)
+               {
+               result.test_failure(e.what());
+               }
+
+            results.push_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs_hash_id", PKCS_HashID_Test);
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_kdf.cpp b/third_party/botan/src/tests/test_kdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_kdf.cpp
@@ -0,0 +1,106 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_KDF_BASE)
+   #include <botan/kdf.h>
+#endif
+
+#if defined(BOTAN_HAS_HKDF)
+   #include <botan/hkdf.h>
+   #include <botan/hash.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_KDF_BASE)
+class KDF_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      KDF_KAT_Tests() : Text_Based_Test("kdf", "Salt,Secret,Label,Output", "IKM,XTS") {}
+
+      Test::Result run_one_test(const std::string& kdf_name, const VarMap& vars) override
+         {
+         Test::Result result(kdf_name);
+
+         std::unique_ptr<Botan::KDF> kdf(Botan::KDF::create(kdf_name));
+
+         if(!kdf)
+            {
+            result.note_missing(kdf_name);
+            return result;
+            }
+
+         const std::vector<uint8_t> salt = vars.get_opt_bin("Salt");
+         const std::vector<uint8_t> secret = vars.get_req_bin("Secret");
+         const std::vector<uint8_t> label = vars.get_opt_bin("Label");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         result.test_eq("name", kdf->name(), kdf_name);
+         result.test_eq("derived key", kdf->derive_key(expected.size(), secret, salt, label), expected);
+
+         // Test that clone works
+         std::unique_ptr<Botan::KDF> clone(kdf->clone());
+         result.confirm("Clone has different pointer", kdf.get() != clone.get());
+         result.test_eq("Clone has same name", kdf->name(), clone->name());
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("kdf", KDF_KAT_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_HKDF)
+class HKDF_Expand_Label_Tests final : public Text_Based_Test
+   {
+   public:
+      HKDF_Expand_Label_Tests() :
+         Text_Based_Test("hkdf_label.vec", "Secret,Label,HashValue,Output") {}
+
+      Test::Result run_one_test(const std::string& hash_name, const VarMap& vars) override
+         {
+         Test::Result result("HKDF-Expand-Label(" + hash_name + ")");
+
+         const std::vector<uint8_t> secret = vars.get_req_bin("Secret");
+         const std::vector<uint8_t> hashval = vars.get_req_bin("HashValue");
+         const std::string label = vars.get_req_str("Label");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         auto hash = Botan::HashFunction::create(hash_name);
+
+         if(!hash)
+            {
+            result.test_note("Skipping test due to missing hash");
+            return result;
+            }
+
+         Botan::secure_vector<uint8_t> output =
+            Botan::hkdf_expand_label(hash_name,
+                                     secret.data(), secret.size(),
+                                     label,
+                                     hashval.data(), hashval.size(),
+                                     expected.size());
+
+         result.test_eq("Output matches", output, expected);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("hkdf_expand_label", HKDF_Expand_Label_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_keywrap.cpp b/third_party/botan/src/tests/test_keywrap.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_keywrap.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+   #include <botan/rfc3394.h>
+#endif
+
+#if defined(BOTAN_HAS_NIST_KEYWRAP)
+   #include <botan/nist_keywrap.h>
+   #include <botan/block_cipher.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+class RFC3394_Keywrap_Tests final : public Text_Based_Test
+   {
+   public:
+      RFC3394_Keywrap_Tests() : Text_Based_Test("keywrap/rfc3394.vec", "Key,KEK,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("RFC3394 keywrap");
+
+         try
+            {
+            const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+            const std::vector<uint8_t> key = vars.get_req_bin("Key");
+            const std::vector<uint8_t> kek = vars.get_req_bin("KEK");
+
+            const Botan::SymmetricKey kek_sym(kek);
+            const Botan::secure_vector<uint8_t> key_l(key.begin(), key.end());
+            const Botan::secure_vector<uint8_t> exp_l(expected.begin(), expected.end());
+
+            result.test_eq("encryption", Botan::rfc3394_keywrap(key_l, kek_sym), expected);
+            result.test_eq("decryption", Botan::rfc3394_keyunwrap(exp_l, kek_sym), key);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("", e.what());
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("rfc3394", RFC3394_Keywrap_Tests);
+#endif
+
+#if defined(BOTAN_HAS_NIST_KEYWRAP) && defined(BOTAN_HAS_AES)
+
+class NIST_Keywrap_Tests final : public Text_Based_Test
+   {
+   public:
+      NIST_Keywrap_Tests() : Text_Based_Test("keywrap/nist_key_wrap.vec", "Input,Key,Output") {}
+
+      Test::Result run_one_test(const std::string& typ, const VarMap& vars) override
+         {
+         Test::Result result("NIST keywrap");
+
+         try
+            {
+            if(typ != "KW" && typ != "KWP")
+               throw Test_Error("Unknown type in NIST key wrap tests");
+
+            const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+            const std::vector<uint8_t> input = vars.get_req_bin("Input");
+            const std::vector<uint8_t> key = vars.get_req_bin("Key");
+
+            std::unique_ptr<Botan::BlockCipher> bc =
+               Botan::BlockCipher::create_or_throw("AES-" + std::to_string(key.size()*8));
+
+            bc->set_key(key);
+
+            std::vector<uint8_t> wrapped;
+
+            if(typ == "KW")
+               {
+               wrapped = nist_key_wrap(input.data(), input.size(), *bc);
+               }
+            else if(typ == "KWP")
+               {
+               wrapped = nist_key_wrap_padded(input.data(), input.size(), *bc);
+               }
+
+            result.test_eq("key wrap", wrapped, expected);
+
+            try
+               {
+               Botan::secure_vector<uint8_t> unwrapped;
+               if(typ == "KW")
+                  {
+                  unwrapped = nist_key_unwrap(expected.data(), expected.size(), *bc);
+                  }
+               else if(typ == "KWP")
+                  {
+                  unwrapped = nist_key_unwrap_padded(expected.data(), expected.size(), *bc);
+                  }
+
+               result.test_eq("key unwrap", unwrapped, input);
+               }
+            catch(Botan::Integrity_Failure& e)
+               {
+               result.test_failure("NIST key unwrap failed with integrity failure", e.what());
+               }
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("", e.what());
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("nist_key_wrap", NIST_Keywrap_Tests);
+
+class NIST_Keywrap_Invalid_Tests final : public Text_Based_Test
+   {
+   public:
+      NIST_Keywrap_Invalid_Tests() : Text_Based_Test("keywrap/nist_key_wrap_invalid.vec", "Key,Input") {}
+
+      Test::Result run_one_test(const std::string& typ, const VarMap& vars) override
+         {
+         Test::Result result("NIST keywrap (invalid inputs)");
+
+         try
+            {
+            if(typ != "KW" && typ != "KWP")
+               throw Test_Error("Unknown type in NIST key wrap tests");
+
+            const std::vector<uint8_t> input = vars.get_req_bin("Input");
+            const std::vector<uint8_t> key = vars.get_req_bin("Key");
+
+            std::unique_ptr<Botan::BlockCipher> bc =
+               Botan::BlockCipher::create_or_throw("AES-" + std::to_string(key.size()*8));
+
+            bc->set_key(key);
+
+            try
+               {
+               Botan::secure_vector<uint8_t> unwrapped;
+               if(typ == "KW")
+                  {
+                  unwrapped = nist_key_unwrap(input.data(), input.size(), *bc);
+                  }
+               else if(typ == "KWP")
+                  {
+                  unwrapped = nist_key_unwrap_padded(input.data(), input.size(), *bc);
+                  }
+
+               result.test_failure("Was able to unwrap invalid keywrap input");
+               }
+            catch(Botan::Integrity_Failure&)
+               {
+               result.test_success("Rejected invalid input");
+               }
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("", e.what());
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("nist_key_wrap_invalid", NIST_Keywrap_Invalid_Tests);
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_mac.cpp b/third_party/botan/src/tests/test_mac.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_mac.cpp
@@ -0,0 +1,158 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+* (C) 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_MAC)
+   #include <botan/mac.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_MAC)
+
+class Message_Auth_Tests final : public Text_Based_Test
+   {
+   public:
+      Message_Auth_Tests() : Text_Based_Test("mac", "Key,In,Out", "IV") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::MessageAuthenticationCode::providers(algo));
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+         const std::vector<uint8_t> iv       = vars.get_opt_bin("IV");
+
+         Test::Result result(algo);
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(providers.empty())
+            {
+            result.note_missing("MAC " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::MessageAuthenticationCode> mac(Botan::MessageAuthenticationCode::create(algo, provider_ask));
+
+            if(!mac)
+               {
+               result.test_failure("MAC " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            const std::string provider(mac->provider());
+
+            result.test_is_nonempty("provider", provider);
+            result.test_eq(provider, mac->name(), algo);
+
+            try
+               {
+               std::vector<uint8_t> buf(128);
+               mac->update(buf.data(), buf.size());
+               result.test_failure("Was able to MAC without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to MAC with no key set fails");
+               }
+
+            mac->set_key(key);
+            mac->start(iv);
+
+            mac->update(input);
+
+            result.test_eq(provider, "correct mac", mac->final(), expected);
+
+            // Test to make sure clear() resets what we need it to
+            mac->set_key(key);
+            mac->start(iv);
+            mac->update("some discarded input");
+            mac->clear();
+
+            // do the same to test verify_mac()
+            mac->set_key(key);
+            mac->start(iv);
+            mac->update(input);
+
+            // Test that clone works and does not affect parent object
+            std::unique_ptr<Botan::MessageAuthenticationCode> clone(mac->clone());
+            result.confirm("Clone has different pointer", mac.get() != clone.get());
+            result.test_eq("Clone has same name", mac->name(), clone->name());
+            clone->set_key(key);
+            clone->start(iv);
+            clone->update(Test::rng().random_vec(32));
+
+            result.test_eq(provider + " verify mac", mac->verify_mac(expected.data(), expected.size()), true);
+
+            if(input.size() > 2)
+               {
+               mac->set_key(key); // Poly1305 requires the re-key
+               mac->start(iv);
+
+               mac->update(input[0]);
+               mac->update(&input[1], input.size() - 2);
+               mac->update(input[input.size() - 1]);
+
+               result.test_eq(provider, "split mac", mac->final(), expected);
+
+               // do the same to test verify_mac()
+               mac->set_key(key);
+               mac->start(iv);
+
+               mac->update(input[ 0 ]);
+               mac->update(&input[ 1 ], input.size() - 2);
+               mac->update(input[ input.size() - 1 ]);
+
+               result.test_eq(provider + " split mac", mac->verify_mac(expected.data(), expected.size()), true);
+               }
+
+            mac->clear();
+
+            try
+               {
+               std::vector<uint8_t> buf(128);
+               mac->update(buf.data(), buf.size());
+               result.test_failure("Was able to MAC without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to MAC with no key set (after clear) fails");
+               }
+
+            try
+               {
+               std::vector<uint8_t> buf(mac->output_length());
+               mac->final(buf.data());
+               result.test_failure("Was able to MAC without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to MAC with no key set (after clear) fails");
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("mac", Message_Auth_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_mceliece.cpp b/third_party/botan/src/tests/test_mceliece.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_mceliece.cpp
@@ -0,0 +1,290 @@
+/*
+* (C) 2014 cryptosource GmbH
+* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_MCELIECE)
+
+   #include <botan/mceliece.h>
+   #include <botan/pubkey.h>
+   #include <botan/loadstor.h>
+   #include <botan/hash.h>
+   #include <botan/hex.h>
+
+   #if defined(BOTAN_HAS_HMAC_DRBG)
+      #include <botan/hmac_drbg.h>
+   #endif
+
+   #if defined(BOTAN_HAS_MCEIES)
+      #include <botan/mceies.h>
+   #endif
+
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_MCELIECE)
+
+#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
+class McEliece_Keygen_Encrypt_Test final : public Text_Based_Test
+   {
+   public:
+      McEliece_Keygen_Encrypt_Test()
+         : Text_Based_Test("pubkey/mce.vec",
+                           "McElieceSeed,KeyN,KeyT,PublicKeyFingerprint,PrivateKeyFingerprint,"
+                           "EncryptPRNGSeed,SharedKey,Ciphertext",
+                           "") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> keygen_seed  = vars.get_req_bin("McElieceSeed");
+         const std::vector<uint8_t> fprint_pub   = vars.get_req_bin("PublicKeyFingerprint");
+         const std::vector<uint8_t> fprint_priv  = vars.get_req_bin("PrivateKeyFingerprint");
+         const std::vector<uint8_t> encrypt_seed = vars.get_req_bin("EncryptPRNGSeed");
+         const std::vector<uint8_t> ciphertext   = vars.get_req_bin("Ciphertext");
+         const std::vector<uint8_t> shared_key   = vars.get_req_bin("SharedKey");
+         const size_t keygen_n = vars.get_req_sz("KeyN");
+         const size_t keygen_t = vars.get_req_sz("KeyT");
+
+         Test::Result result("McEliece keygen");
+         result.start_timer();
+
+         if(Test::run_long_tests() == false && keygen_n > 3072)
+            {
+            result.test_note("Skipping because long");
+            return result;
+            }
+
+         Botan::HMAC_DRBG rng("SHA-384");
+         rng.initialize_with(keygen_seed.data(), keygen_seed.size());
+         Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t);
+
+         result.test_eq("public key fingerprint", hash_bytes(mce_priv.public_key_bits()), fprint_pub);
+         result.test_eq("private key fingerprint", hash_bytes(mce_priv.private_key_bits()), fprint_priv);
+
+         rng.clear();
+         rng.initialize_with(encrypt_seed.data(), encrypt_seed.size());
+
+         try
+            {
+            Botan::PK_KEM_Encryptor kem_enc(mce_priv, Test::rng(), "KDF1(SHA-512)");
+            Botan::PK_KEM_Decryptor kem_dec(mce_priv, Test::rng(), "KDF1(SHA-512)");
+
+            Botan::secure_vector<uint8_t> encap_key, prod_shared_key;
+            kem_enc.encrypt(encap_key, prod_shared_key, 64, rng);
+
+            Botan::secure_vector<uint8_t> dec_shared_key = kem_dec.decrypt(encap_key.data(), encap_key.size(), 64);
+
+            result.test_eq("ciphertext", encap_key, ciphertext);
+            result.test_eq("encrypt shared", prod_shared_key, shared_key);
+            result.test_eq("decrypt shared", dec_shared_key, shared_key);
+            }
+         catch(Botan::Lookup_Error&)
+            {
+            }
+
+         result.end_timer();
+         return result;
+         }
+
+   private:
+      std::vector<uint8_t> hash_bytes(const uint8_t b[], size_t len, const std::string& hash_fn = "SHA-256")
+         {
+         std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_fn));
+         hash->update(b, len);
+         std::vector<uint8_t> r(hash->output_length());
+         hash->final(r.data());
+         return r;
+         }
+
+      template<typename A>
+      std::vector<uint8_t> hash_bytes(const std::vector<uint8_t, A>& v)
+         {
+         return hash_bytes(v.data(), v.size());
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("mce_keygen", McEliece_Keygen_Encrypt_Test);
+#endif
+
+
+#if defined(BOTAN_HAS_SHA2_32)
+
+class McEliece_Tests final : public Test
+   {
+   public:
+
+      std::string fingerprint(const Botan::Private_Key& key, const std::string& hash_algo = "SHA-256")
+         {
+         std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_algo));
+         if(!hash)
+            {
+            throw Test_Error("Hash " + hash_algo + " not available");
+            }
+
+         hash->update(key.private_key_bits());
+         return Botan::hex_encode(hash->final());
+         }
+
+      std::string fingerprint(const Botan::Public_Key& key, const std::string& hash_algo = "SHA-256")
+         {
+         std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create(hash_algo));
+         if(!hash)
+            {
+            throw Test_Error("Hash " + hash_algo + " not available");
+            }
+
+         hash->update(key.public_key_bits());
+         return Botan::hex_encode(hash->final());
+         }
+
+      std::vector<Test::Result> run() override
+         {
+         struct keygen_params { size_t code_length, t_min, t_max; };
+
+         const keygen_params param_sets[] =
+            {
+               { 256, 5, 15 },
+               { 512, 5, 33 },
+               { 1024, 15, 35 },
+               { 2048, 33, 50 },
+               { 6624, 110, 115 }
+            };
+
+         std::vector<Test::Result> results;
+
+         for(size_t i = 0; i < sizeof(param_sets) / sizeof(param_sets[0]); ++i)
+            {
+            if(Test::run_long_tests() == false && param_sets[i].code_length >= 2048)
+               {
+               continue;
+               }
+
+            for(size_t t = param_sets[i].t_min; t <= param_sets[i].t_max; ++t)
+               {
+               Test::Result result("McEliece keygen");
+               result.start_timer();
+
+               Botan::McEliece_PrivateKey sk1(Test::rng(), param_sets[i].code_length, t);
+               const Botan::McEliece_PublicKey& pk1 = sk1;
+
+               const std::vector<uint8_t> pk_enc = pk1.public_key_bits();
+               const Botan::secure_vector<uint8_t> sk_enc = sk1.private_key_bits();
+
+               Botan::McEliece_PublicKey pk(pk_enc);
+               Botan::McEliece_PrivateKey sk(sk_enc);
+
+               result.test_eq("decoded public key equals original", fingerprint(pk1), fingerprint(pk));
+               result.test_eq("decoded private key equals original", fingerprint(sk1), fingerprint(sk));
+               result.test_eq("key validation passes", sk.check_key(Test::rng(), false), true);
+               result.end_timer();
+
+               result.end_timer();
+
+               results.push_back(result);
+
+#if defined(BOTAN_HAS_KDF2)
+               results.push_back(test_kem(sk, pk));
+#endif
+
+#if defined(BOTAN_HAS_MCEIES)
+               results.push_back(test_mceies(sk, pk));
+#endif
+               }
+            }
+
+         return results;
+         }
+
+   private:
+      Test::Result test_kem(const Botan::McEliece_PrivateKey& sk,
+                            const Botan::McEliece_PublicKey& pk)
+         {
+         Test::Result result("McEliece KEM");
+         result.start_timer();
+
+         Botan::PK_KEM_Encryptor enc_op(pk, Test::rng(), "KDF2(SHA-256)");
+         Botan::PK_KEM_Decryptor dec_op(sk, Test::rng(), "KDF2(SHA-256)");
+
+         const size_t trials = (Test::run_long_tests() ? 30 : 10);
+         for(size_t i = 0; i < trials; i++)
+            {
+            Botan::secure_vector<uint8_t> salt = Test::rng().random_vec(i);
+
+            Botan::secure_vector<uint8_t> encap_key, shared_key;
+            enc_op.encrypt(encap_key, shared_key, 64, Test::rng(), salt);
+
+            Botan::secure_vector<uint8_t> shared_key2 = dec_op.decrypt(encap_key, 64, salt);
+
+            result.test_eq("same key", shared_key, shared_key2);
+            }
+         result.end_timer();
+         return result;
+         }
+
+#if defined(BOTAN_HAS_MCEIES)
+      Test::Result test_mceies(const Botan::McEliece_PrivateKey& sk,
+                               const Botan::McEliece_PublicKey& pk)
+         {
+         Test::Result result("McEliece IES");
+         result.start_timer();
+
+         for(size_t i = 0; i <= 10; ++i)
+            {
+            uint8_t ad[8];
+            Botan::store_be(static_cast<uint64_t>(i), ad);
+            const size_t ad_len = sizeof(ad);
+
+            const Botan::secure_vector<uint8_t> pt = Test::rng().random_vec(Test::rng().next_byte());
+
+            const Botan::secure_vector<uint8_t> ct = mceies_encrypt(pk, pt.data(), pt.size(), ad, ad_len, Test::rng());
+            const Botan::secure_vector<uint8_t> dec = mceies_decrypt(sk, ct.data(), ct.size(), ad, ad_len);
+
+            result.test_eq("decrypted ok", dec, pt);
+
+            Botan::secure_vector<uint8_t> bad_ct = ct;
+            for(size_t j = 0; j != 3; ++j)
+               {
+               bad_ct = mutate_vec(ct, true);
+
+               try
+                  {
+                  mceies_decrypt(sk, bad_ct.data(), bad_ct.size(), ad, ad_len);
+                  result.test_failure("AEAD decrypted manipulated ciphertext");
+                  result.test_note("Manipulated text was " + Botan::hex_encode(bad_ct));
+                  }
+               catch(Botan::Integrity_Failure&)
+                  {
+                  result.test_note("AEAD rejected manipulated ciphertext");
+                  }
+               catch(std::exception& e)
+                  {
+                  result.test_failure("AEAD rejected manipulated ciphertext with unexpected error", e.what());
+                  }
+               }
+            }
+
+         result.end_timer();
+         return result;
+         }
+#endif
+
+   };
+
+BOTAN_REGISTER_TEST("mceliece", McEliece_Tests);
+
+#endif
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_modes.cpp b/third_party/botan/src/tests/test_modes.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_modes.cpp
@@ -0,0 +1,437 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+   #include <botan/cipher_mode.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+
+class Cipher_Mode_Tests final : public Text_Based_Test
+   {
+   public:
+      Cipher_Mode_Tests()
+         : Text_Based_Test("modes", "Key,Nonce,In,Out") {}
+
+      std::vector<std::string> possible_providers(const std::string& algo) override
+         {
+         return provider_filter(Botan::Cipher_Mode::providers(algo));
+         }
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> nonce    = vars.get_opt_bin("Nonce");
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+
+         Test::Result result(algo);
+
+         const std::vector<std::string> providers = possible_providers(algo);
+
+         if(providers.empty())
+            {
+            result.note_missing("cipher mode " + algo);
+            return result;
+            }
+
+         for(auto&& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::Cipher_Mode> enc(Botan::Cipher_Mode::create(
+                  algo, Botan::ENCRYPTION, provider_ask));
+            std::unique_ptr<Botan::Cipher_Mode> dec(Botan::Cipher_Mode::create(
+                  algo, Botan::DECRYPTION, provider_ask));
+
+            if(!enc || !dec)
+               {
+               if(enc)
+                  result.test_failure("Provider " + provider_ask + " has encrypt but not decrypt");
+               if(dec)
+                  result.test_failure("Provider " + provider_ask + " has decrypt but not encrypt");
+               result.note_missing(algo);
+               return result;
+               }
+
+            result.test_eq("enc and dec granularity is the same",
+                           enc->update_granularity(), dec->update_granularity());
+
+            try
+               {
+               test_mode(result, algo, provider_ask, "encryption", *enc, key, nonce, input, expected);
+               }
+            catch(Botan::Exception& e)
+               {
+               result.test_failure("Encryption tests failed", e.what());
+               }
+
+            try
+               {
+               test_mode(result, algo, provider_ask, "decryption", *dec, key, nonce, expected, input);
+               }
+            catch(Botan::Exception& e)
+               {
+               result.test_failure("Decryption tests failed", e.what());
+               }
+
+            }
+
+         return result;
+         }
+
+   private:
+      void test_mode(Test::Result& result,
+                     const std::string& algo,
+                     const std::string& provider,
+                     const std::string& direction,
+                     Botan::Cipher_Mode& mode,
+                     const std::vector<uint8_t>& key,
+                     const std::vector<uint8_t>& nonce,
+                     const std::vector<uint8_t>& input,
+                     const std::vector<uint8_t>& expected)
+         {
+         const bool is_cbc = (algo.find("/CBC") != std::string::npos);
+         const bool is_ctr = (algo.find("CTR") != std::string::npos);
+
+         result.test_eq("name", mode.name(), algo);
+
+         // Some modes report base even if got from another provider
+         if(mode.provider() != "base")
+            {
+            result.test_eq("provider", mode.provider(), provider);
+            }
+
+         result.test_eq("mode not authenticated", mode.authenticated(), false);
+
+         const size_t update_granularity = mode.update_granularity();
+         const size_t min_final_bytes = mode.minimum_final_size();
+
+         // FFI currently requires this, so assure it is true for all modes
+         result.test_gt("buffer sizes ok", update_granularity, min_final_bytes);
+
+         result.test_throws("Unkeyed object throws", [&]() {
+            Botan::secure_vector<uint8_t> bad(update_granularity);
+            mode.finish(bad);
+            });
+
+         if(is_cbc)
+            {
+            // can't test equal due to CBC padding
+
+            if(direction == "encryption")
+               {
+               result.test_lte("output_length", mode.output_length(input.size()), expected.size());
+               }
+            else
+               {
+               result.test_gte("output_length", mode.output_length(input.size()), expected.size());
+               }
+            }
+         else
+            {
+            // assume all other modes are not expanding (currently true)
+            result.test_eq("output_length", mode.output_length(input.size()), expected.size());
+            }
+
+         result.confirm("default nonce size is allowed",
+                        mode.valid_nonce_length(mode.default_nonce_length()));
+
+         // Test that disallowed nonce sizes result in an exception
+         static constexpr size_t large_nonce_size = 65000;
+         result.test_eq("Large nonce not allowed", mode.valid_nonce_length(large_nonce_size), false);
+         result.test_throws("Large nonce causes exception",
+                            [&mode]() { mode.start(nullptr, large_nonce_size); });
+
+         Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(update_granularity);
+
+         // Test to make sure reset() resets what we need it to
+         result.test_throws("Cannot process data (update) until key is set",
+                            [&]() { mode.update(garbage); });
+         result.test_throws("Cannot process data (finish) until key is set",
+                            [&]() { mode.finish(garbage); });
+
+         mode.set_key(mutate_vec(key));
+
+         if(is_ctr == false)
+            {
+            result.test_throws("Cannot process data until nonce is set",
+                               [&]() { mode.update(garbage); });
+            }
+
+         mode.start(mutate_vec(nonce));
+         mode.reset();
+
+         if(is_ctr == false)
+            {
+            result.test_throws("Cannot process data until nonce is set (after start/reset)",
+                               [&]() { mode.update(garbage); });
+            }
+
+         mode.start(mutate_vec(nonce));
+         mode.update(garbage);
+
+         mode.reset();
+
+         mode.set_key(key);
+         mode.start(nonce);
+
+         Botan::secure_vector<uint8_t> buf;
+
+         buf.assign(input.begin(), input.end());
+         mode.finish(buf);
+         result.test_eq(direction + " all-in-one", buf, expected);
+
+         // additionally test update() and process() if possible
+         if(input.size() >= update_granularity + min_final_bytes)
+            {
+            const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity;
+            const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+
+            // test update, 1 block at a time
+            if(max_blocks_to_process > 1)
+               {
+               Botan::secure_vector<uint8_t> block(update_granularity);
+               buf.clear();
+
+               mode.start(nonce);
+               for(size_t i = 0; i != max_blocks_to_process; ++i)
+                  {
+                  block.assign(input.data() + i*update_granularity,
+                               input.data() + (i+1)*update_granularity);
+
+                  mode.update(block);
+                  buf += block;
+                  }
+
+               Botan::secure_vector<uint8_t> last_bits(input.data() + bytes_to_process, input.data() + input.size());
+               mode.finish(last_bits);
+               buf += last_bits;
+
+               result.test_eq(direction + " update-1", buf, expected);
+               }
+
+            // test update with maximum length input
+            buf.assign(input.data(), input.data() + bytes_to_process);
+            Botan::secure_vector<uint8_t> last_bits(input.data() + bytes_to_process, input.data() + input.size());
+
+            mode.start(nonce);
+            mode.update(buf);
+            mode.finish(last_bits);
+
+            buf += last_bits;
+
+            result.test_eq(direction + " update-all", buf, expected);
+
+            // test process with maximum length input
+            mode.start(nonce);
+            buf.assign(input.begin(), input.end());
+
+            const size_t bytes_written = mode.process(buf.data(), bytes_to_process);
+
+            result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+
+            mode.finish(buf, bytes_to_process);
+            result.test_eq(direction + " process", buf, expected);
+            }
+
+         mode.clear();
+
+         result.test_throws("Unkeyed object throws after clear", [&]() {
+            Botan::secure_vector<uint8_t> bad(update_granularity);
+            mode.finish(bad);
+            });
+
+         }
+   };
+
+BOTAN_REGISTER_TEST("modes", Cipher_Mode_Tests);
+
+class Cipher_Mode_IV_Carry_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         results.push_back(test_cbc_iv_carry());
+         results.push_back(test_cfb_iv_carry());
+         results.push_back(test_ctr_iv_carry());
+         return results;
+         }
+
+   private:
+      Test::Result test_cbc_iv_carry()
+         {
+         Test::Result result("CBC IV carry");
+
+#if defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_AES)
+         std::unique_ptr<Botan::Cipher_Mode> enc(
+            Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::ENCRYPTION));
+         std::unique_ptr<Botan::Cipher_Mode> dec(
+            Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::DECRYPTION));
+
+         const std::vector<uint8_t> key(16, 0xAA);
+         const std::vector<uint8_t> iv(16, 0xAA);
+
+         Botan::secure_vector<uint8_t> msg1 =
+            Botan::hex_decode_locked("446F6E27742075736520706C61696E20434243206D6F6465");
+         Botan::secure_vector<uint8_t> msg2 =
+            Botan::hex_decode_locked("49562063617272796F766572");
+         Botan::secure_vector<uint8_t> msg3 =
+            Botan::hex_decode_locked("49562063617272796F76657232");
+
+         enc->set_key(key);
+         dec->set_key(key);
+
+         enc->start(iv);
+         enc->finish(msg1);
+         result.test_eq("First ciphertext", msg1,
+                        "9BDD7300E0CB61CA71FFF957A71605DB6836159C36781246A1ADF50982757F4B");
+
+         enc->start();
+         enc->finish(msg2);
+
+         result.test_eq("Second ciphertext", msg2,
+                        "AA8D682958A4A044735DAC502B274DB2");
+
+         enc->start();
+         enc->finish(msg3);
+
+         result.test_eq("Third ciphertext", msg3,
+                        "1241B9976F73051BCF809525D6E86C25");
+
+         dec->start(iv);
+         dec->finish(msg1);
+
+         dec->start();
+         dec->finish(msg2);
+
+         dec->start();
+         dec->finish(msg3);
+         result.test_eq("Third plaintext", msg3, "49562063617272796F76657232");
+
+#endif
+         return result;
+         }
+
+      Test::Result test_cfb_iv_carry()
+         {
+         Test::Result result("CFB IV carry");
+#if defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_AES)
+         std::unique_ptr<Botan::Cipher_Mode> enc(
+            Botan::Cipher_Mode::create("AES-128/CFB(8)", Botan::ENCRYPTION));
+         std::unique_ptr<Botan::Cipher_Mode> dec(
+            Botan::Cipher_Mode::create("AES-128/CFB(8)", Botan::DECRYPTION));
+
+         const std::vector<uint8_t> key(16, 0xAA);
+         const std::vector<uint8_t> iv(16, 0xAB);
+
+         Botan::secure_vector<uint8_t> msg1 = Botan::hex_decode_locked("ABCDEF01234567");
+         Botan::secure_vector<uint8_t> msg2 = Botan::hex_decode_locked("0000123456ABCDEF");
+         Botan::secure_vector<uint8_t> msg3 = Botan::hex_decode_locked("012345");
+
+         enc->set_key(key);
+         dec->set_key(key);
+
+         enc->start(iv);
+         enc->finish(msg1);
+         result.test_eq("First ciphertext", msg1, "a51522387c4c9b");
+
+         enc->start();
+         enc->finish(msg2);
+
+         result.test_eq("Second ciphertext", msg2, "105457dc2e0649d4");
+
+         enc->start();
+         enc->finish(msg3);
+
+         result.test_eq("Third ciphertext", msg3, "53bd65");
+
+         dec->start(iv);
+         dec->finish(msg1);
+         result.test_eq("First plaintext", msg1, "ABCDEF01234567");
+
+         dec->start();
+         dec->finish(msg2);
+         result.test_eq("Second plaintext", msg2, "0000123456ABCDEF");
+
+         dec->start();
+         dec->finish(msg3);
+         result.test_eq("Third plaintext", msg3, "012345");
+#endif
+         return result;
+         }
+
+      Test::Result test_ctr_iv_carry()
+         {
+         Test::Result result("CTR IV carry");
+#if defined(BOTAN_HAS_CTR_BE) && defined(BOTAN_HAS_AES)
+
+         std::unique_ptr<Botan::Cipher_Mode> enc(
+            Botan::Cipher_Mode::create("AES-128/CTR-BE", Botan::ENCRYPTION));
+         std::unique_ptr<Botan::Cipher_Mode> dec(
+            Botan::Cipher_Mode::create("AES-128/CTR-BE", Botan::DECRYPTION));
+
+         const std::vector<uint8_t> key =
+            Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+         const std::vector<uint8_t> iv =
+            Botan::hex_decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+
+         enc->set_key(key);
+         dec->set_key(key);
+
+         const std::vector<std::string> exp_ciphertext = {
+            "EC",
+            "8CDF",
+            "739860",
+            "7CB0F2D2",
+            "1675EA9EA1",
+            "E4362B7C3C67",
+            "73516318A077D7",
+            "FC5073AE6A2CC378",
+            "7889374FBEB4C81B17",
+            "BA6C44E89C399FF0F198C",
+         };
+
+         for(size_t i = 1; i != 10; ++i)
+            {
+            if(i == 1)
+               {
+               enc->start(iv);
+               dec->start(iv);
+               }
+            else
+               {
+               enc->start();
+               dec->start();
+               }
+
+            Botan::secure_vector<uint8_t> msg(i, 0);
+            enc->finish(msg);
+
+            result.test_eq("Ciphertext", msg, exp_ciphertext[i-1].c_str());
+
+            dec->finish(msg);
+
+            for(size_t j = 0; j != msg.size(); ++j)
+               result.test_eq("Plaintext zeros", static_cast<size_t>(msg[j]), 0);
+
+            }
+#endif
+         return result;
+         }
+   };
+
+
+BOTAN_REGISTER_TEST("iv_carryover", Cipher_Mode_IV_Carry_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_mp.cpp b/third_party/botan/src/tests/test_mp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_mp.cpp
@@ -0,0 +1,147 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_BIGINT_MP)
+   #include <botan/internal/mp_core.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_BIGINT_MP)
+
+class MP_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_cnd_swap());
+         results.push_back(test_cnd_add());
+         results.push_back(test_cnd_sub());
+         results.push_back(test_cnd_abs());
+
+         return results;
+         }
+   private:
+      Result test_cnd_add()
+         {
+         Result result("bigint_cnd_add");
+
+         const Botan::word max = Botan::MP_WORD_MAX;
+
+         Botan::word a = 2;
+         Botan::word c = Botan::bigint_cnd_add(0, &a, &max, 1);
+
+         result.test_int_eq(a, 2, "No op");
+         result.test_int_eq(c, 0, "No op");
+
+         c = Botan::bigint_cnd_add(1, &a, &max, 1);
+
+         result.test_int_eq(a, 1, "Add");
+         result.test_int_eq(c, 1, "Carry");
+
+         // TODO more tests
+
+         return result;
+         }
+
+      Result test_cnd_sub()
+         {
+         Result result("bigint_cnd_sub");
+
+         Botan::word a = 2;
+         Botan::word b = 3;
+         Botan::word c = Botan::bigint_cnd_sub(0, &a, &b, 1);
+
+         result.test_int_eq(a, 2, "No op");
+         result.test_int_eq(c, 0, "No op");
+
+         c = Botan::bigint_cnd_sub(1, &a, &b, 1);
+
+         result.test_int_eq(a, Botan::MP_WORD_MAX, "Sub");
+         result.test_int_eq(c, 1, "Borrow");
+
+         return result;
+         }
+
+      Result test_cnd_abs()
+         {
+         Result result("bigint_cnd_abs");
+
+         Botan::word x1 = Botan::MP_WORD_MAX;
+         Botan::bigint_cnd_abs(1, &x1, 1);
+         result.test_int_eq(x1, 1, "Abs");
+
+         x1 = 0;
+         Botan::bigint_cnd_abs(1, &x1, 1);
+         result.test_int_eq(x1, 0, "Abs");
+
+         x1 = 1;
+         Botan::bigint_cnd_abs(1, &x1, 1);
+         result.test_int_eq(x1, Botan::MP_WORD_MAX, "Abs");
+
+         x1 = 1;
+         Botan::bigint_cnd_abs(0, &x1, 1);
+         result.test_int_eq(x1, 1, "No change");
+
+         Botan::word x2[2] = { Botan::MP_WORD_MAX, Botan::MP_WORD_MAX };
+
+         Botan::bigint_cnd_abs(1, x2, 2);
+         result.test_int_eq(x2[0], 1, "Abs");
+         result.test_int_eq(x2[1], 0, "Abs");
+
+         return result;
+         }
+
+      Result test_cnd_swap()
+         {
+         Result result("bigint_cnd_swap");
+
+         // null with zero length is ok
+         Botan::bigint_cnd_swap(0, nullptr, nullptr, 0);
+         Botan::bigint_cnd_swap(1, nullptr, nullptr, 0);
+
+         Botan::word x1 = 5, y1 = 9;
+
+         Botan::bigint_cnd_swap(0, &x1, &y1, 1);
+         result.test_int_eq(x1, 5, "No swap");
+         Botan::bigint_cnd_swap(1, &x1, &y1, 1);
+         result.test_int_eq(x1, 9, "Swap");
+
+         Botan::word x5[5] = { 0, 1, 2, 3, 4 };
+         Botan::word y5[5] = { 3, 2, 1, 0, 9 };
+
+         // Should only modify first four
+         Botan::bigint_cnd_swap(1, x5, y5, 4);
+
+         for(size_t i = 0; i != 4; ++i)
+            {
+            result.test_int_eq(x5[i], 3 - i, "Swap x5");
+            }
+         result.test_int_eq(x5[4], 4, "Not touched");
+
+         for(size_t i = 0; i != 4; ++i)
+            {
+            result.test_int_eq(y5[i], i, "Swap y5");
+            }
+         result.test_int_eq(y5[4], 9, "Not touched");
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("mp_unit", MP_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_name_constraint.cpp b/third_party/botan/src/tests/test_name_constraint.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_name_constraint.cpp
@@ -0,0 +1,97 @@
+/*
+* (C) 2015,2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509path.h>
+   #include <botan/calendar.h>
+#endif
+
+#include <utility>
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+class Name_Constraint_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         const std::vector<std::tuple<std::string, std::string, std::string, std::string>> test_cases =
+            {
+            std::make_tuple(
+               "Root_Email_Name_Constraint.crt",
+               "Invalid_Email_Name_Constraint.crt",
+               "Invalid Email Name Constraint",
+               "Certificate does not pass name constraint"),
+            std::make_tuple(
+               "Root_DN_Name_Constraint.crt",
+               "Invalid_DN_Name_Constraint.crt",
+               "Invalid DN Name Constraint",
+               "Certificate does not pass name constraint"),
+            std::make_tuple(
+               "Root_DN_Name_Constraint.crt",
+               "Valid_DN_Name_Constraint.crt",
+               "Valid DN Name Constraint",
+               "Verified"),
+            std::make_tuple(
+               "Root_DNS_Name_Constraint.crt",
+               "Valid_DNS_Name_Constraint.crt",
+               "aexample.com",
+               "Verified"),
+            std::make_tuple(
+               "Root_IP_Name_Constraint.crt",
+               "Valid_IP_Name_Constraint.crt",
+               "Valid IP Name Constraint",
+               "Verified"),
+            std::make_tuple(
+               "Root_IP_Name_Constraint.crt",
+               "Invalid_IP_Name_Constraint.crt",
+               "Invalid IP Name Constraint",
+               "Certificate does not pass name constraint"),
+            };
+         std::vector<Test::Result> results;
+         const Botan::Path_Validation_Restrictions restrictions(false, 80);
+
+         std::chrono::system_clock::time_point validation_time =
+            Botan::calendar_point(2016, 10, 21, 4, 20, 0).to_std_timepoint();
+
+         for(const auto& t : test_cases)
+            {
+            Botan::X509_Certificate root(Test::data_file("x509/name_constraint/" + std::get<0>(t)));
+            Botan::X509_Certificate sub(Test::data_file("x509/name_constraint/" + std::get<1>(t)));
+            Botan::Certificate_Store_In_Memory trusted;
+            Test::Result result("X509v3 Name Constraints: " + std::get<1>(t));
+
+            trusted.add_certificate(root);
+            Botan::Path_Validation_Result path_result = Botan::x509_path_validate(
+                     sub, restrictions, trusted, std::get<2>(t), Botan::Usage_Type::TLS_SERVER_AUTH,
+                     validation_time);
+
+            if(path_result.successful_validation() && path_result.trust_root() != root)
+               {
+               path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+               }
+
+            result.test_eq("validation result", path_result.result_string(), std::get<3>(t));
+            results.emplace_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("x509_path_name_constraint", Name_Constraint_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_newhope.cpp b/third_party/botan/src/tests/test_newhope.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_newhope.cpp
@@ -0,0 +1,159 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA)
+   #include <botan/newhope.h>
+   #include <botan/hash.h>
+   #include <botan/stream_cipher.h>
+   #include <botan/rng.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA)
+
+class NEWHOPE_RNG final : public Botan::RandomNumberGenerator
+   {
+   public:
+      std::string name() const override
+         {
+         return "NEWHOPE_RNG";
+         }
+      void clear() override
+         {
+         /* ignored */
+         }
+
+      void randomize(uint8_t out[], size_t len) override
+         {
+         if(m_first.size() == len)
+            {
+            if(len != 32)
+               {
+               throw Test_Error("NEWHOPE_RNG called in unexpected way, bad test?");
+               }
+
+            Botan::copy_mem(out, m_first.data(), m_first.size());
+            return;
+            }
+
+         /*
+         * This slavishly emulates the behavior of the reference
+         * implementations RNG, in order to ensure that from the same
+         * random seed we compute the exact same result.
+         */
+         Botan::clear_mem(out, len);
+         m_chacha->cipher1(out, len);
+
+         m_calls += 1;
+
+         uint8_t nonce[8] = { 0 };
+
+         if(m_calls < 3)
+            {
+            nonce[0] = m_calls;
+            }
+         else
+            {
+            nonce[7] = m_calls;
+            }
+
+         m_chacha->set_iv(nonce, 8);
+         }
+
+      bool is_seeded() const override
+         {
+         return true;
+         }
+
+      bool accepts_input() const override { return false; }
+
+      void add_entropy(const uint8_t[], size_t) override
+         {
+         /* ignored */
+         }
+
+      NEWHOPE_RNG(const std::vector<uint8_t>& seed)
+         {
+         m_chacha = Botan::StreamCipher::create_or_throw("ChaCha20");
+
+         if(seed.size() != 64 && seed.size() != 32)
+            {
+            throw Test_Error("Invalid NEWHOPE RNG seed");
+            }
+
+         if(seed.size() == 64)
+            {
+            m_first.assign(seed.begin(), seed.begin() + 32);
+            m_chacha->set_key(seed.data() + 32, 32);
+            }
+         else
+            {
+            m_chacha->set_key(seed.data(), 32);
+            }
+         }
+
+   private:
+      std::unique_ptr<Botan::StreamCipher> m_chacha;
+      std::vector<uint8_t> m_first;
+      uint8_t m_calls = 0;
+   };
+
+class NEWHOPE_Tests final : public Text_Based_Test
+   {
+   public:
+      NEWHOPE_Tests()
+         : Text_Based_Test(
+              "pubkey/newhope.vec",
+              "DRBG_SeedA,H_OutputA,DRBG_SeedB,H_OutputB,SharedKey") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("NEWHOPE");
+
+         const std::vector<uint8_t> h_output_a = vars.get_req_bin("H_OutputA");
+         const std::vector<uint8_t> h_output_b = vars.get_req_bin("H_OutputB");
+         const std::vector<uint8_t> shared_key = vars.get_req_bin("SharedKey");
+
+         NEWHOPE_RNG drbg_a(vars.get_req_bin("DRBG_SeedA"));
+         NEWHOPE_RNG drbg_b(vars.get_req_bin("DRBG_SeedB"));
+
+         std::unique_ptr<Botan::HashFunction> sha3 = Botan::HashFunction::create("SHA-3(256)");
+
+         std::vector<uint8_t> send_a(Botan::NEWHOPE_SENDABYTES);
+         Botan::newhope_poly a_sk;
+         Botan::newhope_keygen(send_a.data(), &a_sk, drbg_a);
+
+         std::vector<uint8_t> h_send_a(sha3->output_length());
+         sha3->update(send_a);
+         sha3->final(h_send_a.data());
+         result.test_eq("Hash Output A", h_send_a, h_output_a);
+
+         std::vector<uint8_t> sharedkey_b(32);
+         std::vector<uint8_t> send_b(Botan::NEWHOPE_SENDBBYTES);
+         Botan::newhope_sharedb(sharedkey_b.data(), send_b.data(), send_a.data(), drbg_b);
+         result.test_eq("Key B", sharedkey_b, shared_key);
+
+         std::vector<uint8_t> h_send_b(sha3->output_length());
+         sha3->update(send_b);
+         sha3->final(h_send_b.data());
+         result.test_eq("Hash Output B", h_send_b, h_output_b);
+
+         std::vector<uint8_t> sharedkey_a(32);
+         newhope_shareda(sharedkey_a.data(), &a_sk, send_b.data());
+         result.test_eq("Key A", sharedkey_a, shared_key);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("newhope", NEWHOPE_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_ocb.cpp b/third_party/botan/src/tests/test_ocb.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ocb.cpp
@@ -0,0 +1,367 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+   #include <botan/ocb.h>
+   #include <botan/block_cipher.h>
+   #include <botan/loadstor.h>
+   #include <botan/internal/poly_dbl.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+
+// Toy cipher used for wide block tests
+
+class OCB_Wide_Test_Block_Cipher final : public Botan::BlockCipher
+   {
+   public:
+      OCB_Wide_Test_Block_Cipher(size_t bs) : m_bs(bs) {}
+
+      std::string name() const override { return "OCB_ToyCipher"; }
+      size_t block_size() const override { return m_bs; }
+      void clear() override { m_key.clear(); }
+
+      Botan::BlockCipher* clone() const override
+         {
+         return new OCB_Wide_Test_Block_Cipher(m_bs);
+         }
+
+      void key_schedule(const uint8_t key[], size_t length) override
+         {
+         m_key.assign(key, key + length);
+         }
+
+      Botan::Key_Length_Specification key_spec() const override
+         {
+         return Botan::Key_Length_Specification(m_bs);
+         }
+
+      void encrypt_n(const uint8_t in[], uint8_t out[],
+                     size_t blocks) const override
+         {
+         while(blocks)
+            {
+            Botan::copy_mem(out, in, m_bs);
+            Botan::poly_double_n(out, m_bs);
+
+            for(size_t i = 0; i != m_bs; ++i)
+               out[i] ^= m_key[i];
+
+            blocks--;
+            in += block_size();
+            out += block_size();
+            }
+         }
+
+      void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+         {
+         while(blocks)
+            {
+            for(size_t i = 0; i != m_bs; ++i)
+               out[i] = in[i] ^ m_key[i];
+
+            uint8_t carry = in[m_bs-1] & 0x01;
+
+            if(carry)
+               {
+               if(m_bs == 16 || m_bs == 24)
+                  {
+                  out[m_bs-1] ^= 0x87;
+                  }
+               else if(m_bs == 32)
+                  {
+                  out[m_bs-2] ^= 0x4;
+                  out[m_bs-1] ^= 0x25;
+                  }
+               else if(m_bs == 64)
+                  {
+                  out[m_bs-2] ^= 0x1;
+                  out[m_bs-1] ^= 0x25;
+                  }
+               else
+                  throw Test_Error("Bad OCB test block size");
+               }
+
+            carry <<= 7;
+
+            for(size_t i = 0; i != m_bs; ++i)
+               {
+               const uint8_t temp = out[i];
+               out[i] = (temp >> 1) | carry;
+               carry = (temp & 0x1);
+               carry <<= 7;
+               }
+
+            blocks--;
+            in += block_size();
+            out += block_size();
+            }
+         }
+   private:
+      size_t m_bs;
+      std::vector<uint8_t> m_key;
+   };
+
+class OCB_Wide_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      OCB_Wide_KAT_Tests()
+         : Text_Based_Test("ocb/ocb_wide.vec", "Key,Nonce,AD,In,Out") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("OCB wide block KAT");
+
+         const std::vector<uint8_t> key = vars.get_req_bin("Key");
+         const std::vector<uint8_t> nonce = vars.get_req_bin("Nonce");
+         const std::vector<uint8_t> ad = vars.get_req_bin("AD");
+         const std::vector<uint8_t> input = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+
+         const size_t bs = key.size();
+         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+
+         Botan::OCB_Encryption enc(new OCB_Wide_Test_Block_Cipher(bs), std::min<size_t>(bs, 32));
+         enc.set_key(key);
+         enc.set_ad(ad);
+         enc.start(nonce);
+         enc.finish(buf);
+         result.test_eq("Ciphertext matches", buf, expected);
+
+         Botan::OCB_Decryption dec(new OCB_Wide_Test_Block_Cipher(bs), std::min<size_t>(bs, 32));
+         dec.set_key(key);
+         dec.set_ad(ad);
+         dec.start(nonce);
+         dec.finish(buf);
+         result.test_eq("Decryption correct", buf, input);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ocb_wide", OCB_Wide_KAT_Tests);
+
+class OCB_Wide_Long_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      OCB_Wide_Long_KAT_Tests()
+         : Text_Based_Test("ocb/ocb_wide_long.vec", "Output") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         Test::Result result("OCB wide block long test");
+
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         std::unique_ptr<Botan::BlockCipher> cipher;
+         size_t bs = 0;
+
+         if(algo == "SHACAL2")
+            {
+#if defined(BOTAN_HAS_SHACAL2)
+            cipher = Botan::BlockCipher::create_or_throw("SHACAL2");
+            bs = 32;
+#else
+            return {result};
+#endif
+            }
+         else
+            {
+            if(algo == "Toy128")
+               bs = 16;
+            else if(algo == "Toy192")
+               bs = 24;
+            else if(algo == "Toy256")
+               bs = 32;
+            else if(algo == "Toy512")
+               bs = 64;
+            else
+               throw Test_Error("Unknown cipher for OCB wide block long test");
+            cipher.reset(new OCB_Wide_Test_Block_Cipher(bs));
+            }
+
+         Botan::OCB_Encryption enc(cipher.release(), std::min<size_t>(bs, 32));
+
+         /*
+         Y, string of length min(B, 256) bits
+
+         Y is defined as follows.
+
+         K = (0xA0 || 0xA1 || 0xA2 || ...)[1..B]
+         C = <empty string>
+         for i = 0 to 127 do
+           S = (0x50 || 0x51 || 0x52 || ...)[1..8i]
+           N = num2str(3i+1,16)
+           C = C || OCB-ENCRYPT(K,N,S,S)
+           N = num2str(3i+2,16)
+           C = C || OCB-ENCRYPT(K,N,<empty string>,S)
+           N = num2str(3i+3,16)
+           C = C || OCB-ENCRYPT(K,N,S,<empty string>)
+         end for
+         N = num2str(385,16)
+         Y = OCB-ENCRYPT(K,N,C,<empty string>)
+         */
+
+         std::vector<uint8_t> key(bs);
+         for(size_t i = 0; i != bs; ++i)
+            key[i] = static_cast<uint8_t>(0xA0 + i);
+
+         enc.set_key(key);
+
+         const std::vector<uint8_t> empty;
+         std::vector<uint8_t> N(2);
+         std::vector<uint8_t> C;
+
+         for(size_t i = 0; i != 128; ++i)
+            {
+            std::vector<uint8_t> S(i);
+            for(size_t j = 0; j != S.size(); ++j)
+               S[j] = static_cast<uint8_t>(0x50 + j);
+
+            Botan::store_be(static_cast<uint16_t>(3 * i + 1), &N[0]);
+
+            ocb_encrypt(result, C, enc, N, S, S);
+            Botan::store_be(static_cast<uint16_t>(3 * i + 2), &N[0]);
+            ocb_encrypt(result, C, enc, N, S, empty);
+            Botan::store_be(static_cast<uint16_t>(3 * i + 3), &N[0]);
+            ocb_encrypt(result, C, enc, N, empty, S);
+            }
+
+         Botan::store_be(static_cast<uint16_t>(385), &N[0]);
+         std::vector<uint8_t> final_result;
+         ocb_encrypt(result, final_result, enc, N, empty, C);
+
+         result.test_eq("correct value", final_result, expected);
+
+         return result;
+         }
+
+   private:
+      void ocb_encrypt(Test::Result& /*result*/,
+                       std::vector<uint8_t>& output_to,
+                       Botan::OCB_Encryption& enc,
+                       const std::vector<uint8_t>& nonce,
+                       const std::vector<uint8_t>& pt,
+                       const std::vector<uint8_t>& ad)
+         {
+         enc.set_associated_data(ad.data(), ad.size());
+         enc.start(nonce.data(), nonce.size());
+         Botan::secure_vector<uint8_t> buf(pt.begin(), pt.end());
+         enc.finish(buf, 0);
+         output_to.insert(output_to.end(), buf.begin(), buf.end());
+         }
+   };
+
+BOTAN_REGISTER_TEST("ocb_long_wide", OCB_Wide_Long_KAT_Tests);
+
+class OCB_Long_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      OCB_Long_KAT_Tests()
+         : Text_Based_Test("ocb/ocb_long.vec",
+                           "Keylen,Taglen,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const size_t keylen = vars.get_req_sz("Keylen");
+         const size_t taglen = vars.get_req_sz("Taglen");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         // Test from RFC 7253 Appendix A
+
+         const std::string algo = "AES-" + std::to_string(keylen);
+
+         Test::Result result("OCB long");
+
+         std::unique_ptr<Botan::BlockCipher> aes(Botan::BlockCipher::create(algo));
+         if(!aes)
+            {
+            result.note_missing(algo);
+            return result;
+            }
+
+         Botan::OCB_Encryption enc(aes->clone(), taglen / 8);
+         Botan::OCB_Decryption dec(aes->clone(), taglen / 8);
+
+         std::vector<uint8_t> key(keylen / 8);
+         key[keylen / 8 - 1] = static_cast<uint8_t>(taglen);
+
+         enc.set_key(key);
+         dec.set_key(key);
+
+         const std::vector<uint8_t> empty;
+         std::vector<uint8_t> N(12);
+         std::vector<uint8_t> C;
+
+         for(size_t i = 0; i != 128; ++i)
+            {
+            const std::vector<uint8_t> S(i);
+
+            Botan::store_be(static_cast<uint32_t>(3 * i + 1), &N[8]);
+
+            ocb_encrypt(result, C, enc, dec, N, S, S);
+            Botan::store_be(static_cast<uint32_t>(3 * i + 2), &N[8]);
+            ocb_encrypt(result, C, enc, dec, N, S, empty);
+            Botan::store_be(static_cast<uint32_t>(3 * i + 3), &N[8]);
+            ocb_encrypt(result, C, enc, dec, N, empty, S);
+            }
+
+         Botan::store_be(static_cast<uint32_t>(385), &N[8]);
+         std::vector<uint8_t> final_result;
+         ocb_encrypt(result, final_result, enc, dec, N, empty, C);
+
+         result.test_eq("correct value", final_result, expected);
+
+         return result;
+         }
+   private:
+      void ocb_encrypt(Test::Result& result,
+                       std::vector<uint8_t>& output_to,
+                       Botan::OCB_Encryption& enc,
+                       Botan::OCB_Decryption& dec,
+                       const std::vector<uint8_t>& nonce,
+                       const std::vector<uint8_t>& pt,
+                       const std::vector<uint8_t>& ad)
+         {
+         enc.set_associated_data(ad.data(), ad.size());
+
+         enc.start(nonce.data(), nonce.size());
+
+         Botan::secure_vector<uint8_t> buf(pt.begin(), pt.end());
+         enc.finish(buf, 0);
+         output_to.insert(output_to.end(), buf.begin(), buf.end());
+
+         try
+            {
+            dec.set_associated_data(ad.data(), ad.size());
+
+            dec.start(nonce.data(), nonce.size());
+
+            dec.finish(buf, 0);
+
+            result.test_eq("OCB round tripped", buf, pt);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("OCB round trip error", e.what());
+            }
+
+         }
+   };
+
+BOTAN_REGISTER_TEST("ocb_long", OCB_Long_KAT_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_ocsp.cpp b/third_party/botan/src/tests/test_ocsp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_ocsp.cpp
@@ -0,0 +1,377 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_OCSP)
+   #include <botan/ocsp.h>
+   #include <botan/x509path.h>
+   #include <botan/certstor.h>
+   #include <botan/calendar.h>
+   #include <botan/cert_status.h>
+   #include <fstream>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+class OCSP_Tests final : public Test
+   {
+   private:
+      std::shared_ptr<const Botan::X509_Certificate> load_test_X509_cert(const std::string& path)
+         {
+         return std::make_shared<const Botan::X509_Certificate>(Test::data_file(path));
+         }
+
+      std::shared_ptr<const Botan::OCSP::Response> load_test_OCSP_resp(const std::string& path)
+         {
+         return std::make_shared<const Botan::OCSP::Response>(Test::read_binary_data_file(path));
+         }
+
+      Test::Result test_response_parsing()
+         {
+         Test::Result result("OCSP response parsing");
+
+         // Simple parsing tests
+         const std::vector<std::string> ocsp_input_paths =
+            {
+            "x509/ocsp/resp1.der",
+            "x509/ocsp/resp2.der",
+            "x509/ocsp/resp3.der"
+            };
+
+         for(std::string ocsp_input_path : ocsp_input_paths)
+            {
+            try
+               {
+               Botan::OCSP::Response resp(Test::read_binary_data_file(ocsp_input_path));
+               result.confirm("parsing was successful", resp.status() == Botan::OCSP::Response_Status_Code::Successful);
+               result.test_success("Parsed input " + ocsp_input_path);
+               }
+            catch(Botan::Exception& e)
+               {
+               result.test_failure("Parsing failed", e.what());
+               }
+            }
+
+         Botan::OCSP::Response resp(Test::read_binary_data_file("x509/ocsp/patrickschmidt_ocsp_try_later_wrong_sig.der"));
+         result.confirm("parsing exposes correct status code", resp.status() == Botan::OCSP::Response_Status_Code::Try_Later);
+
+         return result;
+         }
+
+      Test::Result test_response_certificate_access()
+         {
+         Test::Result result("OCSP response certificate access");
+
+         try
+            {
+            Botan::OCSP::Response resp1(Test::read_binary_data_file("x509/ocsp/resp1.der"));
+            const auto &certs1 = resp1.certificates();
+            if(result.test_eq("Expected count of certificates", certs1.size(), 1))
+               {
+               const auto cert = certs1.front();
+               const Botan::X509_DN expected_dn({std::make_pair(
+                  "X520.CommonName",
+                  "Symantec Class 3 EV SSL CA - G3 OCSP Responder")});
+               const bool matches = cert.subject_dn() == expected_dn;
+               result.test_eq("CN matches expected", matches, true);
+               }
+
+            Botan::OCSP::Response resp2(Test::read_binary_data_file("x509/ocsp/resp2.der"));
+            const auto &certs2 = resp2.certificates();
+            result.test_eq("Expect no certificates", certs2.size(), 0);
+            }
+         catch(Botan::Exception& e)
+            {
+            result.test_failure("Parsing failed", e.what());
+            }
+
+         return result;
+         }
+
+      Test::Result test_request_encoding()
+         {
+         Test::Result result("OCSP request encoding");
+
+         const Botan::X509_Certificate end_entity(Test::data_file("x509/ocsp/gmail.pem"));
+         const Botan::X509_Certificate issuer(Test::data_file("x509/ocsp/google_g2.pem"));
+
+         try
+            {
+            const Botan::OCSP::Request bogus(end_entity, issuer);
+            result.test_failure("Bad arguments (swapped end entity, issuer) accepted");
+            }
+         catch(Botan::Invalid_Argument&)
+            {
+            result.test_success("Bad arguments rejected");
+            }
+
+
+         const std::string expected_request =
+            "ME4wTKADAgEAMEUwQzBBMAkGBSsOAwIaBQAEFPLgavmFih2NcJtJGSN6qbUaKH5kBBRK3QYWG7z2aLV29YG2u2IaulqBLwIIQkg+DF+RYMY=";
+
+         const Botan::OCSP::Request req1(issuer, end_entity);
+         result.test_eq("Encoded OCSP request",
+                        req1.base64_encode(),
+                        expected_request);
+
+         const Botan::OCSP::Request req2(issuer, BigInt::decode(end_entity.serial_number()));
+         result.test_eq("Encoded OCSP request",
+                        req2.base64_encode(),
+                        expected_request);
+
+         return result;
+         }
+
+      Test::Result test_response_verification_with_next_update_without_max_age()
+         {
+         Test::Result result("OCSP request check with next_update w/o max_age");
+
+         std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("x509/ocsp/randombit.pem");
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/geotrust.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time);
+
+            return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) &&
+                   result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) &&
+                   result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'",
+                                  ocsp_status[0].count(expected) > 0);
+            };
+
+         check_ocsp(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_ocsp(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
+
+         return result;
+         }
+
+      Test::Result test_response_verification_with_next_update_with_max_age()
+         {
+         Test::Result result("OCSP request check with next_update with max_age");
+
+         std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("x509/ocsp/randombit.pem");
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/geotrust.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         // Some arbitrary time within the validity period of the test certs
+         const auto max_age = std::chrono::minutes(59);
+
+         auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, max_age);
+
+            return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) &&
+                   result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) &&
+                   result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'",
+                                  ocsp_status[0].count(expected) > 0);
+            };
+
+         check_ocsp(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_ocsp(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
+
+         return result;
+         }
+
+      Test::Result test_response_verification_without_next_update_with_max_age()
+         {
+         Test::Result result("OCSP request check w/o next_update with max_age");
+
+         std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         // Some arbitrary time within the validity period of the test certs
+         const auto max_age = std::chrono::minutes(59);
+
+         auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, max_age);
+
+            return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) &&
+                   result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) &&
+                   result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'",
+                                  ocsp_status[0].count(expected) > 0);
+            };
+
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_IS_TOO_OLD);
+
+         return result;
+         }
+
+      Test::Result test_response_verification_without_next_update_without_max_age()
+         {
+         Test::Result result("OCSP request check w/o next_update w/o max_age");
+
+         std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time);
+
+            return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) &&
+                   result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) &&
+                   result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'",
+                                  ocsp_status[0].count(expected) > 0);
+            };
+
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+         check_ocsp(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD);
+
+         return result;
+         }
+
+      Test::Result test_response_verification_softfail()
+         {
+         Test::Result result("OCSP request softfail check");
+
+         std::shared_ptr<const Botan::X509_Certificate> ee = load_test_X509_cert("x509/ocsp/randombit.pem");
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/geotrust.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp =
+            std::make_shared<const Botan::OCSP::Response>(Botan::Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         // Some arbitrary time within the validity period of the test certs
+         const auto valid_time = Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint();
+         const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time);
+
+         if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1))
+            {
+            if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1))
+               {
+               result.confirm("Status warning", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_NO_REVOCATION_URL) > 0);
+               }
+            }
+
+         return result;
+         }
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+      Test::Result test_online_request()
+         {
+         Test::Result result("OCSP online check");
+
+         // Expired end-entity certificate:
+         std::shared_ptr<const Botan::X509_Certificate> ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         std::shared_ptr<const Botan::X509_Certificate> trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
+
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { ca, trust_root };
+
+         Botan::Certificate_Store_In_Memory certstore;
+         certstore.add_certificate(trust_root);
+
+         typedef std::chrono::system_clock Clock;
+         const auto ocspTimeout = std::chrono::milliseconds(3000);
+         auto ocsp_status = Botan::PKIX::check_ocsp_online(cert_path, { &certstore }, Clock::now(), ocspTimeout, false);
+
+         if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1))
+            {
+            if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1))
+               {
+               const bool status_good = ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD) > 0;
+               const bool server_not_found = ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) > 0;
+               result.confirm("Expected status", status_good || server_not_found);
+               }
+            }
+
+         return result;
+         }
+#endif
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_request_encoding());
+         results.push_back(test_response_parsing());
+         results.push_back(test_response_certificate_access());
+         results.push_back(test_response_verification_with_next_update_without_max_age());
+         results.push_back(test_response_verification_with_next_update_with_max_age());
+         results.push_back(test_response_verification_without_next_update_with_max_age());
+         results.push_back(test_response_verification_without_next_update_without_max_age());
+         results.push_back(test_response_verification_softfail());
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+         if(Test::options().run_online_tests())
+            {
+            results.push_back(test_online_request());
+            }
+#endif
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ocsp", OCSP_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_octetstring.cpp b/third_party/botan/src/tests/test_octetstring.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_octetstring.cpp
@@ -0,0 +1,168 @@
+/*
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include <botan/symkey.h>
+
+namespace Botan_Tests {
+
+namespace {
+
+Test::Result test_from_rng()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os(Test::rng(), 32);
+   result.test_eq("length is 32 bytes", os.size(), 32);
+
+   return result;
+   }
+
+Test::Result test_from_hex()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os("0123456789ABCDEF");
+   result.test_eq("length is 8 bytes", os.size(), 8);
+
+   return result;
+   }
+
+Test::Result test_from_byte()
+   {
+   Test::Result result("OctetString");
+
+   auto rand_bytes = Test::rng().random_vec(8);
+   Botan::OctetString os(rand_bytes.data(), rand_bytes.size());
+   result.test_eq("length is 8 bytes", os.size(), 8);
+
+   return result;
+   }
+
+Test::Result test_odd_parity()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os("FFFFFFFFFFFFFFFF");
+   os.set_odd_parity();
+   Botan::OctetString expected("FEFEFEFEFEFEFEFE");
+   result.test_eq("odd parity set correctly", os, expected);
+
+   Botan::OctetString os2("EFCBDA4FAA997F63");
+   os2.set_odd_parity();
+   Botan::OctetString expected2("EFCBDA4FAB987F62");
+   result.test_eq("odd parity set correctly", os2, expected2);
+
+   return result;
+   }
+
+Test::Result test_to_string()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os("0123456789ABCDEF");
+   result.test_eq("OctetString::to_string() returns correct string", os.to_string(), "0123456789ABCDEF");
+
+   return result;
+   }
+
+Test::Result test_xor()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os1("0000000000000000");
+   Botan::OctetString os2("FFFFFFFFFFFFFFFF");
+
+   Botan::OctetString xor_result = os1 ^ os2;
+   result.test_eq("OctetString XOR operations works as expected", xor_result, os2);
+
+   xor_result = os1;
+   xor_result ^= os2;
+   result.test_eq("OctetString XOR operations works as expected", xor_result, os2);
+
+   xor_result = os2 ^ os2;
+   result.test_eq("OctetString XOR operations works as expected", xor_result, os1);
+
+   Botan::OctetString os3("0123456789ABCDEF");
+   xor_result = os3 ^ os2;
+   Botan::OctetString expected("FEDCBA9876543210");
+   result.test_eq("OctetString XOR operations works as expected", xor_result, expected);
+
+   return result;
+   }
+
+Test::Result test_equality()
+   {
+   Test::Result result("OctetString");
+
+   const Botan::OctetString os1("0000000000000000");
+   const Botan::OctetString os1_copy = os1;
+   const Botan::OctetString os2("FFFFFFFFFFFFFFFF");
+   const Botan::OctetString os2_copy = os2;
+
+   result.confirm("OctetString equality operations works as expected", os1 == os1_copy);
+   result.confirm("OctetString equality operations works as expected", os2 == os2_copy);
+   result.confirm("OctetString equality operations works as expected", os1 != os2);
+
+   return result;
+   }
+
+Test::Result test_append()
+   {
+   Test::Result result("OctetString");
+
+   Botan::OctetString os1("0000");
+   Botan::OctetString os2("FFFF");
+   Botan::OctetString expected("0000FFFF");
+
+   Botan::OctetString append_result = os1 + os2;
+
+   result.test_eq("OctetString append operations works as expected", append_result, expected);
+
+   return result;
+   }
+
+class OctetString_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         std::vector<std::function<Test::Result()>> fns =
+            {
+            test_from_rng,
+            test_from_hex,
+            test_from_byte,
+            test_odd_parity,
+            test_to_string,
+            test_xor,
+            test_equality,
+            test_append
+            };
+
+         for(size_t i = 0; i != fns.size(); ++i)
+            {
+            try
+               {
+               results.push_back(fns[ i ]());
+               }
+            catch(std::exception& e)
+               {
+               results.push_back(Test::Result::Failure("OctetString tests " + std::to_string(i), e.what()));
+               }
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("octetstring", OctetString_Tests);
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_oid.cpp b/third_party/botan/src/tests/test_oid.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_oid.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ASN1)
+   #include <botan/oids.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ASN1)
+
+Test::Result test_add_have_OID()
+   {
+   Test::Result result("OID add");
+
+   result.test_eq("there is no OID 'botan-test-oid1'", Botan::OIDS::have_oid("botan-test-oid1"), false);
+
+   Botan::OIDS::add_oid(Botan::OID("1.2.345.6.666"), "botan-test-oid1");
+
+   result.test_eq("OID 'botan-test-oid1' added successfully", Botan::OIDS::have_oid("botan-test-oid1"), true);
+
+   result.test_eq("name of OID '1.2.345.6.666' is 'botan-test-oid1'",
+                  Botan::OIDS::oid2str_or_throw(Botan::OID("1.2.345.6.666")), "botan-test-oid1");
+
+   return result;
+   }
+
+Test::Result test_add_have_OID_str()
+   {
+   Test::Result result("OID add string");
+
+   result.test_eq("there is no OID 'botan-test-oid2'", Botan::OIDS::have_oid("botan-test-oid2"), false);
+
+   Botan::OIDS::add_oidstr("1.2.345.6.777", "botan-test-oid2");
+
+   result.test_eq("OID 'botan-test-oid2' added successfully", Botan::OIDS::have_oid("botan-test-oid2"), true);
+
+   result.test_eq("name of OID '1.2.345.6.777' is 'botan-test-oid2'",
+                  Botan::OIDS::oid2str_or_throw(Botan::OID("1.2.345.6.777")), "botan-test-oid2");
+   return result;
+   }
+
+Test::Result test_add_and_lookup()
+   {
+   Test::Result result("OID add and lookup");
+
+   result.test_eq("OIDS::oid2str_or_empty returns empty string for non-existent OID object",
+                  Botan::OIDS::oid2str_or_empty(Botan::OID("1.2.345.6.888")), std::string());
+
+   result.test_eq("OIDS::str2oid_or_empty returns empty OID for non-existent OID name",
+                  Botan::OIDS::str2oid_or_empty("botan-test-oid3").to_string(), Botan::OID().to_string());
+
+   // add oid -> string mapping
+   Botan::OIDS::add_oid2str(Botan::OID("1.2.345.6.888"), "botan-test-oid3");
+   result.test_eq("Lookup works after adding the OID",
+                  Botan::OIDS::oid2str_or_throw(Botan::OID("1.2.345.6.888")), "botan-test-oid3");
+
+   // still returns empty OID
+   result.test_eq("OIDS::str2oid_or_empty still returns empty OID without adding name mapping",
+                  Botan::OIDS::str2oid_or_empty("botan-test-oid3").to_string(), Botan::OID().to_string());
+
+   // add string -> oid mapping
+   Botan::OIDS::add_str2oid(Botan::OID("1.2.345.6.888"), "botan-test-oid3");
+   result.test_eq("OIDS::str2oid_or_empty returns value after adding name mapping",
+                  Botan::OIDS::str2oid_or_empty("botan-test-oid3").to_string(), Botan::OID({1,2,345,6,888}).to_string());
+
+   return result;
+   }
+
+class OID_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         std::vector<std::function<Test::Result()>> fns =
+            {
+            test_add_have_OID,
+            test_add_have_OID_str,
+            test_add_and_lookup,
+            };
+
+         for(size_t i = 0; i != fns.size(); ++i)
+            {
+            try
+               {
+               results.emplace_back(fns[ i ]());
+               }
+            catch(const std::exception& e)
+               {
+               results.emplace_back(Test::Result::Failure("OID tests " + std::to_string(i), e.what()));
+               }
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("oid", OID_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_os_utils.cpp b/third_party/botan/src/tests/test_os_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_os_utils.cpp
@@ -0,0 +1,208 @@
+/*
+* Testing operating system specific wrapper code
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include <botan/internal/os_utils.h>
+
+// For __ud2 intrinsic
+#if defined(BOTAN_TARGET_COMPILER_IS_MSVC)
+#include <intrin.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+/*
+uint32_t get_process_id();
+uint64_t get_cpu_cycle_counter();
+uint64_t get_system_timestamp_ns();
+size_t get_memory_locking_limit();
+void* allocate_locked_pages(size_t length);
+void free_locked_pages(void* ptr, size_t length);
+int run_cpu_instruction_probe(std::function<int ()> probe_fn);
+*/
+
+class OS_Utils_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_get_process_id());
+         results.push_back(test_get_cpu_cycle_counter());
+         results.push_back(test_get_high_resolution_clock());
+         results.push_back(test_get_cpu_numbers());
+         results.push_back(test_get_system_timestamp());
+         results.push_back(test_memory_locking());
+         results.push_back(test_cpu_instruction_probe());
+
+         return results;
+         }
+
+   private:
+
+      Test::Result test_get_process_id()
+         {
+         Test::Result result("OS::get_process_id");
+
+         uint32_t pid1 = Botan::OS::get_process_id();
+         uint32_t pid2 = Botan::OS::get_process_id();
+
+         result.test_eq("PID same across calls", static_cast<size_t>(pid1), static_cast<size_t>(pid2));
+
+#if defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM)
+         result.test_eq("PID is expected to be zero on this platform", pid1, size_t(0));
+#else
+         result.test_ne("PID is non-zero on systems with processes", pid1, 0);
+#endif
+
+         return result;
+         }
+
+      Test::Result test_get_cpu_cycle_counter()
+         {
+         const size_t max_trials = 1024;
+         const size_t max_repeats = 32;
+
+         Test::Result result("OS::get_cpu_cycle_counter");
+
+         const uint64_t proc_ts1 = Botan::OS::get_cpu_cycle_counter();
+
+         if(proc_ts1 == 0)
+            {
+            const uint64_t proc_ts2 = Botan::OS::get_cpu_cycle_counter();
+            result.test_is_eq("Disabled processor timestamp stays at zero", proc_ts1, proc_ts2);
+            return result;
+            }
+
+         size_t counts = 0;
+         while(counts < max_trials && (Botan::OS::get_cpu_cycle_counter() == proc_ts1))
+            ++counts;
+
+         result.test_lt("CPU cycle counter eventually changes value", counts, max_repeats);
+
+         return result;
+         }
+
+      Test::Result test_get_high_resolution_clock()
+         {
+         const size_t max_trials = 1024;
+         const size_t max_repeats = 128;
+
+         Test::Result result("OS::get_high_resolution_clock");
+
+         // TODO better tests
+         const uint64_t hr_ts1 = Botan::OS::get_high_resolution_clock();
+         result.confirm("high resolution timestamp value is never zero", hr_ts1 != 0);
+
+         size_t counts = 0;
+         while(counts < max_trials && (Botan::OS::get_high_resolution_clock() == hr_ts1))
+            ++counts;
+
+         result.test_lt("high resolution clock eventually changes value", counts, max_repeats);
+
+         return result;
+         }
+
+      Test::Result test_get_cpu_numbers()
+         {
+         Test::Result result("OS::get_cpu_total/OS::get_cpu_available");
+
+         size_t tt = Botan::OS::get_cpu_total();
+         size_t ta = Botan::OS::get_cpu_available();
+
+         result.test_lte("get_cpu_available not greater than total", ta, tt);
+
+         return result;
+         }
+
+      Test::Result test_get_system_timestamp()
+         {
+         // TODO better tests
+         Test::Result result("OS::get_system_timestamp_ns");
+
+         uint64_t sys_ts1 = Botan::OS::get_system_timestamp_ns();
+         result.confirm("System timestamp value is never zero", sys_ts1 != 0);
+
+         // do something that consumes a little time
+         Botan::OS::get_process_id();
+
+         uint64_t sys_ts2 = Botan::OS::get_system_timestamp_ns();
+
+         result.confirm("System time moves forward", sys_ts1 <= sys_ts2);
+
+         return result;
+         }
+
+      Test::Result test_memory_locking()
+         {
+         Test::Result result("OS memory locked pages");
+
+         // TODO any tests...
+
+         return result;
+         }
+
+      Test::Result test_cpu_instruction_probe()
+         {
+         Test::Result result("OS::run_cpu_instruction_probe");
+
+         // OS::run_cpu_instruction_probe only implemented for Unix signals or Windows SEH
+
+         std::function<int ()> ok_fn = []() noexcept -> int { return 5; };
+         const int run_rc = Botan::OS::run_cpu_instruction_probe(ok_fn);
+
+         if(run_rc == -3)
+            {
+            result.test_note("run_cpu_instruction_probe not implemented on this platform");
+            return {result};
+            }
+
+         result.confirm("Correct result returned by working probe fn", run_rc == 5);
+
+         std::function<int ()> crash_probe;
+
+#if defined(BOTAN_TARGET_COMPILER_IS_MSVC)
+         crash_probe = []() noexcept -> int { __ud2(); return 3; };
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+         crash_probe = []() noexcept -> int { asm volatile("ud2"); return 3; };
+
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+         //ARM: asm volatile (".word 0xf7f0a000\n");
+         // illegal instruction in both ARM and Thumb modes
+         crash_probe = []() noexcept -> int { asm volatile(".word 0xe7f0def0\n"); return 3; };
+
+#else
+         /*
+         PPC: "The instruction with primary opcode 0, when the instruction does not consist
+         entirely of binary zeros"
+         Others ?
+         */
+#endif
+
+#endif
+
+         if(crash_probe)
+            {
+            const int crash_rc = Botan::OS::run_cpu_instruction_probe(crash_probe);
+            result.confirm("Result for function executing undefined opcode", crash_rc < 0);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("os_utils", OS_Utils_Tests);
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_otp.cpp b/third_party/botan/src/tests/test_otp.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_otp.cpp
@@ -0,0 +1,143 @@
+/*
+* OTP tests
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_HOTP)
+   #include <botan/parsing.h>
+   #include <botan/hotp.h>
+   #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_TOTP)
+   #include <botan/totp.h>
+   #include <botan/calendar.h>
+   #include <botan/hash.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_HOTP)
+
+class HOTP_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      HOTP_KAT_Tests()
+         : Text_Based_Test("otp/hotp.vec", "Key,Digits,Counter,OTP")
+         {}
+
+      bool clear_between_callbacks() const override { return false; }
+
+      Test::Result run_one_test(const std::string& hash_algo, const VarMap& vars) override
+         {
+         Test::Result result("HOTP " + hash_algo);
+
+         std::unique_ptr<Botan::HashFunction> hash_test = Botan::HashFunction::create(hash_algo);
+         if(!hash_test)
+            return {result};
+
+         const std::vector<uint8_t> key = vars.get_req_bin("Key");
+         const uint32_t otp = static_cast<uint32_t>(vars.get_req_sz("OTP"));
+         const uint64_t counter = vars.get_req_sz("Counter");
+         const size_t digits = vars.get_req_sz("Digits");
+
+         Botan::HOTP hotp(key, hash_algo, digits);
+
+         result.test_int_eq("OTP", hotp.generate_hotp(counter), otp);
+
+         std::pair<bool, uint64_t> otp_res = hotp.verify_hotp(otp, counter, 0);
+         result.test_eq("OTP verify result", otp_res.first, true);
+         result.confirm("OTP verify next counter", otp_res.second == counter + 1);
+
+         // Test invalid OTP
+         otp_res = hotp.verify_hotp(otp + 1, counter, 0);
+         result.test_eq("OTP verify result", otp_res.first, false);
+         result.confirm("OTP verify next counter", otp_res.second == counter);
+
+         // Test invalid OTP with long range
+         otp_res = hotp.verify_hotp(otp + 1, counter, 100);
+         result.test_eq("OTP verify result", otp_res.first, false);
+         result.confirm("OTP verify next counter", otp_res.second == counter);
+
+         // Test valid OTP with long range
+         otp_res = hotp.verify_hotp(otp, counter - 90, 100);
+         result.test_eq("OTP verify result", otp_res.first, true);
+         result.confirm("OTP verify next counter", otp_res.second == counter + 1);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("otp_hotp", HOTP_KAT_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_TOTP)
+
+class TOTP_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      TOTP_KAT_Tests()
+         : Text_Based_Test("otp/totp.vec", "Key,Digits,Timestep,Timestamp,OTP")
+         {}
+
+      bool clear_between_callbacks() const override { return false; }
+
+      Test::Result run_one_test(const std::string& hash_algo, const VarMap& vars) override
+         {
+         Test::Result result("TOTP " + hash_algo);
+
+         std::unique_ptr<Botan::HashFunction> hash_test = Botan::HashFunction::create(hash_algo);
+         if(!hash_test)
+            return {result};
+
+         const std::vector<uint8_t> key = vars.get_req_bin("Key");
+         const uint32_t otp = static_cast<uint32_t>(vars.get_req_sz("OTP"));
+         const size_t digits = vars.get_req_sz("Digits");
+         const size_t timestep = vars.get_req_sz("Timestep");
+         const std::string timestamp = vars.get_req_str("Timestamp");
+
+         Botan::TOTP totp(key, hash_algo, digits, timestep);
+
+         std::chrono::system_clock::time_point time = from_timestring(timestamp);
+         std::chrono::system_clock::time_point later_time = time + std::chrono::seconds(timestep);
+         std::chrono::system_clock::time_point too_late = time + std::chrono::seconds(2*timestep);
+
+         result.test_int_eq("TOTP generate", totp.generate_totp(time), otp);
+
+         result.test_eq("TOTP verify valid", totp.verify_totp(otp, time, 0), true);
+         result.test_eq("TOTP verify invalid", totp.verify_totp(otp ^ 1, time, 0), false);
+         result.test_eq("TOTP verify time slip", totp.verify_totp(otp, later_time, 0), false);
+         result.test_eq("TOTP verify time slip allowed", totp.verify_totp(otp, later_time, 1), true);
+         result.test_eq("TOTP verify time slip out of range", totp.verify_totp(otp, too_late, 1), false);
+
+         return result;
+         }
+
+   private:
+      std::chrono::system_clock::time_point from_timestring(const std::string& time_str)
+         {
+         if(time_str.size() != 19)
+            throw Test_Error("Invalid TOTP timestamp string " + time_str);
+         // YYYY-MM-DDTHH:MM:SS
+         // 0123456789012345678
+         const uint32_t year = Botan::to_u32bit(time_str.substr(0, 4));
+         const uint32_t month = Botan::to_u32bit(time_str.substr(5, 2));
+         const uint32_t day = Botan::to_u32bit(time_str.substr(8, 2));
+         const uint32_t hour = Botan::to_u32bit(time_str.substr(11, 2));
+         const uint32_t minute = Botan::to_u32bit(time_str.substr(14, 2));
+         const uint32_t second = Botan::to_u32bit(time_str.substr(17, 2));
+         return Botan::calendar_point(year, month, day, hour, minute, second).to_std_timepoint();
+         }
+   };
+
+BOTAN_REGISTER_TEST("otp_totp", TOTP_KAT_Tests);
+#endif
+
+}
+
+
diff --git a/third_party/botan/src/tests/test_package_transform.cpp b/third_party/botan/src/tests/test_package_transform.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_package_transform.cpp
@@ -0,0 +1,72 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PACKAGE_TRANSFORM)
+   #include <botan/package.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_PACKAGE_TRANSFORM)
+
+class Package_Transform_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("Package transform");
+
+         std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create("AES-128"));
+
+         if(!cipher)
+            return {result};
+
+         for(size_t input_len = 2; input_len != 256; ++input_len)
+            {
+            std::vector<uint8_t> input;
+            Test::rng().random_vec(input, input_len);
+            std::vector<uint8_t> output(input.size() + cipher->block_size());
+
+            // aont_package owns/deletes the passed cipher object, kind of a bogus API
+            Botan::aont_package(Test::rng(),
+                                cipher->clone(),
+                                input.data(), input.size(),
+                                output.data());
+
+            std::vector<uint8_t> decoded(output.size() - cipher->block_size());
+            Botan::aont_unpackage(cipher->clone(),
+                                  output.data(), output.size(),
+                                  decoded.data());
+            result.test_eq("Package transform is reversible", decoded, input);
+
+            output[0] ^= 1;
+            Botan::aont_unpackage(cipher->clone(),
+                                  output.data(), output.size(),
+                                  decoded.data());
+            result.test_ne("Bitflip breaks package transform", decoded, input);
+
+            output[0] ^= 1;
+            Botan::aont_unpackage(cipher->clone(),
+                                  output.data(), output.size(),
+                                  decoded.data());
+            result.test_eq("Package transform is still reversible", decoded, input);
+
+            // More tests including KATs would be useful for these functions
+            }
+
+         return std::vector<Test::Result> {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("package_transform", Package_Transform_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_pad.cpp b/third_party/botan/src/tests/test_pad.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pad.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_CIPHER_MODE_PADDING)
+   #include <botan/mode_pad.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_CIPHER_MODE_PADDING)
+
+class Cipher_Mode_Padding_Tests final : public Text_Based_Test
+   {
+   public:
+      Cipher_Mode_Padding_Tests() : Text_Based_Test("pad.vec", "In,Blocksize", "Out") {}
+
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_opt_bin("Out");
+         const size_t block_size = vars.get_req_sz("Blocksize");
+
+         std::string algo = header;
+
+         auto underscore = algo.find('_');
+         if(underscore != std::string::npos)
+            {
+            if(algo.substr(underscore + 1, std::string::npos) != "Invalid")
+               {
+               throw Test_Error("Unexpected padding header " + header);
+               }
+            algo = algo.substr(0, underscore);
+            }
+
+         Test::Result result(algo);
+
+         std::unique_ptr<Botan::BlockCipherModePaddingMethod> pad(Botan::get_bc_pad(algo));
+
+         if(!pad)
+            {
+            result.test_failure("Invalid padding method: " + algo);
+            return result;
+            }
+
+         if(expected.empty())
+            {
+            // This is an unpad an invalid input and ensure we reject
+            if(pad->unpad(input.data(), block_size) != block_size)
+               {
+               result.test_failure("Did not reject invalid padding", Botan::hex_encode(input));
+               }
+            else
+               {
+               result.test_success("Rejected invalid padding");
+               }
+            }
+         else
+            {
+            // This is a pad plaintext and unpad valid padding round trip test
+            Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+            pad->add_padding(buf, input.size() % block_size, block_size);
+            result.test_eq("pad", buf, expected);
+
+            buf.assign(expected.begin(), expected.end());
+
+            const size_t last_block = (buf.size() < block_size) ? 0 : buf.size() - block_size;
+            const size_t pad_bytes = block_size - pad->unpad(&buf[last_block], block_size);
+            buf.resize(buf.size() - pad_bytes); // remove padding
+            result.test_eq("unpad", buf, input);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bc_pad", Cipher_Mode_Padding_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_passhash.cpp b/third_party/botan/src/tests/test_passhash.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_passhash.cpp
@@ -0,0 +1,210 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_BCRYPT)
+   #include <botan/bcrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+   #include <botan/passhash9.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+   #include <botan/argon2.h>
+   #include "test_rng.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_BCRYPT)
+class Bcrypt_Tests final : public Text_Based_Test
+   {
+   public:
+      Bcrypt_Tests() : Text_Based_Test("passhash/bcrypt.vec", "Password,Passhash") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         // Encoded as binary so we can test binary inputs
+         const std::vector<uint8_t> password_vec = vars.get_req_bin("Password");
+         const std::string password(reinterpret_cast<const char*>(password_vec.data()),
+                                    password_vec.size());
+
+         const std::string passhash = vars.get_req_str("Passhash");
+
+         Test::Result result("bcrypt");
+         result.test_eq("correct hash accepted", Botan::check_bcrypt(password, passhash), true);
+
+         // self-test low levels for each test password
+         for(uint16_t level = 4; level <= 6; ++level)
+            {
+            const std::string gen_hash = Botan::generate_bcrypt(password, Test::rng(), level);
+            result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true);
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("bcrypt");
+
+         uint64_t start = Test::timestamp();
+
+         const std::string password = "ag00d1_2BE5ur3";
+
+         const uint16_t max_level = (Test::run_long_tests() ? 15 : 10);
+
+         for(uint16_t level = 4; level <= max_level; ++level)
+            {
+            const std::string gen_hash = Botan::generate_bcrypt(password, Test::rng(), level);
+            result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true);
+            }
+
+         result.test_throws("Invalid bcrypt version rejected",
+                            "Unknown bcrypt version 'q'",
+                            []() { Botan::generate_bcrypt("pass", Test::rng(), 4, 'q'); });
+
+         result.set_ns_consumed(Test::timestamp() - start);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("bcrypt", Bcrypt_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+class Argon2_Tests final : public Text_Based_Test
+   {
+   public:
+      Argon2_Tests() : Text_Based_Test("passhash/argon2.vec", "Password,Passhash", "Mode,M,T,P,Salt,OutLen") {}
+
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override
+         {
+         const std::string password = vars.get_req_str("Password");
+         const std::string passhash = vars.get_req_str("Passhash");
+
+         Test::Result result("Argon2 password hash");
+
+         if(header == "Verify")
+            {
+            const bool accepted = Botan::argon2_check_pwhash(password.data(), password.size(), passhash);
+            result.test_eq("correct hash accepted", accepted, true);
+            }
+         else if(header == "Generate")
+            {
+            const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+            const uint8_t y = vars.get_req_u8("Mode");
+            const size_t M = vars.get_req_sz("M");
+            const size_t t = vars.get_req_sz("T");
+            const size_t p = vars.get_req_sz("P");
+            const size_t out_len = vars.get_req_sz("OutLen");
+
+            Fixed_Output_RNG rng(salt);
+
+            const std::string generated = Botan::argon2_generate_pwhash(password.data(), password.size(),
+                                                                        rng,
+                                                                        p, M, t, y, salt.size(), out_len);
+
+            result.test_eq("expected hash generated", generated, passhash);
+            const bool accepted = Botan::argon2_check_pwhash(password.data(), password.size(), generated);
+            result.test_eq("generated hash accepted", accepted, true);
+            }
+         else
+            throw Test_Error("Unexpected header in Argon2 password hash test file");
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("argon2_pass", Argon2_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+class Passhash9_Tests final : public Text_Based_Test
+   {
+   public:
+      Passhash9_Tests() : Text_Based_Test("passhash/passhash9.vec", "Password,Passhash,PRF") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         // Encoded as binary so we can test binary inputs
+         const std::vector<uint8_t> password_vec = vars.get_req_bin("Password");
+         const std::string password(reinterpret_cast<const char*>(password_vec.data()),
+                                    password_vec.size());
+
+         const std::string passhash = vars.get_req_str("Passhash");
+         const std::size_t prf = vars.get_req_sz("PRF");
+
+         Test::Result result("passhash9");
+
+         if(Botan::is_passhash9_alg_supported(uint8_t(prf)))
+            {
+            result.test_eq("correct hash accepted", Botan::check_passhash9(password, passhash), true);
+            }
+
+         for(uint8_t alg_id = 0; alg_id <= 4; ++alg_id)
+            {
+            if(Botan::is_passhash9_alg_supported(alg_id))
+               {
+               const std::string gen_hash = Botan::generate_passhash9(password, Test::rng(), 2, alg_id);
+
+               if(!result.test_eq("generated hash accepted", Botan::check_passhash9(password, gen_hash), true))
+                  {
+                  result.test_note("hash was " + gen_hash);
+                  }
+               }
+            }
+
+         const uint16_t max_level = (Test::run_long_tests() ? 14 : 8);
+
+         for(uint16_t level = 1; level <= max_level; ++level)
+            {
+            const uint8_t alg_id = 1; // default used by generate_passhash9()
+            if(Botan::is_passhash9_alg_supported(alg_id))
+               {
+               const std::string gen_hash = Botan::generate_passhash9(password, Test::rng(), level, alg_id);
+               if(!result.test_eq("generated hash accepted", Botan::check_passhash9(password, gen_hash), true))
+                  {
+                  result.test_note("hash was " + gen_hash);
+                  }
+               }
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("passhash9");
+
+         result.confirm("Unknown algorithm is unknown",
+                        Botan::is_passhash9_alg_supported(255) == false);
+
+         result.test_throws("Throws if algorithm not supported",
+                            "Passhash9: Algorithm id 255 is not defined",
+                            []() { Botan::generate_passhash9("pass", Test::rng(), 3, 255); });
+
+         result.test_throws("Throws if iterations is too high",
+                            "Requested passhash9 work factor 513 is too large",
+                            []() { Botan::check_passhash9("floof", "$9$AgIB3c5J3kvAuML84sZ5hWT9WzJtiYRPLCEARaujS7I6IKbNCwp0"); });
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("passhash9", Passhash9_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_pbkdf.cpp b/third_party/botan/src/tests/test_pbkdf.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pbkdf.cpp
@@ -0,0 +1,378 @@
+/*
+* (C) 2014,2015,2019 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PBKDF)
+   #include <botan/pbkdf.h>
+   #include <botan/pwdhash.h>
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+   #include <botan/pgp_s2k.h>
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+   #include <botan/scrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+   #include <botan/argon2.h>
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+   #include <botan/bcrypt_pbkdf.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_PBKDF)
+class PBKDF_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      PBKDF_KAT_Tests() : Text_Based_Test("pbkdf", "Iterations,Salt,Passphrase,Output") {}
+
+      Test::Result run_one_test(const std::string& pbkdf_name, const VarMap& vars) override
+         {
+         const size_t iterations = vars.get_req_sz("Iterations");
+         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+         const std::string passphrase = vars.get_req_str("Passphrase");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+         const size_t outlen = expected.size();
+
+         Test::Result result(pbkdf_name);
+         std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create(pbkdf_name));
+
+         if(!pbkdf)
+            {
+            result.note_missing(pbkdf_name);
+            return result;
+            }
+
+         result.test_eq("Expected name", pbkdf->name(), pbkdf_name);
+
+         const Botan::secure_vector<uint8_t> derived =
+            pbkdf->derive_key(outlen, passphrase, salt.data(), salt.size(), iterations).bits_of();
+         result.test_eq("derived key", derived, expected);
+
+         auto pwdhash_fam = Botan::PasswordHashFamily::create(pbkdf_name);
+
+         if(!pwdhash_fam)
+            {
+            result.note_missing("No PasswordHashFamily for " + pbkdf_name);
+            return result;
+            }
+
+         auto pwdhash = pwdhash_fam->from_params(iterations);
+
+         std::vector<uint8_t> pwdhash_derived(outlen);
+         pwdhash->derive_key(pwdhash_derived.data(), pwdhash_derived.size(),
+                             passphrase.c_str(), passphrase.size(),
+                             salt.data(), salt.size());
+
+         result.test_eq("pwdhash derived key", pwdhash_derived, expected);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("pbkdf", PBKDF_KAT_Tests);
+
+class Pwdhash_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         const std::vector<std::string> all_pwdhash = {
+            "Scrypt",
+            "PBKDF2(SHA-256)",
+            "OpenPGP-S2K(SHA-384)",
+            "Argon2d",
+            "Argon2i",
+            "Argon2id",
+            "Bcrypt-PBKDF"
+         };
+
+         for(std::string pwdhash : all_pwdhash)
+            {
+            Test::Result result("Pwdhash " + pwdhash);
+            auto pwdhash_fam = Botan::PasswordHashFamily::create(pwdhash);
+
+            if(pwdhash_fam)
+               {
+               result.start_timer();
+
+               const std::vector<uint8_t> salt(8);
+               const std::string password = "test";
+
+               auto tuned_pwhash = pwdhash_fam->tune(32, std::chrono::milliseconds(10));
+
+               std::vector<uint8_t> output1(32);
+               tuned_pwhash->derive_key(output1.data(), output1.size(),
+                                        password.c_str(), password.size(),
+                                        salt.data(), salt.size());
+
+               std::unique_ptr<Botan::PasswordHash> pwhash;
+
+               if(pwdhash_fam->name() == "Scrypt" || pwdhash_fam->name().find("Argon2") == 0)
+                  {
+                  pwhash = pwdhash_fam->from_params(tuned_pwhash->memory_param(),
+                                                    tuned_pwhash->iterations(),
+                                                    tuned_pwhash->parallelism());
+                  }
+               else
+                  {
+                  pwhash = pwdhash_fam->from_params(tuned_pwhash->iterations());
+                  }
+
+               std::vector<uint8_t> output2(32);
+               pwhash->derive_key(output2.data(), output2.size(),
+                                  password.c_str(), password.size(),
+                                  salt.data(), salt.size());
+
+               result.test_eq("PasswordHash produced same output when run with same params",
+                              output1, output2);
+
+               auto default_pwhash = pwdhash_fam->default_params();
+               std::vector<uint8_t> output3(32);
+               default_pwhash->derive_key(output3.data(), output3.size(),
+                                          password.c_str(), password.size(),
+                                          salt.data(), salt.size());
+
+               result.end_timer();
+               }
+            else
+               result.test_note("No such algo " + pwdhash);
+
+            results.push_back(result);
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("pwdhash", Pwdhash_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+
+class Bcrypt_PBKDF_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      Bcrypt_PBKDF_KAT_Tests() : Text_Based_Test("bcrypt_pbkdf.vec", "Passphrase,Salt,Iterations,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const size_t rounds = vars.get_req_sz("Iterations");
+         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+         const std::string passphrase = vars.get_req_str("Passphrase");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         Test::Result result("bcrypt PBKDF");
+
+         std::vector<uint8_t> output(expected.size());
+         Botan::bcrypt_pbkdf(output.data(), output.size(),
+                             passphrase.data(), passphrase.size(),
+                             salt.data(), salt.size(),
+                             rounds);
+
+         result.test_eq("derived key", output, expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bcrypt_pbkdf", Bcrypt_PBKDF_KAT_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+
+class Scrypt_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      Scrypt_KAT_Tests() : Text_Based_Test("scrypt.vec", "Passphrase,Salt,N,R,P,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const size_t N = vars.get_req_sz("N");
+         const size_t R = vars.get_req_sz("R");
+         const size_t P = vars.get_req_sz("P");
+         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+         const std::string passphrase = vars.get_req_str("Passphrase");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         Test::Result result("scrypt");
+
+         if(N >= 1048576 && Test::run_long_tests() == false)
+            return result;
+
+         std::vector<uint8_t> output(expected.size());
+         Botan::scrypt(output.data(), output.size(),
+                       passphrase, salt.data(), salt.size(),
+                       N, R, P);
+
+         result.test_eq("derived key", output, expected);
+
+         auto pwdhash_fam = Botan::PasswordHashFamily::create("Scrypt");
+
+         if(!pwdhash_fam)
+            {
+            result.test_failure("Scrypt is missing PasswordHashFamily");
+            return result;
+            }
+
+         auto pwdhash = pwdhash_fam->from_params(N, R, P);
+
+         std::vector<uint8_t> pwdhash_derived(expected.size());
+         pwdhash->derive_key(pwdhash_derived.data(), pwdhash_derived.size(),
+                             passphrase.c_str(), passphrase.size(),
+                             salt.data(), salt.size());
+
+         result.test_eq("pwdhash derived key", pwdhash_derived, expected);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("scrypt", Scrypt_KAT_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+
+class Argon2_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      Argon2_KAT_Tests() : Text_Based_Test("argon2.vec", "Passphrase,Salt,P,M,T,Output", "Secret,AD") {}
+
+      Test::Result run_one_test(const std::string& mode, const VarMap& vars) override
+         {
+         const size_t P = vars.get_req_sz("P");
+         const size_t M = vars.get_req_sz("M");
+         const size_t T = vars.get_req_sz("T");
+         const std::vector<uint8_t> key = vars.get_opt_bin("Secret");
+         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
+         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
+         const std::vector<uint8_t> passphrase = vars.get_req_bin("Passphrase");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
+
+         uint8_t family;
+         if(mode == "Argon2d")
+            family = 0;
+         else if(mode == "Argon2i")
+            family = 1;
+         else if(mode == "Argon2id")
+            family = 2;
+         else
+            throw Test_Error("Unknown Argon2 mode");
+
+         Test::Result result(mode);
+
+         std::vector<uint8_t> output(expected.size());
+         Botan::argon2(output.data(), output.size(),
+                       reinterpret_cast<const char*>(passphrase.data()),
+                       passphrase.size(),
+                       salt.data(), salt.size(),
+                       key.data(), key.size(),
+                       ad.data(), ad.size(),
+                       family, P, M, T);
+
+         result.test_eq("derived key", output, expected);
+
+         auto pwdhash_fam = Botan::PasswordHashFamily::create(mode);
+
+         if(!pwdhash_fam)
+            {
+            result.test_failure("Argon2 is missing PasswordHashFamily");
+            return result;
+            }
+
+         if(ad.size() == 0)
+            {
+            auto pwdhash = pwdhash_fam->from_params(M, T, P);
+
+            std::vector<uint8_t> pwdhash_derived(expected.size());
+            pwdhash->derive_key(pwdhash_derived.data(), pwdhash_derived.size(),
+                                reinterpret_cast<const char*>(passphrase.data()),
+                                passphrase.size(),
+                                salt.data(), salt.size());
+
+            result.test_eq("pwdhash derived key", pwdhash_derived, expected);
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("argon2", Argon2_KAT_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+
+class PGP_S2K_Iter_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("PGP_S2K iteration encoding");
+
+         // The maximum representable iteration count
+         const size_t max_iter = 65011712;
+
+         result.test_eq("Encoding of large value accepted",
+                        Botan::RFC4880_encode_count(max_iter * 2), size_t(255));
+         result.test_eq("Encoding of small value accepted",
+                        Botan::RFC4880_encode_count(0), size_t(0));
+
+         for(size_t c = 0; c != 256; ++c)
+            {
+            const size_t dec = Botan::RFC4880_decode_count(static_cast<uint8_t>(c));
+            const size_t comp_dec = (16 + (c & 0x0F)) << ((c >> 4) + 6);
+            result.test_eq("Decoded value matches PGP formula", dec, comp_dec);
+
+            const size_t enc = Botan::RFC4880_encode_count(comp_dec);
+            result.test_eq("Encoded value matches PGP formula", enc, c);
+            }
+
+         uint8_t last_enc = 0;
+
+         for(size_t i = 0; i <= max_iter; i += 64)
+            {
+            const uint8_t enc = Botan::RFC4880_encode_count(i);
+            result.test_lte("Encoded value non-decreasing", last_enc, enc);
+
+            /*
+            The iteration count as encoded may not be exactly the
+            value requested, but should never be less
+            */
+            const size_t dec = Botan::RFC4880_decode_count(enc);
+            result.test_gte("Decoded value is >= requested", dec, i);
+
+            last_enc = enc;
+            }
+
+         return std::vector<Test::Result>{result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("pgp_s2k_iter", PGP_S2K_Iter_Test);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_pem.cpp b/third_party/botan/src/tests/test_pem.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pem.cpp
@@ -0,0 +1,56 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PEM_CODEC)
+
+#include <botan/pem.h>
+
+namespace Botan_Tests {
+
+class PEM_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("PEM encoding");
+
+         std::vector<uint8_t> vec = { 0, 1, 2, 3, 4 };
+
+         const std::string pem1 = Botan::PEM_Code::encode(vec, "BUNNY", 3);
+
+         result.test_eq("PEM encoding", pem1, "-----BEGIN BUNNY-----\nAAE\nCAw\nQ=\n-----END BUNNY-----\n");
+
+         std::string label1 = "this is overwritten";
+         const Botan::secure_vector<uint8_t> decoded1 = Botan::PEM_Code::decode(pem1, label1);
+
+         result.test_eq("PEM decoding label", label1, "BUNNY");
+
+         result.test_throws("PEM decoding unexpected label",
+                            "PEM: Label mismatch, wanted FLOOFY, got BUNNY",
+                            [pem1]() { Botan::PEM_Code::decode_check_label(pem1, "FLOOFY"); });
+
+         const std::string malformed_pem1 = "---BEGIN BUNNY-----\n-----END BUNNY-----";
+         result.test_throws("PEM decoding bad init label",
+                            "PEM: No PEM header found",
+                            [malformed_pem1]() { Botan::PEM_Code::decode_check_label(malformed_pem1, "BUNNY"); });
+
+         const std::string malformed_pem2 = "-----BEGIN BUNNY-----\n-----END FLOOFY-----";
+         result.test_throws("PEM decoding bad init label",
+                            "PEM: Malformed PEM trailer",
+                            [malformed_pem2]() { Botan::PEM_Code::decode_check_label(malformed_pem2, "BUNNY"); });
+
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("pem", PEM_Tests);
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_pk_pad.cpp b/third_party/botan/src/tests/test_pk_pad.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pk_pad.cpp
@@ -0,0 +1,185 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PK_PADDING)
+   #include <botan/emsa.h>
+#endif
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+   #include <botan/eme_pkcs.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+
+class EME_PKCS1v15_Decoding_Tests final : public Text_Based_Test
+   {
+   public:
+      EME_PKCS1v15_Decoding_Tests()
+         : Text_Based_Test(
+              "pk_pad_eme/pkcs1.vec",
+              "RawCiphertext",
+              "Plaintext") {}
+
+      Test::Result run_one_test(const std::string& hdr, const VarMap& vars) override
+         {
+         const bool is_valid = (hdr == "valid");
+
+         Test::Result result("PKCSv15 Decoding");
+
+         Botan::EME_PKCS1v15 pkcs;
+
+         const std::vector<uint8_t> ciphertext = vars.get_req_bin("RawCiphertext");
+         const std::vector<uint8_t> plaintext = vars.get_opt_bin("Plaintext");
+
+         if(is_valid == false)
+            {
+            result.test_eq("Plaintext value should be empty for invalid EME inputs", plaintext.size(), 0);
+            }
+
+         uint8_t valid_mask = 0;
+         Botan::secure_vector<uint8_t> decoded =
+            pkcs.unpad(valid_mask, ciphertext.data(), ciphertext.size());
+
+         result.confirm("EME valid_mask has expected value", valid_mask == 0x00 || valid_mask == 0xFF);
+         result.test_eq("EME decoding valid/invalid matches", valid_mask == 0xFF, is_valid);
+
+         if(valid_mask == 0xFF)
+            {
+            result.test_eq("EME decoded plaintext correct", decoded, plaintext);
+            }
+         else
+            {
+            bool all_zeros = true;
+            for(size_t i = 0; i != decoded.size(); ++i)
+               if(decoded[i] != 0)
+                  all_zeros = false;
+
+            result.confirm("On invalid padding output is all zero", all_zeros);
+            }
+
+         // TODO: also test that encoding is accepted
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("eme_pkcs1v15", EME_PKCS1v15_Decoding_Tests);
+
+class EMSA_unit_tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result name_tests("EMSA_name_tests");
+
+         std::vector<std::string> pads_need_hash =
+            {
+#if BOTAN_HAS_EMSA1
+             "EMSA1",
+#endif
+#if BOTAN_HAS_EMSA_X931
+             "EMSA2",
+#endif
+#if BOTAN_HAS_EMSA_PKCS1
+             "EMSA3",
+#endif
+#if BOTAN_HAS_EMSA_PSSR
+             "EMSA4",
+             "PSSR_Raw",
+#endif
+#if BOTAN_HAS_ISO_9796
+             "ISO_9796_DS2",
+             "ISO_9796_DS3",
+#endif
+            };
+
+         std::vector<std::string> pads_no_hash =
+            {
+#if BOTAN_HAS_EMSA_RAW
+            "Raw",
+#endif
+#if BOTAN_HAS_EMSA_PKCS1
+            "EMSA3(Raw)",
+            "EMSA3(Raw,SHA-512)",
+#endif
+            };
+
+         for(auto pad : pads_need_hash)
+            {
+            try
+               {
+               const std::string hash_to_use = Botan::hash_for_emsa(pad);
+               std::unique_ptr<Botan::EMSA> emsa_1(
+                  Botan::get_emsa(pad + "(" + hash_to_use + ")"));
+               std::unique_ptr<Botan::EMSA> emsa_2(Botan::get_emsa(emsa_1->name()));
+               name_tests.test_eq("EMSA_name_test for " + pad,
+                     emsa_1->name(), emsa_2->name());
+               }
+            catch(Botan::Lookup_Error&)
+               {
+               name_tests.test_note("Skipping test due to missing hash");
+               }
+            catch(const std::exception& e)
+               {
+               name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what());
+               }
+            }
+
+         for(auto pad : pads_need_hash)
+            {
+            std::string algo_name = pad + "(YYZ)";
+            try
+               {
+               std::unique_ptr<Botan::EMSA> emsa(
+                  Botan::get_emsa(algo_name));
+               name_tests.test_failure("EMSA_name_test for " + pad + ": " +
+                   "Could create EMSA with fantasy hash YYZ");
+               }
+            catch(Botan::Lookup_Error&)
+               {
+               name_tests.test_note("Skipping test due to missing hash");
+               }
+            catch(const std::exception& e)
+               {
+               name_tests.test_eq("EMSA_name_test for " + pad,
+                   e.what(),
+                   "Could not find any algorithm named \"" + algo_name + "\"");
+               }
+            }
+
+         for(auto pad : pads_no_hash)
+            {
+            try
+               {
+               std::unique_ptr<Botan::EMSA> emsa_1(Botan::get_emsa(pad));
+               std::unique_ptr<Botan::EMSA> emsa_2(Botan::get_emsa(emsa_1->name()));
+               name_tests.test_eq("EMSA_name_test for " + pad,
+                     emsa_1->name(), emsa_2->name());
+               }
+            catch(Botan::Lookup_Error&)
+               {
+               name_tests.test_note("Skipping test due to missing hash");
+               }
+            catch(const std::exception& e)
+               {
+               name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what());
+               }
+            }
+
+
+         return { name_tests };
+         }
+   };
+
+BOTAN_REGISTER_TEST("pk_pad_emsa_unit", EMSA_unit_tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_pkcs11.h b/third_party/botan/src/tests/test_pkcs11.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pkcs11.h
@@ -0,0 +1,66 @@
+/*
+* (C) 2016 Daniel Neus
+* (C) 2019 Michael Boric
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TESTS_PKCS11_H_
+#define BOTAN_TESTS_PKCS11_H_
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PKCS11)
+   #include <botan/p11.h>
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_PKCS11)
+
+#define STRING_AND_FUNCTION(x) #x, x
+
+// PIN is expected to be set to "123456" prior to running the tests
+const std::string PKCS11_USER_PIN = "123456";
+// SO PIN is expected to be set to "12345678" prior to running the tests
+const std::string PKCS11_SO_PIN = "12345678";
+
+// These are pins that should just not match the above (valid) PINs
+const std::string PKCS11_TEST_USER_PIN = "654321";
+const std::string PKCS11_TEST_SO_PIN = "87654321";
+
+inline Botan::PKCS11::secure_string to_sec_string(const std::string& str)
+   {
+   return Botan::PKCS11::secure_string(str.begin(), str.end());
+   }
+
+inline Botan::PKCS11::secure_string PIN()
+   {
+   return to_sec_string(PKCS11_USER_PIN);
+   }
+
+inline Botan::PKCS11::secure_string SO_PIN()
+   {
+   return to_sec_string(PKCS11_SO_PIN);
+   }
+
+inline Botan::PKCS11::secure_string TEST_PIN()
+   {
+   return to_sec_string(PKCS11_TEST_USER_PIN);
+   }
+
+inline Botan::PKCS11::secure_string TEST_SO_PIN()
+   {
+   return to_sec_string(PKCS11_TEST_SO_PIN);
+   }
+
+std::vector<Test::Result> run_pkcs11_tests(const std::string& name,
+   std::vector<std::pair<std::string, std::function<Test::Result()>>>& fns);
+
+#endif
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_pkcs11_high_level.cpp b/third_party/botan/src/tests/test_pkcs11_high_level.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pkcs11_high_level.cpp
@@ -0,0 +1,1656 @@
+/*
+* (C) 2016 Daniel Neus
+* (C) 2016 Philipp Weber
+* (C) 2019 Michael Boric
+* (C) 2020 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_pkcs11.h"
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <numeric>
+
+#if defined(BOTAN_HAS_PKCS11)
+   #include <botan/p11.h>
+   #include <botan/p11_slot.h>
+   #include <botan/p11_session.h>
+   #include <botan/p11_module.h>
+   #include <botan/p11_object.h>
+   #include <botan/p11_randomgenerator.h>
+#endif
+
+#if defined(BOTAN_HAS_ASN1)
+   #include <botan/der_enc.h>
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+   #include <botan/pubkey.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+   #include <botan/ec_group.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCS11)
+   #include <botan/rsa.h>
+   #include <botan/p11_rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_PKCS11)
+   #include <botan/ecdsa.h>
+   #include <botan/p11_ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_PKCS11)
+   #include <botan/ecdh.h>
+   #include <botan/p11_ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_PKCS11)
+   #include <botan/p11_x509.h>
+   #include <botan/x509_dn.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+   #include <botan/hmac_drbg.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_PKCS11)
+
+std::vector<Test::Result> run_pkcs11_tests(
+   const std::string& name,
+   std::vector<std::pair<std::string, std::function<Test::Result()>>>& fns)
+   {
+   std::vector<Test::Result> results;
+
+   for(size_t i = 0; i != fns.size(); ++i)
+      {
+      try
+         {
+         results.push_back(fns[i].second());
+         }
+      catch(Botan::PKCS11::PKCS11_ReturnError& e)
+         {
+         results.push_back(Test::Result::Failure(name + " test " + fns[i].first, e.what()));
+
+         if(e.get_return_value() == Botan::PKCS11::ReturnValue::PinIncorrect)
+            {
+            break; // Do not continue to not potentially lock the token
+            }
+         }
+      catch(std::exception& e)
+         {
+         results.push_back(Test::Result::Failure(name + " test " + fns[i].first, e.what()));
+         }
+      }
+
+   return results;
+   }
+
+namespace {
+
+using namespace Botan;
+using namespace PKCS11;
+
+class TestSession
+   {
+   public:
+      explicit TestSession(bool login) :
+         m_module(new Module(Test::pkcs11_lib()))
+         {
+         std::vector<SlotId> slot_vec = Slot::get_available_slots(*m_module, true);
+         m_slot.reset(new Slot(*m_module, slot_vec.at(0)));
+         m_session.reset(new Session(*m_slot, false));
+         if(login)
+            {
+            m_session->login(UserType::User, PIN());
+            }
+         }
+      inline Session& session() const
+         {
+         return *m_session;
+         }
+
+      inline Slot& slot() const
+         {
+         return *m_slot;
+         }
+
+   private:
+      std::unique_ptr<Module> m_module = nullptr;
+      std::unique_ptr<Slot> m_slot = nullptr;
+      std::unique_ptr<Session> m_session = nullptr;
+   };
+
+/***************************** Module *****************************/
+
+Test::Result test_module_ctor()
+   {
+   Test::Result result("Module ctor");
+
+   result.test_throws("Module ctor fails for non existent path", []()
+      {
+      Module failing_module("/a/b/c");
+      });
+
+   Module module(Test::pkcs11_lib());
+   result.test_success("Module ctor did not throw and completed successfully");
+
+   return result;
+   }
+
+Test::Result test_module_reload()
+   {
+   Test::Result result("Module reload");
+
+   Module module(Test::pkcs11_lib());
+
+   module.reload();
+   result.test_success("Module reload did not throw and completed successfully");
+
+   module.get_info();
+   result.test_success("Module get_info() still works after reload");
+
+   return result;
+   }
+
+Test::Result test_multiple_modules()
+   {
+   Test::Result result("Module copy");
+   Module first_module(Test::pkcs11_lib());
+
+   result.test_throws("Module ctor fails if module is already initialized", []()
+      {
+      Module second_module(Test::pkcs11_lib());
+      });
+
+   return result;
+   }
+
+Test::Result test_module_get_info()
+   {
+   Test::Result result("Module info");
+
+   Module module(Test::pkcs11_lib());
+
+   Info info = module.get_info();
+   result.test_ne("Cryptoki version != 0", info.cryptokiVersion.major, 0);
+
+   return result;
+   }
+
+class Module_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns =
+            {
+            {STRING_AND_FUNCTION(test_module_ctor)},
+            {STRING_AND_FUNCTION(test_multiple_modules)},
+            {STRING_AND_FUNCTION(test_module_get_info)},
+            {STRING_AND_FUNCTION(test_module_reload)}
+            };
+
+         return run_pkcs11_tests("Module", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-module", Module_Tests);
+
+/***************************** Slot *****************************/
+
+Test::Result test_slot_get_available_slots()
+   {
+   Test::Result result("Slot get_available_slots");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   result.test_gte("Available Slots with attached token >= 1", slot_vec.size(), 1);
+
+   return result;
+   }
+
+Test::Result test_slot_ctor()
+   {
+   Test::Result result("Slot ctor");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+
+   Slot slot(module, slot_vec.at(0));
+   result.test_success("Slot ctor completed successfully");
+   result.test_is_eq(slot.slot_id(), slot_vec.at(0));
+
+   return result;
+   }
+
+Test::Result test_get_slot_info()
+   {
+   Test::Result result("Slot get_slot_info");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   SlotInfo info = slot.get_slot_info();
+   std::string description = reinterpret_cast< char* >(info.slotDescription);
+   result.confirm("Slot description is not empty", !description.empty());
+
+   return result;
+   }
+
+SlotId get_invalid_slot_id(Module& module)
+   {
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, false);
+
+   SlotId invalid_id = 0;
+
+   // find invalid slot id
+   while(std::find(slot_vec.begin(), slot_vec.end(), invalid_id) != slot_vec.end())
+      {
+      invalid_id++;
+      }
+
+   return invalid_id;
+   }
+
+Test::Result test_slot_invalid_id()
+   {
+   Test::Result result("Slot get_slot_info with invalid slot id");
+
+   Module module(Test::pkcs11_lib());
+
+   SlotId invalid_id = get_invalid_slot_id(module);
+
+   Slot slot(module, invalid_id);
+
+   result.test_throws("get_slot_info fails for non existent slot id", [ &slot ]()
+      {
+      slot.get_slot_info();
+      });
+
+   return result;
+   }
+
+Test::Result test_get_token_info()
+   {
+   Test::Result result("Slot get_token_info");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   TokenInfo info = slot.get_token_info();
+   std::string label = reinterpret_cast< char* >(info.label);
+   result.confirm("Token label is not empty", ! label.empty());
+
+   return result;
+   }
+
+Test::Result test_get_mechanism_list()
+   {
+   Test::Result result("Slot get_mechanism_list");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   std::vector<MechanismType> mechanisms = slot.get_mechanism_list();
+   result.confirm("The Slot supports at least one mechanism", !mechanisms.empty());
+
+   return result;
+   }
+
+Test::Result test_get_mechanisms_info()
+   {
+   Test::Result result("Slot get_mechanism_info");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   slot.get_mechanism_info(MechanismType::RsaPkcsKeyPairGen);
+   result.test_success("get_mechanism_info() completed successfully.");
+
+   return result;
+   }
+
+class Slot_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_slot_get_available_slots)},
+            {STRING_AND_FUNCTION(test_slot_ctor)},
+            {STRING_AND_FUNCTION(test_get_slot_info)},
+            {STRING_AND_FUNCTION(test_slot_invalid_id)},
+            {STRING_AND_FUNCTION(test_get_token_info)},
+            {STRING_AND_FUNCTION(test_get_mechanism_list)},
+            {STRING_AND_FUNCTION(test_get_mechanisms_info)}
+         };
+
+         return run_pkcs11_tests("Slot", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-slot", Slot_Tests);
+
+/***************************** Session *****************************/
+
+Test::Result test_session_ctor()
+   {
+   Test::Result result("Session ctor");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+      {
+      Session read_only_session(slot, true);
+      result.test_success("read only session opened successfully");
+      }
+      {
+      Session read_write_session(slot, false);
+      result.test_success("read write session opened successfully");
+      }
+      {
+      Flags flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+      Session read_write_session2(slot, flags, nullptr, nullptr);
+      result.test_success("read write session with flags param opened successfully");
+      }
+      {
+      Session read_only_session(slot, true);
+      Session read_write_session(slot, false);
+      result.test_success("Opened multiple sessions successfully");
+      }
+
+   return result;
+   }
+
+Test::Result test_session_ctor_invalid_slot()
+   {
+   Test::Result result("Session ctor with invalid slot id");
+
+   Module module(Test::pkcs11_lib());
+
+   SlotId invalid_id = get_invalid_slot_id(module);
+   Slot slot(module, invalid_id);
+
+   result.test_throws("Session ctor with invalid slot id fails", [&slot]()
+      {
+      Session session(slot, true);
+      });
+
+   return result;
+   }
+
+Test::Result test_session_release()
+   {
+   Test::Result result("Session release/take ownership");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   Session session(slot, false);
+   SessionHandle handle = session.release();
+
+   Session session2(slot, handle);
+   result.test_success("releasing ownership and taking ownership works as expected.");
+
+   return result;
+   }
+
+Test::Result test_session_login_logout()
+   {
+   Test::Result result("Session login/logout");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   Session session(slot, false);
+   session.login(UserType::User, PIN());
+   session.logoff();
+   result.test_success("user login/logout succeeded");
+
+   session.login(UserType::SO, SO_PIN());
+   result.test_success("SO login succeeded");
+
+   return result;
+   }
+
+Test::Result test_session_info()
+   {
+   Test::Result result("Session session info");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   Session session(slot, false);
+   SessionInfo info = session.get_info();
+   result.test_is_eq("slot id is correct", info.slotID, slot_vec.at(0));
+   result.test_is_eq("state is a read write public session", info.state,
+                     static_cast<CK_STATE>(SessionState::RwPublicSession));
+
+   session.login(UserType::User, PIN());
+   info = session.get_info();
+   result.test_is_eq("state is a read write user session", info.state,
+                     static_cast<CK_STATE>(SessionState::RwUserFunctions));
+
+   session.logoff();
+   result.test_success("user login/logout succeeded");
+
+   session.login(UserType::SO, SO_PIN());
+   result.test_success("SO login succeeded");
+
+   return result;
+   }
+
+class Session_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_session_ctor)},
+            {STRING_AND_FUNCTION(test_session_ctor_invalid_slot)},
+            {STRING_AND_FUNCTION(test_session_release)},
+            {STRING_AND_FUNCTION(test_session_login_logout)},
+            {STRING_AND_FUNCTION(test_session_info)}
+         };
+
+         return run_pkcs11_tests("Session", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-session", Session_Tests);
+
+/***************************** Object *****************************/
+
+Test::Result test_attribute_container()
+   {
+   Test::Result result("AttributeContainer");
+
+   AttributeContainer attributes;
+   attributes.add_class(ObjectClass::PrivateKey);
+
+   std::string label("test");
+   attributes.add_string(AttributeType::Label, label);
+
+   std::vector<uint8_t> bin(4);
+   attributes.add_binary(AttributeType::Value, bin);
+
+   attributes.add_bool(AttributeType::Sensitive, true);
+   attributes.add_numeric(AttributeType::ObjectId, 12);
+
+   result.test_eq("Five elements in attribute container", attributes.count(), 5);
+
+   return result;
+   }
+
+DataObjectProperties make_test_object(const std::string& label)
+   {
+   std::string value_string("test data");
+   secure_vector<uint8_t> value(value_string.begin(), value_string.end());
+
+   std::size_t id = 1337;
+   std::string application = "Botan test application";
+
+   std::vector<uint8_t> encoded_id;
+   DER_Encoder(encoded_id).encode(id);
+
+   DataObjectProperties data_obj_props;
+   data_obj_props.set_application(application);
+   data_obj_props.set_label(label);
+   data_obj_props.set_value(value);
+   data_obj_props.set_token(true);
+   data_obj_props.set_modifiable(true);
+   data_obj_props.set_object_id(encoded_id);
+
+   return data_obj_props;
+   }
+
+#if defined(BOTAN_HAS_ASN1)
+Test::Result test_create_destroy_data_object()
+   {
+   Test::Result result("Object create/delete data object");
+
+   TestSession test_session(true);
+
+   const std::string label = "Botan test data object";
+   auto data_obj_props = make_test_object(label);
+   Object data_obj(test_session.session(), data_obj_props);
+   result.test_success("Data object creation was successful");
+
+   data_obj.destroy();
+   result.test_success("Data object deletion  was successful");
+
+   return result;
+   }
+
+Test::Result test_get_set_attribute_values()
+   {
+   Test::Result result("Object get/set attributes");
+
+   TestSession test_session(true);
+
+   // create object
+   const std::string label = "Botan test data object";
+   auto data_obj_props = make_test_object(label);
+   Object data_obj(test_session.session(), data_obj_props);
+
+   // get attribute
+   secure_vector<uint8_t> retrieved_label = data_obj.get_attribute_value(AttributeType::Label);
+   std::string retrieved_label_string(retrieved_label.begin(), retrieved_label.end());
+   result.test_eq("label was set correctly", retrieved_label_string, label);
+
+   // set attribute
+   std::string new_label = "Botan test modified data object label";
+   secure_vector<uint8_t> new_label_secvec(new_label.begin(), new_label.end());
+   data_obj.set_attribute_value(AttributeType::Label, new_label_secvec);
+
+   // get and check attribute
+   retrieved_label = data_obj.get_attribute_value(AttributeType::Label);
+   retrieved_label_string = std::string(retrieved_label.begin(), retrieved_label.end());
+   result.test_eq("label was modified correctly", retrieved_label_string, new_label);
+
+   data_obj.destroy();
+   return result;
+   }
+
+Test::Result test_object_finder()
+   {
+   Test::Result result("ObjectFinder");
+
+   TestSession test_session(true);
+
+   // create object
+   const std::string label = "Botan test data object";
+   auto data_obj_props = make_test_object(label);
+   Object data_obj(test_session.session(), data_obj_props);
+
+   // search created object
+   AttributeContainer search_template;
+   search_template.add_string(AttributeType::Label, label);
+   ObjectFinder finder(test_session.session(), search_template.attributes());
+
+   auto search_result = finder.find();
+   result.test_eq("one object found", search_result.size(), 1);
+   finder.finish();
+
+   Object obj_found(test_session.session(), search_result.at(0));
+   result.test_eq("found the object just created (same application)",
+                  obj_found.get_attribute_value(AttributeType::Application), data_obj.get_attribute_value(AttributeType::Application));
+
+   auto search_result2 = Object::search<Object>(test_session.session(), search_template.attributes());
+   result.test_eq("found the object just created (same label)", obj_found.get_attribute_value(AttributeType::Label),
+                  search_result2.at(0).get_attribute_value(AttributeType::Label));
+
+   data_obj.destroy();
+   return result;
+   }
+
+Test::Result test_object_copy()
+   {
+   Test::Result result("Object copy");
+
+   TestSession test_session(true);
+
+   // create object
+   const std::string label = "Botan test data object";
+   auto data_obj_props = make_test_object(label);
+   Object data_obj(test_session.session(), data_obj_props);
+
+   // copy created object
+   AttributeContainer copy_attributes;
+   copy_attributes.add_string(AttributeType::Label, "Botan test copied object");
+   ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes);
+
+   ObjectFinder searcher(test_session.session(), copy_attributes.attributes());
+   auto search_result = searcher.find();
+   result.test_eq("one object found", search_result.size(), 1);
+
+   data_obj.destroy();
+
+   Object copied_obj(test_session.session(), copied_obj_handle);
+   copied_obj.destroy();
+   return result;
+   }
+#endif
+
+class Object_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_attribute_container)}
+#if defined(BOTAN_HAS_ASN1)
+            , {STRING_AND_FUNCTION(test_create_destroy_data_object)}
+            , {STRING_AND_FUNCTION(test_get_set_attribute_values)}
+            , {STRING_AND_FUNCTION(test_object_finder)}
+            , {STRING_AND_FUNCTION(test_object_copy)}
+#endif
+         };
+
+         return run_pkcs11_tests("Object", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-object", Object_Tests);
+
+/***************************** PKCS11 RSA *****************************/
+
+#if defined(BOTAN_HAS_RSA)
+
+Test::Result test_rsa_privkey_import()
+   {
+   Test::Result result("PKCS11 import RSA private key");
+
+   TestSession test_session(true);
+
+   // create private key
+   RSA_PrivateKey priv_key(Test::rng(), 2048);
+   result.confirm("Key self test OK", priv_key.check_key(Test::rng(), true));
+
+   // import to card
+   RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d());
+   props.set_pub_exponent(priv_key.get_e());
+   props.set_prime_1(priv_key.get_p());
+   props.set_prime_2(priv_key.get_q());
+   props.set_coefficient(priv_key.get_c());
+   props.set_exponent_1(priv_key.get_d1());
+   props.set_exponent_2(priv_key.get_d2());
+
+   props.set_token(true);
+   props.set_private(true);
+   props.set_decrypt(true);
+   props.set_sign(true);
+
+   PKCS11_RSA_PrivateKey pk(test_session.session(), props);
+   result.test_success("RSA private key import was successful");
+   result.confirm("PK self test OK", pk.check_key(Test::rng(), true));
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_rsa_privkey_export()
+   {
+   Test::Result result("PKCS11 export RSA private key");
+
+   TestSession test_session(true);
+
+   // create private key
+   RSA_PrivateKey priv_key(Test::rng(), 2048);
+
+   // import to card
+   RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d());
+   props.set_pub_exponent(priv_key.get_e());
+   props.set_prime_1(priv_key.get_p());
+   props.set_prime_2(priv_key.get_q());
+   props.set_coefficient(priv_key.get_c());
+   props.set_exponent_1(priv_key.get_d1());
+   props.set_exponent_2(priv_key.get_d2());
+
+   props.set_token(true);
+   props.set_private(true);
+   props.set_decrypt(true);
+   props.set_sign(true);
+   props.set_extractable(true);
+   props.set_sensitive(false);
+
+   PKCS11_RSA_PrivateKey pk(test_session.session(), props);
+   result.confirm("Check PK11 key", pk.check_key(Test::rng(), true));
+
+   RSA_PrivateKey exported = pk.export_key();
+   result.test_success("RSA private key export was successful");
+   result.confirm("Check exported key", exported.check_key(Test::rng(), true));
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_rsa_pubkey_import()
+   {
+   Test::Result result("PKCS11 import RSA public key");
+
+   TestSession test_session(true);
+
+   // create public key from private key
+   RSA_PrivateKey priv_key(Test::rng(), 2048);
+
+   // import to card
+   RSA_PublicKeyImportProperties props(priv_key.get_n(), priv_key.get_e());
+   props.set_token(true);
+   props.set_encrypt(true);
+   props.set_private(false);
+
+   PKCS11_RSA_PublicKey pk(test_session.session(), props);
+   result.test_success("RSA public key import was successful");
+   result.confirm("Check PK11 key", pk.check_key(Test::rng(), true));
+
+   pk.destroy();
+
+   return result;
+   }
+
+Test::Result test_rsa_generate_private_key()
+   {
+   Test::Result result("PKCS11 generate RSA private key");
+   TestSession test_session(true);
+
+   RSA_PrivateKeyGenerationProperties props;
+   props.set_token(true);
+   props.set_private(true);
+   props.set_sign(true);
+   props.set_decrypt(true);
+
+   PKCS11_RSA_PrivateKey pk(test_session.session(), 2048, props);
+   result.test_success("RSA private key generation was successful");
+
+   pk.destroy();
+
+   return result;
+   }
+
+PKCS11_RSA_KeyPair generate_rsa_keypair(const TestSession& test_session)
+   {
+   RSA_PublicKeyGenerationProperties pub_props(2048UL);
+   pub_props.set_pub_exponent();
+   pub_props.set_label("BOTAN_TEST_RSA_PUB_KEY");
+   pub_props.set_token(true);
+   pub_props.set_encrypt(true);
+   pub_props.set_verify(true);
+   pub_props.set_private(false);
+
+   RSA_PrivateKeyGenerationProperties priv_props;
+   priv_props.set_label("BOTAN_TEST_RSA_PRIV_KEY");
+   priv_props.set_token(true);
+   priv_props.set_private(true);
+   priv_props.set_sign(true);
+   priv_props.set_decrypt(true);
+
+   return PKCS11::generate_rsa_keypair(test_session.session(), pub_props, priv_props);
+   }
+
+Test::Result test_rsa_generate_key_pair()
+   {
+   Test::Result result("PKCS11 generate RSA key pair");
+   TestSession test_session(true);
+
+   PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session);
+   result.test_success("RSA key pair generation was successful");
+
+   keypair.first.destroy();
+   keypair.second.destroy();
+
+   return result;
+   }
+
+Test::Result test_rsa_encrypt_decrypt()
+   {
+   Test::Result result("PKCS11 RSA encrypt decrypt");
+   TestSession test_session(true);
+
+   // generate key pair
+   PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session);
+
+   auto encrypt_and_decrypt = [&keypair, &result](const std::vector<uint8_t>& plaintext, const std::string& padding)
+      {
+      Botan::PK_Encryptor_EME encryptor(keypair.first, Test::rng(), padding);
+      auto encrypted = encryptor.encrypt(plaintext, Test::rng());
+
+      Botan::PK_Decryptor_EME decryptor(keypair.second, Test::rng(), padding);
+      auto decrypted = decryptor.decrypt(encrypted);
+
+      result.test_eq("RSA PKCS11 encrypt and decrypt: " + padding, decrypted, plaintext);
+      };
+
+   std::vector<uint8_t> plaintext(256);
+   std::iota(std::begin(plaintext), std::end(plaintext), static_cast<uint8_t>(0));
+   encrypt_and_decrypt(plaintext, "Raw");
+
+   plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x00 };
+   encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5");
+
+   encrypt_and_decrypt(plaintext, "OAEP(SHA-1)");
+
+   keypair.first.destroy();
+   keypair.second.destroy();
+
+   return result;
+   }
+
+Test::Result test_rsa_sign_verify()
+   {
+   Test::Result result("PKCS11 RSA sign and verify");
+   TestSession test_session(true);
+
+   // generate key pair
+   PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session);
+
+   std::vector<uint8_t> plaintext(256);
+   std::iota(std::begin(plaintext), std::end(plaintext), static_cast<uint8_t>(0));
+
+   auto sign_and_verify = [&keypair, &plaintext, &result](std::string const& emsa, bool multipart)
+      {
+      Botan::PK_Signer signer(keypair.second, Test::rng(), emsa, Botan::IEEE_1363);
+      std::vector<uint8_t> signature;
+      if(multipart)
+         {
+         signer.update(plaintext.data(), plaintext.size() / 2);
+         signature = signer.sign_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, Test::rng());
+         }
+      else
+         {
+         signature = signer.sign_message(plaintext, Test::rng());
+         }
+
+      Botan::PK_Verifier verifier(keypair.first, emsa, Botan::IEEE_1363);
+      bool rsa_ok = false;
+      if(multipart)
+         {
+         verifier.update(plaintext.data(), plaintext.size() / 2);
+         rsa_ok = verifier.verify_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2,
+                                          signature.data(), signature.size());
+         }
+      else
+         {
+         rsa_ok = verifier.verify_message(plaintext, signature);
+         }
+
+      result.test_eq("RSA PKCS11 sign and verify: " + emsa, rsa_ok, true);
+      };
+
+   // single-part sign
+   sign_and_verify("Raw", false);
+   sign_and_verify("EMSA3(SHA-256)", false);
+   sign_and_verify("EMSA4(SHA-256)", false);
+
+   // multi-part sign
+   sign_and_verify("EMSA3(SHA-256)", true);
+   sign_and_verify("EMSA4(SHA-256)", true);
+
+   keypair.first.destroy();
+   keypair.second.destroy();
+
+   return result;
+   }
+
+class PKCS11_RSA_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_rsa_privkey_import)},
+            {STRING_AND_FUNCTION(test_rsa_pubkey_import)},
+            {STRING_AND_FUNCTION(test_rsa_privkey_export)},
+            {STRING_AND_FUNCTION(test_rsa_generate_private_key)},
+            {STRING_AND_FUNCTION(test_rsa_generate_key_pair)},
+            {STRING_AND_FUNCTION(test_rsa_encrypt_decrypt)},
+            {STRING_AND_FUNCTION(test_rsa_sign_verify)}
+         };
+
+         return run_pkcs11_tests("PKCS11 RSA", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-rsa", PKCS11_RSA_Tests);
+#endif
+
+/***************************** PKCS11 ECDSA *****************************/
+
+#if defined(BOTAN_HAS_ECC_GROUP) && (defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_ECDH))
+std::vector<uint8_t> encode_ec_point_in_octet_str(const Botan::PointGFp& point)
+   {
+   std::vector<uint8_t> enc;
+   DER_Encoder(enc).encode(point.encode(PointGFp::UNCOMPRESSED), OCTET_STRING);
+   return enc;
+   }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+Test::Result test_ecdsa_privkey_import()
+   {
+   Test::Result result("PKCS11 import ECDSA private key");
+
+   TestSession test_session(true);
+
+   // create ecdsa private key
+   ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   result.confirm("Key self test OK", priv_key.check_key(Test::rng(), true));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   // import to card
+   EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value());
+   props.set_token(true);
+   props.set_private(true);
+   props.set_sign(true);
+
+   // label
+   std::string label = "Botan test ecdsa key";
+   props.set_label(label);
+
+   PKCS11_ECDSA_PrivateKey pk(test_session.session(), props);
+   result.test_success("ECDSA private key import was successful");
+   pk.set_public_point(priv_key.public_point());
+   result.confirm("P11 key self test OK", pk.check_key(Test::rng(), false));
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdsa_privkey_export()
+   {
+   Test::Result result("PKCS11 export ECDSA private key");
+
+   TestSession test_session(true);
+
+   // create private key
+   ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   result.confirm("Check ECDSA key", priv_key.check_key(Test::rng(), true));
+   // import to card
+   EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value());
+   props.set_token(true);
+   props.set_private(true);
+   props.set_sign(true);
+   props.set_extractable(true);
+
+   // label
+   std::string label = "Botan test ecdsa key";
+   props.set_label(label);
+
+   PKCS11_ECDSA_PrivateKey pk(test_session.session(), props);
+   pk.set_public_point(priv_key.public_point());
+   result.confirm("Check PK11 key", pk.check_key(Test::rng(), false));
+
+   ECDSA_PrivateKey exported = pk.export_key();
+   result.test_success("ECDSA private key export was successful");
+   result.confirm("Check exported key valid", exported.check_key(Test::rng(), true));
+   result.test_eq("Check exported key contents", exported.private_key_bits(), priv_key.private_key_bits());
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdsa_pubkey_import()
+   {
+   Test::Result result("PKCS11 import ECDSA public key");
+
+   TestSession test_session(true);
+
+   // create ecdsa private key
+   ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point());
+
+   // import to card
+   EC_PublicKeyImportProperties props(priv_key.DER_domain(), enc_point);
+   props.set_token(true);
+   props.set_verify(true);
+   props.set_private(false);
+
+   // label
+   std::string label = "Botan test ecdsa pub key";
+   props.set_label(label);
+
+   PKCS11_ECDSA_PublicKey pk(test_session.session(), props);
+   result.test_success("ECDSA public key import was successful");
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdsa_pubkey_export()
+   {
+   Test::Result result("PKCS11 export ECDSA public key");
+
+   TestSession test_session(true);
+
+   // create public key from private key
+   ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point());
+
+   // import to card
+   EC_PublicKeyImportProperties props(priv_key.DER_domain(), enc_point);
+   props.set_token(true);
+   props.set_verify(true);
+   props.set_private(false);
+
+   // label
+   std::string label = "Botan test ecdsa pub key";
+   props.set_label(label);
+
+   PKCS11_ECDSA_PublicKey pk(test_session.session(), props);
+
+   ECDSA_PublicKey exported = pk.export_key();
+   result.test_success("ECDSA public key export was successful");
+
+   pk.destroy();
+
+   return result;
+   }
+
+Test::Result test_ecdsa_generate_private_key()
+   {
+   Test::Result result("PKCS11 generate ECDSA private key");
+   TestSession test_session(true);
+
+   EC_PrivateKeyGenerationProperties props;
+   props.set_token(true);
+   props.set_private(true);
+   props.set_sign(true);
+
+   PKCS11_ECDSA_PrivateKey pk(test_session.session(),
+                              EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props);
+   result.test_success("ECDSA private key generation was successful");
+
+   pk.destroy();
+
+   return result;
+   }
+
+PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(const TestSession& test_session, const std::string& curve, EC_Group_Encoding ec_dompar_enc)
+   {
+    EC_PublicKeyGenerationProperties pub_props(EC_Group(curve).DER_encode(ec_dompar_enc));
+    pub_props.set_label("BOTAN_TEST_ECDSA_PUB_KEY");
+    pub_props.set_token(true);
+    pub_props.set_verify(true);
+    pub_props.set_private(false);
+    pub_props.set_modifiable(true);
+
+    EC_PrivateKeyGenerationProperties priv_props;
+    priv_props.set_label("BOTAN_TEST_ECDSA_PRIV_KEY");
+    priv_props.set_token(true);
+    priv_props.set_private(true);
+    priv_props.set_sensitive(true);
+    priv_props.set_extractable(false);
+    priv_props.set_sign(true);
+    priv_props.set_modifiable(true);
+
+    return PKCS11::generate_ecdsa_keypair(test_session.session(), pub_props, priv_props);
+   }
+
+Test::Result test_ecdsa_generate_keypair()
+   {
+   Test::Result result("PKCS11 generate ECDSA key pair");
+   TestSession test_session(true);
+   std::vector<std::string> curves;
+
+   curves.push_back("secp256r1");
+   curves.push_back("brainpool512r1");
+
+   for(auto &curve : curves)
+       {
+       PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, EC_DOMPAR_ENC_OID);
+
+       keypair.first.destroy();
+       keypair.second.destroy();
+       }
+   result.test_success("ECDSA key pair generation was successful");
+
+   return result;
+   }
+
+Test::Result test_ecdsa_sign_verify_core(EC_Group_Encoding ec_dompar_enc, std::string test_name)
+    {
+    Test::Result result(test_name);
+    TestSession test_session(true);
+    std::vector<std::string> curves;
+
+    curves.push_back("secp256r1");
+    curves.push_back("brainpool512r1");
+
+    Slot& slot = test_session.slot();
+    SlotInfo info = slot.get_slot_info();
+    std::string manufacturer(reinterpret_cast< char* >(info.manufacturerID));
+
+    for(auto &curve : curves)
+        {
+        // generate key pair
+        PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, ec_dompar_enc);
+
+        std::vector<uint8_t> plaintext(20, 0x01);
+
+        auto sign_and_verify = [&keypair, &plaintext, &result](const std::string& emsa,
+               const Botan::Signature_Format format, bool check_soft)
+            {
+            Botan::PK_Signer signer(keypair.second, Test::rng(), emsa, format);
+            auto signature = signer.sign_message(plaintext, Test::rng());
+
+            Botan::PK_Verifier token_verifier(keypair.first, emsa, format);
+            bool ecdsa_ok = token_verifier.verify_message(plaintext, signature);
+
+            result.test_eq("ECDSA PKCS11 sign and verify: " + emsa, ecdsa_ok, true);
+
+            // test against software implementation if available
+            if(check_soft)
+               {
+               Botan::PK_Verifier soft_verifier(keypair.first, emsa, format);
+               bool soft_ecdsa_ok = soft_verifier.verify_message(plaintext, signature);
+
+               result.test_eq("ECDSA PKCS11 verify (in software): " + emsa, soft_ecdsa_ok, true);
+               }
+            };
+
+        // SoftHSMv2 until now only supports "Raw"
+        if(manufacturer.find("SoftHSM project") == std::string::npos)
+           {
+           sign_and_verify("EMSA1(SHA-256)", Botan::IEEE_1363, true);
+           sign_and_verify("EMSA1(SHA-256)", Botan::DER_SEQUENCE, true);
+           }
+
+#if defined (BOTAN_HAS_EMSA_RAW)
+        sign_and_verify("Raw", Botan::IEEE_1363, true);
+#else
+        sign_and_verify("Raw", Botan::IEEE_1363, false);
+#endif
+
+        keypair.first.destroy();
+        keypair.second.destroy();
+        }
+
+    return result;
+    }
+
+Test::Result test_ecdsa_sign_verify()
+    {
+        // pass the curve OID to the PKCS#11 library
+        return test_ecdsa_sign_verify_core(EC_DOMPAR_ENC_OID, "PKCS11 ECDSA sign and verify");
+    }
+
+Test::Result test_ecdsa_curve_import()
+    {
+        // pass the curve parameters to the PKCS#11 library and perform sign/verify to test them
+        return test_ecdsa_sign_verify_core(EC_DOMPAR_ENC_EXPLICIT, "PKCS11 ECDSA sign and verify with imported curve");
+    }
+
+class PKCS11_ECDSA_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_ecdsa_privkey_import)},
+            {STRING_AND_FUNCTION(test_ecdsa_privkey_export)},
+            {STRING_AND_FUNCTION(test_ecdsa_pubkey_import)},
+            {STRING_AND_FUNCTION(test_ecdsa_pubkey_export)},
+            {STRING_AND_FUNCTION(test_ecdsa_generate_private_key)},
+            {STRING_AND_FUNCTION(test_ecdsa_generate_keypair)},
+            {STRING_AND_FUNCTION(test_ecdsa_sign_verify)},
+            {STRING_AND_FUNCTION(test_ecdsa_curve_import)}
+         };
+
+         return run_pkcs11_tests("PKCS11 ECDSA", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-ecdsa", PKCS11_ECDSA_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+
+/***************************** PKCS11 ECDH *****************************/
+
+Test::Result test_ecdh_privkey_import()
+   {
+   Test::Result result("PKCS11 import ECDH private key");
+
+   TestSession test_session(true);
+
+   // create ecdh private key
+   ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   // import to card
+   EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value());
+   props.set_token(true);
+   props.set_private(true);
+   props.set_derive(true);
+
+   // label
+   std::string label = "Botan test ecdh key";
+   props.set_label(label);
+
+   PKCS11_ECDH_PrivateKey pk(test_session.session(), props);
+   result.test_success("ECDH private key import was successful");
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdh_privkey_export()
+   {
+   Test::Result result("PKCS11 export ECDH private key");
+
+   TestSession test_session(true);
+
+   // create private key
+   ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   // import to card
+   EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value());
+   props.set_token(true);
+   props.set_private(true);
+   props.set_derive(true);
+   props.set_extractable(true);
+
+   // label
+   std::string label = "Botan test ecdh key";
+   props.set_label(label);
+
+   PKCS11_ECDH_PrivateKey pk(test_session.session(), props);
+
+   ECDH_PrivateKey exported = pk.export_key();
+   result.test_success("ECDH private key export was successful");
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdh_pubkey_import()
+   {
+   Test::Result result("PKCS11 import ECDH public key");
+
+   TestSession test_session(true);
+
+   // create ECDH private key
+   ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point());
+
+   // import to card
+   EC_PublicKeyImportProperties props(priv_key.DER_domain(), enc_point);
+   props.set_token(true);
+   props.set_private(false);
+   props.set_derive(true);
+
+   // label
+   std::string label = "Botan test ECDH pub key";
+   props.set_label(label);
+
+   PKCS11_ECDH_PublicKey pk(test_session.session(), props);
+   result.test_success("ECDH public key import was successful");
+
+   pk.destroy();
+   return result;
+   }
+
+Test::Result test_ecdh_pubkey_export()
+   {
+   Test::Result result("PKCS11 export ECDH public key");
+
+   TestSession test_session(true);
+
+   // create public key from private key
+   ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1"));
+   priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID);
+
+   const auto enc_point = encode_ec_point_in_octet_str(priv_key.public_point());
+
+   // import to card
+   EC_PublicKeyImportProperties props(priv_key.DER_domain(), enc_point);
+   props.set_token(true);
+   props.set_derive(true);
+   props.set_private(false);
+
+   // label
+   std::string label = "Botan test ECDH pub key";
+   props.set_label(label);
+
+   PKCS11_ECDH_PublicKey pk(test_session.session(), props);
+
+   ECDH_PublicKey exported = pk.export_key();
+   result.test_success("ECDH public key export was successful");
+
+   pk.destroy();
+
+   return result;
+   }
+
+Test::Result test_ecdh_generate_private_key()
+   {
+   Test::Result result("PKCS11 generate ECDH private key");
+   TestSession test_session(true);
+
+   EC_PrivateKeyGenerationProperties props;
+   props.set_token(true);
+   props.set_private(true);
+   props.set_derive(true);
+
+   PKCS11_ECDH_PrivateKey pk(test_session.session(),
+                             EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props);
+   result.test_success("ECDH private key generation was successful");
+
+   pk.destroy();
+
+   return result;
+   }
+
+PKCS11_ECDH_KeyPair generate_ecdh_keypair(const TestSession& test_session, const std::string& label)
+   {
+   EC_PublicKeyGenerationProperties pub_props(EC_Group("secp256r1").DER_encode(
+            EC_Group_Encoding::EC_DOMPAR_ENC_OID));
+   pub_props.set_label(label + "_PUB_KEY");
+   pub_props.set_token(true);
+   pub_props.set_derive(true);
+   pub_props.set_private(false);
+   pub_props.set_modifiable(true);
+
+   EC_PrivateKeyGenerationProperties priv_props;
+   priv_props.set_label(label + "_PRIV_KEY");
+   priv_props.set_token(true);
+   priv_props.set_private(true);
+   priv_props.set_sensitive(true);
+   priv_props.set_extractable(false);
+   priv_props.set_derive(true);
+   priv_props.set_modifiable(true);
+
+   return PKCS11::generate_ecdh_keypair(test_session.session(), pub_props, priv_props);
+   }
+
+Test::Result test_ecdh_generate_keypair()
+   {
+   Test::Result result("PKCS11 generate ECDH key pair");
+   TestSession test_session(true);
+
+   PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1");
+   result.test_success("ECDH key pair generation was successful");
+
+   keypair.first.destroy();
+   keypair.second.destroy();
+
+   return result;
+   }
+
+Test::Result test_ecdh_derive()
+   {
+   Test::Result result("PKCS11 ECDH derive");
+   TestSession test_session(true);
+
+   PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1");
+   PKCS11_ECDH_KeyPair keypair2 = generate_ecdh_keypair(test_session, "Botan test ECDH key2");
+
+   // SoftHSMv2 only supports CKD_NULL KDF at the moment
+   Botan::PK_Key_Agreement ka(keypair.second, Test::rng(), "Raw");
+   Botan::PK_Key_Agreement kb(keypair2.second, Test::rng(), "Raw");
+
+   Botan::SymmetricKey alice_key = ka.derive_key(32, keypair2.first.public_point().encode(PointGFp::UNCOMPRESSED));
+   Botan::SymmetricKey bob_key = kb.derive_key(32, keypair.first.public_point().encode(PointGFp::UNCOMPRESSED));
+
+   bool eq = alice_key == bob_key;
+   result.test_eq("same secret key derived", eq, true);
+
+   keypair.first.destroy();
+   keypair.second.destroy();
+   keypair2.first.destroy();
+   keypair2.second.destroy();
+
+   return result;
+   }
+
+class PKCS11_ECDH_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_ecdh_privkey_import)},
+            {STRING_AND_FUNCTION(test_ecdh_privkey_export)},
+            {STRING_AND_FUNCTION(test_ecdh_pubkey_import)},
+            {STRING_AND_FUNCTION(test_ecdh_pubkey_export)},
+            {STRING_AND_FUNCTION(test_ecdh_generate_private_key)},
+            {STRING_AND_FUNCTION(test_ecdh_generate_keypair)},
+            {STRING_AND_FUNCTION(test_ecdh_derive)}
+         };
+
+         return run_pkcs11_tests("PKCS11 ECDH", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-ecdh", PKCS11_ECDH_Tests);
+
+#endif
+
+/***************************** PKCS11 RNG *****************************/
+
+Test::Result test_rng_generate_random()
+   {
+   Test::Result result("PKCS11 RNG generate random");
+   TestSession test_session(true);
+
+   PKCS11_RNG rng(test_session.session());
+   result.confirm("RNG already seeded", rng.is_seeded());
+
+   std::vector<uint8_t> random(20);
+   rng.randomize(random.data(), random.size());
+   result.test_ne("random data generated", random, std::vector<uint8_t>(20));
+
+   return result;
+   }
+
+Test::Result test_rng_add_entropy()
+   {
+   Test::Result result("PKCS11 RNG add entropy random");
+   TestSession test_session(true);
+
+   PKCS11_RNG rng(test_session.session());
+
+   result.confirm("RNG already seeded", rng.is_seeded());
+   rng.clear();
+   result.confirm("RNG ignores call to clear", rng.is_seeded());
+
+   result.test_eq("RNG ignores calls to reseed",
+                  rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(300)),
+                  0);
+
+   auto random = Test::rng().random_vec(20);
+   rng.add_entropy(random.data(), random.size());
+   result.test_success("entropy added");
+
+   return result;
+   }
+
+#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_64)
+
+Test::Result test_pkcs11_hmac_drbg()
+   {
+   Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG");
+   TestSession test_session(true);
+
+   PKCS11_RNG p11_rng(test_session.session());
+   HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)"), p11_rng);
+   // result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG");
+
+   result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false);
+   secure_vector<uint8_t> rnd = drbg.random_vec(64);
+   result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true);
+
+   std::string personalization_string = "Botan PKCS#11 Tests";
+   std::vector<uint8_t> personalization_data(personalization_string.begin(), personalization_string.end());
+   drbg.add_entropy(personalization_data.data(), personalization_data.size());
+
+   auto rnd_vec = drbg.random_vec(256);
+   result.test_ne("HMAC_DRBG generated a random vector", rnd_vec, std::vector<uint8_t>(256));
+
+   return result;
+   }
+#endif
+
+class PKCS11_RNG_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+#if defined(BOTAN_HAS_HMAC_DRBG )&& defined(BOTAN_HAS_SHA2_64)
+            {STRING_AND_FUNCTION(test_pkcs11_hmac_drbg)},
+#endif
+            {STRING_AND_FUNCTION(test_rng_generate_random)},
+            {STRING_AND_FUNCTION(test_rng_add_entropy)}
+            };
+
+         return run_pkcs11_tests("PKCS11 RNG", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-rng", PKCS11_RNG_Tests);
+
+/***************************** PKCS11 token management *****************************/
+
+Test::Result test_set_pin()
+   {
+   Test::Result result("PKCS11 set pin");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   PKCS11::set_pin(slot, SO_PIN(), TEST_PIN());
+   result.test_success("PIN set with SO_PIN to TEST_PIN");
+
+   PKCS11::set_pin(slot, SO_PIN(), PIN());
+   result.test_success("PIN changed back with SO_PIN");
+
+   return result;
+   }
+
+Test::Result test_initialize()
+   {
+   Test::Result result("PKCS11 initialize token");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   PKCS11::initialize_token(slot, "Botan PKCS#11 tests", SO_PIN(), PIN());
+   result.test_success("token initialized");
+
+   return result;
+   }
+
+Test::Result test_change_pin()
+   {
+   Test::Result result("PKCS11 change pin");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   PKCS11::change_pin(slot, PIN(), TEST_PIN());
+   result.test_success("PIN changed with PIN to TEST_PIN");
+
+   PKCS11::change_pin(slot, TEST_PIN(), PIN());
+   result.test_success("PIN changed back with TEST_PIN to PIN");
+
+   return result;
+   }
+
+Test::Result test_change_so_pin()
+   {
+   Test::Result result("PKCS11 change so_pin");
+
+   Module module(Test::pkcs11_lib());
+   std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true);
+   Slot slot(module, slot_vec.at(0));
+
+   PKCS11::change_so_pin(slot, SO_PIN(), TEST_SO_PIN());
+   result.test_success("SO_PIN changed with SO_PIN to TEST_SO_PIN");
+
+   PKCS11::change_so_pin(slot, TEST_SO_PIN(), SO_PIN());
+   result.test_success("SO_PIN changed back with TEST_SO_PIN to SO_PIN");
+
+   return result;
+   }
+
+class PKCS11_Token_Management_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_set_pin)},
+            {STRING_AND_FUNCTION(test_initialize)},
+            {STRING_AND_FUNCTION(test_change_pin)},
+            {STRING_AND_FUNCTION(test_change_so_pin)}
+         };
+
+         return run_pkcs11_tests("PKCS11 token management", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-manage", PKCS11_Token_Management_Tests);
+
+/***************************** PKCS11 token management *****************************/
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+Test::Result test_x509_import()
+   {
+   Test::Result result("PKCS11 X509 cert import");
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+   TestSession test_session(true);
+
+   X509_Certificate root(Test::data_file("x509/nist/test01/end.crt"));
+   X509_CertificateProperties props(root);
+   props.set_label("Botan PKCS#11 test certificate");
+   props.set_private(false);
+   props.set_token(true);
+
+   PKCS11_X509_Certificate pkcs11_cert(test_session.session(), props);
+   result.test_success("X509 certificate imported");
+
+   PKCS11_X509_Certificate pkcs11_cert2(test_session.session(), pkcs11_cert.handle());
+   result.test_eq("X509 certificate by handle", pkcs11_cert == pkcs11_cert2, true);
+
+   pkcs11_cert.destroy();
+#endif
+
+   return result;
+   }
+
+class PKCS11_X509_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_x509_import)}
+         };
+
+         return run_pkcs11_tests("PKCS11 X509", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-x509", PKCS11_X509_Tests);
+
+#endif
+
+}
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_pkcs11_low_level.cpp b/third_party/botan/src/tests/test_pkcs11_low_level.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pkcs11_low_level.cpp
@@ -0,0 +1,840 @@
+/*
+* (C) 2016 Daniel Neus
+* (C) 2016 Philipp Weber
+* (C) 2019 Michael Boric
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_pkcs11.h"
+
+#include <string>
+#include <vector>
+#include <functional>
+#include <memory>
+#include <array>
+#include <map>
+
+#if defined(BOTAN_HAS_PKCS11)
+   #include <botan/p11.h>
+#endif
+
+#if defined(BOTAN_HAS_DYNAMIC_LOADER)
+   #include <botan/dyn_load.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_PKCS11)
+#if defined(BOTAN_HAS_DYNAMIC_LOADER)
+
+using namespace Botan;
+using namespace PKCS11;
+
+class RAII_LowLevel
+   {
+   public:
+      RAII_LowLevel() : m_module(Test::pkcs11_lib()), m_func_list(nullptr), m_low_level(), m_session_handle(0),
+         m_is_session_open(false), m_is_logged_in(false)
+         {
+         LowLevel::C_GetFunctionList(m_module, &m_func_list);
+         m_low_level.reset(new LowLevel(m_func_list));
+
+         C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr };
+
+         m_low_level->C_Initialize(&init_args);
+         }
+      ~RAII_LowLevel() noexcept
+         {
+         try
+            {
+            if(m_is_session_open)
+               {
+               if(m_is_logged_in)
+                  {
+                  m_low_level.get()->C_Logout(m_session_handle, nullptr);
+                  }
+
+               m_low_level.get()->C_CloseSession(m_session_handle, nullptr);
+               }
+            m_low_level.get()->C_Finalize(nullptr, nullptr);
+            }
+         catch(...)
+            {
+            // ignore errors here
+            }
+         }
+
+      std::vector<SlotId> get_slots(bool token_present) const
+         {
+         std::vector<SlotId> slots;
+         m_low_level.get()->C_GetSlotList(token_present, slots);
+
+         if(slots.empty())
+            {
+            throw Test_Error("No slot with attached token found");
+            }
+
+         return slots;
+         }
+
+      inline SessionHandle open_session(Flags session_flags)
+         {
+         std::vector<SlotId> slots = get_slots(true);
+         m_low_level.get()->C_OpenSession(slots.at(0), session_flags, nullptr, nullptr, &m_session_handle);
+         m_is_session_open = true;
+         return m_session_handle;
+         }
+
+      inline SessionHandle open_rw_session_with_user_login()
+         {
+         Flags session_flags =  PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+         SessionHandle handle = open_session(session_flags);
+         login(UserType::User, PIN());
+         return handle;
+         }
+
+      inline SessionHandle get_session_handle() const
+         {
+         if(!m_is_session_open)
+            {
+            throw Test_Error("no open session");
+            }
+         return m_session_handle;
+         }
+
+      inline void close_session()
+         {
+         if(!m_is_session_open)
+            {
+            throw Test_Error("no open session");
+            }
+
+         m_low_level.get()->C_CloseSession(m_session_handle);
+         m_is_session_open = false;
+         }
+
+      inline void login(UserType user_type, const secure_vector<uint8_t>& pin)
+         {
+         if(!m_is_session_open)
+            {
+            throw Test_Error("no open session");
+            }
+
+         if(m_is_logged_in)
+            {
+            throw Test_Error("Already logged in");
+            }
+
+         m_low_level.get()->C_Login(m_session_handle, user_type, pin);
+         m_is_logged_in = true;
+         }
+
+      inline void logout()
+         {
+         if(!m_is_logged_in)
+            {
+            throw Test_Error("Not logged in");
+            }
+
+         m_low_level.get()->C_Logout(m_session_handle);
+         m_is_logged_in = false;
+         }
+
+      LowLevel* get() const
+         {
+         return m_low_level.get();
+         }
+   private:
+      Dynamically_Loaded_Library m_module;
+      FunctionListPtr m_func_list;
+      std::unique_ptr<LowLevel> m_low_level;
+      SessionHandle m_session_handle;
+      bool m_is_session_open;
+      bool m_is_logged_in;
+   };
+
+bool no_op(ReturnValue*)
+   {
+   return true;
+   }
+
+using PKCS11_BoundTestFunction = std::function<bool(ReturnValue* return_value)>;
+
+// tests all 3 variants
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+                           const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func,
+                           bool expect_failure, ReturnValue expected_return_value)
+   {
+   std::string test_name = revert_fn_name.empty() ? "PKCS 11 low level - " + name : "PKCS 11 low level - " + name + "/" +
+                           revert_fn_name;
+   Test::Result result(test_name);
+
+   // test throw variant
+   if(expect_failure)
+      {
+      result.test_throws(name + " fails as expected", [ test_func ]()
+         {
+         test_func(ThrowException);
+         });
+      }
+   else
+      {
+      test_func(ThrowException);
+      result.test_success(name + " did not throw and completed successfully");
+
+      if(!revert_fn_name.empty())
+         {
+         revert_func(ThrowException);
+         result.test_success(revert_fn_name + " did not throw and completed successfully");
+         }
+      }
+
+   // test bool return variant
+   bool success = test_func(nullptr);
+   result.test_eq(name, success, !expect_failure);
+   if(success && !revert_fn_name.empty())
+      {
+      success = revert_func(nullptr);
+      result.test_eq(revert_fn_name, success, !expect_failure);
+      }
+
+   // test ReturnValue variant
+   ReturnValue rv;
+   success = test_func(&rv);
+   result.test_eq(name, success, !expect_failure);
+   if(!expect_failure)
+      {
+      result.test_rc_ok(name, static_cast< uint32_t >(rv));
+      }
+   else
+      {
+      result.test_rc_fail(name, "return value should be: " + std::to_string(static_cast< uint32_t >(expected_return_value)),
+                          static_cast< uint32_t >(rv));
+      }
+
+   if(success && !revert_fn_name.empty())
+      {
+      success = revert_func(&rv);
+      result.test_eq(revert_fn_name, success, !expect_failure);
+      result.test_rc_ok(revert_fn_name, static_cast< uint32_t >(rv));
+      }
+
+   return result;
+   }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+                           bool expect_failure, ReturnValue expected_return_value)
+   {
+   return test_function(name, test_func, std::string(), no_op, expect_failure, expected_return_value);
+   }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func)
+   {
+   return test_function(name, test_func, std::string(), no_op, false, ReturnValue::OK);
+   }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+                           const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func)
+   {
+   return test_function(name, test_func, revert_fn_name, revert_func, false, ReturnValue::OK);
+   }
+
+Test::Result test_low_level_ctor()
+   {
+   Test::Result result("PKCS 11 low level - LowLevel ctor");
+
+   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+   FunctionListPtr func_list(nullptr);
+   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
+
+   LowLevel p11_low_level(func_list);
+   result.test_success("LowLevel ctor does complete for valid function list");
+
+   result.test_throws("LowLevel ctor fails for invalid function list pointer", []()
+      {
+      LowLevel p11_low_level2(nullptr);
+      });
+
+   return result;
+   }
+
+Test::Result test_c_get_function_list()
+   {
+   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+   FunctionListPtr func_list = nullptr;
+   return test_function("C_GetFunctionList", std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list,
+                        std::placeholders::_1));
+   }
+
+Test::Result test_initialize_finalize()
+   {
+   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+   FunctionListPtr func_list = nullptr;
+   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
+
+   LowLevel p11_low_level(func_list);
+
+   // setting Flag::OsLockingOk should be the normal use case
+   C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr };
+
+   auto init_bind = std::bind(&LowLevel::C_Initialize, p11_low_level, &init_args, std::placeholders::_1);
+   auto finalize_bind = std::bind(&LowLevel::C_Finalize, p11_low_level, nullptr, std::placeholders::_1);
+   return test_function("C_Initialize", init_bind, "C_Finalize", finalize_bind);
+   }
+
+Test::Result test_c_get_info()
+   {
+   RAII_LowLevel p11_low_level;
+
+   Info info = {};
+   Test::Result result = test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, *p11_low_level.get(), &info,
+                                       std::placeholders::_1));
+   result.test_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0);
+
+   return result;
+   }
+
+Test::Result test_c_get_slot_list()
+   {
+   RAII_LowLevel p11_low_level;
+
+   std::vector<SlotId> slot_vec;
+
+   // assumes at least one smartcard reader is attached
+   bool token_present = false;
+
+   auto binder = std::bind(static_cast< bool (LowLevel::*)(bool, std::vector<SlotId>&, ReturnValue*) const>(&LowLevel::C_GetSlotList),
+                           *p11_low_level.get(),
+                           std::ref(token_present),
+                           std::ref(slot_vec),
+                           std::placeholders::_1);
+
+   Test::Result result = test_function("C_GetSlotList", binder);
+   result.test_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0);
+
+   // assumes at least one smartcard reader with connected smartcard is attached
+   slot_vec.clear();
+   token_present = true;
+   result.merge(test_function("C_GetSlotList", binder));
+   result.test_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0);
+
+   return result;
+   }
+
+Test::Result test_c_get_slot_info()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(false);
+
+   SlotInfo slot_info = {};
+   Test::Result result = test_function("C_GetSlotInfo", std::bind(&LowLevel::C_GetSlotInfo, *p11_low_level.get(),
+                                       slot_vec.at(0), &slot_info, std::placeholders::_1));
+
+   std::string slot_desc(reinterpret_cast< char* >(slot_info.slotDescription));
+   result.test_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0);
+
+   return result;
+   }
+
+Test::Result test_c_get_token_info()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   TokenInfo token_info = {};
+   Test::Result result = test_function("C_GetTokenInfo", std::bind(&LowLevel::C_GetTokenInfo, *p11_low_level.get(),
+                                       slot_vec.at(0), &token_info, std::placeholders::_1));
+
+   std::string serial(reinterpret_cast< char* >(token_info.serialNumber));
+   result.test_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0);
+
+   return result;
+   }
+
+Test::Result test_c_wait_for_slot_event()
+   {
+   RAII_LowLevel p11_low_level;
+
+   Flags flags = PKCS11::flags(Flag::DontBlock);
+   SlotId slot_id = 0;
+
+   return test_function("C_WaitForSlotEvent", std::bind(&LowLevel::C_WaitForSlotEvent, *p11_low_level.get(),
+                        flags, &slot_id, nullptr, std::placeholders::_1), true, ReturnValue::NoEvent);
+   }
+
+Test::Result test_c_get_mechanism_list()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   std::vector<MechanismType> mechanisms;
+
+   auto binder = std::bind(static_cast< bool (LowLevel::*)(SlotId, std::vector<MechanismType>&, ReturnValue*) const>
+                           (&LowLevel::C_GetMechanismList), *p11_low_level.get(), slot_vec.at(0), std::ref(mechanisms), std::placeholders::_1);
+
+   Test::Result result = test_function("C_GetMechanismList", binder);
+   result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty());
+
+   return result;
+   }
+
+Test::Result test_c_get_mechanism_info()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   std::vector<MechanismType> mechanisms;
+   p11_low_level.get()->C_GetMechanismList(slot_vec.at(0), mechanisms);
+
+   MechanismInfo mechanism_info = {};
+   return test_function("C_GetMechanismInfo", std::bind(&LowLevel::C_GetMechanismInfo, *p11_low_level.get(),
+                        slot_vec.at(0), mechanisms.at(0), &mechanism_info, std::placeholders::_1));
+   }
+
+Test::Result test_c_init_token()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   const std::string label = "Botan PKCS#11 tests";
+
+   auto sec_vec_binder = std::bind(
+                            static_cast< bool (LowLevel::*)(SlotId, const secure_vector<uint8_t>&, const std::string&, ReturnValue*) const>
+                            (&LowLevel::C_InitToken<secure_allocator<uint8_t>>), *p11_low_level.get(), slot_vec.at(0), SO_PIN(),
+                            std::ref(label), std::placeholders::_1);
+
+   return test_function("C_InitToken", sec_vec_binder);
+   }
+
+Test::Result test_open_close_session()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   // public read only session
+   Flags flags = PKCS11::flags(Flag::SerialSession);
+   SessionHandle session_handle = 0;
+
+   auto open_session_bind = std::bind(&LowLevel::C_OpenSession, *p11_low_level.get(),
+                                      slot_vec.at(0), std::ref(flags), nullptr, nullptr, &session_handle, std::placeholders::_1);
+
+   auto close_session_bind = std::bind(&LowLevel::C_CloseSession, *p11_low_level.get(),
+                                       std::ref(session_handle), std::placeholders::_1);
+
+   Test::Result result = test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind);
+
+   // public read write session
+   flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   result.merge(test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind));
+
+   return result;
+   }
+
+Test::Result test_c_close_all_sessions()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   auto open_two_sessions = [ &slot_vec, &p11_low_level ]() -> void
+      {
+      // public read only session
+      Flags flags = PKCS11::flags(Flag::SerialSession);
+      SessionHandle first_session_handle = 0, second_session_handle = 0;
+
+      p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &first_session_handle);
+
+      flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+      p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &second_session_handle);
+      };
+
+   open_two_sessions();
+
+   Test::Result result("PKCS 11 low level - C_CloseAllSessions");
+
+   // test throw variant
+   p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0));
+   result.test_success("C_CloseAllSessions does not throw");
+
+   // test bool return variant
+   open_two_sessions();
+
+   bool success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), nullptr);
+   result.test_eq("C_CloseAllSessions", success, true);
+
+   // test ReturnValue variant
+   open_two_sessions();
+
+   ReturnValue rv = static_cast< ReturnValue >(-1);
+   success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), &rv);
+   result.test_eq("C_CloseAllSessions", success, true);
+   result.test_rc_ok("C_CloseAllSessions", static_cast< uint32_t >(rv));
+
+   return result;
+   }
+
+Test::Result test_c_get_session_info()
+   {
+   RAII_LowLevel p11_low_level;
+   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+   // public read only session
+   Flags flags = PKCS11::flags(Flag::SerialSession);
+   SessionHandle session_handle = p11_low_level.open_session(flags);
+
+   SessionInfo session_info = {};
+   Test::Result result = test_function("C_GetSessionInfo", std::bind(&LowLevel::C_GetSessionInfo, *p11_low_level.get(),
+                                       session_handle, &session_info, std::placeholders::_1));
+
+   result.confirm("C_GetSessionInfo returns same slot id as during call to C_OpenSession",
+                  session_info.slotID == slot_vec.at(0));
+   result.confirm("C_GetSessionInfo returns same flags as during call to C_OpenSession", session_info.flags == flags);
+   result.confirm("C_GetSessionInfo returns public read only session state",
+                  session_info.state == static_cast<CK_FLAGS>(SessionState::RoPublicSession));
+
+   return result;
+   }
+
+Test::Result login_logout_helper(const RAII_LowLevel& p11_low_level, SessionHandle handle, UserType user_type,
+                                 const std::string& pin)
+   {
+   secure_vector<uint8_t> pin_as_sec_vec(pin.begin(), pin.end());
+
+   auto login_secvec_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, UserType,
+                                        const secure_vector<uint8_t>&, ReturnValue*) const>
+                                        (&LowLevel::C_Login<secure_allocator<uint8_t>>), *p11_low_level.get(),
+                                        handle, user_type, std::ref(pin_as_sec_vec), std::placeholders::_1);
+
+   auto logout_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ReturnValue*) const>
+                                  (&LowLevel::C_Logout), *p11_low_level.get(), handle, std::placeholders::_1);
+
+   return test_function("C_Login", login_secvec_binder, "C_Logout", logout_binder);
+   }
+
+Test::Result test_c_login_logout_security_officier()
+   {
+   RAII_LowLevel p11_low_level;
+
+   // can only login to R/W session
+   Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+   return login_logout_helper(p11_low_level, session_handle, UserType::SO, PKCS11_SO_PIN);
+   }
+
+Test::Result test_c_login_logout_user()
+   {
+   RAII_LowLevel p11_low_level;
+
+   // R/O session
+   Flags session_flags = PKCS11::flags(Flag::SerialSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+   Test::Result result = login_logout_helper(p11_low_level, session_handle, UserType::User, PKCS11_USER_PIN);
+   p11_low_level.close_session();
+
+   // R/W session
+   session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   session_handle = p11_low_level.open_session(session_flags);
+
+   result.merge(login_logout_helper(p11_low_level, session_handle, UserType::User, PKCS11_USER_PIN));
+
+   return result;
+   }
+
+Test::Result test_c_init_pin()
+   {
+   RAII_LowLevel p11_low_level;
+
+   // C_InitPIN can only be called in the "R/W SO Functions" state
+   Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+   p11_low_level.login(UserType::SO, SO_PIN());
+
+   auto sec_vec_binder = std::bind(
+                            static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector<uint8_t>&, ReturnValue*) const>
+                            (&LowLevel::C_InitPIN<secure_allocator<uint8_t>>), *p11_low_level.get(), session_handle, PIN(),
+                            std::placeholders::_1);
+
+   return test_function("C_InitPIN", sec_vec_binder);
+   }
+
+Test::Result test_c_set_pin()
+   {
+   RAII_LowLevel p11_low_level;
+
+   // C_SetPIN can only be called in the "R / W Public Session" state, "R / W SO Functions" state, or "R / W User Functions" state
+   Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+   // now we are in "R / W Public Session" state: this will change the pin of the user
+
+   auto get_pin_bind = [ &session_handle, &p11_low_level ](const secure_vector<uint8_t>& old_pin,
+                       const secure_vector<uint8_t>& new_pin) -> PKCS11_BoundTestFunction
+      {
+      return std::bind(static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector<uint8_t>&,
+                       const secure_vector<uint8_t>&, ReturnValue*) const>
+                       (&LowLevel::C_SetPIN<secure_allocator<uint8_t>>), *p11_low_level.get(), session_handle,
+                       old_pin, new_pin, std::placeholders::_1);
+      };
+
+   const std::string test_pin("654321");
+   const auto test_pin_secvec = secure_vector<uint8_t>(test_pin.begin(), test_pin.end());
+
+   PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN(), test_pin_secvec);
+   PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN());
+
+   Test::Result result = test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind);
+
+   // change pin in "R / W User Functions" state
+   p11_low_level.login(UserType::User, PIN());
+
+   result.merge(test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind));
+   p11_low_level.logout();
+
+   // change so_pin in "R / W SO Functions" state
+   const std::string test_so_pin = "87654321";
+   secure_vector<uint8_t> test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end());
+   p11_low_level.login(UserType::SO, SO_PIN());
+
+   PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN(), test_so_pin_secvec);
+   PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN());
+
+   result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind));
+
+   return result;
+   }
+
+// Simple data object
+ObjectClass object_class = ObjectClass::Data;
+std::string label = "A data object";
+std::string data = "Sample data";
+Bbool btrue = True;
+
+std::array<Attribute, 4> dtemplate =
+   {
+      {
+         { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Class), &object_class, sizeof(object_class) },
+         { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Token), &btrue, sizeof(btrue) },
+         { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Label), const_cast<char*>(label.c_str()), static_cast<CK_ULONG>(label.size()) },
+         { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Value), const_cast<char*>(data.c_str()), static_cast<CK_ULONG>(data.size()) }
+      }
+   };
+
+ObjectHandle create_simple_data_object(const RAII_LowLevel& p11_low_level)
+   {
+   ObjectHandle object_handle;
+   p11_low_level.get()->C_CreateObject(p11_low_level.get_session_handle(),
+                                       dtemplate.data(), static_cast<Ulong>(dtemplate.size()),
+                                       &object_handle);
+   return object_handle;
+   }
+
+Test::Result test_c_create_object_c_destroy_object()
+   {
+   RAII_LowLevel p11_low_level;
+   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+   ObjectHandle object_handle(0);
+
+   auto create_bind = std::bind(&LowLevel::C_CreateObject, *p11_low_level.get(),
+                                session_handle, dtemplate.data(), static_cast<Ulong>(dtemplate.size()),
+                                &object_handle, std::placeholders::_1);
+
+   auto destroy_bind = std::bind(&LowLevel::C_DestroyObject, *p11_low_level.get(), session_handle,
+                                 std::ref(object_handle), std::placeholders::_1);
+
+   return test_function("C_CreateObject", create_bind, "C_DestroyObject", destroy_bind);
+   }
+
+Test::Result test_c_get_object_size()
+   {
+   RAII_LowLevel p11_low_level;
+
+   Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+   p11_low_level.login(UserType::User, PIN());
+
+   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+   Ulong object_size = 0;
+
+   auto bind = std::bind(&LowLevel::C_GetObjectSize, *p11_low_level.get(),
+                         session_handle, object_handle, &object_size, std::placeholders::_1);
+
+   Test::Result result = test_function("C_GetObjectSize", bind);
+   result.test_ne("Object size", object_size, 0);
+
+   // cleanup
+   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+   return result;
+   }
+
+Test::Result test_c_get_attribute_value()
+   {
+   RAII_LowLevel p11_low_level;
+   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+
+   std::map < AttributeType, secure_vector<uint8_t>> getter =
+      {
+         { AttributeType::Label, secure_vector<uint8_t>() },
+         { AttributeType::Value, secure_vector<uint8_t>() }
+      };
+
+   auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle,
+                         std::map<AttributeType, secure_vector<uint8_t>>&, ReturnValue*) const>
+                         (&LowLevel::C_GetAttributeValue<secure_allocator<uint8_t>>), *p11_low_level.get(), session_handle,
+                         object_handle, std::ref(getter), std::placeholders::_1);
+
+   Test::Result result = test_function("C_GetAttributeValue", bind);
+
+   std::string _label(getter[ AttributeType::Label ].begin(), getter[ AttributeType::Label ].end());
+   std::string value(getter[ AttributeType::Value ].begin(), getter[ AttributeType::Value ].end());
+   result.test_eq("label", _label, "A data object");
+   result.test_eq("value", value, "Sample data");
+
+   // cleanup
+   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+   return result;
+   }
+
+std::map < AttributeType, std::vector<uint8_t>> get_attribute_values(const RAII_LowLevel& p11_low_level,
+      SessionHandle session_handle,
+      ObjectHandle object_handle, const std::vector<AttributeType>& attribute_types)
+   {
+   std::map < AttributeType, std::vector<uint8_t>> received_attributes;
+
+   for(const auto& type : attribute_types)
+      {
+      received_attributes.emplace(type, std::vector<uint8_t>());
+      }
+
+   p11_low_level.get()->C_GetAttributeValue(session_handle, object_handle, received_attributes);
+
+   return received_attributes;
+   }
+
+Test::Result test_c_set_attribute_value()
+   {
+   RAII_LowLevel p11_low_level;
+
+   Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+   SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+   p11_low_level.login(UserType::User, PIN());
+
+   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+
+   std::string new_label = "A modified data object";
+
+   std::map < AttributeType, secure_vector<uint8_t>> new_attributes =
+      {
+         { AttributeType::Label, secure_vector<uint8_t>(new_label.begin(), new_label.end()) }
+      };
+
+   auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle,
+                         std::map<AttributeType, secure_vector<uint8_t>>&, ReturnValue*) const>
+                         (&LowLevel::C_SetAttributeValue<secure_allocator<uint8_t>>), *p11_low_level.get(), session_handle,
+                         object_handle, std::ref(new_attributes), std::placeholders::_1);
+
+   Test::Result result = test_function("C_SetAttributeValue", bind);
+
+   // get attributes and check if they are changed correctly
+   std::vector<AttributeType> types = { AttributeType::Label, AttributeType::Value };
+   auto received_attributes = get_attribute_values(p11_low_level, session_handle, object_handle, types);
+
+   std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(),
+                               received_attributes[ AttributeType::Label ].end());
+
+   result.test_eq("label", new_label, retrieved_label);
+
+   // cleanup
+   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+   return result;
+   }
+
+Test::Result test_c_copy_object()
+   {
+   RAII_LowLevel p11_low_level;
+   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+   ObjectHandle copied_object_handle = 0;
+
+   std::string copied_label = "A copied data object";
+
+   Attribute copy_attribute_values = {
+      static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Label), const_cast<char*>(copied_label.c_str()), static_cast<CK_ULONG>(copied_label.size())
+   };
+
+   auto binder = std::bind(&LowLevel::C_CopyObject, *p11_low_level.get(), session_handle, object_handle,
+                           &copy_attribute_values, 1, &copied_object_handle, std::placeholders::_1);
+
+   Test::Result result = test_function("C_CopyObject", binder);
+
+   // get attributes and check if its copied correctly
+   std::vector<AttributeType> types = { AttributeType::Label, AttributeType::Value };
+   auto received_attributes = get_attribute_values(p11_low_level, session_handle, copied_object_handle, types);
+
+   std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(),
+                               received_attributes[ AttributeType::Label ].end());
+
+   result.test_eq("label", copied_label, retrieved_label);
+
+   // cleanup
+   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+   p11_low_level.get()->C_DestroyObject(session_handle, copied_object_handle);
+
+   return result;
+   }
+
+class LowLevelTests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
+            {STRING_AND_FUNCTION(test_c_get_function_list)},
+            {STRING_AND_FUNCTION(test_low_level_ctor)},
+            {STRING_AND_FUNCTION(test_initialize_finalize)},
+            {STRING_AND_FUNCTION(test_c_get_info)},
+            {STRING_AND_FUNCTION(test_c_get_slot_list)},
+            {STRING_AND_FUNCTION(test_c_get_slot_info)},
+            {STRING_AND_FUNCTION(test_c_get_token_info)},
+            {STRING_AND_FUNCTION(test_c_wait_for_slot_event)},
+            {STRING_AND_FUNCTION(test_c_get_mechanism_list)},
+            {STRING_AND_FUNCTION(test_c_get_mechanism_info)},
+            {STRING_AND_FUNCTION(test_open_close_session)},
+            {STRING_AND_FUNCTION(test_c_close_all_sessions)},
+            {STRING_AND_FUNCTION(test_c_get_session_info)},
+            {STRING_AND_FUNCTION(test_c_init_token)},
+            {STRING_AND_FUNCTION(test_c_login_logout_security_officier)}, /* only possible if token is initialized */
+            {STRING_AND_FUNCTION(test_c_init_pin)},
+            {STRING_AND_FUNCTION(test_c_login_logout_user)}, /* only possible if token is initialized and user pin is set */
+            {STRING_AND_FUNCTION(test_c_set_pin)},
+            {STRING_AND_FUNCTION(test_c_create_object_c_destroy_object)},
+            {STRING_AND_FUNCTION(test_c_get_object_size)},
+            {STRING_AND_FUNCTION(test_c_get_attribute_value)},
+            {STRING_AND_FUNCTION(test_c_set_attribute_value)},
+            {STRING_AND_FUNCTION(test_c_copy_object)}
+         };
+
+         return run_pkcs11_tests("PKCS11 low level", fns);
+         }
+   };
+
+BOTAN_REGISTER_TEST("pkcs11-lowlevel", LowLevelTests);
+
+#endif
+#endif
+
+}
+}
diff --git a/third_party/botan/src/tests/test_psk_db.cpp b/third_party/botan/src/tests/test_psk_db.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_psk_db.cpp
@@ -0,0 +1,259 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PSK_DB)
+
+#include <botan/psk_db.h>
+
+#if defined(BOTAN_HAS_SQLITE3)
+  #include <botan/psk_db_sql.h>
+  #include <botan/sqlite3.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+class Test_Map_PSK_Db : public Botan::Encrypted_PSK_Database
+   {
+   public:
+      Test_Map_PSK_Db(const Botan::secure_vector<uint8_t>& master_key) :
+         Botan::Encrypted_PSK_Database(master_key)
+         {}
+
+      void test_entry(Test::Result& result,
+                      const std::string& index, const std::string& value)
+         {
+         auto i = m_vals.find(index);
+
+         if(i == m_vals.end())
+            {
+            result.test_failure("Expected to find encrypted name " + index);
+            }
+         else
+            {
+            result.test_eq("Encrypted value", i->second, value);
+            }
+         }
+
+      void kv_set(const std::string& index, const std::string& value) override
+         {
+         m_vals[index] = value;
+         }
+
+      std::string kv_get(const std::string& index) const override
+         {
+         auto i = m_vals.find(index);
+         if(i == m_vals.end())
+            return "";
+         return i->second;
+         }
+
+      void kv_del(const std::string& index) override
+         {
+         auto i = m_vals.find(index);
+         if(i != m_vals.end())
+            {
+            m_vals.erase(i);
+            }
+         }
+
+      std::set<std::string> kv_get_all() const override
+         {
+         std::set<std::string> names;
+
+         for(auto kv : m_vals)
+            {
+            names.insert(kv.first);
+            }
+
+         return names;
+         }
+
+   private:
+      std::map<std::string, std::string> m_vals;
+   };
+
+}
+
+class PSK_DB_Tests final : public Test
+   {
+   public:
+
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_psk_db());
+
+#if defined(BOTAN_HAS_SQLITE3)
+         results.push_back(test_psk_sql_db());
+#endif
+
+         return results;
+         }
+
+   private:
+
+      Test::Result test_psk_db()
+         {
+         Test::Result result("PSK_DB");
+
+         const Botan::secure_vector<uint8_t> zeros(32);
+         Test_Map_PSK_Db db(zeros);
+
+         db.set_str("name", "value");
+         db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA==");
+         result.test_eq("DB read", db.get_str("name"), "value");
+
+         db.set_str("name", "value1");
+         db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "7R8am3x/gLawOzMp5WwIJg==");
+         result.test_eq("DB read", db.get_str("name"), "value1");
+
+         db.set_str("name", "value");
+         db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA==");
+         result.test_eq("DB read", db.get_str("name"), "value");
+
+         db.set_str("name2", "value");
+         db.test_entry(result, "7CvsM7HDCZsV6VsFwWylNg==", "BqVQo4rdwOmf+ItCzEmjAg==");
+         result.test_eq("DB read", db.get_str("name2"), "value");
+
+         db.set_vec("name2", zeros);
+         db.test_entry(result, "7CvsM7HDCZsV6VsFwWylNg==", "x+I1bUF/fJYPOTvKwOihEPWGR1XGzVuyRdsw4n5gpBRzNR7LjH7vjw==");
+         result.test_eq("DB read", db.get("name2"), zeros);
+
+         // Test longer names
+         db.set_str("leroy jeeeeeeeenkins", "chicken");
+         db.test_entry(result, "KyYo272vlSjClM2F0OZBMlRYjr33ZXv2jN1oY8OfCEs=", "tCl1qShSTsXi9tA5Kpo9vg==");
+         result.test_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken");
+
+         std::set<std::string> all_names = db.list_names();
+
+         result.test_eq("Expected number of names", all_names.size(), 3);
+         result.test_eq("Have expected name", all_names.count("name"), 1);
+         result.test_eq("Have expected name", all_names.count("name2"), 1);
+         result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1);
+
+         db.remove("name2");
+
+         all_names = db.list_names();
+
+         result.test_eq("Expected number of names", all_names.size(), 2);
+         result.test_eq("Have expected name", all_names.count("name"), 1);
+         result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1);
+
+         result.test_throws("exception if get called on non-existent PSK",
+                            "Named PSK not located",
+                            [&]() { db.get("name2"); });
+
+         // test that redundant remove calls accepted
+         db.remove("name2");
+
+         return result;
+         }
+
+#if defined(BOTAN_HAS_SQLITE3)
+
+      void test_entry(Test::Result& result,
+                      std::shared_ptr<Botan::SQL_Database> db,
+                      const std::string& table,
+                      const std::string& expected_name,
+                      const std::string& expected_value)
+         {
+         auto stmt = db->new_statement("select psk_value from " + table + " where psk_name='" + expected_name + "'");
+
+         bool got_it = stmt->step();
+         result.confirm("Had expected name", got_it);
+
+         if(got_it)
+            {
+            result.test_eq("Had expected value", stmt->get_str(0), expected_value);
+            }
+         }
+
+      Test::Result test_psk_sql_db()
+         {
+         Test::Result result("PSK_DB SQL");
+
+         const Botan::secure_vector<uint8_t> zeros(32);
+         const Botan::secure_vector<uint8_t> not_zeros = Test::rng().random_vec(32);
+
+         const std::string table_name = "bobby";
+         std::shared_ptr<Botan::SQL_Database> sqldb = std::make_shared<Botan::Sqlite3_Database>(":memory:");
+
+         Botan::Encrypted_PSK_Database_SQL db(zeros, sqldb, table_name);
+         db.set_str("name", "value");
+
+         test_entry(result, sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA==");
+         result.test_eq("DB read", db.get_str("name"), "value");
+
+         db.set_str("name", "value1");
+         test_entry(result, sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "7R8am3x/gLawOzMp5WwIJg==");
+         result.test_eq("DB read", db.get_str("name"), "value1");
+
+         db.set_str("name", "value");
+         test_entry(result, sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA==");
+         result.test_eq("DB read", db.get_str("name"), "value");
+
+         db.set_str("name2", "value");
+         test_entry(result, sqldb, table_name, "7CvsM7HDCZsV6VsFwWylNg==", "BqVQo4rdwOmf+ItCzEmjAg==");
+         result.test_eq("DB read", db.get_str("name2"), "value");
+
+         db.set_vec("name2", zeros);
+         test_entry(result, sqldb, table_name, "7CvsM7HDCZsV6VsFwWylNg==", "x+I1bUF/fJYPOTvKwOihEPWGR1XGzVuyRdsw4n5gpBRzNR7LjH7vjw==");
+         result.test_eq("DB read", db.get("name2"), zeros);
+
+         // Test longer names
+         db.set_str("leroy jeeeeeeeenkins", "chicken");
+         test_entry(result, sqldb, table_name, "KyYo272vlSjClM2F0OZBMlRYjr33ZXv2jN1oY8OfCEs=", "tCl1qShSTsXi9tA5Kpo9vg==");
+         result.test_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken");
+
+         /*
+         * Test that we can have another database in the same table with distinct key
+         * without any problems.
+         */
+         Botan::Encrypted_PSK_Database_SQL db2(not_zeros, sqldb, table_name);
+         db2.set_str("name", "price&value");
+         result.test_eq("DB read", db2.get_str("name"), "price&value");
+         result.test_eq("DB2 size", db2.list_names().size(), 1);
+
+         std::set<std::string> all_names = db.list_names();
+
+         result.test_eq("Expected number of names", all_names.size(), 3);
+         result.test_eq("Have expected name", all_names.count("name"), 1);
+         result.test_eq("Have expected name", all_names.count("name2"), 1);
+         result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1);
+
+         db.remove("name2");
+
+         all_names = db.list_names();
+
+         result.test_eq("Expected number of names", all_names.size(), 2);
+         result.test_eq("Have expected name", all_names.count("name"), 1);
+         result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1);
+
+         result.test_throws("exception if get called on non-existent PSK",
+                            "Named PSK not located",
+                            [&]() { db.get("name2"); });
+
+         // test that redundant remove calls accepted
+         db.remove("name2");
+
+
+         return result;
+         }
+#endif
+
+
+   };
+
+BOTAN_REGISTER_TEST("psk_db", PSK_DB_Tests);
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_pubkey.cpp b/third_party/botan/src/tests/test_pubkey.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pubkey.cpp
@@ -0,0 +1,769 @@
+/*
+* (C) 2009,2015 Jack Lloyd
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "test_pubkey.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include "test_rng.h"
+
+#include <botan/pubkey.h>
+#include <botan/pk_algs.h>
+#include <botan/x509_key.h>
+#include <botan/pkcs8.h>
+#include <botan/hex.h>
+#include <botan/data_src.h>
+
+namespace Botan_Tests {
+
+void check_invalid_signatures(Test::Result& result,
+                              Botan::PK_Verifier& verifier,
+                              const std::vector<uint8_t>& message,
+                              const std::vector<uint8_t>& signature)
+   {
+   const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5);
+
+   const std::vector<uint8_t> zero_sig(signature.size());
+   result.test_eq("all zero signature invalid", verifier.verify_message(message, zero_sig), false);
+
+   for(size_t i = 0; i < tests_to_run; ++i)
+      {
+      const std::vector<uint8_t> bad_sig = Test::mutate_vec(signature);
+
+      try
+         {
+         if(!result.test_eq("incorrect signature invalid",
+                            verifier.verify_message(message, bad_sig), false))
+            {
+            result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig));
+            }
+         }
+      catch(std::exception& e)
+         {
+         result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig));
+         result.test_failure("Modified signature rejected with exception", e.what());
+         }
+      }
+   }
+
+void check_invalid_ciphertexts(Test::Result& result,
+                               Botan::PK_Decryptor& decryptor,
+                               const std::vector<uint8_t>& plaintext,
+                               const std::vector<uint8_t>& ciphertext)
+   {
+   const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5);
+
+   size_t ciphertext_accepted = 0, ciphertext_rejected = 0;
+
+   for(size_t i = 0; i < tests_to_run; ++i)
+      {
+      const std::vector<uint8_t> bad_ctext = Test::mutate_vec(ciphertext);
+
+      try
+         {
+         const Botan::secure_vector<uint8_t> decrypted = decryptor.decrypt(bad_ctext);
+         ++ciphertext_accepted;
+
+         if(!result.test_ne("incorrect ciphertext different", decrypted, plaintext))
+            {
+            result.test_eq("used corrupted ciphertext", bad_ctext, ciphertext);
+            }
+         }
+      catch(std::exception&)
+         {
+         ++ciphertext_rejected;
+         }
+      }
+
+   result.test_note("Accepted " + std::to_string(ciphertext_accepted) +
+                    " invalid ciphertexts, rejected " + std::to_string(ciphertext_rejected));
+   }
+
+std::string PK_Test::choose_padding(const VarMap& vars,
+                                    const std::string& pad_hdr)
+   {
+   if(pad_hdr != "")
+      return pad_hdr;
+   return vars.get_opt_str("Padding", this->default_padding(vars));
+   }
+
+std::vector<std::string> PK_Test::possible_providers(const std::string& /*params*/)
+   {
+   return Test::provider_filter({ "base", "commoncrypto", "openssl", "tpm" });
+   }
+
+Test::Result
+PK_Signature_Generation_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars)
+   {
+   const std::vector<uint8_t> message   = vars.get_req_bin("Msg");
+   const std::vector<uint8_t> signature = vars.get_req_bin("Signature");
+   const std::string padding = choose_padding(vars, pad_hdr);
+
+   Test::Result result(algo_name() + "/" + padding + " signature generation");
+
+   std::unique_ptr<Botan::Private_Key> privkey;
+   try
+      {
+      privkey = load_private_key(vars);
+      }
+   catch(Botan::Lookup_Error& e)
+      {
+      result.note_missing(e.what());
+      return result;
+      }
+
+   std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(Botan::X509::BER_encode(*privkey)));
+
+   std::vector<std::unique_ptr<Botan::PK_Verifier>> verifiers;
+
+   for(auto const& verify_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Verifier> verifier;
+
+      try
+         {
+         verifier.reset(new Botan::PK_Verifier(*pubkey, padding, Botan::IEEE_1363, verify_provider));
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         //result.test_note("Skipping verifying with " + verify_provider);
+         continue;
+         }
+
+      result.test_eq("KAT signature valid", verifier->verify_message(message, signature), true);
+
+      check_invalid_signatures(result, *verifier, message, signature);
+      verifiers.push_back(std::move(verifier));
+      }
+
+   for(auto const& sign_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Signer> signer;
+
+      std::vector<uint8_t> generated_signature;
+
+      try
+         {
+         signer.reset(new Botan::PK_Signer(*privkey, Test::rng(), padding, Botan::IEEE_1363, sign_provider));
+
+         if(vars.has_key("Nonce"))
+            {
+            std::unique_ptr<Botan::RandomNumberGenerator> rng(test_rng(vars.get_req_bin("Nonce")));
+            generated_signature = signer->sign_message(message, *rng);
+            }
+         else
+            {
+            generated_signature = signer->sign_message(message, Test::rng());
+            }
+
+         result.test_lte("Generated signature within announced bound",
+                         generated_signature.size(), signer->signature_length());
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         //result.test_note("Skipping signing with " + sign_provider);
+         continue;
+         }
+
+      if(sign_provider == "base")
+         {
+         result.test_eq("generated signature matches KAT", generated_signature, signature);
+         }
+      else if(generated_signature != signature)
+         {
+         for(std::unique_ptr<Botan::PK_Verifier>& verifier : verifiers)
+            {
+            if(!result.test_eq("generated signature valid",
+                               verifier->verify_message(message, generated_signature), true))
+               {
+               result.test_failure("generated signature", generated_signature);
+               }
+            }
+         }
+      }
+
+   return result;
+   }
+
+Botan::Signature_Format
+PK_Signature_Verification_Test::sig_format() const
+   {
+   return Botan::IEEE_1363;
+   }
+
+Test::Result
+PK_Signature_Verification_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars)
+   {
+   const std::vector<uint8_t> message   = vars.get_req_bin("Msg");
+   const std::vector<uint8_t> signature = vars.get_req_bin("Signature");
+   const std::string padding = choose_padding(vars, pad_hdr);
+
+   const bool expected_valid = (vars.get_opt_sz("Valid", 1) == 1);
+
+   std::unique_ptr<Botan::Public_Key> pubkey = load_public_key(vars);
+
+   Test::Result result(algo_name() + "/" + padding + " signature verification");
+
+   for(auto const& verify_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Verifier> verifier;
+
+      try
+         {
+         verifier.reset(new Botan::PK_Verifier(*pubkey, padding, sig_format(), verify_provider));
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         //result.test_note("Skipping verifying with " + verify_provider);
+         }
+
+      if(verifier)
+         {
+         try
+            {
+            const bool verified = verifier->verify_message(message, signature);
+
+            if(expected_valid)
+               {
+               result.test_eq("correct signature valid with " + verify_provider, verified, true);
+
+               if(test_random_invalid_sigs())
+                  {
+                  check_invalid_signatures(result, *verifier, message, signature);
+                  }
+               }
+            else
+               result.test_eq("incorrect signature invalid", verified, false);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("verification threw exception", e.what());
+            }
+         }
+      }
+
+   return result;
+   }
+
+Test::Result
+PK_Signature_NonVerification_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars)
+   {
+   const std::string padding = choose_padding(vars, pad_hdr);
+   const std::vector<uint8_t> message   = vars.get_req_bin("Msg");
+   std::unique_ptr<Botan::Public_Key> pubkey = load_public_key(vars);
+
+   const std::vector<uint8_t> invalid_signature = vars.get_req_bin("InvalidSignature");
+
+   Test::Result result(algo_name() + "/" + padding + " verify invalid signature");
+
+   for(auto const& verify_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Verifier> verifier;
+
+      try
+         {
+         verifier.reset(new Botan::PK_Verifier(*pubkey, padding, Botan::IEEE_1363, verify_provider));
+         result.test_eq("incorrect signature rejected", verifier->verify_message(message, invalid_signature), false);
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         result.test_note("Skipping verifying with " + verify_provider);
+         }
+      }
+
+   return result;
+   }
+
+std::vector<Test::Result>
+PK_Sign_Verify_DER_Test::run()
+   {
+   const std::vector<uint8_t> message = {'f', 'o', 'o', 'b', 'a', 'r'};
+   const std::string padding = m_padding;
+
+   std::unique_ptr<Botan::Private_Key> privkey = key();
+
+   Test::Result result(algo_name() + "/" + padding + " signature sign/verify using DER format");
+
+   for(auto const& provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Signer> signer;
+      std::unique_ptr<Botan::PK_Verifier> verifier;
+
+      try
+         {
+         signer.reset(new Botan::PK_Signer(*privkey, Test::rng(), padding, Botan::DER_SEQUENCE, provider));
+         verifier.reset(new Botan::PK_Verifier(*privkey, padding, Botan::DER_SEQUENCE, provider));
+         }
+      catch(Botan::Lookup_Error& e)
+         {
+         result.test_note("Skipping sign/verify with " + provider, e.what());
+         }
+
+      if(signer && verifier)
+         {
+         try
+            {
+            std::vector<uint8_t> generated_signature = signer->sign_message(message, Test::rng());
+            const bool verified = verifier->verify_message(message, generated_signature);
+
+            result.test_eq("correct signature valid with " + provider, verified, true);
+
+            if(test_random_invalid_sigs())
+               {
+               check_invalid_signatures(result, *verifier, message, generated_signature);
+               }
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("verification threw exception", e.what());
+            }
+         }
+      }
+
+   return {result};
+   }
+
+std::vector<std::string> PK_Sign_Verify_DER_Test::possible_providers(
+   const std::string& algo)
+   {
+   std::vector<std::string> pk_provider =
+      Botan::probe_provider_private_key(algo, { "base", "commoncrypto", "openssl", "tpm" });
+   return Test::provider_filter(pk_provider);
+   }
+
+Test::Result
+PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars)
+   {
+   const std::vector<uint8_t> plaintext  = vars.get_req_bin("Msg");
+   const std::vector<uint8_t> ciphertext = vars.get_req_bin("Ciphertext");
+   const std::string padding = choose_padding(vars, pad_hdr);
+
+   Test::Result result(algo_name() + (padding.empty() ? padding : "/" + padding) + " encryption");
+
+   std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars);
+
+   // instead slice the private key to work around elgamal test inputs
+   //std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(Botan::X509::BER_encode(*privkey)));
+   Botan::Public_Key* pubkey = privkey.get();
+
+   std::vector<std::unique_ptr<Botan::PK_Decryptor>> decryptors;
+
+   for(auto const& dec_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Decryptor> decryptor;
+
+      try
+         {
+         decryptor.reset(new Botan::PK_Decryptor_EME(*privkey, Test::rng(), padding, dec_provider));
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         continue;
+         }
+
+      Botan::secure_vector<uint8_t> decrypted;
+      try
+         {
+         decrypted = decryptor->decrypt(ciphertext);
+
+         result.test_lte("Plaintext within length",
+                         decrypted.size(),
+                         decryptor->plaintext_length(ciphertext.size()));
+         }
+      catch(Botan::Exception& e)
+         {
+         result.test_failure("Failed to decrypt KAT ciphertext", e.what());
+         }
+
+      result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext);
+      check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext);
+      }
+
+
+   for(auto const& enc_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Encryptor> encryptor;
+
+      try
+         {
+         encryptor.reset(new Botan::PK_Encryptor_EME(*pubkey, Test::rng(), padding, enc_provider));
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         continue;
+         }
+
+      std::unique_ptr<Botan::RandomNumberGenerator> kat_rng;
+      if(vars.has_key("Nonce"))
+         {
+         kat_rng.reset(test_rng(vars.get_req_bin("Nonce")));
+         }
+
+      if(padding == "Raw")
+         {
+         /*
+         Hack for RSA with no padding since sometimes one more bit will fit in but maximum_input_size
+         rounds down to nearest byte
+         */
+         result.test_lte("Input within accepted bounds",
+                         plaintext.size(), encryptor->maximum_input_size() + 1);
+         }
+      else
+         {
+         result.test_lte("Input within accepted bounds",
+                         plaintext.size(), encryptor->maximum_input_size());
+         }
+
+      const std::vector<uint8_t> generated_ciphertext =
+         encryptor->encrypt(plaintext, kat_rng ? *kat_rng : Test::rng());
+
+      result.test_lte("Ciphertext within length",
+                      generated_ciphertext.size(),
+                      encryptor->ciphertext_length(plaintext.size()));
+
+      if(enc_provider == "base")
+         {
+         result.test_eq(enc_provider, "generated ciphertext matches KAT",
+                        generated_ciphertext, ciphertext);
+         }
+      else if(generated_ciphertext != ciphertext)
+         {
+         for(std::unique_ptr<Botan::PK_Decryptor>& dec : decryptors)
+            {
+            result.test_eq("decryption of generated ciphertext",
+                           dec->decrypt(generated_ciphertext), plaintext);
+            }
+         }
+
+      }
+
+   return result;
+   }
+
+Test::Result
+PK_Decryption_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars)
+   {
+   const std::vector<uint8_t> plaintext  = vars.get_req_bin("Msg");
+   const std::vector<uint8_t> ciphertext = vars.get_req_bin("Ciphertext");
+   const std::string padding = choose_padding(vars, pad_hdr);
+
+   Test::Result result(algo_name() + (padding.empty() ? padding : "/" + padding) + " decryption");
+
+   std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars);
+
+   for(auto const& dec_provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Decryptor> decryptor;
+
+      try
+         {
+         decryptor.reset(new Botan::PK_Decryptor_EME(*privkey, Test::rng(), padding, dec_provider));
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         continue;
+         }
+
+      Botan::secure_vector<uint8_t> decrypted;
+      try
+         {
+         decrypted = decryptor->decrypt(ciphertext);
+         }
+      catch(Botan::Exception& e)
+         {
+         result.test_failure("Failed to decrypt KAT ciphertext", e.what());
+         }
+
+      result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext);
+      check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext);
+      }
+
+   return result;
+   }
+
+Test::Result PK_KEM_Test::run_one_test(const std::string&, const VarMap& vars)
+   {
+   const std::vector<uint8_t> K = vars.get_req_bin("K");
+   const std::vector<uint8_t> C0 = vars.get_req_bin("C0");
+   const std::vector<uint8_t> salt = vars.get_opt_bin("Salt");
+   const std::string kdf = vars.get_req_str("KDF");
+
+   Test::Result result(algo_name() + "/" + kdf + " KEM");
+
+   std::unique_ptr<Botan::Private_Key> privkey = load_private_key(vars);
+
+   const Botan::Public_Key& pubkey = *privkey;
+
+   const size_t desired_key_len = K.size();
+
+   std::unique_ptr<Botan::PK_KEM_Encryptor> enc;
+   try
+      {
+      enc.reset(new Botan::PK_KEM_Encryptor(pubkey, Test::rng(), kdf));
+      }
+   catch(Botan::Lookup_Error&)
+      {
+      result.test_note("Skipping due to missing KDF: " + kdf);
+      return result;
+      }
+
+   Fixed_Output_RNG fixed_output_rng(vars.get_req_bin("R"));
+
+   Botan::secure_vector<uint8_t> produced_encap_key, shared_key;
+   enc->encrypt(produced_encap_key,
+                shared_key,
+                desired_key_len,
+                fixed_output_rng,
+                salt);
+
+   result.test_eq("C0 matches", produced_encap_key, C0);
+   result.test_eq("K matches", shared_key, K);
+
+   std::unique_ptr<Botan::PK_KEM_Decryptor> dec;
+   try
+      {
+      dec.reset(new Botan::PK_KEM_Decryptor(*privkey, Test::rng(), kdf));
+      }
+   catch(Botan::Lookup_Error& e)
+      {
+      result.test_note("Skipping test", e.what());
+      return result;
+      }
+
+   const Botan::secure_vector<uint8_t> decr_shared_key =
+      dec->decrypt(C0.data(), C0.size(),
+                   desired_key_len,
+                   salt.data(),
+                   salt.size());
+
+   result.test_eq("decrypted K matches", decr_shared_key, K);
+
+   return result;
+   }
+
+Test::Result PK_Key_Agreement_Test::run_one_test(const std::string& header, const VarMap& vars)
+   {
+   const std::vector<uint8_t> shared = vars.get_req_bin("K");
+   const std::string kdf = vars.get_opt_str("KDF", default_kdf(vars));
+
+   Test::Result result(algo_name() + "/" + kdf +
+                       (header.empty() ? header : " " + header) +
+                       " key agreement");
+
+   std::unique_ptr<Botan::Private_Key> privkey = load_our_key(header, vars);
+   const std::vector<uint8_t> pubkey = load_their_key(header, vars);
+
+   const size_t key_len = vars.get_opt_sz("OutLen", 0);
+
+   for(auto const& provider : possible_providers(algo_name()))
+      {
+      std::unique_ptr<Botan::PK_Key_Agreement> kas;
+
+      try
+         {
+         kas.reset(new Botan::PK_Key_Agreement(*privkey, Test::rng(), kdf, provider));
+
+         auto derived_key = kas->derive_key(key_len, pubkey).bits_of();
+         result.test_eq(provider, "agreement", derived_key, shared);
+
+         if(key_len == 0 && kdf == "Raw")
+            {
+            result.test_eq("Expected size", derived_key.size(), kas->agreed_value_size());
+            }
+         }
+      catch(Botan::Lookup_Error&)
+         {
+         //result.test_note("Skipping key agreement with with " + provider);
+         }
+      }
+
+   return result;
+   }
+
+std::vector<std::string> PK_Key_Generation_Test::possible_providers(
+   const std::string& algo)
+   {
+   std::vector<std::string> pk_provider =
+      Botan::probe_provider_private_key(algo, { "base", "commoncrypto", "openssl", "tpm" });
+   return Test::provider_filter(pk_provider);
+   }
+
+namespace {
+
+#if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && (defined(BOTAN_HAS_SHA2_32) || defined(BOTAN_HAS_SCRYPT))
+void test_pbe_roundtrip(Test::Result& result,
+                        const Botan::Private_Key& key,
+                        const std::string& pbe_algo,
+                        const std::string& passphrase)
+   {
+   const auto pkcs8 = key.private_key_info();
+
+   try
+      {
+      Botan::DataSource_Memory data_src(
+         Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase,
+                                  std::chrono::milliseconds(10),
+                                  pbe_algo));
+
+      std::unique_ptr<Botan::Private_Key> loaded(
+         Botan::PKCS8::load_key(data_src, Test::rng(), passphrase));
+
+      result.confirm("recovered private key from encrypted blob", loaded.get() != nullptr);
+      result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name());
+      result.test_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("roundtrip encrypted PEM private key", e.what());
+      }
+
+   try
+      {
+      Botan::DataSource_Memory data_src(
+         Botan::PKCS8::BER_encode(key, Test::rng(), passphrase,
+                                  std::chrono::milliseconds(10),
+                                  pbe_algo));
+
+      std::unique_ptr<Botan::Private_Key> loaded(
+         Botan::PKCS8::load_key(data_src, Test::rng(), passphrase));
+
+      result.confirm("recovered private key from BER blob", loaded.get() != nullptr);
+      result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name());
+      result.test_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("roundtrip encrypted BER private key", e.what());
+      }
+   }
+#endif
+
+}
+
+std::vector<Test::Result> PK_Key_Generation_Test::run()
+   {
+   std::vector<Test::Result> results;
+
+   for(auto const& param : keygen_params())
+      {
+      const std::string report_name = algo_name() + (param.empty() ? param : " " + param);
+
+      Test::Result result(report_name + " keygen");
+
+      const std::vector<std::string> providers = possible_providers(algo_name());
+
+      if(providers.empty())
+         {
+         result.note_missing("provider key generation " + algo_name());
+         }
+
+      result.start_timer();
+      for(auto&& prov : providers)
+         {
+         std::unique_ptr<Botan::Private_Key> key_p =
+            Botan::create_private_key(algo_name(), Test::rng(), param, prov);
+
+         const Botan::Private_Key& key = *key_p;
+
+         try
+            {
+            result.confirm("Key passes self tests", key.check_key(Test::rng(), true));
+            }
+         catch(Botan::Lookup_Error&) {}
+
+         result.test_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64);
+         result.test_lt("Key has reasonable estimated strength (upper)", key.estimated_strength(), 512);
+
+         // Test PEM public key round trips OK
+         try
+            {
+            Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(key));
+            std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src));
+
+            result.confirm("recovered public key from private", loaded.get() != nullptr);
+            result.test_eq("public key has same type", loaded->algo_name(), key.algo_name());
+
+            try
+               {
+               result.test_eq("public key passes checks", loaded->check_key(Test::rng(), false), true);
+               }
+            catch(Botan::Lookup_Error&) {}
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("roundtrip PEM public key", e.what());
+            }
+
+         // Test DER public key round trips OK
+         try
+            {
+            const auto ber = key.subject_public_key();
+            Botan::DataSource_Memory data_src(ber);
+            std::unique_ptr<Botan::Public_Key> loaded(Botan::X509::load_key(data_src));
+
+            result.confirm("recovered public key from private", loaded.get() != nullptr);
+            result.test_eq("public key has same type", loaded->algo_name(), key.algo_name());
+            result.test_eq("public key has same encoding", loaded->subject_public_key(), ber);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("roundtrip BER public key", e.what());
+            }
+
+         // Test PEM private key round trips OK
+         try
+            {
+            const auto ber = key.private_key_info();
+            Botan::DataSource_Memory data_src(ber);
+            std::unique_ptr<Botan::Private_Key> loaded(
+               Botan::PKCS8::load_key(data_src, Test::rng()));
+
+            result.confirm("recovered private key from PEM blob", loaded.get() != nullptr);
+            result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name());
+            result.test_eq("reloaded key has same encoding", loaded->private_key_info(), ber);
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("roundtrip PEM private key", e.what());
+            }
+
+         try
+            {
+            Botan::DataSource_Memory data_src(Botan::PKCS8::BER_encode(key));
+            std::unique_ptr<Botan::Public_Key> loaded(Botan::PKCS8::load_key(data_src, Test::rng()));
+
+            result.confirm("recovered public key from private", loaded.get() != nullptr);
+            result.test_eq("public key has same type", loaded->algo_name(), key.algo_name());
+            }
+         catch(std::exception& e)
+            {
+            result.test_failure("roundtrip BER private key", e.what());
+            }
+
+#if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SHA2_32)
+
+         test_pbe_roundtrip(result, key, "PBE-PKCS5v20(AES-128/CBC,SHA-256)", Test::random_password());
+#endif
+
+#if defined(BOTAN_HAS_PKCS5_PBES2) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SCRYPT)
+
+         test_pbe_roundtrip(result, key, "PBES2(AES-128/CBC,Scrypt)", Test::random_password());
+#endif
+
+         }
+
+      result.end_timer();
+
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_pubkey.h b/third_party/botan/src/tests/test_pubkey.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_pubkey.h
@@ -0,0 +1,242 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TEST_PUBKEY_H_
+#define BOTAN_TEST_PUBKEY_H_
+
+#include "tests.h"
+
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include <botan/pubkey.h>
+
+namespace Botan_Tests {
+
+class PK_Test : public Text_Based_Test
+   {
+   public:
+      PK_Test(const std::string& algo,
+              const std::string& test_src,
+              const std::string& required_keys,
+              const std::string& optional_keys = {})
+         : Text_Based_Test(test_src, required_keys, optional_keys), m_algo(algo) {}
+
+      std::string algo_name() const
+         {
+         return m_algo;
+         }
+
+   protected:
+      std::vector<std::string> possible_providers(const std::string& params) override;
+
+      virtual std::string default_padding(const VarMap&) const
+         {
+         throw Test_Error("No default padding scheme set for " + algo_name());
+         }
+
+      virtual std::string choose_padding(const VarMap& vars,
+                                         const std::string& pad_hdr);
+
+   private:
+      std::string m_algo;
+   };
+
+class PK_Signature_Generation_Test : public PK_Test
+   {
+   public:
+      PK_Signature_Generation_Test(const std::string& algo,
+                                   const std::string& test_src,
+                                   const std::string& required_keys,
+                                   const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+
+      virtual std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) = 0;
+
+      virtual Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const
+         {
+         return new Fixed_Output_RNG(nonce);
+         }
+
+   private:
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override final;
+   };
+
+class PK_Signature_Verification_Test : public PK_Test
+   {
+   public:
+      PK_Signature_Verification_Test(const std::string& algo,
+                                     const std::string& test_src,
+                                     const std::string& required_keys,
+                                     const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      virtual Botan::Signature_Format sig_format() const;
+
+      virtual bool test_random_invalid_sigs() const { return true; }
+
+      virtual std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) = 0;
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_Signature_NonVerification_Test : public PK_Test
+   {
+   public:
+      PK_Signature_NonVerification_Test(const std::string& algo,
+                                        const std::string& test_src,
+                                        const std::string& required_keys,
+                                        const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      virtual std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) = 0;
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_Sign_Verify_DER_Test : public Test
+   {
+   public:
+      PK_Sign_Verify_DER_Test(const std::string& algo,
+                              const std::string& padding)
+         : m_algo(algo), m_padding(padding) {}
+
+      std::string algo_name() const
+         {
+         return m_algo;
+         }
+
+   protected:
+      std::vector<Test::Result> run() override final;
+
+      virtual std::unique_ptr<Botan::Private_Key> key() const = 0;
+
+      virtual bool test_random_invalid_sigs() const { return true; }
+
+      std::vector<std::string> possible_providers(const std::string& params) override;
+
+   private:
+      std::string m_algo;
+      std::string m_padding;
+   };
+
+class PK_Encryption_Decryption_Test : public PK_Test
+   {
+   public:
+      PK_Encryption_Decryption_Test(const std::string& algo,
+                                    const std::string& test_src,
+                                    const std::string& required_keys,
+                                    const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      virtual std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) = 0;
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      virtual Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const
+         {
+         return new Fixed_Output_RNG(nonce);
+         }
+
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_Decryption_Test : public PK_Test
+   {
+   public:
+      PK_Decryption_Test(const std::string& algo,
+                         const std::string& test_src,
+                         const std::string& required_keys,
+                         const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      virtual std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) = 0;
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_Key_Agreement_Test : public PK_Test
+   {
+   public:
+      PK_Key_Agreement_Test(const std::string& algo,
+                            const std::string& test_src,
+                            const std::string& required_keys,
+                            const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      virtual std::unique_ptr<Botan::Private_Key> load_our_key(const std::string& header,
+            const VarMap& vars) = 0;
+
+      virtual std::vector<uint8_t> load_their_key(const std::string& header,
+            const VarMap& vars) = 0;
+
+      virtual std::string default_kdf(const VarMap&) const
+         {
+         return "Raw";
+         }
+
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_KEM_Test : public PK_Test
+   {
+   public:
+      PK_KEM_Test(const std::string& algo,
+                  const std::string& test_src,
+                  const std::string& required_keys,
+                  const std::string& optional_keys = "")
+         : PK_Test(algo, test_src, required_keys, optional_keys) {}
+
+      virtual std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) = 0;
+   private:
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override final;
+   };
+
+class PK_Key_Generation_Test : public Test
+   {
+   protected:
+      std::vector<Test::Result> run() override final;
+
+      virtual std::vector<std::string> keygen_params() const = 0;
+
+      virtual std::string algo_name() const = 0;
+
+      std::vector<std::string> possible_providers(const std::string& params) override;
+   };
+
+void check_invalid_signatures(Test::Result& result,
+                              Botan::PK_Verifier& verifier,
+                              const std::vector<uint8_t>& message,
+                              const std::vector<uint8_t>& signature);
+
+void check_invalid_ciphertexts(Test::Result& result,
+                               Botan::PK_Decryptor& decryptor,
+                               const std::vector<uint8_t>& plaintext,
+                               const std::vector<uint8_t>& ciphertext);
+
+}
+
+#endif
+
+#endif
diff --git a/third_party/botan/src/tests/test_rfc6979.cpp b/third_party/botan/src/tests/test_rfc6979.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_rfc6979.cpp
@@ -0,0 +1,60 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+   #include <botan/rfc6979.h>
+   #include <botan/hash.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+
+class RFC6979_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      RFC6979_KAT_Tests() : Text_Based_Test("rfc6979.vec", "Q,X,H,K") {}
+
+      Test::Result run_one_test(const std::string& hash, const VarMap& vars) override
+         {
+         const BigInt Q = vars.get_req_bn("Q");
+         const BigInt X = vars.get_req_bn("X");
+         const BigInt H = vars.get_req_bn("H");
+         const BigInt K = vars.get_req_bn("K");
+
+         Test::Result result("RFC 6979 nonce generation");
+
+         auto hash_func = Botan::HashFunction::create(hash);
+
+         if(!hash_func)
+            {
+            result.test_note("Skipping due to missing: " + hash);
+            return result;
+            }
+
+         result.test_eq("vector matches", Botan::generate_rfc6979_nonce(X, Q, H, hash), K);
+
+         Botan::RFC6979_Nonce_Generator gen(hash, Q, X);
+
+         result.test_eq("vector matches", gen.nonce_for(H), K);
+         result.test_ne("different output for H+1", gen.nonce_for(H + 1), K);
+         result.test_eq("vector matches when run again", gen.nonce_for(H), K);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("rfc6979", RFC6979_KAT_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_rng.cpp b/third_party/botan/src/tests/test_rng.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_rng.cpp
@@ -0,0 +1,837 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_STATEFUL_RNG)
+   #include <botan/stateful_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+   #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_AUTO_RNG)
+   #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+   #include <botan/chacha_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+   #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SOURCE)
+   #include <botan/entropy_src.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   #include <unistd.h>
+   #include <sys/wait.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_STATEFUL_RNG)
+
+class Stateful_RNG_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         results.push_back(test_reseed_kat());
+         results.push_back(test_reseed());
+         results.push_back(test_reseed_interval_limits());
+         results.push_back(test_max_number_of_bytes_per_request());
+         results.push_back(test_broken_entropy_input());
+         results.push_back(test_check_nonce());
+         results.push_back(test_prediction_resistance());
+         results.push_back(test_randomize_with_ts_input());
+         results.push_back(test_security_level());
+
+         /*
+         * This test uses the library in both parent and child processes. But
+         * this causes a race with other threads, where if any other test thread
+         * is holding the mlock pool mutex, it is killed after the fork. Then,
+         * in the child, any attempt to allocate or free memory will cause a
+         * deadlock.
+         */
+         if(Test::options().test_threads() == 1)
+            results.push_back(test_fork_safety());
+
+         return results;
+         }
+
+   protected:
+      virtual std::string rng_name() const = 0;
+
+      virtual std::unique_ptr<Botan::Stateful_RNG> create_rng(
+         Botan::RandomNumberGenerator* underlying_rng,
+         Botan::Entropy_Sources* underlying_es,
+         size_t reseed_interval) = 0;
+
+      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng,
+                                                    size_t reseed_interval = 1024)
+         {
+         return create_rng(&underlying_rng, nullptr, reseed_interval);
+         }
+
+      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::Entropy_Sources& underlying_srcs,
+                                                    size_t reseed_interval = 1024)
+         {
+         return create_rng(nullptr, &underlying_srcs, reseed_interval);
+         }
+
+      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng,
+                                                    Botan::Entropy_Sources& underlying_srcs,
+                                                    size_t reseed_interval = 1024)
+         {
+         return create_rng(&underlying_rng, &underlying_srcs, reseed_interval);
+         }
+
+      virtual Test::Result test_reseed_kat() = 0;
+
+      virtual Test::Result test_security_level() = 0;
+
+      virtual Test::Result test_max_number_of_bytes_per_request() = 0;
+
+      virtual Test::Result test_reseed_interval_limits() = 0;
+   private:
+      Test::Result test_reseed()
+         {
+         Test::Result result(rng_name() + " Reseed");
+
+         // test reseed_interval is enforced
+         Request_Counting_RNG counting_rng;
+
+         std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2);
+
+         rng->random_vec(7);
+         result.test_eq("initial seeding", counting_rng.randomize_count(), 1);
+         rng->random_vec(9);
+         result.test_eq("still initial seed", counting_rng.randomize_count(), 1);
+
+         rng->random_vec(1);
+         result.test_eq("first reseed", counting_rng.randomize_count(), 2);
+         rng->random_vec(15);
+         result.test_eq("still first reseed", counting_rng.randomize_count(), 2);
+
+         rng->random_vec(15);
+         result.test_eq("second reseed", counting_rng.randomize_count(), 3);
+         rng->random_vec(1);
+         result.test_eq("still second reseed", counting_rng.randomize_count(), 3);
+
+         if(rng->max_number_of_bytes_per_request() > 0)
+            {
+            // request > max_number_of_bytes_per_request, do reseeds occur?
+            rng->random_vec(64 * 1024 + 1);
+            result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 4);
+
+            rng->random_vec(9 * 64 * 1024 + 1);
+            result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 9);
+            }
+
+         return result;
+         }
+
+      Test::Result test_broken_entropy_input()
+         {
+         Test::Result result(rng_name() + " Broken Entropy Input");
+
+         class Broken_Entropy_Source final : public Botan::Entropy_Source
+            {
+            public:
+               std::string name() const override
+                  {
+                  return "Broken Entropy Source";
+                  }
+
+               size_t poll(Botan::RandomNumberGenerator&) override
+                  {
+                  throw Botan::Not_Implemented("polling not available");
+                  }
+            };
+
+         class Insufficient_Entropy_Source final : public Botan::Entropy_Source
+            {
+            public:
+               std::string name() const override
+                  {
+                  return "Insufficient Entropy Source";
+                  }
+
+               size_t poll(Botan::RandomNumberGenerator&) override
+                  {
+                  return 0;
+                  }
+            };
+
+         // make sure no output is generated when the entropy input source is broken
+
+         // underlying_rng throws exception
+         Botan::Null_RNG broken_entropy_input_rng;
+         result.test_eq("Null_RNG not seeded", broken_entropy_input_rng.is_seeded(), false);
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng = make_rng(broken_entropy_input_rng);
+
+         result.test_throws("broken underlying rng", [&rng_with_broken_rng]() { rng_with_broken_rng->random_vec(16); });
+
+         // entropy_sources throw exception
+         std::unique_ptr<Broken_Entropy_Source> broken_entropy_source_1(new Broken_Entropy_Source());
+         std::unique_ptr<Broken_Entropy_Source> broken_entropy_source_2(new Broken_Entropy_Source());
+
+         Botan::Entropy_Sources broken_entropy_sources;
+         broken_entropy_sources.add_source(std::move(broken_entropy_source_1));
+         broken_entropy_sources.add_source(std::move(broken_entropy_source_2));
+
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_es = make_rng(broken_entropy_sources);
+         result.test_throws("broken entropy sources", [&rng_with_broken_es]() { rng_with_broken_es->random_vec(16); });
+
+         // entropy source returns insufficient entropy
+         Botan::Entropy_Sources insufficient_entropy_sources;
+         std::unique_ptr<Insufficient_Entropy_Source> insufficient_entropy_source(new Insufficient_Entropy_Source());
+         insufficient_entropy_sources.add_source(std::move(insufficient_entropy_source));
+
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_insufficient_es = make_rng(insufficient_entropy_sources);
+         result.test_throws("insufficient entropy source", [&rng_with_insufficient_es]() { rng_with_insufficient_es->random_vec(16); });
+
+         // one of or both underlying_rng and entropy_sources throw exception
+
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng_and_good_es =
+            make_rng(broken_entropy_input_rng, Botan::Entropy_Sources::global_sources());
+
+         result.test_throws("broken underlying rng but good entropy sources", [&rng_with_broken_rng_and_good_es]()
+            { rng_with_broken_rng_and_good_es->random_vec(16); });
+
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_good_rng_and_broken_es =
+            make_rng(Test::rng(), broken_entropy_sources);
+
+         result.test_throws("good underlying rng but broken entropy sources", [&rng_with_good_rng_and_broken_es]()
+            { rng_with_good_rng_and_broken_es->random_vec(16); });
+
+         std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng_and_broken_es =
+            make_rng(broken_entropy_input_rng, broken_entropy_sources);
+
+         result.test_throws("underlying rng and entropy sources broken", [&rng_with_broken_rng_and_broken_es]()
+            { rng_with_broken_rng_and_broken_es->random_vec(16); });
+
+         return result;
+         }
+
+      Test::Result test_check_nonce()
+         {
+         Test::Result result(rng_name() + " Nonce Check");
+
+         // make sure the nonce has at least security_strength bits
+         std::unique_ptr<Botan::Stateful_RNG> rng = create_rng(nullptr, nullptr, 0);
+
+         for(size_t nonce_size : { 0, 4, 8, 16, 31, 32, 34, 64 })
+            {
+            rng->clear();
+            result.test_eq("not seeded", rng->is_seeded(), false);
+
+            const std::vector<uint8_t> nonce(nonce_size);
+            rng->initialize_with(nonce.data(), nonce.size());
+
+            if(nonce_size < rng->security_level() / 8)
+               {
+               result.test_eq("not seeded", rng->is_seeded(), false);
+               result.test_throws("invalid nonce size", [&rng]() { rng->random_vec(32); });
+               }
+            else
+               {
+               result.test_eq("is seeded", rng->is_seeded(), true);
+               rng->random_vec(32);
+               }
+            }
+
+         return result;
+         }
+
+      Test::Result test_prediction_resistance()
+         {
+         Test::Result result(rng_name() + " Prediction Resistance");
+
+         // set reseed_interval = 1, forcing a reseed for every RNG request
+         Request_Counting_RNG counting_rng;
+         std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 1);
+
+         rng->random_vec(16);
+         result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
+
+         rng->random_vec(16);
+         result.test_eq("second request", counting_rng.randomize_count(), size_t(2));
+
+         rng->random_vec(16);
+         result.test_eq("third request", counting_rng.randomize_count(), size_t(3));
+
+         return result;
+         }
+
+      Test::Result test_fork_safety()
+         {
+         Test::Result result(rng_name() + " Fork Safety");
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+         const size_t reseed_interval = 1024;
+
+         // make sure rng is reseeded after every fork
+         Request_Counting_RNG counting_rng;
+         std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, reseed_interval);
+
+         rng->random_vec(16);
+         result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
+
+         // fork and request from parent and child, both should output different sequences
+         size_t count = counting_rng.randomize_count();
+         Botan::secure_vector<uint8_t> parent_bytes(16), child_bytes(16);
+         int fd[2];
+         int rc = ::pipe(fd);
+         if(rc != 0)
+            {
+            result.test_failure("failed to create pipe");
+            }
+
+         pid_t pid = ::fork();
+         if(pid == -1)
+            {
+#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+            result.test_note("failed to fork process");
+#else
+            result.test_failure("failed to fork process");
+#endif
+            return result;
+            }
+         else if(pid != 0)
+            {
+            // parent process, wait for randomize_count from child's rng
+            ::close(fd[1]); // close write end in parent
+            ssize_t got = ::read(fd[0], &count, sizeof(count));
+
+            if(got > 0)
+               {
+               result.test_eq("expected bytes from child", got, sizeof(count));
+               result.test_eq("parent not reseeded",  counting_rng.randomize_count(), 1);
+               result.test_eq("child reseed occurred", count, 2);
+               }
+            else
+               {
+               result.test_failure("Failed to read count size from child process");
+               }
+
+            parent_bytes = rng->random_vec(16);
+            got = ::read(fd[0], &child_bytes[0], child_bytes.size());
+
+            if(got > 0)
+               {
+               result.test_eq("expected bytes from child", got, child_bytes.size());
+               result.test_ne("parent and child output sequences differ", parent_bytes, child_bytes);
+               }
+            else
+               {
+               result.test_failure("Failed to read RNG bytes from child process");
+               }
+            ::close(fd[0]); // close read end in parent
+
+            // wait for the child to exit
+            int status = 0;
+            ::waitpid(pid, &status, 0);
+            }
+         else
+            {
+            // child process, send randomize_count and first output sequence back to parent
+            ::close(fd[0]); // close read end in child
+            rng->randomize(&child_bytes[0], child_bytes.size());
+            count = counting_rng.randomize_count();
+            ssize_t written = ::write(fd[1], &count, sizeof(count));
+            try
+               {
+               rng->randomize(&child_bytes[0], child_bytes.size());
+               }
+            catch(std::exception& e)
+               {
+               fprintf(stderr, "%s", e.what());
+               }
+            written = ::write(fd[1], &child_bytes[0], child_bytes.size());
+            BOTAN_UNUSED(written);
+            ::close(fd[1]); // close write end in child
+
+            /*
+            * We can't call exit because it causes the mlock pool to be freed (#602)
+            * We can't call _exit because it makes valgrind think we leaked memory.
+            * So instead we execute something that will return 0 for us.
+            */
+            ::execl("/bin/true", "true", NULL);
+            ::_exit(0); // just in case /bin/true isn't available (sandbox?)
+            }
+#endif
+         return result;
+         }
+
+      Test::Result test_randomize_with_ts_input()
+         {
+         Test::Result result(rng_name() + " Randomize With Timestamp Input");
+
+         const size_t request_bytes = 64;
+         const std::vector<uint8_t> seed(128);
+
+         // check that randomize_with_ts_input() creates different output based on a timestamp
+         // and possibly additional data, such as process id even with identical seeds
+         Fixed_Output_RNG fixed_output_rng1(seed);
+         Fixed_Output_RNG fixed_output_rng2(seed);
+
+         std::unique_ptr<Botan::Stateful_RNG> rng1 = make_rng(fixed_output_rng1);
+         std::unique_ptr<Botan::Stateful_RNG> rng2 = make_rng(fixed_output_rng2);
+
+         Botan::secure_vector<uint8_t> output1(request_bytes);
+         Botan::secure_vector<uint8_t> output2(request_bytes);
+
+         rng1->randomize(output1.data(), output1.size());
+         rng2->randomize(output2.data(), output2.size());
+
+         result.test_eq("equal output due to same seed", output1, output2);
+
+         rng1->randomize_with_ts_input(output1.data(), output1.size());
+         rng2->randomize_with_ts_input(output2.data(), output2.size());
+
+         result.test_ne("output differs due to different timestamp", output1, output2);
+
+         return result;
+         }
+
+   };
+
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32)
+
+class HMAC_DRBG_Unit_Tests final : public Stateful_RNG_Tests
+   {
+   public:
+      std::string rng_name() const override { return "HMAC_DRBG"; }
+
+      std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng,
+                                                      Botan::Entropy_Sources* underlying_es,
+                                                      size_t reseed_interval) override
+         {
+         std::unique_ptr<Botan::MessageAuthenticationCode> mac =
+            Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+
+         if(underlying_rng && underlying_es)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_rng, *underlying_es, reseed_interval));
+         else if(underlying_rng)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_rng, reseed_interval));
+         else if(underlying_es)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_es, reseed_interval));
+         else if(reseed_interval == 0)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac)));
+         else
+            throw Test_Error("Invalid reseed interval in HMAC_DRBG unit test");
+         }
+
+      Test::Result test_max_number_of_bytes_per_request() override
+         {
+         Test::Result result("HMAC_DRBG max_number_of_bytes_per_request");
+
+         const std::string mac_string = "HMAC(SHA-256)";
+
+         Request_Counting_RNG counting_rng;
+
+         result.test_throws("HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request",
+                            [&mac_string, &counting_rng]()
+            {
+            Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0);
+            });
+
+         result.test_throws("HMAC_DRBG does not accept values higher than 64KB for max_number_of_bytes_per_request",
+                            [&mac_string, &counting_rng]()
+            {
+            Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 64 * 1024 + 1);
+            });
+
+         // set reseed_interval to 1 so we can test that a long request is split
+         // into multiple, max_number_of_bytes_per_request long requests
+         // for each smaller request, reseed_check() calls counting_rng::randomize(),
+         // which we can compare with
+         Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 1, 64);
+
+         rng.random_vec(63);
+         result.test_eq("one request", counting_rng.randomize_count(), 1);
+
+         rng.clear();
+         counting_rng.clear();
+
+         rng.random_vec(64);
+         result.test_eq("one request", counting_rng.randomize_count(), 1);
+
+         rng.clear();
+         counting_rng.clear();
+
+         rng.random_vec(65);
+         result.test_eq("two requests", counting_rng.randomize_count(), 2);
+
+         rng.clear();
+         counting_rng.clear();
+
+         rng.random_vec(1025);
+         result.test_eq("17 requests", counting_rng.randomize_count(), 17);
+
+         return result;
+         }
+
+      Test::Result test_reseed_interval_limits() override
+         {
+         Test::Result result("HMAC_DRBG reseed_interval limits");
+
+         const std::string mac_string = "HMAC(SHA-256)";
+
+         Request_Counting_RNG counting_rng;
+
+         result.test_throws("HMAC_DRBG does not accept 0 for reseed_interval",
+                            [&mac_string, &counting_rng]()
+            {
+            Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 0);
+            });
+
+         result.test_throws("HMAC_DRBG does not accept values higher than 2^24 for reseed_interval",
+                            [&mac_string, &counting_rng]()
+            {
+            Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, (static_cast<size_t>(1) << 24) + 1);
+            });
+
+         return result;
+         }
+
+      Test::Result test_security_level() override
+         {
+         Test::Result result("HMAC_DRBG Security Level");
+
+         std::vector<std::string> approved_hash_fns { "SHA-160", "SHA-224", "SHA-256", "SHA-512/256", "SHA-384", "SHA-512" };
+         std::vector<uint32_t> security_strengths { 128, 192, 256, 256, 256, 256 };
+
+         for(size_t i = 0; i < approved_hash_fns.size(); ++i)
+            {
+            std::string hash_fn = approved_hash_fns[i];
+            std::string mac_name = "HMAC(" + hash_fn + ")";
+            auto mac = Botan::MessageAuthenticationCode::create(mac_name);
+            if(!mac)
+               {
+               result.note_missing(mac_name);
+               continue;
+               }
+
+            Botan::HMAC_DRBG rng(std::move(mac));
+            result.test_eq(hash_fn + " security level", rng.security_level(), security_strengths[i]);
+            }
+
+         return result;
+         }
+
+      Test::Result test_reseed_kat() override
+         {
+         Test::Result result("HMAC_DRBG Reseed KAT");
+
+         Request_Counting_RNG counting_rng;
+         std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2);
+
+         const Botan::secure_vector<uint8_t> seed_input(
+            {
+            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
+            });
+
+         result.test_eq("is_seeded", rng->is_seeded(), false);
+
+         rng->initialize_with(seed_input.data(), seed_input.size());
+
+         Botan::secure_vector<uint8_t> out(32);
+
+         rng->randomize(out.data(), out.size());
+         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0));
+         result.test_eq("out before reseed", out, "48D3B45AAB65EF92CCFCB9427EF20C90297065ECC1B8A525BFE4DC6FF36D0E38");
+
+         // reseed must happen here
+         rng->randomize(out.data(), out.size());
+         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1));
+         result.test_eq("out after reseed", out, "2F8FCA696832C984781123FD64F4B20C7379A25C87AB29A21C9BF468B0081CE2");
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("hmac_drbg_unit", HMAC_DRBG_Unit_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+
+class ChaCha_RNG_Unit_Tests final : public Stateful_RNG_Tests
+   {
+   public:
+
+      std::string rng_name() const override { return "ChaCha_RNG"; }
+
+      std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng,
+                                                      Botan::Entropy_Sources* underlying_es,
+                                                      size_t reseed_interval) override
+         {
+         if(underlying_rng && underlying_es)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_rng, *underlying_es, reseed_interval));
+         else if(underlying_rng)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_rng, reseed_interval));
+         else if(underlying_es)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_es, reseed_interval));
+         else if(reseed_interval == 0)
+            return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG());
+         else
+            throw Test_Error("Invalid reseed interval in ChaCha_RNG unit test");
+         }
+
+      Test::Result test_security_level() override
+         {
+         Test::Result result("ChaCha_RNG Security Level");
+         Botan::ChaCha_RNG rng;
+         result.test_eq("Expected security level", rng.security_level(), size_t(256));
+         return result;
+         }
+
+      Test::Result test_max_number_of_bytes_per_request() override
+         {
+         Test::Result result("ChaCha_RNG max_number_of_bytes_per_request");
+         // ChaCha_RNG doesn't have this notion
+         return result;
+         }
+
+      Test::Result test_reseed_interval_limits() override
+         {
+         Test::Result result("ChaCha_RNG reseed_interval limits");
+         // ChaCha_RNG doesn't apply any limits to reseed_interval
+         return result;
+         }
+
+      Test::Result test_reseed_kat() override
+         {
+         Test::Result result("ChaCha_RNG Reseed KAT");
+
+         Request_Counting_RNG counting_rng;
+         std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2);
+
+         const Botan::secure_vector<uint8_t> seed_input(32);
+
+         result.test_eq("is_seeded", rng->is_seeded(), false);
+
+         rng->initialize_with(seed_input.data(), seed_input.size());
+
+         Botan::secure_vector<uint8_t> out(32);
+
+         rng->randomize(out.data(), out.size());
+         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0));
+         result.test_eq("out before reseed", out, "1F0E6F13429D5073B59C057C37CBE9587740A0A894D247E2596C393CE91DDC6F");
+
+         // reseed must happen here
+         rng->randomize(out.data(), out.size());
+         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1));
+         result.test_eq("out after reseed", out, "F2CAE73F22684D5D773290B48FDCDA0E6C0661EBA0A854AFEC922832BDBB9C49");
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("chacha_rng_unit", ChaCha_RNG_Unit_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_AUTO_RNG)
+
+class AutoSeeded_RNG_Tests final : public Test
+   {
+   private:
+      Test::Result auto_rng_tests()
+         {
+         Test::Result result("AutoSeeded_RNG");
+
+         Botan::Entropy_Sources no_entropy_for_you;
+         Botan::Null_RNG null_rng;
+
+         result.test_eq("Null_RNG is null", null_rng.is_seeded(), false);
+
+         try
+            {
+            Botan::AutoSeeded_RNG rng(no_entropy_for_you);
+            result.test_failure("AutoSeeded_RNG should have rejected useless entropy source");
+            }
+         catch(Botan::PRNG_Unseeded&)
+            {
+            result.test_success("AutoSeeded_RNG rejected empty entropy source");
+            }
+
+         try
+            {
+            Botan::AutoSeeded_RNG rng(null_rng);
+            }
+         catch(Botan::PRNG_Unseeded&)
+            {
+            result.test_success("AutoSeeded_RNG rejected useless RNG");
+            }
+
+         try
+            {
+            Botan::AutoSeeded_RNG rng(null_rng,
+                                      no_entropy_for_you);
+            }
+         catch(Botan::PRNG_Unseeded&)
+            {
+            result.test_success("AutoSeeded_RNG rejected useless RNG+entropy sources");
+            }
+
+         Botan::AutoSeeded_RNG rng;
+
+         result.test_eq("AutoSeeded_RNG::name", rng.name(),
+                        std::string("HMAC_DRBG(") + BOTAN_AUTO_RNG_HMAC + ")");
+
+         result.confirm("AutoSeeded_RNG starts seeded", rng.is_seeded());
+         rng.random_vec(16); // generate and discard output
+         rng.clear();
+         result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false);
+
+         // AutoSeeded_RNG automatically reseeds as required:
+         rng.random_vec(16);
+         result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded());
+
+         result.confirm("AutoSeeded_RNG ", rng.is_seeded());
+         rng.random_vec(16); // generate and discard output
+         rng.clear();
+         result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false);
+
+         const size_t no_entropy_bits = rng.reseed(no_entropy_for_you, 256, std::chrono::milliseconds(300));
+         result.test_eq("AutoSeeded_RNG can't reseed from nothing", no_entropy_bits, 0);
+         result.test_eq("AutoSeeded_RNG still unseeded", rng.is_seeded(), false);
+
+         rng.random_vec(16); // generate and discard output
+         result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded());
+
+         rng.clear();
+
+         return result;
+         }
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         results.push_back(auto_rng_tests());
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("auto_rng_unit", AutoSeeded_RNG_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+
+class System_RNG_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("System_RNG");
+
+         Botan::System_RNG rng;
+
+         result.test_gte("Some non-empty name is returned", rng.name().size(), 1);
+
+         result.confirm("System RNG always seeded", rng.is_seeded());
+         rng.clear(); // clear is a noop for system rng
+         result.confirm("System RNG always seeded", rng.is_seeded());
+
+         rng.reseed(Botan::Entropy_Sources::global_sources(),
+                    256,
+                    std::chrono::milliseconds(100));
+
+         for(size_t i = 0; i != 128; ++i)
+            {
+            std::vector<uint8_t> out_buf(i);
+            rng.randomize(out_buf.data(), out_buf.size());
+            rng.add_entropy(out_buf.data(), out_buf.size());
+            }
+
+         return std::vector<Test::Result>{result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("system_rng", System_RNG_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+
+class Processor_RNG_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("Processor_RNG");
+
+         if(Botan::Processor_RNG::available())
+            {
+            Botan::Processor_RNG rng;
+
+            result.test_ne("Has a name", rng.name(), "");
+            result.confirm("CPU RNG always seeded", rng.is_seeded());
+            rng.clear(); // clear is a noop for rdrand
+            result.confirm("CPU RNG always seeded", rng.is_seeded());
+
+            size_t reseed_bits = rng.reseed(Botan::Entropy_Sources::global_sources(),
+                                            256,
+                                            std::chrono::seconds(1));
+            result.test_eq("CPU RNG cannot consume inputs", reseed_bits, size_t(0));
+
+            /*
+            Processor_RNG ignores add_entropy calls - confirm this by passing
+            an invalid ptr/length field to add_entropy. If it examined its
+            arguments, it would crash...
+            */
+            const uint8_t* invalid_ptr = reinterpret_cast<const uint8_t*>(static_cast<uintptr_t>(0xDEADC0DE));
+            const size_t invalid_ptr_len = 64*1024;
+            rng.add_entropy(invalid_ptr, invalid_ptr_len);
+
+            for(size_t i = 0; i != 128; ++i)
+               {
+               std::vector<uint8_t> out_buf(i);
+               rng.randomize(out_buf.data(), out_buf.size());
+               }
+            }
+         else
+            {
+            result.test_throws("Processor_RNG throws if instruction not available",
+                               []() { Botan::Processor_RNG rng; });
+            }
+
+
+         return std::vector<Test::Result>{result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("processor_rng", Processor_RNG_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_rng.h b/third_party/botan/src/tests/test_rng.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_rng.h
@@ -0,0 +1,244 @@
+/*
+* (C) 2009 Jack Lloyd
+* (C) 2016 René Korthaus, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TESTS_FIXED_RNG_H_
+#define BOTAN_TESTS_FIXED_RNG_H_
+
+#include "tests.h"
+#include <deque>
+#include <string>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <botan/exceptn.h>
+
+namespace Botan_Tests {
+
+/**
+ * RNG that outputs only a given set of fixed bytes, throws otherwise.
+ * Useful for test vectors with fixed nonces, where the algorithm consumes only the fixed nonce.
+ */
+class Fixed_Output_RNG : public Botan::RandomNumberGenerator
+   {
+   public:
+      bool is_seeded() const override
+         {
+         return !m_buf.empty();
+         }
+
+      bool accepts_input() const override { return true; }
+
+      size_t reseed(Botan::Entropy_Sources&,
+                    size_t,
+                    std::chrono::milliseconds) override
+         {
+         return 0;
+         }
+
+      void randomize(uint8_t out[], size_t len) override
+         {
+         for(size_t j = 0; j != len; j++)
+            {
+            out[j] = random();
+            }
+         }
+
+      void add_entropy(const uint8_t b[], size_t s) override
+         {
+         m_buf.insert(m_buf.end(), b, b + s);
+         }
+
+      std::string name() const override
+         {
+         return "Fixed_Output_RNG";
+         }
+
+      void clear() noexcept override {}
+
+      explicit Fixed_Output_RNG(const std::vector<uint8_t>& in)
+         {
+         m_buf.insert(m_buf.end(), in.begin(), in.end());
+         }
+
+      explicit Fixed_Output_RNG(const std::string& in_str)
+         {
+         std::vector<uint8_t> in = Botan::hex_decode(in_str);
+         m_buf.insert(m_buf.end(), in.begin(), in.end());
+         }
+
+      Fixed_Output_RNG(RandomNumberGenerator& rng, size_t len)
+         {
+         std::vector<uint8_t> output;
+         rng.random_vec(output, len);
+         m_buf.insert(m_buf.end(), output.begin(), output.end());
+         }
+
+      Fixed_Output_RNG() = default;
+   protected:
+      uint8_t random()
+         {
+         if(m_buf.empty())
+            {
+            throw Test_Error("Fixed output RNG ran out of bytes, test bug?");
+            }
+
+         uint8_t out = m_buf.front();
+         m_buf.pop_front();
+         return out;
+         }
+
+   private:
+      std::deque<uint8_t> m_buf;
+   };
+
+/**
+ * RNG that outputs a given set of fixed bytes for a specific request count, outputs random otherwise.
+ * Useful for test vectors with fixed nonces, where the algorithm consumes more random than just the fixed nonce.
+ */
+class Fixed_Output_Position_RNG final : public Fixed_Output_RNG
+   {
+   public:
+      bool is_seeded() const override
+         {
+         return Fixed_Output_RNG::is_seeded() || Test::rng().is_seeded();
+         }
+
+      void randomize(uint8_t out[], size_t len) override
+         {
+         ++m_requests;
+
+         if(m_requests == m_pos)
+            {
+            // return fixed output
+            for(size_t j = 0; j != len; j++)
+               {
+               out[j] = random();
+               }
+            }
+         else
+            {
+            // return random
+            Test::rng().randomize(out, len);
+            }
+         }
+
+      bool accepts_input() const override { return false; }
+
+      void add_entropy(const uint8_t*, size_t) override
+         {
+         throw Test_Error("add_entropy() not supported by this RNG, test bug?");
+         }
+
+      std::string name() const override
+         {
+         return "Fixed_Output_Position_RNG";
+         }
+
+      explicit Fixed_Output_Position_RNG(const std::vector<uint8_t>& in, size_t pos)
+         : Fixed_Output_RNG(in)
+         , m_pos(pos) {}
+
+      explicit Fixed_Output_Position_RNG(const std::string& in_str, size_t pos)
+         : Fixed_Output_RNG(in_str)
+         , m_pos(pos) {}
+
+   private:
+      size_t m_pos = 0;
+      size_t m_requests = 0;
+   };
+
+class SeedCapturing_RNG final : public Botan::RandomNumberGenerator
+   {
+   public:
+      void randomize(uint8_t[], size_t) override
+         {
+         throw Test_Error("SeedCapturing_RNG has no output");
+         }
+
+      bool accepts_input() const override { return true; }
+
+      void add_entropy(const uint8_t input[], size_t len) override
+         {
+         m_samples++;
+         m_seed.insert(m_seed.end(), input, input + len);
+         }
+
+      void clear() override {}
+      bool is_seeded() const override
+         {
+         return false;
+         }
+      std::string name() const override
+         {
+         return "SeedCapturing";
+         }
+
+      size_t samples() const
+         {
+         return m_samples;
+         }
+
+      const std::vector<uint8_t>& seed_material() const
+         {
+         return m_seed;
+         }
+
+   private:
+      std::vector<uint8_t> m_seed;
+      size_t m_samples = 0;
+   };
+
+/*
+* RNG that counts the number of requests made to it, for example
+* to verify that a reseed attempt was made at the expected time.
+*/
+class Request_Counting_RNG final : public Botan::RandomNumberGenerator
+   {
+   public:
+      Request_Counting_RNG() : m_randomize_count(0) {}
+
+      size_t randomize_count() const
+         {
+         return m_randomize_count;
+         }
+
+      bool accepts_input() const override { return false; }
+
+      bool is_seeded() const override
+         {
+         return true;
+         }
+
+      void clear() override
+         {
+         m_randomize_count = 0;
+         }
+
+      void randomize(uint8_t out[], size_t out_len) override
+         {
+         /*
+         The HMAC_DRBG and ChaCha reseed KATs assume this RNG type
+         outputs all 0x80
+         */
+         for(size_t i = 0; i != out_len; ++i)
+            out[i] = 0x80;
+         m_randomize_count++;
+         }
+
+      void add_entropy(const uint8_t[], size_t) override {}
+
+      std::string name() const override
+         {
+         return "Request_Counting_RNG";
+         }
+
+   private:
+      size_t m_randomize_count;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_rng_kat.cpp b/third_party/botan/src/tests/test_rng_kat.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_rng_kat.cpp
@@ -0,0 +1,116 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+   #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+   #include <botan/chacha_rng.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+
+class HMAC_DRBG_Tests final : public Text_Based_Test
+   {
+   public:
+      HMAC_DRBG_Tests()
+         : Text_Based_Test("rng/hmac_drbg.vec",
+                           "EntropyInput,EntropyInputReseed,Out",
+                           "AdditionalInput1,AdditionalInput2") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> seed_input   = vars.get_req_bin("EntropyInput");
+         const std::vector<uint8_t> reseed_input = vars.get_req_bin("EntropyInputReseed");
+         const std::vector<uint8_t> expected     = vars.get_req_bin("Out");
+
+         const std::vector<uint8_t> ad1 = vars.get_opt_bin("AdditionalInput1");
+         const std::vector<uint8_t> ad2 = vars.get_opt_bin("AdditionalInput2");
+
+         Test::Result result("HMAC_DRBG(" + algo + ")");
+
+         auto mac = Botan::MessageAuthenticationCode::create("HMAC(" + algo + ")");
+
+         if(!mac)
+            {
+            result.note_missing("HMAC(" + algo + ")");
+            return result;
+            }
+
+         std::unique_ptr<Botan::HMAC_DRBG> rng(new Botan::HMAC_DRBG(std::move(mac)));
+         rng->initialize_with(seed_input.data(), seed_input.size());
+
+         // now reseed
+         rng->add_entropy(reseed_input.data(), reseed_input.size());
+
+         std::vector<uint8_t> out(expected.size());
+         // first block is discarded
+         rng->randomize_with_input(out.data(), out.size(), ad1.data(), ad1.size());
+         rng->randomize_with_input(out.data(), out.size(), ad2.data(), ad2.size());
+
+         result.test_eq("rng", out, expected);
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("hmac_drbg", HMAC_DRBG_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+
+class ChaCha_RNG_Tests final : public Text_Based_Test
+   {
+   public:
+      ChaCha_RNG_Tests()
+         : Text_Based_Test("rng/chacha_rng.vec",
+                           "EntropyInput,EntropyInputReseed,Out",
+                           "AdditionalInput1,AdditionalInput2") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> seed_input   = vars.get_req_bin("EntropyInput");
+         const std::vector<uint8_t> reseed_input = vars.get_req_bin("EntropyInputReseed");
+         const std::vector<uint8_t> expected     = vars.get_req_bin("Out");
+
+         const std::vector<uint8_t> ad1 = vars.get_opt_bin("AdditionalInput1");
+         const std::vector<uint8_t> ad2 = vars.get_opt_bin("AdditionalInput2");
+
+         Test::Result result("ChaCha_RNG");
+
+         Botan::ChaCha_RNG rng;
+         rng.initialize_with(seed_input.data(), seed_input.size());
+
+         // now reseed
+         rng.add_entropy(reseed_input.data(), reseed_input.size());
+
+         std::vector<uint8_t> out(expected.size());
+         // first block is discarded
+         rng.randomize_with_input(out.data(), out.size(), ad1.data(), ad1.size());
+         rng.randomize_with_input(out.data(), out.size(), ad2.data(), ad2.size());
+
+         result.test_eq("rng", out, expected);
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("chacha_rng", ChaCha_RNG_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_roughtime.cpp b/third_party/botan/src/tests/test_roughtime.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_roughtime.cpp
@@ -0,0 +1,264 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include <cassert>
+
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_BIGINT)
+   #include <botan/bigint.h>
+#endif
+
+#if defined(BOTAN_HAS_ROUGHTIME)
+   #include <botan/base64.h>
+   #include <botan/ed25519.h>
+   #include <botan/hex.h>
+   #include <botan/roughtime.h>
+#endif
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_ROUGHTIME)
+
+class Roughtime_Request_Tests final : public Text_Based_Test
+   {
+   public:
+      Roughtime_Request_Tests() :
+         Text_Based_Test("misc/roughtime_request.vec", "Nonce,Request") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("roughtime request");
+
+         const auto nonce = vars.get_req_bin("Nonce");
+         const auto request_v = vars.get_req_bin("Request");
+
+         const auto request = Botan::Roughtime::encode_request(nonce);
+         result.test_eq(
+            "encode",
+            type == "Valid",
+            request == Botan::typecast_copy<std::array<uint8_t, 1024>>(request_v.data()));
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("roughtime_request", Roughtime_Request_Tests);
+
+
+class Roughtime_Response_Tests final : public Text_Based_Test
+   {
+   public:
+      Roughtime_Response_Tests() :
+         Text_Based_Test("misc/roughtime_response.vec",
+                         "Response",
+                         "Nonce,Pubkey,MidpointMicroSeconds,RadiusMicroSeconds") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("roughtime response");
+
+         const auto response_v = vars.get_req_bin("Response");
+         const auto n = vars.has_key("Nonce") ? vars.get_req_bin("Nonce") : std::vector<uint8_t>(64);
+         assert(n.size() == 64);
+         const Botan::Roughtime::Nonce nonce(n);
+         try
+            {
+            const auto response = Botan::Roughtime::Response::from_bits(response_v, nonce);
+
+            const auto pubkey = vars.get_req_bin("Pubkey");
+            assert(pubkey.size() == 32);
+
+            if(!response.validate(Botan::Ed25519_PublicKey(pubkey)))
+               {
+               result.confirm("fail_validation", type == "Invalid");
+               }
+            else
+               {
+               const auto midpoint = Botan::Roughtime::Response::sys_microseconds64(
+                                        std::chrono::microseconds(
+                                           vars.get_req_u64("MidpointMicroSeconds")));
+               const auto radius = std::chrono::microseconds(
+                                      vars.get_req_u32("RadiusMicroSeconds"));
+
+               result.confirm("midpoint", response.utc_midpoint() == midpoint);
+               result.confirm("radius", response.utc_radius() == radius);
+               result.confirm("OK", type == "Valid");
+               }
+            }
+         catch(const Botan::Roughtime::Roughtime_Error& e)
+            {
+            result.confirm(e.what(), type == "Invalid");
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("roughtime_response", Roughtime_Response_Tests);
+
+class Roughtime_nonce_from_blind_Tests final : public Text_Based_Test
+   {
+   public:
+      Roughtime_nonce_from_blind_Tests() :
+         Text_Based_Test("misc/roughtime_nonce_from_blind.vec", "Response,Blind,Nonce") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("roughtime nonce_from_blind");
+
+         const auto response = vars.get_req_bin("Response");
+         const auto blind = vars.get_req_bin("Blind");
+         const auto nonce = vars.get_req_bin("Nonce");
+
+         result.test_eq("fail_validation",
+                        Botan::Roughtime::nonce_from_blind(response, blind) == nonce,
+                        type == "Valid");
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("roughtime_nonce_from_blind", Roughtime_nonce_from_blind_Tests);
+
+
+
+class Roughtime final : public Test
+   {
+      Test::Result test_nonce()
+         {
+         Test::Result result("roughtime nonce");
+
+         auto rand64 = Botan::unlock(Test::rng().random_vec(64));;
+         Botan::Roughtime::Nonce nonce_v(rand64);
+         result.confirm("nonce from vector", nonce_v.get_nonce() == Botan::typecast_copy<std::array<uint8_t, 64>>
+                        (rand64.data()));
+         Botan::Roughtime::Nonce nonce_a(Botan::typecast_copy<std::array<uint8_t, 64>>(rand64.data()));
+         result.confirm("nonce from array", nonce_v.get_nonce() == Botan::typecast_copy<std::array<uint8_t, 64>>(rand64.data()));
+         rand64.push_back(10);
+         result.test_throws("vector oversize", [&rand64]() {Botan::Roughtime::Nonce nonce_v2(rand64);}); //size 65
+         rand64.pop_back();
+         rand64.pop_back();
+         result.test_throws("vector undersize", [&rand64]() {Botan::Roughtime::Nonce nonce_v2(rand64);}); //size 63
+
+         return result;
+         }
+
+      Test::Result test_chain()
+         {
+         Test::Result result("roughtime chain");
+
+         Botan::Roughtime::Chain c1;
+         result.confirm("default constructed is empty", c1.links().empty() && c1.responses().empty());
+
+         auto rand64 = Botan::unlock(Test::rng().random_vec(64));;
+         Botan::Roughtime::Nonce nonce_v(rand64);
+         result.confirm("empty chain nonce is blind",
+                        c1.next_nonce(nonce_v).get_nonce() == Botan::typecast_copy<std::array<uint8_t, 64>>(rand64.data()));
+
+         const std::string chain_str =
+            "ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA\n"
+            "ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= uLeTON9D+2HqJMzK6sYWLNDEdtBl9t/9yw1cVAOm0/sONH5Oqdq9dVPkC9syjuWbglCiCPVF+FbOtcxCkrgMmA== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWOw1jl0uSiBEH9HE8/6r7zxoSc01f48vw+UzH8+VJoPelnvVJBj4lnH8uRLh5Aw0i4Du7XM1dp2u0r/I5PzhMQoDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AUBo+tEqPBQC47l77to7ESFTVhlw1SC74P5ssx6gpuJ6eP+1916GuUiySGE/x3Fp0c3otUGAdsRQou5p9PDTeane/YEeVq4/8AgAAAEAAAABTSUcAREVMRe5T1ml8wHyWAcEtHP/U5Rg/jFXTEXOSglngSa4aI/CECVdy4ZNWeP6vv+2//ZW7lQsrWo7ZkXpvm9BdBONRSQIDAAAAIAAAACgAAABQVUJLTUlOVE1BWFQpXlenV0OfVisvp9jDHXLw8vymZVK9Pgw9k6Edf8ZEhUgSGEc5jwUASHLvZE2PBQAAAAAA\n";
+
+         Botan::Roughtime::Chain c2(chain_str);
+         result.confirm("have two elements", c2.links().size() == 2 && c2.responses().size() == 2);
+         result.confirm("serialize loopback", c2.to_string() == chain_str);
+
+         c1.append(c2.links()[0], 1);
+         result.confirm("append ok", c1.links().size() == 1 && c1.responses().size() == 1);
+         c1.append(c2.links()[1], 1);
+         result.confirm("max size", c1.links().size() == 1 && c1.responses().size() == 1);
+
+         result.test_throws("non-positive max chain size", [&]() {c1.append(c2.links()[1], 0);});
+         result.test_throws("1 field", [&]() {Botan::Roughtime::Chain a("ed25519");});
+         result.test_throws("2 fields", [&]() {Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE=");});
+         result.test_throws("3 fields", [&]() {Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw==");});
+         result.test_throws("5 fields", [&]() {Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA abc");});
+         result.test_throws("invalid key type", [&]() {Botan::Roughtime::Chain a("rsa bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA");});
+         result.test_throws("invalid key", [&]() {Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6wssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA");});
+         result.test_throws("invalid nonce", [&]() {Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2UByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA");});
+
+         return result;
+         }
+
+      Test::Result test_server_information()
+         {
+         Test::Result result("roughtime server_information");
+
+         const auto servers = Botan::Roughtime::servers_from_str(
+                                 "Chainpoint-Roughtime ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= udp roughtime.chainpoint.org:2002\n"
+                                 "Cloudflare-Roughtime ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= udp roughtime.cloudflare.com:2002\n"
+                                 "Google-Sandbox-Roughtime ed25519 etPaaIxcBMY1oUeGpwvPMCJMwlRVNxv51KK/tktoJTQ= udp roughtime.sandbox.google.com:2002\n"
+                                 "int08h-Roughtime ed25519 AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE= udp roughtime.int08h.com:2002\n"
+                                 "ticktock ed25519 cj8GsiNlRkqiDElAeNMSBBMwrAl15hYPgX50+GWX/lA= udp ticktock.mixmin.net:5333\n"
+                              );
+
+         result.confirm("size", servers.size() == 5);
+         result.test_eq("name", servers[0].name(), "Chainpoint-Roughtime");
+         result.test_eq("name", servers[4].name(), "ticktock");
+         result.confirm("public key", servers[0].public_key().get_public_key() == Botan::Ed25519_PublicKey(
+                           Botan::base64_decode("bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE=")).get_public_key());
+         result.confirm("single address", servers[0].addresses().size()==1);
+         result.test_eq("address", servers[0].addresses()[0], "roughtime.chainpoint.org:2002");
+
+         result.test_throws("1 field", [&]() {Botan::Roughtime::servers_from_str("A");});
+         result.test_throws("2 fields", [&]() {Botan::Roughtime::servers_from_str("A ed25519");});
+         result.test_throws("3 fields", [&]() {Botan::Roughtime::servers_from_str("A ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE=");});
+         result.test_throws("4 fields", [&]() {Botan::Roughtime::servers_from_str("A ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= udp");});
+         result.test_throws("invalid address", [&]() {Botan::Roughtime::servers_from_str("A ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= udp ");});
+         result.test_throws("invalid key type", [&]() {Botan::Roughtime::servers_from_str("A rsa bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= udp roughtime.chainpoint.org:2002");});
+         result.test_throws("invalid key", [&]() {Botan::Roughtime::servers_from_str("A ed25519 bbT+RP7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= udp roughtime.chainpoint.org:2002");});
+         result.test_throws("invalid protocol", [&]() {Botan::Roughtime::servers_from_str("A ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= tcp roughtime.chainpoint.org:2002");});
+
+         return result;
+         }
+
+      Test::Result test_request_online()
+         {
+         Test::Result result("roughtime request online");
+
+         Botan::Roughtime::Nonce nonce(Test::rng());
+         try
+            {
+            const auto response_raw = Botan::Roughtime::online_request("roughtime.cloudflare.com:2002", nonce,
+                                      std::chrono::seconds(5));
+            const auto now = std::chrono::system_clock::now();
+            const auto response = Botan::Roughtime::Response::from_bits(response_raw, nonce);
+            std::chrono::milliseconds local_clock_max_error(1000);
+            const auto diff_abs = now >= response.utc_midpoint() ? now - response.utc_midpoint() : response.utc_midpoint() - now;
+            result.confirm("online", diff_abs <= (response.utc_radius() + local_clock_max_error));
+            }
+         catch(const std::exception& e)
+            {
+            result.test_failure(e.what());
+            }
+         return result;
+         }
+
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         results.push_back(test_nonce());
+         results.push_back(test_chain());
+         results.push_back(test_server_information());
+
+         if(Test::options().run_online_tests())
+            {
+            results.push_back(test_request_online());
+            }
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("roughtime", Roughtime);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_rsa.cpp b/third_party/botan/src/tests/test_rsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_rsa.cpp
@@ -0,0 +1,335 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_RSA)
+   #include <botan/rsa.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_RSA)
+
+std::unique_ptr<Botan::Private_Key> load_rsa_private_key(const VarMap& vars)
+   {
+   const BigInt p = vars.get_req_bn("P");
+   const BigInt q = vars.get_req_bn("Q");
+   const BigInt e = vars.get_req_bn("E");
+
+   return std::unique_ptr<Botan::Private_Key>(new Botan::RSA_PrivateKey(p, q, e));
+   }
+
+std::unique_ptr<Botan::Public_Key> load_rsa_public_key(const VarMap& vars)
+   {
+   const BigInt n = vars.get_req_bn("N");
+   const BigInt e = vars.get_req_bn("E");
+
+   return std::unique_ptr<Botan::Public_Key>(new Botan::RSA_PublicKey(n, e));
+   }
+
+class RSA_ES_KAT_Tests final : public PK_Encryption_Decryption_Test
+   {
+   public:
+      RSA_ES_KAT_Tests()
+         : PK_Encryption_Decryption_Test(
+              "RSA",
+              "pubkey/rsaes.vec",
+              "E,P,Q,Msg,Ciphertext",
+              "Nonce") {}
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+   };
+
+class RSA_Decryption_KAT_Tests final : public PK_Decryption_Test
+   {
+   public:
+      RSA_Decryption_KAT_Tests() :
+         PK_Decryption_Test("RSA",
+                            "pubkey/rsa_decrypt.vec",
+                            "E,P,Q,Ciphertext,Msg") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+   };
+
+class RSA_KEM_Tests final : public PK_KEM_Test
+   {
+   public:
+      RSA_KEM_Tests()
+         : PK_KEM_Test(
+              "RSA",
+              "pubkey/rsa_kem.vec",
+              "E,P,Q,R,C0,KDF,OutLen,K") {}
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+
+   };
+
+class RSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      RSA_Signature_KAT_Tests()
+         : PK_Signature_Generation_Test(
+              "RSA",
+              "pubkey/rsa_sig.vec",
+              "E,P,Q,Msg,Signature",
+              "Nonce") {}
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+   };
+
+class RSA_PSS_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      RSA_PSS_KAT_Tests()
+         : PK_Signature_Generation_Test(
+              "RSA",
+              "pubkey/rsa_pss.vec",
+              "P,Q,E,Hash,Nonce,Msg,Signature",
+              "") {}
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         const std::string hash_name = vars.get_req_str("Hash");
+         const size_t salt_size = vars.get_req_bin("Nonce").size();
+         return "PSSR(" + hash_name + ",MGF1," + std::to_string(salt_size) + ")";
+         }
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+   };
+
+class RSA_PSS_Raw_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      RSA_PSS_Raw_KAT_Tests()
+         : PK_Signature_Generation_Test(
+              "RSA",
+              "pubkey/rsa_pss_raw.vec",
+              "P,Q,E,Hash,Nonce,Msg,Signature",
+              "") {}
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         const std::string hash_name = vars.get_req_str("Hash");
+         const size_t salt_size = vars.get_req_bin("Nonce").size();
+         return "PSSR_Raw(" + hash_name + ",MGF1," + std::to_string(salt_size) + ")";
+         }
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_rsa_private_key(vars);
+         }
+   };
+
+class RSA_Signature_Verify_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      RSA_Signature_Verify_Tests()
+         : PK_Signature_Verification_Test(
+              "RSA",
+              "pubkey/rsa_verify.vec",
+              "E,N,Msg,Signature") {}
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         return load_rsa_public_key(vars);
+         }
+   };
+
+class RSA_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerification_Test
+   {
+   public:
+      RSA_Signature_Verify_Invalid_Tests()
+         : PK_Signature_NonVerification_Test(
+              "RSA",
+              "pubkey/rsa_invalid.vec",
+              "E,N,Msg,InvalidSignature") {}
+
+      std::string default_padding(const VarMap&) const override
+         {
+         return "Raw";
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         return load_rsa_public_key(vars);
+         }
+   };
+
+class RSA_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "1024", "1280" };
+         }
+      std::string algo_name() const override
+         {
+         return "RSA";
+         }
+   };
+
+class RSA_Blinding_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("RSA blinding");
+
+         /* This test makes only sense with the base provider, else skip it. */
+         if (provider_filter({"base"}).empty())
+            {
+            result.note_missing("base provider");
+            return std::vector<Test::Result> {result};
+            }
+
+#if defined(BOTAN_HAS_EMSA_RAW) || defined(BOTAN_HAS_EME_RAW)
+         Botan::RSA_PrivateKey rsa(Test::rng(), 1024);
+         Botan::Null_RNG null_rng;
+#endif
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+
+         /*
+         * The blinder chooses a new starting point BOTAN_BLINDING_REINIT_INTERVAL
+         * so sign several times that with a single key.
+         *
+         * Very small values (padding/hashing disabled, only low byte set on input)
+         * are used as an additional test on the blinders.
+         */
+
+         Botan::PK_Signer signer(rsa, Test::rng(), "Raw", Botan::IEEE_1363, "base"); // don't try this at home
+         Botan::PK_Verifier verifier(rsa, "Raw", Botan::IEEE_1363, "base");
+
+         for(size_t i = 1; i <= BOTAN_BLINDING_REINIT_INTERVAL * 6; ++i)
+            {
+            std::vector<uint8_t> input(16);
+            input[input.size() - 1] = static_cast<uint8_t>(i | 1);
+
+            signer.update(input);
+
+            // assert RNG is not called in this situation
+            std::vector<uint8_t> signature = signer.signature(null_rng);
+
+            result.test_eq("Signature verifies",
+                           verifier.verify_message(input, signature), true);
+            }
+#endif
+
+#if defined(BOTAN_HAS_EME_RAW)
+
+         /*
+         * The blinder chooses a new starting point BOTAN_BLINDING_REINIT_INTERVAL
+         * so decrypt several times that with a single key.
+         *
+         * Very small values (padding/hashing disabled, only low byte set on input)
+         * are used as an additional test on the blinders.
+         */
+
+         Botan::PK_Encryptor_EME encryptor(rsa, Test::rng(), "Raw", "base");   // don't try this at home
+
+         /*
+         Test blinding reinit interval
+
+         Seed Fixed_Output_RNG only with enough bytes for the initial
+         blinder initialization plus the exponent blinding bits which
+         is 2*64 bits per operation.
+         */
+         const size_t rng_bytes = rsa.get_n().bytes() + (2*8*BOTAN_BLINDING_REINIT_INTERVAL);
+
+         Botan_Tests::Fixed_Output_RNG fixed_rng(Test::rng(), rng_bytes);
+         Botan::PK_Decryptor_EME decryptor(rsa, fixed_rng, "Raw", "base");
+
+         for(size_t i = 1; i <= BOTAN_BLINDING_REINIT_INTERVAL ; ++i)
+            {
+            std::vector<uint8_t> input(16);
+            input[ input.size() - 1 ] = static_cast<uint8_t>(i);
+
+            std::vector<uint8_t> ciphertext = encryptor.encrypt(input, null_rng);
+
+            std::vector<uint8_t> plaintext = Botan::unlock(decryptor.decrypt(ciphertext));
+            plaintext.insert(plaintext.begin(), input.size() - 1, 0);
+
+            result.test_eq("Successful decryption", plaintext, input);
+            }
+
+         result.test_eq("RNG is no longer seeded", fixed_rng.is_seeded(), false);
+
+         // one more decryption should trigger a blinder reinitialization
+         result.test_throws("RSA blinding reinit",
+                            "Test error Fixed output RNG ran out of bytes, test bug?",
+                            [&decryptor, &encryptor, &null_rng]()
+            {
+            std::vector<uint8_t> ciphertext = encryptor.encrypt(std::vector<uint8_t>(16, 5), null_rng);
+            decryptor.decrypt(ciphertext);
+            });
+
+#endif
+
+         return std::vector<Test::Result> {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("rsa_encrypt", RSA_ES_KAT_Tests);
+BOTAN_REGISTER_TEST("rsa_decrypt", RSA_Decryption_KAT_Tests);
+BOTAN_REGISTER_TEST("rsa_sign", RSA_Signature_KAT_Tests);
+BOTAN_REGISTER_TEST("rsa_pss", RSA_PSS_KAT_Tests);
+BOTAN_REGISTER_TEST("rsa_pss_raw", RSA_PSS_Raw_KAT_Tests);
+BOTAN_REGISTER_TEST("rsa_verify", RSA_Signature_Verify_Tests);
+BOTAN_REGISTER_TEST("rsa_verify_invalid", RSA_Signature_Verify_Invalid_Tests);
+BOTAN_REGISTER_TEST("rsa_kem", RSA_KEM_Tests);
+BOTAN_REGISTER_TEST("rsa_keygen", RSA_Keygen_Tests);
+BOTAN_REGISTER_TEST("rsa_blinding", RSA_Blinding_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_runner.cpp b/third_party/botan/src/tests/test_runner.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_runner.cpp
@@ -0,0 +1,397 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "test_runner.h"
+#include "tests.h"
+
+#include <botan/version.h>
+#include <botan/rotate.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+   #include <botan/internal/thread_pool.h>
+   #include <botan/internal/rwlock.h>
+#endif
+
+namespace Botan_Tests {
+
+Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}
+
+namespace {
+
+/*
+* This is a fast, simple, deterministic PRNG that's used for running
+* the tests. It is not intended to be cryptographically secure.
+*/
+class Testsuite_RNG final : public Botan::RandomNumberGenerator
+   {
+   public:
+      std::string name() const override { return "Testsuite_RNG"; }
+
+      void clear() override
+         {
+         m_a = m_b = m_c = m_d = 0;
+         }
+
+      bool accepts_input() const override { return true; }
+
+      void add_entropy(const uint8_t data[], size_t len) override
+         {
+         for(size_t i = 0; i != len; ++i)
+            {
+            m_a ^= data[i];
+            m_b ^= i;
+            mix();
+            }
+         }
+
+      bool is_seeded() const override
+         {
+         return true;
+         }
+
+      void randomize(uint8_t out[], size_t len) override
+         {
+         for(size_t i = 0; i != len; ++i)
+            {
+            out[i] = static_cast<uint8_t>(m_a);
+            mix();
+            }
+         }
+
+      Testsuite_RNG(const std::vector<uint8_t>& seed, size_t test_counter = 0)
+         {
+         m_d = static_cast<uint32_t>(test_counter);
+
+         add_entropy(seed.data(), seed.size());
+         }
+   private:
+      void mix()
+         {
+         const size_t ROUNDS = 3;
+
+         for(size_t i = 0; i != ROUNDS; ++i)
+            {
+            m_a += static_cast<uint32_t>(i);
+
+            m_a = Botan::rotl<9>(m_a);
+            m_b ^= m_a;
+            m_d ^= m_c;
+
+            m_a += m_d;
+            m_c += m_b;
+            m_c = Botan::rotl<23>(m_c);
+            }
+         }
+
+      uint32_t m_a = 0, m_b = 0, m_c = 0, m_d = 0;
+   };
+
+}
+
+int Test_Runner::run(const Test_Options& opts)
+   {
+   std::vector<std::string> req = opts.requested_tests();
+   const std::set<std::string> to_skip = opts.skip_tests();
+
+   if(req.empty())
+      {
+      /*
+      If nothing was requested on the command line, run everything. First
+      run the "essentials" to smoke test, then everything else in
+      alphabetical order.
+      */
+
+      std::vector<std::string> default_first = {
+         "block", "stream", "hash", "mac", "aead",
+         "modes", "kdf", "pbkdf", "hmac_drbg", "util"
+      };
+
+      for(auto s : default_first)
+         {
+         if(to_skip.count(s) == 0)
+            req.push_back(s);
+         }
+
+      std::set<std::string> all_others = Botan_Tests::Test::registered_tests();
+
+      if(opts.pkcs11_lib().empty())
+         {
+         // do not run pkcs11 tests by default unless pkcs11-lib set
+         for(std::set<std::string>::iterator iter = all_others.begin(); iter != all_others.end();)
+            {
+            if((*iter).find("pkcs11") != std::string::npos)
+               {
+               iter = all_others.erase(iter);
+               }
+            else
+               {
+               ++iter;
+               }
+            }
+         }
+
+      for(auto f : req)
+         {
+         all_others.erase(f);
+         }
+
+      for(const std::string& f : to_skip)
+         {
+         all_others.erase(f);
+         }
+
+      req.insert(req.end(), all_others.begin(), all_others.end());
+      }
+   else if(req.size() == 1 && req.at(0) == "pkcs11")
+      {
+      req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa",
+             "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"
+      };
+      }
+   else
+      {
+      std::set<std::string> all = Botan_Tests::Test::registered_tests();
+      for(auto const& r : req)
+         {
+         if(all.find(r) == all.end())
+            {
+            throw Botan_Tests::Test_Error("Unknown test suite: " + r);
+            }
+         }
+      }
+
+   output() << "Testing " << Botan::version_string() << "\n";
+
+   const std::string cpuid = Botan::CPUID::to_string();
+   if(cpuid.size() > 0)
+      output() << "CPU flags: " << cpuid << "\n";
+   output() << "Starting tests";
+
+   if(!opts.pkcs11_lib().empty())
+      {
+      output() << " pkcs11 library:" << opts.pkcs11_lib();
+      }
+
+   if(!opts.provider().empty())
+      {
+      output() << " provider:" << opts.provider();
+      }
+
+   std::vector<uint8_t> seed = Botan::hex_decode(opts.drbg_seed());
+   if(seed.empty())
+      {
+      const uint64_t ts = Botan_Tests::Test::timestamp();
+      seed.resize(8);
+      Botan::store_be(ts, seed.data());
+      }
+
+   output() << " drbg_seed:" << Botan::hex_encode(seed) << "\n";
+
+   Botan_Tests::Test::set_test_options(opts);
+
+   for(size_t i = 0; i != opts.test_runs(); ++i)
+      {
+      std::unique_ptr<Botan::RandomNumberGenerator> rng =
+         std::unique_ptr<Botan::RandomNumberGenerator>(new Testsuite_RNG(seed, i));
+
+      Botan_Tests::Test::set_test_rng(std::move(rng));
+
+      const size_t failed = run_tests(req, opts.test_threads(), i, opts.test_runs());
+      if(failed > 0)
+         return static_cast<int>(failed);
+      }
+
+   return 0;
+   }
+
+namespace {
+
+std::string report_out(const std::vector<Botan_Tests::Test::Result>& results,
+                       size_t& tests_failed,
+                       size_t& tests_ran)
+   {
+   std::ostringstream out;
+
+   std::map<std::string, Botan_Tests::Test::Result> combined;
+   for(auto const& result : results)
+      {
+      const std::string who = result.who();
+      auto i = combined.find(who);
+      if(i == combined.end())
+         {
+         combined.insert(std::make_pair(who, Botan_Tests::Test::Result(who)));
+         i = combined.find(who);
+         }
+
+      i->second.merge(result);
+      }
+
+   for(auto const& result : combined)
+      {
+      out << result.second.result_string();
+      tests_failed += result.second.tests_failed();
+      tests_ran += result.second.tests_run();
+      }
+
+   return out.str();
+   }
+
+std::vector<Test::Result> run_a_test(const std::string& test_name)
+   {
+   std::vector<Test::Result> results;
+
+   try
+      {
+      if(test_name == "simd_32" && Botan::CPUID::has_simd_32() == false)
+         {
+         results.push_back(Test::Result::Note(test_name, "SIMD not available on this platform"));
+         }
+      else if(std::unique_ptr<Test> test = Test::get_test(test_name))
+         {
+         std::vector<Test::Result> test_results = test->run();
+         results.insert(results.end(), test_results.begin(), test_results.end());
+         }
+      else
+         {
+         results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
+         }
+      }
+   catch(std::exception& e)
+      {
+      results.push_back(Test::Result::Failure(test_name, e.what()));
+      }
+   catch(...)
+      {
+      results.push_back(Test::Result::Failure(test_name, "unknown exception"));
+      }
+
+   return results;
+   }
+
+std::string test_summary(size_t test_run, size_t tot_test_runs, uint64_t total_ns,
+                         size_t tests_ran, size_t tests_failed)
+   {
+   std::ostringstream oss;
+
+   if(test_run == 0 && tot_test_runs == 1)
+      oss << "Tests";
+   else
+      oss << "Test run " << (1+test_run) << "/" << tot_test_runs;
+
+   oss << " complete ran " << tests_ran << " tests in "
+            << Botan_Tests::Test::format_time(total_ns) << " ";
+
+   if(tests_failed > 0)
+      {
+      oss << tests_failed << " tests failed";
+      }
+   else if(tests_ran > 0)
+      {
+      oss << "all tests ok";
+      }
+
+   oss << "\n";
+   return oss.str();
+   }
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+
+bool needs_serialization(const std::string& test_name)
+   {
+   if(test_name.substr(0, 6) == "pkcs11")
+      return true;
+   if(test_name == "block" || test_name == "hash" || test_name == "mac" || test_name == "stream" || test_name == "aead")
+      return true;
+   return false;
+   }
+
+#endif
+
+}
+
+size_t Test_Runner::run_tests(const std::vector<std::string>& tests_to_run,
+                              size_t test_threads,
+                              size_t test_run,
+                              size_t tot_test_runs)
+   {
+   size_t tests_ran = 0, tests_failed = 0;
+   const uint64_t start_time = Botan_Tests::Test::timestamp();
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+   if(test_threads != 1)
+      {
+      // If 0 then we let thread pool select the count
+      Botan::Thread_Pool pool(test_threads);
+      Botan::RWLock rwlock;
+
+      std::vector<std::future<std::vector<Test::Result>>> m_fut_results;
+
+      auto run_test_exclusive = [&](const std::string& test_name) {
+         rwlock.lock();
+         std::vector<Test::Result> results = run_a_test(test_name);
+         rwlock.unlock();
+         return results;
+      };
+
+      auto run_test_shared = [&](const std::string& test_name) {
+         rwlock.lock_shared();
+         std::vector<Test::Result> results = run_a_test(test_name);
+         rwlock.unlock_shared();
+         return results;
+      };
+
+      for(auto const& test_name : tests_to_run)
+         {
+         if(needs_serialization(test_name))
+            {
+            m_fut_results.push_back(pool.run(run_test_exclusive, test_name));
+            }
+         else
+            {
+            m_fut_results.push_back(pool.run(run_test_shared, test_name));
+            }
+         }
+
+      for(size_t i = 0; i != m_fut_results.size(); ++i)
+         {
+         output() << tests_to_run[i] << ':' << std::endl;
+         const std::vector<Test::Result> results = m_fut_results[i].get();
+         output() << report_out(results, tests_failed, tests_ran) << std::flush;
+         }
+
+      pool.shutdown();
+
+      const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;
+
+      output() << test_summary(test_run, tot_test_runs, total_ns, tests_ran, tests_failed);
+
+      return tests_failed;
+      }
+#else
+   if(test_threads > 1)
+      {
+      output() << "Running tests in multiple threads not enabled in this build\n";
+      }
+#endif
+
+   for(auto const& test_name : tests_to_run)
+      {
+      output() << test_name << ':' << std::endl;
+      const std::vector<Test::Result> results = run_a_test(test_name);
+      output() << report_out(results, tests_failed, tests_ran) << std::flush;
+      }
+
+   const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;
+
+   output() << test_summary(test_run, tot_test_runs, total_ns, tests_ran, tests_failed);
+
+   return tests_failed;
+   }
+
+}
+
diff --git a/third_party/botan/src/tests/test_runner.h b/third_party/botan/src/tests/test_runner.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_runner.h
@@ -0,0 +1,39 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TEST_RUNNER_H_
+#define BOTAN_TEST_RUNNER_H_
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Botan_Tests {
+
+class Test_Options;
+
+class Test_Runner final
+   {
+   public:
+      Test_Runner(std::ostream& out);
+
+      int run(const Test_Options& options);
+
+   private:
+      std::ostream& output() const { return m_output; }
+
+      size_t run_tests(const std::vector<std::string>& tests_to_run,
+                       size_t test_threads,
+                       size_t test_run,
+                       size_t tot_test_runs);
+
+      std::ostream& m_output;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_simd.cpp b/third_party/botan/src/tests/test_simd.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_simd.cpp
@@ -0,0 +1,202 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_SIMD_32)
+   #include <botan/internal/simd_32.h>
+   #include <botan/cpuid.h>
+   #include <botan/rotate.h>
+   #include <botan/loadstor.h>
+   #include <botan/bswap.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_SIMD_32)
+
+class SIMD_32_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("SIMD_4x32");
+
+         if(Botan::CPUID::has_simd_32() == false)
+            {
+            result.test_note("Skipping SIMD_4x32 tests due to missing CPU support at runtime");
+            return {result};
+            }
+
+         const uint32_t pat1 = 0xAABBCCDD;
+         const uint32_t pat2 = 0x87654321;
+         const uint32_t pat3 = 0x01234567;
+         const uint32_t pat4 = 0xC0D0E0F0;
+
+         // pat1 + pat{1,2,3,4}
+         // precomputed to avoid integer overflow warnings
+         const uint32_t pat1_1 = 0x557799BA;
+         const uint32_t pat1_2 = 0x32210FFE;
+         const uint32_t pat1_3 = 0xABDF1244;
+         const uint32_t pat1_4 = 0x6B8CADCD;
+
+         test_eq(result, "default init", Botan::SIMD_4x32(), 0, 0, 0, 0);
+         test_eq(result, "SIMD scalar constructor", Botan::SIMD_4x32(1, 2, 3, 4), 1, 2, 3, 4);
+
+         const Botan::SIMD_4x32 splat = Botan::SIMD_4x32::splat(pat1);
+
+         test_eq(result, "splat", splat, pat1, pat1, pat1, pat1);
+
+         const Botan::SIMD_4x32 input(pat1, pat2, pat3, pat4);
+
+         Botan::SIMD_4x32 rol = input.rotl<3>();
+
+         test_eq(result, "rotl", rol,
+                 Botan::rotl<3>(pat1),
+                 Botan::rotl<3>(pat2),
+                 Botan::rotl<3>(pat3),
+                 Botan::rotl<3>(pat4));
+
+         Botan::SIMD_4x32 ror = input.rotr<9>();
+
+         test_eq(result, "rotr", ror,
+                 Botan::rotr<9>(pat1),
+                 Botan::rotr<9>(pat2),
+                 Botan::rotr<9>(pat3),
+                 Botan::rotr<9>(pat4));
+
+         Botan::SIMD_4x32 add = input + splat;
+         test_eq(result, "add +", add, pat1_1, pat1_2, pat1_3, pat1_4);
+
+         add -= splat;
+         test_eq(result, "sub -=", add, pat1, pat2, pat3, pat4);
+
+         add += splat;
+         test_eq(result, "add +=", add, pat1_1, pat1_2, pat1_3, pat1_4);
+
+         test_eq(result, "xor", input ^ splat, 0, pat2 ^ pat1, pat3 ^ pat1, pat4 ^ pat1);
+         test_eq(result, "or", input | splat, pat1, pat2 | pat1, pat3 | pat1, pat4 | pat1);
+         test_eq(result, "and", input & splat, pat1, pat2 & pat1, pat3 & pat1, pat4 & pat1);
+
+         Botan::SIMD_4x32 blender = input;
+         blender |= splat;
+         test_eq(result, "|=", blender, pat1, pat2 | pat1, pat3 | pat1, pat4 | pat1);
+         blender &= splat;
+         test_eq(result, "&=", blender, pat1, pat1, pat1, pat1);
+         blender ^= splat;
+         test_eq(result, "^=", blender, 0, 0, 0, 0);
+
+         blender = ~blender;
+         test_eq(result, "~", blender, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
+
+         blender = blender.shr<23>();
+         test_eq(result, ">>", blender, 0x1FF, 0x1FF, 0x1FF, 0x1FF);
+
+         blender = blender.shl<27>();
+         test_eq(result, "<<", blender, 0xF8000000, 0xF8000000, 0xF8000000, 0xF8000000);
+
+         blender = ~blender;
+         test_eq(result, "~", blender, 0x7FFFFFF, 0x7FFFFFF, 0x7FFFFFF, 0x7FFFFFF);
+
+         blender = input.andc(~blender);
+         test_eq(result, "andc", blender,
+                 ~pat1 & 0xF8000000, ~pat2 & 0xF8000000,
+                 ~pat3 & 0xF8000000, ~pat4 & 0xF8000000);
+
+         test_eq(result, "bswap", input.bswap(),
+                 Botan::reverse_bytes(pat1),
+                 Botan::reverse_bytes(pat2),
+                 Botan::reverse_bytes(pat3),
+                 Botan::reverse_bytes(pat4));
+
+         Botan::SIMD_4x32 t1(pat1, pat2, pat3, pat4);
+         Botan::SIMD_4x32 t2(pat1 + 1, pat2 + 1, pat3 + 1, pat4 + 1);
+         Botan::SIMD_4x32 t3(pat1 + 2, pat2 + 2, pat3 + 2, pat4 + 2);
+         Botan::SIMD_4x32 t4(pat1 + 3, pat2 + 3, pat3 + 3, pat4 + 3);
+
+         Botan::SIMD_4x32::transpose(t1, t2, t3, t4);
+
+         test_eq(result, "transpose t1", t1, pat1, pat1 + 1, pat1 + 2, pat1 + 3);
+         test_eq(result, "transpose t2", t2, pat2, pat2 + 1, pat2 + 2, pat2 + 3);
+         test_eq(result, "transpose t3", t3, pat3, pat3 + 1, pat3 + 2, pat3 + 3);
+         test_eq(result, "transpose t4", t4, pat4, pat4 + 1, pat4 + 2, pat4 + 3);
+
+         test_eq(result, "shift left 1", input.shift_elems_left<1>(), 0, pat1, pat2, pat3);
+         test_eq(result, "shift left 2", input.shift_elems_left<2>(), 0, 0, pat1, pat2);
+         test_eq(result, "shift left 3", input.shift_elems_left<3>(), 0, 0, 0, pat1);
+
+         test_eq(result, "shift right 1", input.shift_elems_right<1>(), pat2, pat3, pat4, 0);
+         test_eq(result, "shift right 2", input.shift_elems_right<2>(), pat3, pat4, 0, 0);
+         test_eq(result, "shift right 3", input.shift_elems_right<3>(), pat4, 0, 0, 0);
+
+         return {result};
+         }
+
+   private:
+      void test_eq(Test::Result& result, const std::string& op,
+                   const Botan::SIMD_4x32& simd,
+                   uint32_t exp0, uint32_t exp1, uint32_t exp2, uint32_t exp3)
+         {
+         uint8_t arr_be[16 + 15];
+         uint8_t arr_be2[16 + 15];
+         uint8_t arr_le[16 + 15];
+         uint8_t arr_le2[16 + 15];
+
+         for(size_t misalignment = 0; misalignment != 16; ++misalignment)
+            {
+            uint8_t* mem_be  = arr_be  + misalignment;
+            uint8_t* mem_be2 = arr_be2 + misalignment;
+            uint8_t* mem_le  = arr_le  + misalignment;
+            uint8_t* mem_le2 = arr_le2 + misalignment;
+
+            simd.store_be(mem_be);
+
+            result.test_int_eq("SIMD_4x32 " + op + " elem0 BE",
+                               Botan::make_uint32(mem_be[ 0], mem_be[ 1], mem_be[ 2], mem_be[ 3]),
+                               exp0);
+            result.test_int_eq("SIMD_4x32 " + op + " elem1 BE",
+                               Botan::make_uint32(mem_be[ 4], mem_be[ 5], mem_be[ 6], mem_be[ 7]),
+                               exp1);
+            result.test_int_eq("SIMD_4x32 " + op + " elem2 BE",
+                               Botan::make_uint32(mem_be[ 8], mem_be[ 9], mem_be[10], mem_be[11]),
+                               exp2);
+            result.test_int_eq("SIMD_4x32 " + op + " elem3 BE",
+                               Botan::make_uint32(mem_be[12], mem_be[13], mem_be[14], mem_be[15]),
+                               exp3);
+
+            // Check load_be+store_be results in same value
+            const Botan::SIMD_4x32 reloaded_be = Botan::SIMD_4x32::load_be(mem_be);
+            reloaded_be.store_be(mem_be2);
+            result.test_eq(nullptr, "SIMD_4x32 load_be", mem_be, 16, mem_be2, 16);
+
+            simd.store_le(mem_le);
+
+            result.test_int_eq("SIMD_4x32 " + op + " elem0 LE",
+                               Botan::make_uint32(mem_le[ 3], mem_le[ 2], mem_le[ 1], mem_le[ 0]),
+                               exp0);
+            result.test_int_eq("SIMD_4x32 " + op + " elem1 LE",
+                               Botan::make_uint32(mem_le[ 7], mem_le[ 6], mem_le[ 5], mem_le[ 4]),
+                               exp1);
+            result.test_int_eq("SIMD_4x32 " + op + " elem2 LE",
+                               Botan::make_uint32(mem_le[11], mem_le[10], mem_le[ 9], mem_le[ 8]),
+                               exp2);
+            result.test_int_eq("SIMD_4x32 " + op + " elem3 LE",
+                               Botan::make_uint32(mem_le[15], mem_le[14], mem_le[13], mem_le[12]),
+                               exp3);
+
+            // Check load_le+store_le results in same value
+            const Botan::SIMD_4x32 reloaded_le = Botan::SIMD_4x32::load_le(mem_le);
+            reloaded_le.store_le(mem_le2);
+            result.test_eq(nullptr, "SIMD_4x32 load_le", mem_le, 16, mem_le2, 16);
+            }
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("simd_32", SIMD_32_Tests);
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_siv.cpp b/third_party/botan/src/tests/test_siv.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_siv.cpp
@@ -0,0 +1,72 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+   #include <botan/siv.h>
+   #include <botan/block_cipher.h>
+   #include <botan/parsing.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+
+class SIV_Tests final : public Text_Based_Test
+   {
+   public:
+      SIV_Tests() : Text_Based_Test("siv_ad.vec", "Key,Nonce,ADs,In,Out") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> nonce    = vars.get_opt_bin("Nonce");
+         const std::vector<uint8_t> input    = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+         const std::vector<std::string> ad_list =
+            Botan::split_on(vars.get_req_str("ADs"), ',');
+
+         Test::Result result(algo + "/SIV");
+
+         auto cipher = Botan::BlockCipher::create(algo);
+
+         if(!cipher)
+            {
+            result.test_note("Skipping test due to missing cipher");
+            return result;
+            }
+
+         std::unique_ptr<Botan::SIV_Mode> siv(new Botan::SIV_Encryption(cipher.release()));
+
+         siv->set_key(key);
+
+         for(size_t i = 0; i != ad_list.size(); ++i)
+            {
+            std::vector<uint8_t> ad = Botan::hex_decode(ad_list[i]);
+            siv->set_associated_data_n(i, ad.data(), ad.size());
+            }
+
+         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+         siv->start(nonce);
+         siv->finish(buf, 0);
+
+         result.test_eq("SIV ciphertext", buf, expected);
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("siv_ad", SIV_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_sm2.cpp b/third_party/botan/src/tests/test_sm2.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_sm2.cpp
@@ -0,0 +1,120 @@
+/*
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_SM2)
+   #include <botan/sm2.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_SM2)
+
+namespace {
+
+std::unique_ptr<Botan::Private_Key> load_sm2_private_key(const VarMap& vars)
+   {
+   // group params
+   const BigInt p = vars.get_req_bn("P");
+   const BigInt a = vars.get_req_bn("A");
+   const BigInt b = vars.get_req_bn("B");
+   const BigInt xG = vars.get_req_bn("xG");
+   const BigInt yG = vars.get_req_bn("yG");
+   const BigInt order = vars.get_req_bn("Order");
+   const BigInt cofactor = vars.get_req_bn("Cofactor");
+   const BigInt x = vars.get_req_bn("x");
+
+   Botan::EC_Group domain(p, a, b, xG, yG, order, cofactor);
+
+   Botan::Null_RNG null_rng;
+   return std::unique_ptr<Botan::Private_Key>(new Botan::SM2_PrivateKey(null_rng, domain, x));
+   }
+
+class SM2_Signature_KAT_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      SM2_Signature_KAT_Tests()
+         : PK_Signature_Generation_Test(
+            "SM2",
+            "pubkey/sm2_sig.vec",
+            "P,A,B,xG,yG,Order,Cofactor,Ident,Msg,x,Nonce,Signature",
+            "Hash") {}
+
+      bool clear_between_callbacks() const override { return false; }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return vars.get_req_str("Ident") + "," + vars.get_opt_str("Hash", "SM3");
+         }
+
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_sm2_private_key(vars);
+         }
+   };
+
+BOTAN_REGISTER_TEST("sm2_sig", SM2_Signature_KAT_Tests);
+
+class SM2_Encryption_KAT_Tests final : public PK_Encryption_Decryption_Test
+   {
+   public:
+      SM2_Encryption_KAT_Tests()
+         : PK_Encryption_Decryption_Test(
+            "SM2",
+            "pubkey/sm2_enc.vec",
+            "P,A,B,xG,yG,Order,Cofactor,Msg,x,Nonce,Ciphertext",
+            "Hash") {}
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return vars.get_opt_str("Hash", "SM3");
+         }
+
+      bool clear_between_callbacks() const override { return false; }
+
+      Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+         {
+         return new Fixed_Output_Position_RNG(nonce, 1);
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         return load_sm2_private_key(vars);
+         }
+   };
+
+}
+
+BOTAN_REGISTER_TEST("sm2_enc", SM2_Encryption_KAT_Tests);
+
+class SM2_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "secp256r1", "sm2p256v1" };
+         }
+
+      std::string algo_name() const override
+         {
+         return "SM2";
+         }
+   };
+
+BOTAN_REGISTER_TEST("sm2_keygen", SM2_Keygen_Tests);
+
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_sodium.cpp b/third_party/botan/src/tests/test_sodium.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_sodium.cpp
@@ -0,0 +1,672 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_SODIUM_API)
+   #include <botan/sodium.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_SODIUM_API)
+
+class Sodium_API_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(aead_chacha20poly1305());
+         results.push_back(aead_chacha20poly1305_ietf());
+         results.push_back(aead_xchacha20poly1305());
+         results.push_back(auth_hmacsha256());
+         results.push_back(auth_hmacsha512());
+         results.push_back(auth_hmacsha512256());
+         results.push_back(auth_poly1305());
+         results.push_back(box_curve25519xsalsa20poly1305());
+         results.push_back(hash_sha256());
+         results.push_back(hash_sha512());
+         results.push_back(randombytes_buf_deterministic());
+         results.push_back(secretbox_xsalsa20poly1305());
+         results.push_back(secretbox_xsalsa20poly1305_detached());
+         results.push_back(shorthash_siphash24());
+         results.push_back(stream_chacha20());
+         results.push_back(stream_chacha20_ietf());
+         results.push_back(stream_salsa20());
+         results.push_back(stream_xchacha20());
+         results.push_back(stream_xsalsa20());
+         results.push_back(sign_ed25519());
+         results.push_back(sodium_malloc());
+         results.push_back(sodium_utils());
+
+         return results;
+         }
+
+   private:
+
+      Test::Result sodium_malloc()
+         {
+         Test::Result result("sodium_malloc");
+
+         void* p = Botan::Sodium::sodium_malloc(50);
+         std::memset(p, 0xFF, 50);
+
+         Botan::Sodium::sodium_free(p);
+         Botan::Sodium::sodium_free(nullptr);
+
+         result.test_success("Didn't crash");
+
+         return result;
+         }
+
+      Test::Result sodium_utils()
+         {
+         Test::Result result("sodium math utils");
+
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(nullptr, 0) == 1);
+
+         std::vector<uint8_t> a(5);
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 1);
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0100000000");
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 0);
+
+         std::memset(a.data(), 0xFF, a.size());
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0000000000");
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0100000000");
+
+         result.confirm("sodium_compare", Botan::Sodium::sodium_compare(a.data(), a.data(), a.size()) == 0);
+         result.confirm("sodium_memcmp", Botan::Sodium::sodium_memcmp(a.data(), a.data(), a.size()) == 0);
+
+         std::vector<uint8_t> b(5, 0x10);
+         result.confirm("sodium_compare a<b", Botan::Sodium::sodium_compare(a.data(), b.data(), a.size()) == -1);
+         result.confirm("sodium_compare b<a", Botan::Sodium::sodium_compare(b.data(), a.data(), a.size()) == 1);
+         result.confirm("sodium_memcmp a<b", Botan::Sodium::sodium_memcmp(a.data(), b.data(), a.size()) == -1);
+         result.confirm("sodium_memcmp b<a", Botan::Sodium::sodium_memcmp(b.data(), a.data(), a.size()) == -1);
+
+         Botan::Sodium::sodium_add(a.data(), b.data(), a.size());
+         result.test_eq("sodium_add", a, "1110101010");
+         Botan::Sodium::sodium_add(b.data(), a.data(), a.size());
+         result.test_eq("sodium_add", b, "2120202020");
+         Botan::Sodium::sodium_add(a.data(), b.data(), a.size());
+         result.test_eq("sodium_add", a, "3230303030");
+         Botan::Sodium::sodium_add(b.data(), a.data(), a.size());
+         result.test_eq("sodium_add", b, "5350505050");
+
+         return result;
+         }
+
+      Test::Result randombytes_buf_deterministic()
+         {
+         Test::Result result("randombytes_buf_deterministic");
+
+         const uint8_t seed[32] = { 1, 0 };
+         std::vector<uint8_t> output(18);
+
+         Botan::Sodium::randombytes_buf_deterministic(output.data(), output.size(), seed);
+
+         result.test_eq("output", output, "04069B5F37E82F91DC37FD5EB99F1A4124B1");
+
+         return result;
+         }
+
+      Test::Result hash_sha512()
+         {
+         Test::Result result("crypto_hash_sha512");
+
+         std::vector<uint8_t> output(64);
+         Botan::Sodium::crypto_hash_sha512(output.data(), nullptr, 0);
+
+         result.test_eq("expected output", output,
+                        "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
+
+         return result;
+         }
+
+      Test::Result hash_sha256()
+         {
+         Test::Result result("crypto_hash_sha256");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_hash_sha256(output.data(), nullptr, 0);
+
+         result.test_eq("expected output", output,
+                        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+
+         return result;
+         }
+
+      Test::Result box_curve25519xsalsa20poly1305()
+         {
+         Test::Result result("crypto_box_curve25519xsalsa20poly1305");
+
+         const std::vector<uint8_t> seed(32);
+
+         std::vector<uint8_t> pk1(32), sk1(32);
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk1.data(), sk1.data(), seed.data()));
+         result.test_eq("pk1", pk1, "5BF55C73B82EBE22BE80F3430667AF570FAE2556A6415E6B30D4065300AA947D");
+         result.test_eq("sk1", sk1, "5046ADC1DBA838867B2BBBFDD0C3423E58B57970B5267A90F57960924A87F196");
+
+         std::vector<uint8_t> pk2(32), sk2(32);
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk2.data(), sk2.data(), sk1.data()));
+         result.test_eq("pk2", pk2, "E0CFC9C6B2FE5BF85F48671691225C03D763F2305206FE3D3B0ED7B76153684A");
+         result.test_eq("sk2", sk2, "58E2E4C71F138FBC97F9341735B4581746761F9A104540007FE12CFC4D9FDA15");
+
+         const std::vector<uint8_t> ptext(15);
+         std::vector<uint8_t> ctext(ptext.size() + 16);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_box_noncebytes());
+
+         result.test_rc_ok("crypto_box_easy",
+                           Botan::Sodium::crypto_box_easy(ctext.data(), ptext.data(), ptext.size(), nonce.data(),
+                                           pk2.data(), sk1.data()));
+
+         result.test_eq("ctext1", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92");
+
+         result.test_rc_ok("crypto_box_easy",
+                           Botan::Sodium::crypto_box_easy(ctext.data(), ptext.data(), ptext.size(), nonce.data(),
+                                           pk1.data(), sk2.data()));
+
+         // same shared secret, same nonce, same data -> same ciphertext
+         result.test_eq("ctext2", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92");
+
+         std::vector<uint8_t> recovered(15);
+
+         result.test_rc_ok("crypto_box_open_easy",
+                           Botan::Sodium::crypto_box_open_easy(recovered.data(), ctext.data(), ctext.size(),
+                                                nonce.data(), pk1.data(), sk2.data()));
+
+         result.test_eq("recover1", recovered, ptext);
+
+         result.test_rc_ok("crypto_box_open_easy",
+                           Botan::Sodium::crypto_box_open_easy(recovered.data(), ctext.data(), ctext.size(),
+                                                nonce.data(), pk2.data(), sk1.data()));
+
+         result.test_eq("recover1", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result aead_chacha20poly1305()
+         {
+         Test::Result result("crypto_aead_chacha20poly1305");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("0000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_chacha20poly1305_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "09998877ABA156DDC68F8344098F68B9");
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D0809998877ABA156DDC68F8344098F68B9");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result aead_chacha20poly1305_ietf()
+         {
+         Test::Result result("crypto_aead_chacha20poly1305_ietf");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000000000000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_chacha20poly1305_ietf_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "3679F1FB9843FD81E26D962888296954");
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D083679F1FB9843FD81E26D962888296954");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result aead_xchacha20poly1305()
+         {
+         Test::Result result("crypto_aead_xchacha20poly1305");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "b2f7033812ac9ebd3745e2c99c7bbfeb");
+         result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341f");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt",
+                           Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
+                              recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+                              ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341fb2f7033812ac9ebd3745e2c99c7bbfeb");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha512()
+         {
+         Test::Result result("crypto_auth_hmacsha512");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         result.test_eq("key_size", key.size(), Botan::Sodium::crypto_auth_hmacsha512_keybytes());
+
+         std::vector<uint8_t> mac(64);
+         Botan::Sodium::crypto_auth_hmacsha512(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A624B470E23ABD33AC8700E797F0F2A499B932BAC7D283BBBB37D8FECF70D5E08A7");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha512_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha512_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha512256()
+         {
+         Test::Result result("crypto_auth_hmacsha512256");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         std::vector<uint8_t> mac(32);
+         Botan::Sodium::crypto_auth_hmacsha512256(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A62");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha512256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha512256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha256()
+         {
+         Test::Result result("crypto_auth_hmacsha256");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         std::vector<uint8_t> mac(32);
+         Botan::Sodium::crypto_auth_hmacsha256(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_poly1305()
+         {
+         Test::Result result("crypto_onetimeauth_poly1305");
+
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_onetimeauth_keybytes(), 0x42);
+         const std::vector<uint8_t> in(15);
+
+         std::vector<uint8_t> mac(16);
+
+         result.test_rc_ok("poly1305",
+                           Botan::Sodium::crypto_onetimeauth_poly1305(mac.data(), in.data(), in.size(), key.data()));
+
+         result.test_eq("expected mac", mac, "12154512151545121515451215154584");
+
+         result.test_rc_ok("poly1305 verify",
+                           Botan::Sodium::crypto_onetimeauth_poly1305_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("poly1305 verify", "invalid mac",
+                             Botan::Sodium::crypto_onetimeauth_poly1305_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result shorthash_siphash24()
+         {
+         Test::Result result("crypto_shorthash_siphash24");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F");
+         const std::vector<uint8_t> in = Botan::hex_decode("000102030405060708090A0B0C0D0E");
+
+         std::vector<uint8_t> mac(8);
+         Botan::Sodium::crypto_shorthash_siphash24(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac, "E545BE4961CA29A1");
+
+         return result;
+         }
+
+      Test::Result secretbox_xsalsa20poly1305()
+         {
+         Test::Result result("secretbox_xsalsa20poly1305");
+
+         const std::vector<uint8_t> ptext(33);
+         std::vector<uint8_t> ctext(33);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_noncebytes());
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_keybytes());
+
+         result.test_rc_ok("encrypt",
+                           Botan::Sodium::crypto_secretbox_xsalsa20poly1305(ctext.data(),
+                                                               ptext.data(),
+                                                               ptext.size(),
+                                                               nonce.data(),
+                                                               key.data()));
+
+         result.test_eq("ctext", ctext, "0000000000000000000000000000000042E45EB764A1B706D4776A849BC2526BC6");
+
+         std::vector<uint8_t> recovered(33);
+         result.test_rc_ok("decrypt",
+                           Botan::Sodium::crypto_secretbox_xsalsa20poly1305_open(
+                              recovered.data(),
+                              ctext.data(),
+                              ctext.size(),
+                              nonce.data(),
+                              key.data()));
+
+         result.test_eq("decrypted", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result secretbox_xsalsa20poly1305_detached()
+         {
+         Test::Result result("secretbox_xsalsa20poly1305");
+
+         const std::vector<uint8_t> ptext(33);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_noncebytes());
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_keybytes());
+         std::vector<uint8_t> ctext(33);
+         std::vector<uint8_t> mac(16);
+
+         result.test_rc_ok("encrypt detached",
+                           Botan::Sodium::crypto_secretbox_detached(ctext.data(),
+                                                       mac.data(),
+                                                       ptext.data(),
+                                                       ptext.size(),
+                                                       nonce.data(),
+                                                       key.data()));
+
+         result.test_eq("ctext", ctext, "C63EBBFFFE85CE2CEBDEF7DC42F494576D05BDD7B929EBB045F2A793F740277D05");
+         result.test_eq("mac", mac, "0D6681DCED740667C699F0AC71BFD1BD");
+
+         std::vector<uint8_t> recovered(ctext.size());
+
+         result.test_rc_ok("open detached",
+                           Botan::Sodium::crypto_secretbox_open_detached(recovered.data(),
+                                                                         ctext.data(),
+                                                                         mac.data(),
+                                                                         ctext.size(),
+                                                                         nonce.data(),
+                                                                         key.data()));
+
+         result.test_eq("recovered", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result sign_ed25519()
+         {
+         Test::Result result("crypto_sign_ed25519");
+
+         const std::vector<uint8_t> seed(32);
+         std::vector<uint8_t> pk(32), sk(64);
+
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_sign_ed25519_seed_keypair(pk.data(), sk.data(), seed.data()));
+
+         result.test_eq("pk", pk, "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
+         result.test_eq("sk", sk, "00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
+
+         const std::vector<uint8_t> msg = { 1, 2, 3 };
+         std::vector<uint8_t> sig(64);
+         unsigned long long sig_len = 0;
+         result.test_rc_ok("sign_detached", Botan::Sodium::crypto_sign_ed25519_detached(sig.data(), &sig_len, msg.data(), msg.size(), sk.data()));
+         result.confirm("sig len", sig_len == 64);
+
+         result.test_eq("sig", sig, "2A26779BA6CBB5E54292257F725AF112B273C38728329682D99ED81BA6D7670350AE4CC53C5456FA437128D19298A5D949AB46E3D41AB3DBCFB0B35C895E9304");
+
+         result.test_rc_ok("verify", Botan::Sodium::crypto_sign_ed25519_verify_detached(sig.data(), msg.data(), msg.size(), pk.data()));
+
+         sig[0] ^= 1;
+         result.test_rc_fail("verify", "reject invalid",
+                             Botan::Sodium::crypto_sign_ed25519_verify_detached(sig.data(), msg.data(), msg.size(), pk.data()));
+
+         return result;
+         }
+
+      Test::Result stream_salsa20()
+         {
+         Test::Result result("crypto_stream_salsa20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("288FF65DC42B92F9");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "5E5E71F90199340304ABB22A37B6625BF883FB89CE3B21F54A10B81066EF87DA");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_salsa20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_salsa20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_xsalsa20()
+         {
+         Test::Result result("crypto_stream_xsalsa20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("1B27556473E985D462CD51197A9A46C76009549EAC6474F206C4EE0844F68389");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("69696EE955B62B73CD62BDA875FC73D68219E0036B7A0B37");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "EEA6A7251C1E72916D11C2CB214D3C252539121D8E234E652D651FA4C8CFF880");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_xsalsa20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_xsalsa20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_chacha20()
+         {
+         Test::Result result("crypto_stream_chacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("0001020304050607");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "F798A189F195E66982105FFB640BB7757F579DA31602FC93EC01AC56F85AC3C1");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_chacha20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_chacha20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_chacha20_ietf()
+         {
+         Test::Result result("crypto_stream_chacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000102030405060708090A0B0C");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "103AF111C18B549D39248FB07D60C29A95D1DB88D892F7B4AF709A5FD47A9E4B");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_chacha20_ietf(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_chacha20_ietf_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_xchacha20()
+         {
+         Test::Result result("crypto_stream_xchacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "e53a61cef151e81401067de33adfc02e90ab205361b49b539fda7f0e63b1bc7d");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_xchacha20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_xchacha20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("sodium", Sodium_API_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_srp6.cpp b/third_party/botan/src/tests/test_srp6.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_srp6.cpp
@@ -0,0 +1,134 @@
+/*
+* (C) 2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_SRP6)
+   #include <botan/srp6.h>
+   #include <botan/hash.h>
+   #include <botan/dl_group.h>
+   #include "test_rng.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_SRP6)
+
+class SRP6_KAT_Tests final : public Text_Based_Test
+   {
+   public:
+      SRP6_KAT_Tests() : Text_Based_Test("srp6a.vec", "Hash,N,g,I,P,s,v,a,b,A,B,S") {}
+
+      bool clear_between_callbacks() const override { return false; }
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("SRP6a");
+
+         const std::string hash = vars.get_req_str("Hash");
+         const std::string username = vars.get_req_str("I");
+         const std::string password = vars.get_req_str("P");
+         const std::vector<uint8_t> salt = vars.get_req_bin("s");
+         const BigInt N = vars.get_req_bn("N");
+         const BigInt g = vars.get_req_bn("g");
+         const BigInt exp_v = vars.get_req_bn("v");
+         const std::vector<uint8_t> a = vars.get_req_bin("a");
+         const std::vector<uint8_t> b = vars.get_req_bin("b");
+         const BigInt exp_A = vars.get_req_bn("A");
+         const BigInt exp_B = vars.get_req_bn("B");
+         const std::vector<uint8_t> exp_S = vars.get_req_bin("S");
+
+         if(Botan::HashFunction::create(hash) == nullptr)
+            {
+            result.test_note("Skipping test as hash function not available");
+            return result;
+            }
+
+         if(Test::run_long_tests() == false && N.bits() >= 4096)
+            {
+            result.test_note("Skipping test with long SRP modulus");
+            return result;
+            }
+
+         const std::string group_id = Botan::srp6_group_identifier(N, g);
+
+         result.test_ne("Known SRP group", group_id, "");
+
+         Botan::DL_Group group(group_id);
+
+         const Botan::BigInt v = Botan::generate_srp6_verifier(username, password, salt, group_id, hash);
+         result.test_eq("SRP verifier", v, exp_v);
+
+         Botan::SRP6_Server_Session server;
+
+         const size_t b_bits = Botan::BigInt(b).bits();
+         Fixed_Output_RNG b_rng(b);
+         const Botan::BigInt B = server.step1(v, group, hash, b_bits, b_rng);
+         result.test_eq("SRP B", B, exp_B);
+
+         const size_t a_bits = Botan::BigInt(a).bits();
+         Fixed_Output_RNG a_rng(a);
+         const auto srp_resp = Botan::srp6_client_agree(username, password, group, hash, salt, B, a_bits, a_rng);
+         result.test_eq("SRP A", srp_resp.first, exp_A);
+
+         const auto S = server.step2(srp_resp.first);
+
+         result.test_eq("SRP client and server agree", srp_resp.second, S);
+
+         result.test_eq("SRP S", srp_resp.second.bits_of(), exp_S);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("srp6_kat", SRP6_KAT_Tests);
+
+#if defined(BOTAN_HAS_SHA2_32)
+
+class SRP6_RT_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+         Test::Result result("SRP6");
+
+         const std::string username = "user";
+         const std::string password = "Awellchosen1_to_be_sure_";
+         const std::string group_id = "modp/srp/1024";
+         const std::string hash_id = "SHA-256";
+
+         std::vector<uint8_t> salt;
+         Test::rng().random_vec(salt, 16);
+
+         const Botan::BigInt verifier = Botan::generate_srp6_verifier(username, password, salt, group_id, hash_id);
+
+         Botan::SRP6_Server_Session server;
+
+         const Botan::BigInt B = server.step1(verifier, group_id, hash_id, Test::rng());
+
+         auto client = srp6_client_agree(username, password, group_id, hash_id, salt, B, Test::rng());
+
+         const Botan::SymmetricKey server_K = server.step2(client.first);
+
+         result.test_eq("computed same keys", client.second.bits_of(), server_K.bits_of());
+         results.push_back(result);
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("srp6", SRP6_RT_Tests);
+
+#endif
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_stream.cpp b/third_party/botan/src/tests/test_stream.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_stream.cpp
@@ -0,0 +1,193 @@
+/*
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+   #include <botan/stream_cipher.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+class Stream_Cipher_Tests final : public Text_Based_Test
+   {
+   public:
+      Stream_Cipher_Tests(): Text_Based_Test("stream", "Key,Out", "In,Nonce,Seek") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> key      = vars.get_req_bin("Key");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+         const std::vector<uint8_t> nonce    = vars.get_opt_bin("Nonce");
+         const uint64_t seek                 = vars.get_opt_u64("Seek", 0);
+         std::vector<uint8_t> input          = vars.get_opt_bin("In");
+
+         if(input.empty())
+            {
+            input.resize(expected.size());
+            }
+
+         Test::Result result(algo);
+
+         const std::vector<std::string> providers =
+            provider_filter(Botan::StreamCipher::providers(algo));
+
+         if(providers.empty())
+            {
+            result.note_missing("stream cipher " + algo);
+            return result;
+            }
+
+         for(auto const& provider_ask : providers)
+            {
+            std::unique_ptr<Botan::StreamCipher> cipher(Botan::StreamCipher::create(algo, provider_ask));
+
+            if(!cipher)
+               {
+               result.test_failure("Stream " + algo + " supported by " + provider_ask + " but not found");
+               continue;
+               }
+
+            const std::string provider(cipher->provider());
+            result.test_is_nonempty("provider", provider);
+            result.test_eq(provider, cipher->name(), algo);
+
+            result.confirm("default iv length is valid", cipher->valid_iv_length(cipher->default_iv_length()));
+
+            if(cipher->default_iv_length() == 0)
+               {
+               result.confirm("if default iv length is zero, no iv supported", nonce.size() == 0);
+
+               // This should still succeed
+               cipher->set_iv(nullptr, 0);
+               }
+
+            try
+               {
+               std::vector<uint8_t> buf(128);
+               cipher->cipher1(buf.data(), buf.size());
+               result.test_failure("Was able to encrypt without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to encrypt with no key set fails");
+               }
+
+            try
+               {
+               cipher->seek(0);
+               result.test_failure("Was able to seek without a key being set");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to seek with no key set fails");
+               }
+            catch(Botan::Not_Implemented&)
+               {
+               result.test_success("Trying to seek failed because not implemented");
+               }
+
+            if(!cipher->valid_iv_length(nonce.size()))
+               {
+               throw Test_Error("Invalid nonce for " + algo);
+               }
+
+            bool accepted_nonce_early = false;
+            if(nonce.size() > 0)
+               {
+               try
+                  {
+                  cipher->set_iv(nonce.data(), nonce.size());
+                  accepted_nonce_early = true;
+                  }
+               catch(Botan::Invalid_State&) {}
+               }
+
+            /*
+            * Different providers may have additional restrictions on key sizes.
+            * Avoid testing the cipher with a key size that it does not natively support.
+            */
+            if(!cipher->valid_keylength(key.size()))
+               {
+               result.test_note("Skipping test with provider " + provider +
+                                " as it does not support key length " + std::to_string(key.size()));
+               continue;
+               }
+
+            cipher->set_key(key);
+
+            /*
+            Test invalid nonce sizes. this assumes no implemented cipher supports a nonce of 65000
+            */
+            const size_t large_nonce_size = 65000;
+            result.confirm("Stream cipher does not support very large nonce", cipher->valid_iv_length(large_nonce_size) == false);
+
+            result.test_throws("Throws if invalid nonce size given",
+                               [&]() { cipher->set_iv(nullptr, large_nonce_size); });
+
+            /*
+            If the set_nonce call earlier succeded, then we require that it also
+            worked (ie saved the nonce for later use) even though the key was
+            not set. So, don't set the nonce now, to ensure the previous call
+            had an effect.
+            */
+            if(nonce.size() > 0 && accepted_nonce_early == false)
+               {
+               cipher->set_iv(nonce.data(), nonce.size());
+               }
+
+            if(seek != 0)
+               {
+               cipher->seek(seek);
+               }
+
+            // Test that clone works and does not affect parent object
+            std::unique_ptr<Botan::StreamCipher> clone(cipher->clone());
+            result.confirm("Clone has different pointer", cipher.get() != clone.get());
+            result.test_eq("Clone has same name", cipher->name(), clone->name());
+            clone->set_key(Test::rng().random_vec(cipher->maximum_keylength()));
+
+            {
+            std::vector<uint8_t> buf = input;
+            cipher->encrypt(buf);
+            result.test_eq(provider, "encrypt", buf, expected);
+            }
+
+            if(nonce.size() > 0)
+               {
+               std::vector<uint8_t> buf = input;
+               cipher->set_iv(nonce.data(), nonce.size());
+               if(seek != 0)
+                  cipher->seek(seek);
+               cipher->encrypt(buf);
+               result.test_eq(provider, "second encrypt", buf, expected);
+               }
+
+            cipher->clear();
+
+            try
+               {
+               std::vector<uint8_t> buf(128);
+               cipher->cipher1(buf.data(), buf.size());
+               result.test_failure("Was able to encrypt without a key being set (after clear)");
+               }
+            catch(Botan::Invalid_State&)
+               {
+               result.test_success("Trying to encrypt with no key set (after clear) fails");
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("stream", Stream_Cipher_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_tests.cpp b/third_party/botan/src/tests/test_tests.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tests.cpp
@@ -0,0 +1,203 @@
+/*
+* (C) 2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_BIGINT)
+   #include <botan/bigint.h>
+#endif
+
+namespace Botan_Tests {
+
+/*
+* Test the test framework :)
+*/
+
+class Test_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         /*
+         Notation here is confusing. Idea is the result is the actual
+         result of the test. The test_result is the result that is the
+         testcase, and should typically be in a failure mode.
+         */
+
+         Test::Result result("Test Framework");
+
+         // Test a few success corner cases first
+         const std::string testcase_name = "Failing Test";
+
+         result.test_throws("throws pi", []() { throw 3.14159; });
+
+         // Test expected failure cases
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_throws("doesn't throw", []() { });
+            verify_failure("test_throws", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_failure("explicitly reported failure", std::vector<uint8_t>());
+            verify_failure("explicit failure", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_failure("explicitly reported failure", "test error");
+            verify_failure("explicit failure", result, test_result);
+            }
+
+            {
+            verify_failure("explicit failure", result, Test::Result::Failure(testcase_name, "failure"));
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            std::vector<uint8_t> vec1(5), vec2(3, 9);
+            test_result.test_eq("test vectors equal", vec1, vec2);
+            verify_failure("test vectors equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            std::vector<uint8_t> vec1(5), vec2(5);
+            test_result.test_ne("test vectors not equal", vec1, vec2);
+            verify_failure("test vectors equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            std::vector<uint8_t> vec1(5), vec2(5);
+            test_result.test_ne("test arrays not equal", vec1.data(), vec1.size(), vec2.data(), vec2.size());
+            verify_failure("test vectors equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            size_t x = 5, y = 6;
+            test_result.test_eq("test ints equal", x, y);
+            verify_failure("test ints equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            size_t x = 5, y = 5;
+            test_result.test_ne("test ints not equal", x, y);
+            verify_failure("test ints not equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_is_nonempty("empty", "");
+            verify_failure("test_is_nonempty", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_lt("not less", 5, 5);
+            verify_failure("test_lt", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_lte("not lte", 6, 5);
+            verify_failure("test_lte", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_gte("not gte", 5, 6);
+            verify_failure("test_gte", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_ne("string ne", "foo", "foo");
+            verify_failure("test_ne", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_rc_ok("test_func", -1);
+            verify_failure("test_rc_ok", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_rc("test_func", 0, 5);
+            verify_failure("test_rc", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_rc_fail("test_func", "testing", 0);
+            verify_failure("test_rc_fail", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_throws("test_throws", "expected msg",
+                                    []() { throw std::runtime_error("not the message"); });
+            verify_failure("test_throws", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_throws("test_throws", "expected msg",
+                                    []() { throw std::string("not even a std::exception"); });
+            verify_failure("test_throws", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            test_result.test_throws("test_throws", "expected msg",
+                                    []() { ; });
+            verify_failure("test_throws", result, test_result);
+            }
+
+#if defined(BOTAN_HAS_BIGINT)
+            {
+            Test::Result test_result(testcase_name);
+            Botan::BigInt x = 5, y = 6;
+            test_result.test_eq("test ints equal", x, y);
+            verify_failure("test ints equal", result, test_result);
+            }
+
+            {
+            Test::Result test_result(testcase_name);
+            Botan::BigInt x = 5, y = 5;
+            test_result.test_ne("test ints not equal", x, y);
+            verify_failure("test ints not equal", result, test_result);
+            }
+#endif
+
+         return {result};
+         }
+
+   private:
+      void verify_failure(const std::string& what,
+                          Test::Result& result,
+                          const Test::Result& test_result)
+         {
+         if(test_result.tests_failed() > 0)
+            {
+            result.test_success("Got expected failure for " + what);
+            const std::string result_str = test_result.result_string();
+
+            result.confirm("result string contains FAIL",
+                           result_str.find("FAIL") != std::string::npos);
+            }
+         else
+            result.test_failure("Expected test to fail for " + what);
+         }
+   };
+
+BOTAN_REGISTER_TEST("testcode", Test_Tests);
+
+}
diff --git a/third_party/botan/src/tests/test_thread_utils.cpp b/third_party/botan/src/tests/test_thread_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_thread_utils.cpp
@@ -0,0 +1,74 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_THREAD_UTILS)
+
+#include <botan/internal/thread_pool.h>
+#include <chrono>
+
+namespace Botan_Tests {
+
+// TODO test Barrier
+// TODO test Semaphore
+
+namespace {
+
+Test::Result thread_pool()
+   {
+   Test::Result result("Thread_Pool");
+
+   // Using lots of threads since here the works spend most of the time sleeping
+   Botan::Thread_Pool pool(16);
+
+   auto sleep_or_throw = [](size_t x) -> size_t {
+      std::this_thread::sleep_for(std::chrono::milliseconds((x*97)%127));
+
+      if(x % 2 == 0)
+         throw x;
+      return x;
+      };
+
+   std::vector<std::future<size_t>> futures;
+   for(size_t i = 0; i != 100; ++i)
+      {
+      auto fut = pool.run(sleep_or_throw, i);
+      futures.push_back(std::move(fut));
+      }
+
+   for(size_t i = 0; i != futures.size(); ++i)
+      {
+      if(i % 2 == 0)
+         {
+         try
+            {
+            futures[i].get();
+            result.test_failure("Expected future to throw");
+            }
+         catch(size_t x)
+            {
+            result.test_eq("Expected thrown value", x, i);
+            }
+         }
+      else
+         {
+         result.test_eq("Expected return value", futures[i].get(), i);
+         }
+      }
+
+   pool.shutdown();
+
+   return result;
+   }
+
+BOTAN_REGISTER_TEST_FN("thread_pool", thread_pool);
+
+}
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/test_tls.cpp b/third_party/botan/src/tests/test_tls.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tls.cpp
@@ -0,0 +1,494 @@
+/*
+* (C) 2014,2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include <fstream>
+#include <memory>
+
+#if defined(BOTAN_HAS_TLS)
+  #include "test_rng.h"
+  #include <botan/tls_alert.h>
+  #include <botan/tls_policy.h>
+  #include <botan/tls_session.h>
+  #include <botan/tls_version.h>
+
+  #if defined(BOTAN_HAS_TLS_CBC)
+     #include <botan/internal/tls_cbc.h>
+  #endif
+
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_TLS)
+
+class TLS_Session_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("TLS::Session");
+
+         Botan::TLS::Session default_session;
+
+         Botan::secure_vector<uint8_t> default_der = default_session.DER_encode();
+
+         result.test_gte("Encoded default session has size", default_der.size(), 0);
+
+         Botan::TLS::Session decoded_default(default_der.data(), default_der.size());
+
+         Botan::TLS::Session session(std::vector<uint8_t>{0xAA, 0xBB},
+                                     Botan::secure_vector<uint8_t>{0xCC, 0xDD},
+                                     Botan::TLS::Protocol_Version::TLS_V12,
+                                     0xFE0F,
+                                     Botan::TLS::CLIENT,
+                                     true,
+                                     false,
+                                     std::vector<Botan::X509_Certificate>(),
+                                     std::vector<uint8_t>(),
+                                     Botan::TLS::Server_Information("server"),
+                                     "SRP username",
+                                     0x0000);
+
+         const std::string pem = session.PEM_encode();
+         Botan::TLS::Session session_from_pem(pem);
+         result.test_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode());
+
+         const Botan::SymmetricKey key("ABCDEF");
+         const std::vector<uint8_t> ctext1 = session.encrypt(key, Test::rng());
+         const std::vector<uint8_t> ctext2 = session.encrypt(key, Test::rng());
+
+         result.test_ne("TLS session encryption is non-determinsitic",
+                        ctext1.data(), ctext1.size(),
+                        ctext2.data(), ctext2.size());
+
+         const std::vector<uint8_t> expected_hdr = Botan::hex_decode("068B5A9D396C0000F2322CAE");
+
+         result.test_eq("tls", "TLS session encryption same header",
+                        ctext1.data(), 12, expected_hdr.data(), 12);
+         result.test_eq("tls", "TLS session encryption same header",
+                        ctext2.data(), 12, expected_hdr.data(), 12);
+
+         Botan::TLS::Session dsession = Botan::TLS::Session::decrypt(ctext1.data(), ctext1.size(), key);
+         result.test_eq("Decrypted session access works", dsession.srp_identifier(), "SRP username");
+
+         Fixed_Output_RNG frng1("00112233445566778899AABBCCDDEEFF802802802802802802802802");
+         const std::vector<uint8_t> ctextf1 = session.encrypt(key, frng1);
+         Fixed_Output_RNG frng2("00112233445566778899AABBCCDDEEFF802802802802802802802802");
+         const std::vector<uint8_t> ctextf2 = session.encrypt(key, frng2);
+
+         result.test_eq("Only randomness comes from RNG", ctextf1, ctextf2);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_session", TLS_Session_Tests);
+
+#if defined(BOTAN_HAS_TLS_CBC)
+
+class TLS_CBC_Padding_Tests final : public Text_Based_Test
+   {
+   public:
+      TLS_CBC_Padding_Tests() : Text_Based_Test("tls_cbc_padding.vec", "Record,Output") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> record    = vars.get_req_bin("Record");
+         const size_t output = vars.get_req_sz("Output");
+
+         uint16_t res = Botan::TLS::check_tls_cbc_padding(record.data(), record.size());
+
+         Test::Result result("TLS CBC padding check");
+         result.test_eq("Expected", res, output);
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_cbc_padding", TLS_CBC_Padding_Tests);
+
+class TLS_CBC_Tests final : public Text_Based_Test
+   {
+   public:
+
+      class ZeroMac : public Botan::MessageAuthenticationCode
+         {
+         public:
+            ZeroMac(size_t mac_len) : m_mac_len(mac_len) {}
+
+            void clear() override {}
+
+            std::string name() const override { return "ZeroMac"; }
+            size_t output_length() const override { return m_mac_len; }
+
+            void add_data(const uint8_t[], size_t) override {}
+
+            void final_result(uint8_t out[]) override
+               {
+               for(size_t i = 0; i != m_mac_len; ++i)
+                  out[i] = 0;
+               }
+
+            Botan::Key_Length_Specification key_spec() const override
+               {
+               return Botan::Key_Length_Specification(0, 0, 1);
+               }
+
+            virtual MessageAuthenticationCode* clone() const override { return new ZeroMac(m_mac_len); }
+
+         private:
+            void key_schedule(const uint8_t[], size_t) override {}
+
+            size_t m_mac_len;
+         };
+
+      class Noop_Block_Cipher : public Botan::BlockCipher
+         {
+         public:
+            Noop_Block_Cipher(size_t bs) : m_bs(bs) {}
+
+            void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+               {
+               Botan::copy_mem(out, in, blocks * m_bs);
+               }
+
+            void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+               {
+               Botan::copy_mem(out, in, blocks * m_bs);
+               }
+
+            size_t block_size() const override { return m_bs; }
+            void clear() override { }
+            std::string name() const override { return "noop"; }
+
+            Botan::Key_Length_Specification key_spec() const override
+               {
+               return Botan::Key_Length_Specification(0, 0, 1);
+               }
+
+            virtual BlockCipher* clone() const override { return new Noop_Block_Cipher(m_bs); }
+         private:
+            void key_schedule(const uint8_t[], size_t) override {}
+
+            size_t m_bs;
+         };
+
+      TLS_CBC_Tests() : Text_Based_Test("tls_cbc.vec", "Blocksize,MACsize,Record,Valid") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("TLS CBC");
+
+         const size_t block_size = vars.get_req_sz("Blocksize");
+         const size_t mac_len = vars.get_req_sz("MACsize");
+         const std::vector<uint8_t> record = vars.get_req_bin("Record");
+         const bool is_valid = vars.get_req_sz("Valid") == 1;
+
+         // todo test permutations
+         bool encrypt_then_mac = false;
+
+         Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption tls_cbc(
+            std::unique_ptr<Botan::BlockCipher>(new Noop_Block_Cipher(block_size)),
+            std::unique_ptr<Botan::MessageAuthenticationCode>(new ZeroMac(mac_len)),
+            0, 0, Botan::TLS::Protocol_Version::TLS_V11, encrypt_then_mac);
+
+         tls_cbc.set_key(std::vector<uint8_t>(0));
+         std::vector<uint8_t> ad(13);
+         tls_cbc.set_associated_data(ad.data(), ad.size());
+
+         Botan::secure_vector<uint8_t> vec(record.begin(), record.end());
+
+         try
+            {
+            tls_cbc.finish(vec, 0);
+            if(is_valid)
+               result.test_success("Accepted valid TLS-CBC ciphertext");
+            else
+               result.test_failure("Accepted invalid TLS-CBC ciphertext");
+            }
+         catch(std::exception&)
+            {
+            if(is_valid)
+               result.test_failure("Rejected valid TLS-CBC ciphertext");
+            else
+               result.test_success("Accepted invalid TLS-CBC ciphertext");
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_cbc", TLS_CBC_Tests);
+
+#endif
+
+class Test_TLS_Alert_Strings : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("TLS::Alert::type_string");
+
+         const std::vector<Botan::TLS::Alert::Type> alert_types =
+            {
+               Botan::TLS::Alert::CLOSE_NOTIFY,
+               Botan::TLS::Alert::UNEXPECTED_MESSAGE,
+               Botan::TLS::Alert::BAD_RECORD_MAC,
+               Botan::TLS::Alert::DECRYPTION_FAILED,
+               Botan::TLS::Alert::RECORD_OVERFLOW,
+               Botan::TLS::Alert::DECOMPRESSION_FAILURE,
+               Botan::TLS::Alert::HANDSHAKE_FAILURE,
+               Botan::TLS::Alert::NO_CERTIFICATE,
+               Botan::TLS::Alert::BAD_CERTIFICATE,
+               Botan::TLS::Alert::UNSUPPORTED_CERTIFICATE,
+               Botan::TLS::Alert::CERTIFICATE_REVOKED,
+               Botan::TLS::Alert::CERTIFICATE_EXPIRED,
+               Botan::TLS::Alert::CERTIFICATE_UNKNOWN,
+               Botan::TLS::Alert::ILLEGAL_PARAMETER,
+               Botan::TLS::Alert::UNKNOWN_CA,
+               Botan::TLS::Alert::ACCESS_DENIED,
+               Botan::TLS::Alert::DECODE_ERROR,
+               Botan::TLS::Alert::DECRYPT_ERROR,
+               Botan::TLS::Alert::EXPORT_RESTRICTION,
+               Botan::TLS::Alert::PROTOCOL_VERSION,
+               Botan::TLS::Alert::INSUFFICIENT_SECURITY,
+               Botan::TLS::Alert::INTERNAL_ERROR,
+               Botan::TLS::Alert::INAPPROPRIATE_FALLBACK,
+               Botan::TLS::Alert::USER_CANCELED,
+               Botan::TLS::Alert::NO_RENEGOTIATION,
+               Botan::TLS::Alert::UNSUPPORTED_EXTENSION,
+               Botan::TLS::Alert::CERTIFICATE_UNOBTAINABLE,
+               Botan::TLS::Alert::UNRECOGNIZED_NAME,
+               Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE,
+               Botan::TLS::Alert::BAD_CERTIFICATE_HASH_VALUE,
+               Botan::TLS::Alert::UNKNOWN_PSK_IDENTITY,
+               Botan::TLS::Alert:: NO_APPLICATION_PROTOCOL,
+            };
+
+         std::set<std::string> seen;
+
+         for(auto alert : alert_types)
+            {
+            const std::string str = Botan::TLS::Alert(alert).type_string();
+            result.test_eq("No duplicate strings", seen.count(str), 0);
+            seen.insert(str);
+            }
+
+         Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66});
+
+         result.test_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66");
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_alert_strings", Test_TLS_Alert_Strings);
+
+class Test_TLS_Policy_Text : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("TLS Policy");
+
+         const std::vector<std::string> policies = { "default", "suiteb_128", "suiteb_192", "strict", "datagram", "bsi" };
+
+         for(std::string policy : policies)
+            {
+            const std::string from_policy_obj = tls_policy_string(policy);
+            std::string from_file = read_tls_policy(policy);
+
+#if !defined(BOTAN_HAS_CURVE_25519)
+            auto pos = from_file.find("x25519 ");
+            if(pos != std::string::npos)
+               from_file = from_file.replace(pos, 7, "");
+#endif
+
+            result.test_eq("Values for TLS " + policy + " policy", from_file, from_policy_obj);
+            }
+
+         return {result};
+         }
+
+   private:
+      std::string read_tls_policy(const std::string& policy_str)
+         {
+         const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt");
+
+         std::ifstream is(fspath.c_str());
+         if(!is.good())
+            {
+            throw Test_Error("Missing policy file " + fspath);
+            }
+
+         Botan::TLS::Text_Policy policy(is);
+         return policy.to_string();
+         }
+
+      std::string tls_policy_string(const std::string& policy_str)
+         {
+         std::unique_ptr<Botan::TLS::Policy> policy;
+         if(policy_str == "default")
+            {
+            policy.reset(new Botan::TLS::Policy);
+            }
+         else if(policy_str == "suiteb_128")
+            {
+            policy.reset(new Botan::TLS::NSA_Suite_B_128);
+            }
+         else if(policy_str == "suiteb_192")
+            {
+            policy.reset(new Botan::TLS::NSA_Suite_B_192);
+            }
+         else if(policy_str == "bsi")
+            {
+            policy.reset(new Botan::TLS::BSI_TR_02102_2);
+            }
+         else if(policy_str == "strict")
+            {
+            policy.reset(new Botan::TLS::Strict_Policy);
+            }
+         else if(policy_str == "datagram")
+            {
+            policy.reset(new Botan::TLS::Datagram_Policy);
+            }
+         else
+            {
+            throw Test_Error("Unknown TLS policy type '" + policy_str + "'");
+            }
+
+         return policy->to_string();
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_policy_text", Test_TLS_Policy_Text);
+
+class Test_TLS_Ciphersuites : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("TLS::Ciphersuite");
+
+         for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id)
+            {
+            const uint16_t csuite_id16 = static_cast<uint16_t>(csuite_id);
+            Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id16);
+
+            if(ciphersuite.valid())
+               {
+               result.test_eq("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id16), false);
+
+               if(ciphersuite.cbc_ciphersuite() == false)
+                  {
+                  result.test_eq("Expected MAC name for AEAD ciphersuites", ciphersuite.mac_algo(), "AEAD");
+                  }
+               else
+                  {
+                  result.test_eq("MAC algo and PRF algo same for CBC suites", ciphersuite.prf_algo(), ciphersuite.mac_algo());
+                  }
+
+               // TODO more tests here
+               }
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_ciphersuites", Test_TLS_Ciphersuites);
+
+class Test_TLS_Algo_Strings : public Test
+   {
+   public:
+
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_auth_method_strings());
+         results.push_back(test_kex_algo_strings());
+         results.push_back(test_tls_sig_method_strings());
+
+         return results;
+         }
+
+   private:
+      Test::Result test_tls_sig_method_strings()
+         {
+         Test::Result result("TLS::Signature_Scheme");
+
+         std::vector<Botan::TLS::Signature_Scheme> schemes = Botan::TLS::all_signature_schemes();
+
+         std::set<std::string> scheme_strs;
+         for(auto scheme : schemes)
+            {
+            std::string scheme_str = Botan::TLS::sig_scheme_to_string(scheme);
+
+            result.test_eq("Scheme strings unique", scheme_strs.count(scheme_str), 0);
+
+            scheme_strs.insert(scheme_str);
+            }
+
+         return result;
+         }
+
+      Test::Result test_auth_method_strings()
+         {
+         Test::Result result("TLS::Auth_Method");
+
+         const std::vector<Botan::TLS::Auth_Method> auth_methods({
+            Botan::TLS::Auth_Method::RSA,
+            Botan::TLS::Auth_Method::DSA,
+            Botan::TLS::Auth_Method::ECDSA,
+            Botan::TLS::Auth_Method::IMPLICIT,
+            Botan::TLS::Auth_Method::ANONYMOUS
+            });
+
+         for(Botan::TLS::Auth_Method meth : auth_methods)
+            {
+            std::string meth_str = Botan::TLS::auth_method_to_string(meth);
+            result.test_ne("Method string is not empty", meth_str, "");
+            Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str);
+            result.confirm("Decoded method matches", meth == meth2);
+            }
+
+         return result;
+         }
+
+      Test::Result test_kex_algo_strings()
+         {
+         Test::Result result("TLS::Kex_Algo");
+
+         const std::vector<Botan::TLS::Kex_Algo> kex_algos({
+            Botan::TLS::Kex_Algo::STATIC_RSA,
+            Botan::TLS::Kex_Algo::DH,
+            Botan::TLS::Kex_Algo::ECDH,
+            Botan::TLS::Kex_Algo::CECPQ1,
+            Botan::TLS::Kex_Algo::SRP_SHA,
+            Botan::TLS::Kex_Algo::PSK,
+            Botan::TLS::Kex_Algo::DHE_PSK,
+            Botan::TLS::Kex_Algo::ECDHE_PSK
+            });
+
+         for(Botan::TLS::Kex_Algo meth : kex_algos)
+            {
+            std::string meth_str = Botan::TLS::kex_method_to_string(meth);
+            result.test_ne("Method string is not empty", meth_str, "");
+            Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str);
+            result.confirm("Decoded method matches", meth == meth2);
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("tls_algo_strings", Test_TLS_Algo_Strings);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_tls_messages.cpp b/third_party/botan/src/tests/test_tls_messages.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tls_messages.cpp
@@ -0,0 +1,234 @@
+/*
+* (C) 2016 Juraj Somorovsky
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_TLS)
+   #include <exception>
+   #include <botan/hex.h>
+   #include <botan/mac.h>
+   #include <botan/tls_ciphersuite.h>
+   #include <botan/tls_handshake_msg.h>
+   #include <botan/tls_messages.h>
+   #include <botan/tls_alert.h>
+   #include <botan/loadstor.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_TLS)
+Test::Result test_hello_verify_request()
+   {
+   Test::Result result("hello_verify_request construction");
+
+   std::vector<uint8_t> test_data;
+   std::vector<uint8_t> key_data(32);
+   Botan::SymmetricKey sk(key_data);
+
+   // Compute cookie over an empty string with an empty test data
+   Botan::TLS::Hello_Verify_Request hfr(test_data, "", sk);
+
+   // Compute HMAC
+   std::unique_ptr<Botan::MessageAuthenticationCode> hmac(Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"));
+   hmac->set_key(sk);
+   hmac->update_be(uint64_t(0)); // length of client hello
+   hmac->update_be(uint64_t(0)); // length of client identity
+   std::vector<uint8_t> test = unlock(hmac->final());
+
+   result.test_eq("Cookie comparison", hfr.cookie(), test);
+   return result;
+   }
+
+class TLS_Message_Parsing_Test final : public Text_Based_Test
+   {
+   public:
+      TLS_Message_Parsing_Test()
+         : Text_Based_Test("tls", "Buffer,Protocol,Ciphersuite,AdditionalData,Name,Exception") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> buffer      = vars.get_req_bin("Buffer");
+         const std::vector<uint8_t> protocol    = vars.get_opt_bin("Protocol");
+         const std::vector<uint8_t> ciphersuite = vars.get_opt_bin("Ciphersuite");
+         const std::string exception            = vars.get_req_str("Exception");
+         const std::string expected_name        = vars.get_opt_str("Name", "");
+         const bool is_positive_test            = exception.empty();
+
+         Test::Result result(algo + " parsing");
+
+         if(is_positive_test)
+            {
+            try
+               {
+               if(algo == "cert_verify")
+                  {
+                  Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]);
+                  Botan::TLS::Certificate_Verify message(buffer, pv);
+                  }
+               else if(algo == "client_hello")
+                  {
+                  const std::string extensions = vars.get_req_str("AdditionalData");
+                  Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]);
+                  Botan::TLS::Client_Hello message(buffer);
+                  result.test_eq("Protocol version", message.version().to_string(), pv.to_string());
+                  std::vector<uint8_t> buf;
+                  for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types())
+                     {
+                     uint16_t u16type = static_cast<uint16_t>(type);
+                     buf.push_back(Botan::get_byte(0, u16type));
+                     buf.push_back(Botan::get_byte(1, u16type));
+                     }
+                  result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions);
+                  }
+               else if(algo == "hello_verify")
+                  {
+                  Botan::TLS::Hello_Verify_Request message(buffer);
+                  }
+               else if(algo == "hello_request")
+                  {
+                  Botan::TLS::Hello_Request message(buffer);
+                  }
+               else if(algo == "new_session_ticket")
+                  {
+                  Botan::TLS::New_Session_Ticket message(buffer);
+                  }
+               else if(algo == "server_hello")
+                  {
+                  const std::string extensions = vars.get_req_str("AdditionalData");
+                  Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]);
+                  Botan::TLS::Ciphersuite cs = Botan::TLS::Ciphersuite::by_id(Botan::make_uint16(ciphersuite[0], ciphersuite[1]));
+                  Botan::TLS::Server_Hello message(buffer);
+                  result.test_eq("Protocol version", message.version().to_string(), pv.to_string());
+                  result.confirm("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code()));
+                  std::vector<uint8_t> buf;
+                  for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types())
+                     {
+                     uint16_t u16type = static_cast<uint16_t>(type);
+                     buf.push_back(Botan::get_byte(0, u16type));
+                     buf.push_back(Botan::get_byte(1, u16type));
+                     }
+                  result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions);
+                  }
+               else if(algo == "alert")
+                  {
+                  Botan::secure_vector<uint8_t> sb(buffer.begin(), buffer.end());
+                  Botan::TLS::Alert message(sb);
+                  result.test_lt("Alert type vectors result to UNKNOWN_CA or ACCESS_DENIED, which is shorter than 15",
+                                 message.type_string().size(), 15);
+                  }
+               else if(algo == "cert_status")
+                  {
+                  Botan::TLS::Certificate_Status message(buffer);
+
+                  Botan::OCSP::Response resp(message.response());
+
+                  const std::vector<std::string> CNs = resp.signer_name().get_attribute("CN");
+
+                  // This is not requird by OCSP protocol, we are just using it as a test here
+                  if(result.test_eq("OCSP response has signer name", CNs.size(), 1))
+                     {
+                     result.test_eq("Expected name", CNs[0], expected_name);
+                     }
+                  }
+               else
+                  {
+                  throw Test_Error("Unknown message type " + algo + " in TLS parsing tests");
+                  }
+               result.test_success("Correct parsing");
+               }
+            catch(std::exception& e)
+               {
+               result.test_failure(e.what());
+               }
+            }
+         else
+            {
+            if(algo == "cert_verify")
+               {
+               Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]);
+               result.test_throws("invalid cert_verify input", exception, [&buffer, &pv]()
+                  {
+                  Botan::TLS::Certificate_Verify message(buffer, pv);
+                  });
+               }
+            else if(algo == "client_hello")
+               {
+               result.test_throws("invalid client_hello input", exception, [&buffer]()
+                  {
+                  Botan::TLS::Client_Hello message(buffer);
+                  });
+               }
+            else if(algo == "hello_verify")
+               {
+               result.test_throws("invalid hello_verify input", exception, [&buffer]()
+                  {
+                  Botan::TLS::Hello_Verify_Request message(buffer);
+                  });
+               }
+            else if(algo == "hello_request")
+               {
+               result.test_throws("invalid hello_request input", exception, [&buffer]()
+                  {
+                  Botan::TLS::Hello_Request message(buffer);
+                  });
+               }
+            else if(algo == "cert_status")
+               {
+               result.test_throws("invalid cert_status input", exception, [&buffer]()
+                  {
+                  Botan::TLS::Certificate_Status message(buffer);
+                  });
+               }
+            else if(algo == "new_session_ticket")
+               {
+               result.test_throws("invalid new_session_ticket input", exception, [&buffer]()
+                  {
+                  Botan::TLS::New_Session_Ticket message(buffer);
+                  });
+               }
+            else if(algo == "server_hello")
+               {
+               result.test_throws("invalid server_hello input", exception, [&buffer]()
+                  {
+                  Botan::TLS::Server_Hello message(buffer);
+                  });
+               }
+            else if(algo == "alert")
+               {
+               result.test_throws("invalid alert input", exception, [&buffer]()
+                  {
+                  Botan::secure_vector<uint8_t> sb(buffer.begin(), buffer.end());
+                  Botan::TLS::Alert message(sb);
+                  });
+               }
+            else
+               {
+               throw Test_Error("Unknown message type " + algo + " in TLS parsing tests");
+               }
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_hello_verify_request());
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_messages", TLS_Message_Parsing_Test);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_tls_stream_integration.cpp b/third_party/botan/src/tests/test_tls_stream_integration.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tls_stream_integration.cpp
@@ -0,0 +1,726 @@
+/*
+* TLS ASIO Stream Client-Server Interaction Test
+* (C) 2018-2020 Jack Lloyd
+*     2018-2020 Hannes Rantzsch, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_TLS_ASIO_STREAM) && defined(BOTAN_TARGET_OS_HAS_THREADS)
+
+// first version to be compatible with Networking TS (N4656) and boost::beast
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <functional>
+
+#include <botan/asio_stream.h>
+#include <botan/auto_rng.h>
+
+#include <boost/asio.hpp>
+
+#include "../cli/tls_helpers.h"  // for Basic_Credentials_Manager
+
+namespace {
+
+namespace net = boost::asio;
+
+using tcp = net::ip::tcp;
+using error_code = boost::system::error_code;
+using ssl_stream = Botan::TLS::Stream<net::ip::tcp::socket>;
+using namespace std::placeholders;
+using Result = Botan_Tests::Test::Result;
+
+static const auto k_timeout = std::chrono::seconds(3);
+static const auto k_endpoints = std::vector<tcp::endpoint> {tcp::endpoint{net::ip::make_address("127.0.0.1"), 8082}};
+
+enum { max_msg_length = 512 };
+
+static std::string server_cert() { return Botan_Tests::Test::data_dir() + "/x509/certstor/cert1.crt"; }
+static std::string server_key() { return Botan_Tests::Test::data_dir() + "/x509/certstor/key01.pem"; }
+
+class Timeout_Exception : public std::runtime_error
+   {
+      using std::runtime_error::runtime_error;
+   };
+
+class Side
+   {
+   public:
+      Side()
+         : m_credentials_manager(true, ""),
+           m_ctx(m_credentials_manager, m_rng, m_session_mgr, m_policy, Botan::TLS::Server_Information()) {}
+
+      Side(const std::string& server_cert, const std::string& server_key)
+         : m_credentials_manager(m_rng, server_cert, server_key),
+           m_ctx(m_credentials_manager, m_rng, m_session_mgr, m_policy, Botan::TLS::Server_Information()) {}
+
+      virtual ~Side() {}
+
+      net::mutable_buffer buffer() { return net::buffer(m_data, max_msg_length); }
+      net::mutable_buffer buffer(size_t size) { return net::buffer(m_data, size); }
+
+      std::string message() const { return std::string(m_data); }
+
+      // This is a CompletionCondition for net::async_read().
+      // Our toy protocol always expects a single \0-terminated string.
+      std::size_t received_zero_byte(const boost::system::error_code& error,
+                                     std::size_t bytes_transferred)
+         {
+         if(error)
+            {
+            return 0;
+            }
+
+         if(bytes_transferred > 0 && m_data[bytes_transferred - 1] == '\0')
+            {
+            return 0;
+            }
+
+         return max_msg_length - bytes_transferred;
+         }
+
+   protected:
+      Botan::AutoSeeded_RNG m_rng;
+      Basic_Credentials_Manager m_credentials_manager;
+      Botan::TLS::Session_Manager_Noop m_session_mgr;
+      Botan::TLS::Policy m_policy;
+      Botan::TLS::Context m_ctx;
+      std::unique_ptr<ssl_stream> m_stream;
+
+      char m_data[max_msg_length];
+   };
+
+class Result_Wrapper
+   {
+   public:
+      Result_Wrapper(net::io_context& ioc, const std::string& name) : m_timer(ioc), m_result(name) {}
+
+      Result& result() { return m_result; }
+
+      void set_timer(const std::string& msg)
+         {
+         m_timer.expires_after(k_timeout);
+         m_timer.async_wait([this, msg](const error_code &ec)
+            {
+            if(ec != net::error::operation_aborted)  // timer cancelled
+               {
+               m_result.test_failure(m_result.who() + ": timeout in " + msg);
+               throw Timeout_Exception(m_result.who());
+               }
+            });
+         }
+
+      void stop_timer()
+         {
+         m_timer.cancel();
+         }
+
+      void expect_success(const std::string& msg, const error_code& ec)
+         {
+         error_code success;
+         expect_ec(msg, success, ec);
+         }
+
+      void expect_ec(const std::string& msg, const error_code& expected, const error_code& ec)
+         {
+         if(ec != expected)
+            { m_result.test_failure(msg, "Unexpected error code: " + ec.message()); }
+         else
+            { m_result.test_success(msg); }
+         }
+
+      void confirm(const std::string& msg, bool condition)
+         {
+         m_result.confirm(msg, condition);
+         }
+
+      void test_failure(const std::string& msg)
+         {
+         m_result.test_failure(msg);
+         }
+
+   private:
+      net::system_timer m_timer;
+      Result m_result;
+   };
+
+class Server : public Side, public std::enable_shared_from_this<Server>
+   {
+   public:
+      Server(net::io_context& ioc)
+         : Side(server_cert(), server_key()),
+           m_acceptor(ioc),
+           m_result(ioc, "Server"),
+           m_short_read_expected(false) {}
+
+      // Control messages
+      // The messages below can be used by the test clients in order to configure the server's behavior during a test
+      // case.
+      //
+      // Tell the server that the next read should result in a StreamTruncated error
+      std::string expect_short_read_message = "SHORT_READ";
+      // Prepare the server for the test case "Shutdown No Response"
+      std::string prepare_shutdown_no_response_message = "SHUTDOWN_NOW";
+
+      void listen()
+         {
+         error_code ec;
+         const auto endpoint = k_endpoints.back();
+
+         m_acceptor.open(endpoint.protocol(), ec);
+         m_acceptor.set_option(net::socket_base::reuse_address(true), ec);
+         m_acceptor.bind(endpoint, ec);
+         m_acceptor.listen(net::socket_base::max_listen_connections, ec);
+
+         m_result.expect_success("listen", ec);
+
+         m_result.set_timer("accept");
+         m_acceptor.async_accept(std::bind(&Server::start_session, shared_from_this(), _1, _2));
+         }
+
+      void expect_short_read()
+         {
+         m_short_read_expected = true;
+         }
+
+      Result result() { return m_result.result(); }
+
+   private:
+      void start_session(const error_code& ec, tcp::socket socket)
+         {
+         // Note: If this fails with 'Operation canceled', it likely means the timer expired and the port is taken.
+         m_result.expect_success("accept", ec);
+
+         // Note: If this was a real server, we should create a new session (with its own stream) for each accepted
+         // connection. In this test we only have one connection.
+         m_stream = std::unique_ptr<ssl_stream>(new ssl_stream(std::move(socket), m_ctx));
+
+         m_result.set_timer("handshake");
+         m_stream->async_handshake(Botan::TLS::Connection_Side::SERVER,
+                                   std::bind(&Server::handle_handshake, shared_from_this(), _1));
+         }
+
+      void shutdown()
+         {
+         error_code shutdown_ec;
+         m_stream->shutdown(shutdown_ec);
+         m_result.expect_success("shutdown", shutdown_ec);
+         handle_write(error_code{});
+         }
+
+
+      void handle_handshake(const error_code& ec)
+         {
+         m_result.expect_success("handshake", ec);
+         handle_write(error_code{});
+         }
+
+      void handle_write(const error_code& ec)
+         {
+         m_result.expect_success("send_response", ec);
+         m_result.set_timer("read_message");
+         net::async_read(*m_stream, buffer(),
+                         std::bind(&Server::received_zero_byte, shared_from_this(), _1, _2),
+                         std::bind(&Server::handle_read, shared_from_this(), _1, _2));
+         }
+
+      void handle_read(const error_code& ec, size_t bytes_transferred=0)
+         {
+         if(m_short_read_expected)
+            {
+            m_result.expect_ec("received stream truncated error", Botan::TLS::StreamTruncated, ec);
+            quit();
+            return;
+            }
+
+         if(ec)
+            {
+            if(m_stream->shutdown_received())
+               {
+               m_result.expect_ec("received EOF after close_notify", net::error::eof, ec);
+               m_result.set_timer("shutdown");
+               m_stream->async_shutdown(std::bind(&Server::handle_shutdown, shared_from_this(), _1));
+               }
+            else
+               {
+               m_result.test_failure("Unexpected error code: " + ec.message());
+               quit();
+               }
+            return;
+            }
+
+         m_result.expect_success("read_message", ec);
+
+         if(message() == prepare_shutdown_no_response_message)
+            {
+            m_short_read_expected = true;
+            shutdown();
+            return;
+            }
+
+         if(message() == expect_short_read_message)
+            {
+            m_short_read_expected = true;
+            }
+
+         m_result.set_timer("send_response");
+         net::async_write(*m_stream, buffer(bytes_transferred),
+                          std::bind(&Server::handle_write, shared_from_this(), _1));
+         }
+
+      void handle_shutdown(const error_code& ec)
+         {
+         m_result.expect_success("shutdown", ec);
+         quit();
+         }
+
+      void quit()
+         {
+         m_result.stop_timer();
+         }
+
+   private:
+      tcp::acceptor m_acceptor;
+      Result_Wrapper m_result;
+      bool m_short_read_expected;
+   };
+
+class Client : public Side
+   {
+      static void accept_all(
+         const std::vector<Botan::X509_Certificate>&,
+         const std::vector<std::shared_ptr<const Botan::OCSP::Response>>&,
+         const std::vector<Botan::Certificate_Store*>&, Botan::Usage_Type,
+         const std::string&, const Botan::TLS::Policy&) {}
+
+   public:
+      Client(net::io_context& ioc)
+         : Side()
+         {
+         m_ctx.set_verify_callback(accept_all);
+         m_stream = std::unique_ptr<ssl_stream>(new ssl_stream(ioc, m_ctx));
+         }
+
+      ssl_stream& stream() {return *m_stream; }
+
+      void close_socket()
+         {
+         // Shutdown on TCP level before closing the socket for portable behavior. Otherwise the peer will see a
+         // connection_reset error rather than EOF on Windows.
+         // See the remark in
+         // https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/basic_stream_socket/close/overload1.html
+         m_stream->lowest_layer().shutdown(tcp::socket::shutdown_both);
+         m_stream->lowest_layer().close();
+         }
+
+   };
+
+#include <boost/asio/yield.hpp>
+
+class TestBase
+   {
+   public:
+      TestBase(net::io_context& ioc, std::shared_ptr<Server> server, const std::string& name)
+         : m_client(ioc),
+           m_server(server),
+           m_result(ioc, name) {}
+
+      virtual ~TestBase() = default;
+
+      virtual void finishAsynchronousWork() {}
+
+      Result result() { return m_result.result(); }
+
+   protected:
+      Client m_client;
+      std::shared_ptr<Server> m_server;
+      Result_Wrapper m_result;
+   };
+
+class Synchronous_Test : public TestBase
+   {
+   public:
+      using TestBase::TestBase;
+
+      void finishAsynchronousWork() override
+         {
+         m_client_thread.join();
+         }
+
+      void run(const error_code&)
+         {
+         m_client_thread = std::thread(std::bind(&Synchronous_Test::run_synchronous_client, this));
+         }
+
+      virtual void run_synchronous_client() = 0;
+
+   private:
+      std::thread m_client_thread;
+   };
+
+/* In this test case both parties perform the handshake, exchange a message, and do a full shutdown.
+ *
+ * The client expects the server to echo the same message it sent. The client then initiates the shutdown. The server is
+ * expected to receive a close_notify and complete its shutdown with an error_code Success, the client is expected to
+ * receive a close_notify and complete its shutdown with an error_code EOF.
+ */
+class Test_Conversation : public TestBase, public net::coroutine, public std::enable_shared_from_this<Test_Conversation>
+   {
+   public:
+      Test_Conversation(net::io_context& ioc, std::shared_ptr<Server> server)
+         : TestBase(ioc, server, "Test Conversation") {}
+
+      void run(const error_code& ec)
+         {
+         static auto test_case = &Test_Conversation::run;
+         const std::string message("Time is an illusion. Lunchtime doubly so.");
+
+         reenter(*this)
+            {
+            m_result.set_timer("connect");
+            yield net::async_connect(m_client.stream().lowest_layer(), k_endpoints,
+                                     std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("connect", ec);
+
+            m_result.set_timer("handshake");
+            yield m_client.stream().async_handshake(Botan::TLS::Connection_Side::CLIENT,
+                                                    std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("handshake", ec);
+
+            m_result.set_timer("send_message");
+            yield net::async_write(m_client.stream(),
+                                   net::buffer(message.c_str(), message.size() + 1), // including \0 termination
+                                   std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("send_message", ec);
+
+            m_result.set_timer("receive_response");
+            yield net::async_read(m_client.stream(),
+                                  m_client.buffer(),
+                                  std::bind(&Client::received_zero_byte, &m_client, _1, _2),
+                                  std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("receive_response", ec);
+            m_result.confirm("correct message", m_client.message() == message);
+
+            m_result.set_timer("shutdown");
+            yield m_client.stream().async_shutdown(std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("shutdown", ec);
+
+            m_result.set_timer("await close_notify");
+            yield net::async_read(m_client.stream(), m_client.buffer(),
+                                  std::bind(test_case, shared_from_this(), _1));
+            m_result.confirm("received close_notify", m_client.stream().shutdown_received());
+            m_result.expect_ec("closed with EOF", net::error::eof, ec);
+
+            m_result.stop_timer();
+            }
+         }
+   };
+
+class Test_Conversation_Sync : public Synchronous_Test
+   {
+   public:
+      Test_Conversation_Sync(net::io_context& ioc, std::shared_ptr<Server> server)
+         : Synchronous_Test(ioc, server, "Test Conversation Sync") {}
+
+      void run_synchronous_client() override
+         {
+         const std::string message("Time is an illusion. Lunchtime doubly so.");
+         error_code ec;
+
+         net::connect(m_client.stream().lowest_layer(), k_endpoints, ec);
+         m_result.expect_success("connect", ec);
+
+         m_client.stream().handshake(Botan::TLS::Connection_Side::CLIENT, ec);
+         m_result.expect_success("handshake", ec);
+
+         net::write(m_client.stream(),
+                    net::buffer(message.c_str(), message.size() + 1), // including \0 termination
+                    ec);
+         m_result.expect_success("send_message", ec);
+
+         net::read(m_client.stream(),
+                   m_client.buffer(),
+                   std::bind(&Client::received_zero_byte, &m_client, _1, _2),
+                   ec);
+         m_result.expect_success("receive_response", ec);
+         m_result.confirm("correct message", m_client.message() == message);
+
+         m_client.stream().shutdown(ec);
+         m_result.expect_success("shutdown", ec);
+
+         net::read(m_client.stream(), m_client.buffer(), ec);
+         m_result.confirm("received close_notify", m_client.stream().shutdown_received());
+         m_result.expect_ec("closed with EOF", net::error::eof, ec);
+         }
+   };
+
+/* In this test case the client shuts down the SSL connection, but does not wait for the server's response before
+ * closing the socket. Accordingly, it will not receive the server's close_notify alert. Instead, the async_read
+ * operation will be aborted. The server should be able to successfully shutdown nonetheless.
+ */
+class Test_Eager_Close : public TestBase, public net::coroutine, public std::enable_shared_from_this<Test_Eager_Close>
+   {
+   public:
+      Test_Eager_Close(net::io_context& ioc, std::shared_ptr<Server> server)
+         : TestBase(ioc, server, "Test Eager Close") {}
+
+      void run(const error_code& ec)
+         {
+         static auto test_case = &Test_Eager_Close::run;
+         reenter(*this)
+            {
+            m_result.set_timer("connect");
+            yield net::async_connect(m_client.stream().lowest_layer(), k_endpoints,
+                                     std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("connect", ec);
+
+            m_result.set_timer("handshake");
+            yield m_client.stream().async_handshake(Botan::TLS::Connection_Side::CLIENT,
+                                                    std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("handshake", ec);
+
+            m_result.set_timer("shutdown");
+            yield m_client.stream().async_shutdown(std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("shutdown", ec);
+            m_result.stop_timer();
+
+            m_client.close_socket();
+            m_result.confirm("did not receive close_notify", !m_client.stream().shutdown_received());
+            }
+         }
+   };
+
+class Test_Eager_Close_Sync : public Synchronous_Test
+   {
+   public:
+      Test_Eager_Close_Sync(net::io_context& ioc, std::shared_ptr<Server> server)
+         : Synchronous_Test(ioc, server, "Test Eager Close Sync") {}
+
+      void run_synchronous_client() override
+         {
+         error_code ec;
+
+         net::connect(m_client.stream().lowest_layer(), k_endpoints, ec);
+         m_result.expect_success("connect", ec);
+
+         m_client.stream().handshake(Botan::TLS::Connection_Side::CLIENT, ec);
+         m_result.expect_success("handshake", ec);
+
+         m_client.stream().shutdown(ec);
+         m_result.expect_success("shutdown", ec);
+
+         m_client.close_socket();
+         m_result.confirm("did not receive close_notify", !m_client.stream().shutdown_received());
+         }
+   };
+
+/* In this test case the client closes the socket without properly shutting down the connection.
+ * The server should see a StreamTruncated error.
+ */
+class Test_Close_Without_Shutdown
+   : public TestBase,
+     public net::coroutine,
+     public std::enable_shared_from_this<Test_Close_Without_Shutdown>
+   {
+   public:
+      Test_Close_Without_Shutdown(net::io_context& ioc, std::shared_ptr<Server> server)
+         : TestBase(ioc, server, "Test Close Without Shutdown") {}
+
+      void run(const error_code& ec)
+         {
+         static auto test_case = &Test_Close_Without_Shutdown::run;
+         reenter(*this)
+            {
+            m_result.set_timer("connect");
+            yield net::async_connect(m_client.stream().lowest_layer(), k_endpoints,
+                                     std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("connect", ec);
+
+            m_result.set_timer("handshake");
+            yield m_client.stream().async_handshake(Botan::TLS::Connection_Side::CLIENT,
+                                                    std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("handshake", ec);
+
+            // send the control message to configure the server to expect a short-read
+            m_result.set_timer("send expect_short_read message");
+            yield net::async_write(m_client.stream(),
+                                   net::buffer(m_server->expect_short_read_message.c_str(),
+                                               m_server->expect_short_read_message.size() + 1), // including \0 termination
+                                   std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("send expect_short_read message", ec);
+
+            // read the confirmation of the control message above
+            m_result.set_timer("receive_response");
+            yield net::async_read(m_client.stream(),
+                                  m_client.buffer(),
+                                  std::bind(&Client::received_zero_byte, &m_client, _1, _2),
+                                  std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("receive_response", ec);
+
+            m_result.stop_timer();
+
+            m_client.close_socket();
+            m_result.confirm("did not receive close_notify", !m_client.stream().shutdown_received());
+            }
+         }
+   };
+
+class Test_Close_Without_Shutdown_Sync : public Synchronous_Test
+   {
+   public:
+      Test_Close_Without_Shutdown_Sync(net::io_context& ioc, std::shared_ptr<Server> server)
+         : Synchronous_Test(ioc, server, "Test Close Without Shutdown Sync") {}
+
+      void run_synchronous_client() override
+         {
+         error_code ec;
+         net::connect(m_client.stream().lowest_layer(), k_endpoints, ec);
+         m_result.expect_success("connect", ec);
+
+         m_client.stream().handshake(Botan::TLS::Connection_Side::CLIENT, ec);
+         m_result.expect_success("handshake", ec);
+
+         m_server->expect_short_read();
+
+         m_client.close_socket();
+         m_result.confirm("did not receive close_notify", !m_client.stream().shutdown_received());
+         }
+   };
+
+/* In this test case the server shuts down the connection but the client doesn't send the corresponding close_notify
+ * response. Instead, it closes the socket immediately.
+ * The server should see a short-read error.
+ */
+class Test_No_Shutdown_Response : public TestBase, public net::coroutine,
+   public std::enable_shared_from_this<Test_No_Shutdown_Response>
+   {
+   public:
+      Test_No_Shutdown_Response(net::io_context& ioc, std::shared_ptr<Server> server)
+         : TestBase(ioc, server, "Test No Shutdown Response") {}
+
+      void run(const error_code& ec)
+         {
+         static auto test_case = &Test_No_Shutdown_Response::run;
+         reenter(*this)
+            {
+            m_result.set_timer("connect");
+            yield net::async_connect(m_client.stream().lowest_layer(), k_endpoints,
+                                     std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("connect", ec);
+
+            m_result.set_timer("handshake");
+            yield m_client.stream().async_handshake(Botan::TLS::Connection_Side::CLIENT,
+                                                    std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("handshake", ec);
+
+            // send a control message to make the server shut down
+            m_result.set_timer("send shutdown message");
+            yield net::async_write(m_client.stream(),
+                                   net::buffer(m_server->prepare_shutdown_no_response_message.c_str(),
+                                               m_server->prepare_shutdown_no_response_message.size() + 1), // including \0 termination
+                                   std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_success("send shutdown message", ec);
+
+            // read the server's close-notify message
+            m_result.set_timer("read close_notify");
+            yield net::async_read(m_client.stream(), m_client.buffer(),
+                                  std::bind(test_case, shared_from_this(), _1));
+            m_result.expect_ec("read gives EOF", net::error::eof, ec);
+            m_result.confirm("received close_notify", m_client.stream().shutdown_received());
+
+            m_result.stop_timer();
+
+            // close the socket rather than shutting down
+            m_client.close_socket();
+            }
+         }
+   };
+
+class Test_No_Shutdown_Response_Sync : public Synchronous_Test
+   {
+   public:
+      Test_No_Shutdown_Response_Sync(net::io_context& ioc, std::shared_ptr<Server> server)
+         : Synchronous_Test(ioc, server, "Test No Shutdown Response Sync") {}
+
+      void run_synchronous_client() override
+         {
+         error_code ec;
+         net::connect(m_client.stream().lowest_layer(), k_endpoints, ec);
+         m_result.expect_success("connect", ec);
+
+         m_client.stream().handshake(Botan::TLS::Connection_Side::CLIENT, ec);
+         m_result.expect_success("handshake", ec);
+
+         net::write(m_client.stream(),
+                    net::buffer(m_server->prepare_shutdown_no_response_message.c_str(),
+                                m_server->prepare_shutdown_no_response_message.size() + 1), // including \0 termination
+                    ec);
+         m_result.expect_success("send expect_short_read message", ec);
+
+         net::read(m_client.stream(), m_client.buffer(), ec);
+         m_result.expect_ec("read gives EOF", net::error::eof, ec);
+         m_result.confirm("received close_notify", m_client.stream().shutdown_received());
+
+         // close the socket rather than shutting down
+         m_client.close_socket();
+         }
+   };
+
+#include <boost/asio/unyield.hpp>
+
+template<typename TestT>
+void run_test_case(std::vector<Result>& results)
+   {
+   net::io_context ioc;
+
+   auto s = std::make_shared<Server>(ioc);
+   s->listen();
+
+   auto t = std::make_shared<TestT>(ioc, s);
+   t->run(error_code{});
+
+   try
+      {
+      ioc.run();
+      }
+   catch(Timeout_Exception&) { /* the test result will already contain a failure */ }
+
+   t->finishAsynchronousWork();
+
+   results.push_back(s->result());
+   results.push_back(t->result());
+   }
+
+}  // namespace
+
+namespace Botan_Tests {
+
+class Tls_Stream_Integration_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         run_test_case<Test_Conversation>(results);
+         run_test_case<Test_Eager_Close>(results);
+         run_test_case<Test_Close_Without_Shutdown>(results);
+         run_test_case<Test_No_Shutdown_Response>(results);
+         run_test_case<Test_Conversation_Sync>(results);
+         run_test_case<Test_Eager_Close_Sync>(results);
+         run_test_case<Test_Close_Without_Shutdown_Sync>(results);
+         run_test_case<Test_No_Shutdown_Response_Sync>(results);
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_stream_integration", Tls_Stream_Integration_Tests);
+
+}  // namespace Botan_Tests
+
+#endif // BOOST_VERSION
+#endif // BOTAN_HAS_TLS && BOTAN_HAS_BOOST_ASIO
diff --git a/third_party/botan/src/tests/test_tpm.cpp b/third_party/botan/src/tests/test_tpm.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tpm.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_TPM)
+   #include <botan/tpm.h>
+   #include <botan/uuid.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_TPM)
+
+class TPM_Tests final : public Test
+   {
+   public:
+
+      static std::string pin_cb(const std::string&)
+         {
+         return "123456";
+         }
+
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("TPM");
+
+         std::unique_ptr<Botan::TPM_Context> ctx;
+
+         try
+            {
+            ctx.reset(new Botan::TPM_Context(pin_cb, nullptr));
+            result.test_success("Created TPM context");
+            }
+         catch(Botan::TPM_Error& e)
+            {
+            result.test_success("Error conecting to TPM, skipping tests");
+            return {result};
+            }
+
+         try
+            {
+            result.test_note("TPM counter is " + std::to_string(ctx->current_counter()));
+
+            Botan::TPM_RNG rng(*ctx);
+            Botan::secure_vector<uint8_t> output = rng.random_vec(16);
+
+            result.test_ne("TPM RNG output not all zeros", output, std::vector<uint8_t>(16));
+
+            Botan::TPM_PrivateKey key(*ctx, 1024, nullptr);
+            result.test_success("Created TPM RSA key");
+
+            std::vector<uint8_t> blob = key.export_blob();
+
+            // Has to be at least as large as the key
+            result.test_gte("Blob size is reasonable", blob.size(), 1024 / 8);
+
+            std::vector<std::string> registered_keys = Botan::TPM_PrivateKey::registered_keys(*ctx);
+
+            for(auto url : registered_keys)
+               {
+               result.test_note("TPM registered key " + url);
+               }
+
+            // TODO export public key
+            // TODO generate a signature, verify it
+            // TODO test key registration mechanisms
+            }
+         catch(Botan::Exception& e)
+            {
+            result.test_failure("TPM problem", e.what());
+            }
+
+         return {result};
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("tpm", TPM_Tests);
+
+class UUID_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("UUID");
+
+         const Botan::UUID empty_uuid;
+
+         result.test_eq("Uninitialized UUID not valid", empty_uuid.is_valid(), false);
+
+         const Botan::UUID random_uuid(Test::rng());
+         result.test_eq("Random UUID is valid", empty_uuid.is_valid(), false);
+
+         const Botan::UUID binary_copy(random_uuid.binary_value());
+         result.confirm("UUID copied by binary equals original", random_uuid == binary_copy);
+
+         std::string uuid_str = random_uuid.to_string();
+         result.test_eq("UUID string in expected format", uuid_str.size(), 36);
+
+         const Botan::UUID string_copy(random_uuid.to_string());
+         result.confirm("UUID copied by string equals original", random_uuid == string_copy);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("tpm_uuid", UUID_Tests);
+
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_tss.cpp b/third_party/botan/src/tests/test_tss.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_tss.cpp
@@ -0,0 +1,163 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+   #include <botan/tss.h>
+   #include "test_rng.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+
+class TSS_Recovery_Tests final : public Text_Based_Test
+   {
+   public:
+      TSS_Recovery_Tests() : Text_Based_Test("tss/recovery.vec", "N,M,Shares,Recovered") {}
+
+      Test::Result run_one_test(const std::string& header, const VarMap& vars) override
+         {
+         Test::Result result("TSS");
+
+         const std::vector<uint8_t> input = vars.get_req_bin("Recovered");
+         const size_t N = vars.get_req_sz("N");
+         const size_t M = vars.get_req_sz("M");
+         const std::vector<std::vector<uint8_t>> expected_shares = vars.get_req_bin_list("Shares");
+
+         try
+            {
+            std::vector<Botan::RTSS_Share> shares;
+
+            for(auto&& v : expected_shares)
+               {
+               shares.push_back(Botan::RTSS_Share(v.data(), v.size()));
+               }
+
+            auto reconstructed_secret_all = Botan::RTSS_Share::reconstruct(shares);
+            result.test_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input);
+
+            if(header == "Invalid")
+               result.test_failure("Invalid shares should not result in recovery");
+
+            if(N != M)
+               {
+               while(shares.size() > M)
+                  {
+                  size_t to_remove = Test::rng().next_byte() % shares.size();
+                  shares.erase(shares.begin() + to_remove);
+                  try
+                     {
+                     auto reconstructed_secret = Botan::RTSS_Share::reconstruct(shares);
+                     result.test_eq("Reconstructed secret correctly from reduced shares", reconstructed_secret, input);
+                     }
+                  catch(Botan::Decoding_Error&)
+                     {
+                     result.test_failure("Reconstruction failed with share count " + std::to_string(shares.size()));
+                     }
+                  }
+               }
+
+            }
+         catch(std::exception& e)
+            {
+
+            if(header == "Valid")
+               result.test_failure("Valid TSS failed to recover", e.what());
+            else
+               result.test_success("Invalid TSS rejected as expected");
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tss_recovery", TSS_Recovery_Tests);
+
+class TSS_Generation_Tests final : public Text_Based_Test
+   {
+   public:
+      TSS_Generation_Tests() : Text_Based_Test("tss/generation.vec", "Input,RNG,Hash,Id,N,M,Shares") {}
+
+      static size_t tss_hash_len(const std::string& hash)
+         {
+         if(hash == "None")
+            return 0;
+         else if(hash == "SHA-1")
+            return 20;
+         else if(hash == "SHA-256")
+            return 32;
+         else
+            throw Test_Error("Unknown TSS hash algorithm " + hash);
+         }
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("TSS");
+
+         const std::vector<uint8_t> input = vars.get_req_bin("Input");
+         const std::vector<uint8_t> id = vars.get_req_bin("Id");
+         const std::vector<uint8_t> rng_data = vars.get_req_bin("RNG");
+         const uint8_t N = vars.get_req_u8("N");
+         const uint8_t M = vars.get_req_u8("M");
+         const std::string hash = vars.get_req_str("Hash");
+         const std::vector<std::vector<uint8_t>> expected_shares = vars.get_req_bin_list("Shares");
+
+         if(expected_shares.size() != N)
+            throw Test_Error("Invalid test data for TSS share count != N");
+
+         if(rng_data.size() != (input.size() + tss_hash_len(hash)) * (M-1))
+            throw Test_Error("Invalid test data for TSS share bad RNG input size");
+
+         Fixed_Output_RNG fixed_rng(rng_data);
+
+         std::vector<Botan::RTSS_Share> shares =
+            Botan::RTSS_Share::split(M, N, input.data(), static_cast<uint16_t>(input.size()),
+                                     id, hash, fixed_rng);
+
+         result.test_eq("Expected number of shares", shares.size(), N);
+
+         for(size_t i = 0; i != N; ++i)
+            {
+            result.test_eq("Expected share", shares[i].data(), expected_shares[i]);
+            }
+
+         auto reconstructed_secret_all = Botan::RTSS_Share::reconstruct(shares);
+         result.test_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input);
+
+         if(N != M)
+            {
+            while(shares.size() > M)
+               {
+               size_t to_remove = Test::rng().next_byte() % shares.size();
+               shares.erase(shares.begin() + to_remove);
+
+               try
+                  {
+                  auto reconstructed_secret = Botan::RTSS_Share::reconstruct(shares);
+                  result.test_eq("Reconstructed secret correctly from reduced shares", reconstructed_secret, input);
+                  }
+               catch(Botan::Decoding_Error&)
+                  {
+                  result.test_failure("Reconstruction failed with share count " + std::to_string(shares.size()));
+                  }
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tss_generation", TSS_Generation_Tests);
+
+#endif // BOTAN_HAS_THRESHOLD_SECRET_SHARING
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_uri.cpp b/third_party/botan/src/tests/test_uri.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_uri.cpp
@@ -0,0 +1,120 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_SOCKETS) && (defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2))
+
+#include <botan/internal/uri.h>
+
+namespace Botan_Tests {
+
+class URI_Tests final : public Test
+   {
+      void test_uri_ctor(std::vector<Test::Result>& results)
+         {
+         Test::Result result("uri constructors");
+         Botan::URI uri(Botan::URI::Type::Domain, "localhost", 80);
+         result.confirm("type", uri.type == Botan::URI::Type::Domain);
+         result.test_eq("host", uri.host, "localhost");
+         result.confirm("post", uri.port == 80);
+         results.push_back(result);
+         }
+
+      void test_uri_tostring(std::vector<Test::Result>& results)
+         {
+         Test::Result result("uri to_string");
+
+         result.test_eq("domain", Botan::URI(Botan::URI::Type::Domain, "localhost", 80).to_string(), "localhost:80");
+         result.test_eq("IPv4", Botan::URI(Botan::URI::Type::IPv4, "192.168.1.1", 80).to_string(), "192.168.1.1:80");
+         result.test_eq("IPv6", Botan::URI(Botan::URI::Type::IPv6, "::1", 80).to_string(), "[::1]:80");
+         result.test_eq("IPv6 no port", Botan::URI(Botan::URI::Type::IPv6, "::1", 0).to_string(), "::1");
+         result.test_throws("invalid", []() {Botan::URI(Botan::URI::Type::NotSet, "", 0).to_string();});
+
+         results.push_back(result);
+         }
+
+      void test_uri_factories(std::vector<Test::Result>& results)
+         {
+         Test::Result result("uri factories");
+
+         struct
+            {
+            std::string uri;
+            Botan::URI::Type type;
+            std::string host;
+            unsigned port;
+            } tests []
+            {
+               {"localhost::80", Botan::URI::Type::NotSet, {}, 0},
+               {"localhost:70000", Botan::URI::Type::NotSet, {}, 0},
+               {"[::1]:a", Botan::URI::Type::NotSet, {}, 0},
+               {"[::1]:70000", Botan::URI::Type::NotSet, {}, 0},
+               {"localhost:80", Botan::URI::Type::Domain, "localhost", 80},
+               {"www.example.com", Botan::URI::Type::Domain, "www.example.com", 0},
+               {"192.168.1.1", Botan::URI::Type::IPv4, "192.168.1.1", 0},
+               {"192.168.1.1:34567", Botan::URI::Type::IPv4, "192.168.1.1", 34567},
+               {"[::1]:61234", Botan::URI::Type::IPv6, "::1", 61234},
+            };
+
+         for(const auto& t : tests)
+            {
+            auto test_URI = [&result](const Botan::URI& uri, const std::string& host, const unsigned port)
+               {
+               result.test_eq("host", uri.host, host);
+               result.confirm("port", uri.port==port);
+               };
+
+            if(t.type!=Botan::URI::Type::IPv4)
+               result.test_throws("invalid", [&t]() {Botan::URI::fromIPv4(t.uri);});
+            if(t.type!=Botan::URI::Type::IPv6)
+               result.test_throws("invalid", [&t]() {Botan::URI::fromIPv6(t.uri);});
+            if(t.type!=Botan::URI::Type::Domain)
+               result.test_throws("invalid", [&t]() {Botan::URI::fromDomain(t.uri);});
+            if(t.type==Botan::URI::Type::NotSet)
+               {
+               result.test_throws("invalid", [&t]() {Botan::URI::fromAny(t.uri);});
+               }
+            else
+               {
+               const auto any = Botan::URI::fromAny(t.uri);
+               result.confirm("type any", any.type == t.type);
+               test_URI(any, t.host, t.port);
+               if(t.type == Botan::URI::Type::Domain)
+                  { test_URI(Botan::URI::fromDomain(t.uri), t.host, t.port); }
+               else if(t.type == Botan::URI::Type::IPv4)
+                  { test_URI(Botan::URI::fromIPv4(t.uri), t.host, t.port); }
+               else if(t.type == Botan::URI::Type::IPv6)
+                  { test_URI(Botan::URI::fromIPv6(t.uri), t.host, t.port); }
+               }
+            }
+
+         //since GCC 4.8 does not support regex this would possibly be acceped as valid domains,
+         //but we just want to test IPv6 parsing, so the test needs to be individual
+         result.test_throws("invalid IPv6", [](){ Botan::URI::fromIPv6("]"); });
+         result.test_throws("invalid IPv6", [](){ Botan::URI::fromIPv6("[::1]1"); });
+
+         results.push_back(result);
+         }
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         test_uri_ctor(results);
+         test_uri_tostring(results);
+         test_uri_factories(results);
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("uri", URI_Tests);
+
+}  // namespace Botan_Tests
+
+#endif
diff --git a/third_party/botan/src/tests/test_utils.cpp b/third_party/botan/src/tests/test_utils.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_utils.cpp
@@ -0,0 +1,1086 @@
+/*
+* (C) 2015,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#define BOTAN_NO_DEPRECATED_WARNINGS
+
+#include "tests.h"
+#include <functional>
+#include <ctime>
+#include <botan/loadstor.h>
+#include <botan/calendar.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/cpuid.h>
+#include <botan/charset.h>
+#include <botan/parsing.h>
+#include <botan/version.h>
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+   #include <botan/base64.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+   #include <botan/base32.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+   #include <botan/base58.h>
+#endif
+
+#if defined(BOTAN_HAS_POLY_DBL)
+   #include <botan/internal/poly_dbl.h>
+#endif
+
+#if defined(BOTAN_HAS_UUID)
+   #include <botan/uuid.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+class Utility_Function_Tests final : public Text_Based_Test
+   {
+   public:
+      Utility_Function_Tests() : Text_Based_Test("util.vec", "In1,In2,Out") {}
+
+      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+         {
+         Test::Result result("Util " + algo);
+
+         if(algo == "round_up")
+            {
+            const size_t x = vars.get_req_sz("In1");
+            const size_t to = vars.get_req_sz("In2");
+
+            result.test_eq(algo, Botan::round_up(x, to), vars.get_req_sz("Out"));
+
+            try
+               {
+               Botan::round_up(x, 0);
+               result.test_failure("round_up did not reject invalid input");
+               }
+            catch(std::exception&) {}
+            }
+         else if(algo == "round_down")
+            {
+            const size_t x = vars.get_req_sz("In1");
+            const size_t to = vars.get_req_sz("In2");
+
+            result.test_eq(algo, Botan::round_down<size_t>(x, to), vars.get_req_sz("Out"));
+            result.test_eq(algo, Botan::round_down<size_t>(x, 0), x);
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_loadstore());
+
+         return results;
+         }
+
+      Test::Result test_loadstore()
+         {
+         Test::Result result("Util load/store");
+
+         const std::vector<uint8_t> membuf =
+            Botan::hex_decode("00112233445566778899AABBCCDDEEFF");
+         const uint8_t* mem = membuf.data();
+
+         const uint16_t in16 = 0x1234;
+         const uint32_t in32 = 0xA0B0C0D0;
+         const uint64_t in64 = 0xABCDEF0123456789;
+
+         result.test_is_eq<uint8_t>(Botan::get_byte(0, in32), 0xA0);
+         result.test_is_eq<uint8_t>(Botan::get_byte(1, in32), 0xB0);
+         result.test_is_eq<uint8_t>(Botan::get_byte(2, in32), 0xC0);
+         result.test_is_eq<uint8_t>(Botan::get_byte(3, in32), 0xD0);
+
+         result.test_is_eq<uint16_t>(Botan::make_uint16(0xAA, 0xBB), 0xAABB);
+         result.test_is_eq<uint32_t>(Botan::make_uint32(0x01, 0x02, 0x03, 0x04), 0x01020304);
+
+         result.test_is_eq<uint16_t>(Botan::load_be<uint16_t>(mem, 0), 0x0011);
+         result.test_is_eq<uint16_t>(Botan::load_be<uint16_t>(mem, 1), 0x2233);
+         result.test_is_eq<uint16_t>(Botan::load_be<uint16_t>(mem, 2), 0x4455);
+         result.test_is_eq<uint16_t>(Botan::load_be<uint16_t>(mem, 3), 0x6677);
+
+         result.test_is_eq<uint16_t>(Botan::load_le<uint16_t>(mem, 0), 0x1100);
+         result.test_is_eq<uint16_t>(Botan::load_le<uint16_t>(mem, 1), 0x3322);
+         result.test_is_eq<uint16_t>(Botan::load_le<uint16_t>(mem, 2), 0x5544);
+         result.test_is_eq<uint16_t>(Botan::load_le<uint16_t>(mem, 3), 0x7766);
+
+         result.test_is_eq<uint32_t>(Botan::load_be<uint32_t>(mem, 0), 0x00112233);
+         result.test_is_eq<uint32_t>(Botan::load_be<uint32_t>(mem, 1), 0x44556677);
+         result.test_is_eq<uint32_t>(Botan::load_be<uint32_t>(mem, 2), 0x8899AABB);
+         result.test_is_eq<uint32_t>(Botan::load_be<uint32_t>(mem, 3), 0xCCDDEEFF);
+
+         result.test_is_eq<uint32_t>(Botan::load_le<uint32_t>(mem, 0), 0x33221100);
+         result.test_is_eq<uint32_t>(Botan::load_le<uint32_t>(mem, 1), 0x77665544);
+         result.test_is_eq<uint32_t>(Botan::load_le<uint32_t>(mem, 2), 0xBBAA9988);
+         result.test_is_eq<uint32_t>(Botan::load_le<uint32_t>(mem, 3), 0xFFEEDDCC);
+
+         result.test_is_eq<uint64_t>(Botan::load_be<uint64_t>(mem, 0), 0x0011223344556677);
+         result.test_is_eq<uint64_t>(Botan::load_be<uint64_t>(mem, 1), 0x8899AABBCCDDEEFF);
+
+         result.test_is_eq<uint64_t>(Botan::load_le<uint64_t>(mem, 0), 0x7766554433221100);
+         result.test_is_eq<uint64_t>(Botan::load_le<uint64_t>(mem, 1), 0xFFEEDDCCBBAA9988);
+
+         // Check misaligned loads:
+         result.test_is_eq<uint16_t>(Botan::load_be<uint16_t>(mem + 1, 0), 0x1122);
+         result.test_is_eq<uint16_t>(Botan::load_le<uint16_t>(mem + 3, 0), 0x4433);
+
+         result.test_is_eq<uint32_t>(Botan::load_be<uint32_t>(mem + 1, 1), 0x55667788);
+         result.test_is_eq<uint32_t>(Botan::load_le<uint32_t>(mem + 3, 1), 0xAA998877);
+
+         result.test_is_eq<uint64_t>(Botan::load_be<uint64_t>(mem + 1, 0), 0x1122334455667788);
+         result.test_is_eq<uint64_t>(Botan::load_le<uint64_t>(mem + 7, 0), 0xEEDDCCBBAA998877);
+         result.test_is_eq<uint64_t>(Botan::load_le<uint64_t>(mem + 5, 0), 0xCCBBAA9988776655);
+
+         uint8_t outbuf[16] = { 0 };
+
+         for(size_t offset = 0; offset != 7; ++offset)
+            {
+            uint8_t* out = outbuf + offset;
+
+            Botan::store_be(in16, out);
+            result.test_is_eq<uint8_t>(out[0], 0x12);
+            result.test_is_eq<uint8_t>(out[1], 0x34);
+
+            Botan::store_le(in16, out);
+            result.test_is_eq<uint8_t>(out[0], 0x34);
+            result.test_is_eq<uint8_t>(out[1], 0x12);
+
+            Botan::store_be(in32, out);
+            result.test_is_eq<uint8_t>(out[0], 0xA0);
+            result.test_is_eq<uint8_t>(out[1], 0xB0);
+            result.test_is_eq<uint8_t>(out[2], 0xC0);
+            result.test_is_eq<uint8_t>(out[3], 0xD0);
+
+            Botan::store_le(in32, out);
+            result.test_is_eq<uint8_t>(out[0], 0xD0);
+            result.test_is_eq<uint8_t>(out[1], 0xC0);
+            result.test_is_eq<uint8_t>(out[2], 0xB0);
+            result.test_is_eq<uint8_t>(out[3], 0xA0);
+
+            Botan::store_be(in64, out);
+            result.test_is_eq<uint8_t>(out[0], 0xAB);
+            result.test_is_eq<uint8_t>(out[1], 0xCD);
+            result.test_is_eq<uint8_t>(out[2], 0xEF);
+            result.test_is_eq<uint8_t>(out[3], 0x01);
+            result.test_is_eq<uint8_t>(out[4], 0x23);
+            result.test_is_eq<uint8_t>(out[5], 0x45);
+            result.test_is_eq<uint8_t>(out[6], 0x67);
+            result.test_is_eq<uint8_t>(out[7], 0x89);
+
+            Botan::store_le(in64, out);
+            result.test_is_eq<uint8_t>(out[0], 0x89);
+            result.test_is_eq<uint8_t>(out[1], 0x67);
+            result.test_is_eq<uint8_t>(out[2], 0x45);
+            result.test_is_eq<uint8_t>(out[3], 0x23);
+            result.test_is_eq<uint8_t>(out[4], 0x01);
+            result.test_is_eq<uint8_t>(out[5], 0xEF);
+            result.test_is_eq<uint8_t>(out[6], 0xCD);
+            result.test_is_eq<uint8_t>(out[7], 0xAB);
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("util", Utility_Function_Tests);
+
+class CT_Mask_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("CT utils");
+
+         result.test_eq_sz("CT::is_zero8", Botan::CT::Mask<uint8_t>::is_zero(0).value(), 0xFF);
+         result.test_eq_sz("CT::is_zero8", Botan::CT::Mask<uint8_t>::is_zero(1).value(), 0x00);
+         result.test_eq_sz("CT::is_zero8", Botan::CT::Mask<uint8_t>::is_zero(0xFF).value(), 0x00);
+
+         result.test_eq_sz("CT::is_zero16", Botan::CT::Mask<uint16_t>::is_zero(0).value(), 0xFFFF);
+         result.test_eq_sz("CT::is_zero16", Botan::CT::Mask<uint16_t>::is_zero(1).value(), 0x0000);
+         result.test_eq_sz("CT::is_zero16", Botan::CT::Mask<uint16_t>::is_zero(0xFF).value(), 0x0000);
+
+         result.test_eq_sz("CT::is_zero32", Botan::CT::Mask<uint32_t>::is_zero(0).value(), 0xFFFFFFFF);
+         result.test_eq_sz("CT::is_zero32", Botan::CT::Mask<uint32_t>::is_zero(1).value(), 0x00000000);
+         result.test_eq_sz("CT::is_zero32", Botan::CT::Mask<uint32_t>::is_zero(0xFF).value(), 0x00000000);
+
+         result.test_eq_sz("CT::is_less8", Botan::CT::Mask<uint8_t>::is_lt(0, 1).value(), 0xFF);
+         result.test_eq_sz("CT::is_less8", Botan::CT::Mask<uint8_t>::is_lt(1, 0).value(), 0x00);
+         result.test_eq_sz("CT::is_less8", Botan::CT::Mask<uint8_t>::is_lt(0xFF, 5).value(), 0x00);
+
+         result.test_eq_sz("CT::is_less16", Botan::CT::Mask<uint16_t>::is_lt(0, 1).value(), 0xFFFF);
+         result.test_eq_sz("CT::is_less16", Botan::CT::Mask<uint16_t>::is_lt(1, 0).value(), 0x0000);
+         result.test_eq_sz("CT::is_less16", Botan::CT::Mask<uint16_t>::is_lt(0xFFFF, 5).value(), 0x0000);
+
+         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(0, 1).value(), 0xFFFFFFFF);
+         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(1, 0).value(), 0x00000000);
+         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(0xFFFF5, 5).value(), 0x00000000);
+         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(0xFFFFFFFF, 5).value(), 0x00000000);
+         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(5, 0xFFFFFFFF).value(), 0xFFFFFFFF);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("ct_utils", CT_Mask_Tests);
+
+class BitOps_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_power_of_2());
+         results.push_back(test_ctz());
+         results.push_back(test_sig_bytes());
+
+         return results;
+         }
+   private:
+      template<typename T>
+      void test_ctz(Test::Result& result, T val, size_t expected)
+         {
+         result.test_eq("ctz(" + std::to_string(val) + ")", Botan::ctz<T>(val), expected);
+         }
+
+      Test::Result test_ctz()
+         {
+         Test::Result result("ctz");
+         test_ctz<uint32_t>(result, 0, 32);
+         test_ctz<uint32_t>(result, 1, 0);
+         test_ctz<uint32_t>(result, 0x80, 7);
+         test_ctz<uint32_t>(result, 0x8000000, 27);
+         test_ctz<uint32_t>(result, 0x8100000, 20);
+         test_ctz<uint32_t>(result, 0x80000000, 31);
+
+         return result;
+         }
+
+      template<typename T>
+      void test_sig_bytes(Test::Result& result, T val, size_t expected)
+         {
+         result.test_eq("significant_bytes(" + std::to_string(val) + ")",
+                        Botan::significant_bytes<T>(val), expected);
+         }
+
+      Test::Result test_sig_bytes()
+         {
+         Test::Result result("significant_bytes");
+         test_sig_bytes<uint32_t>(result, 0, 0);
+         test_sig_bytes<uint32_t>(result, 1, 1);
+         test_sig_bytes<uint32_t>(result, 0x80, 1);
+         test_sig_bytes<uint32_t>(result, 255, 1);
+         test_sig_bytes<uint32_t>(result, 256, 2);
+         test_sig_bytes<uint32_t>(result, 65535, 2);
+         test_sig_bytes<uint32_t>(result, 65536, 3);
+         test_sig_bytes<uint32_t>(result, 0x80000000, 4);
+
+         test_sig_bytes<uint64_t>(result, 0, 0);
+         test_sig_bytes<uint64_t>(result, 1, 1);
+         test_sig_bytes<uint64_t>(result, 0x80, 1);
+         test_sig_bytes<uint64_t>(result, 256, 2);
+         test_sig_bytes<uint64_t>(result, 0x80000000, 4);
+         test_sig_bytes<uint64_t>(result, 0x100000000, 5);
+
+         return result;
+         }
+
+      template<typename T>
+      void test_power_of_2(Test::Result& result, T val, bool expected)
+         {
+         result.test_eq("power_of_2(" + std::to_string(val) + ")", Botan::is_power_of_2<T>(val), expected);
+         }
+
+      Test::Result test_power_of_2()
+         {
+         Test::Result result("is_power_of_2");
+
+         test_power_of_2<uint32_t>(result, 0, false);
+         test_power_of_2<uint32_t>(result, 1, false);
+         test_power_of_2<uint32_t>(result, 2, true);
+         test_power_of_2<uint32_t>(result, 3, false);
+         test_power_of_2<uint32_t>(result, 0x8000, true);
+         test_power_of_2<uint32_t>(result, 0x8001, false);
+         test_power_of_2<uint32_t>(result, 0x8000000, true);
+
+         test_power_of_2<uint64_t>(result, 0, false);
+         test_power_of_2<uint64_t>(result, 1, false);
+         test_power_of_2<uint64_t>(result, 2, true);
+         test_power_of_2<uint64_t>(result, 3, false);
+         test_power_of_2<uint64_t>(result, 0x8000, true);
+         test_power_of_2<uint64_t>(result, 0x8001, false);
+         test_power_of_2<uint64_t>(result, 0x8000000, true);
+         test_power_of_2<uint64_t>(result, 0x100000000000, true);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("bit_ops", BitOps_Tests);
+
+#if defined(BOTAN_HAS_POLY_DBL)
+
+class Poly_Double_Tests final : public Text_Based_Test
+   {
+   public:
+      Poly_Double_Tests() : Text_Based_Test("poly_dbl.vec", "In,Out") {}
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("Polynomial doubling");
+         const std::vector<uint8_t> in  = vars.get_req_bin("In");
+         const std::vector<uint8_t> out = vars.get_req_bin("Out");
+
+         std::vector<uint8_t> b = in;
+         Botan::poly_double_n(b.data(), b.size());
+
+         result.test_eq("Expected value", b, out);
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("poly_dbl", Poly_Double_Tests);
+
+#endif
+
+class Version_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("Versions");
+
+         result.confirm("Version datestamp matches macro",
+                        Botan::version_datestamp() == BOTAN_VERSION_DATESTAMP);
+
+         const char* version_cstr = Botan::version_cstr();
+         std::string version_str = Botan::version_string();
+         result.test_eq("Same version string", version_str, std::string(version_cstr));
+
+         const char* sversion_cstr = Botan::short_version_cstr();
+         std::string sversion_str = Botan::short_version_string();
+         result.test_eq("Same short version string", sversion_str, std::string(sversion_cstr));
+
+         std::string expected_sversion =
+            std::to_string(BOTAN_VERSION_MAJOR) + "." +
+            std::to_string(BOTAN_VERSION_MINOR) + "." +
+            std::to_string(BOTAN_VERSION_PATCH);
+
+#if defined(BOTAN_VERSION_SUFFIX)
+         expected_sversion += BOTAN_VERSION_SUFFIX_STR;
+#endif
+
+         result.test_eq("Short version string has expected format",
+                        sversion_str, expected_sversion);
+
+         const std::string version_check_ok =
+            Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
+
+         result.confirm("Correct version no warning", version_check_ok.empty());
+
+         const std::string version_check_bad =
+            Botan::runtime_version_check(1, 19, 42);
+
+         const std::string expected_error =
+            "Warning: linked version (" + sversion_str + ") does not match version built against (1.19.42)\n";
+
+         result.test_eq("Expected warning text", version_check_bad, expected_error);
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("versioning", Version_Tests);
+
+class Date_Format_Tests final : public Text_Based_Test
+   {
+   public:
+      Date_Format_Tests() : Text_Based_Test("dates.vec", "Date") {}
+
+      std::vector<uint32_t> parse_date(const std::string& s)
+         {
+         const std::vector<std::string> parts = Botan::split_on(s, ',');
+         if(parts.size() != 6)
+            {
+            throw Test_Error("Bad date format '" + s + "'");
+            }
+
+         std::vector<uint32_t> u32s;
+         for(auto const& sub : parts)
+            {
+            u32s.push_back(Botan::to_u32bit(sub));
+            }
+         return u32s;
+         }
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         const std::string date_str = vars.get_req_str("Date");
+         Test::Result result("Date parsing");
+
+         const std::vector<uint32_t> d = parse_date(date_str);
+
+         if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t")
+            {
+            Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]);
+            result.test_is_eq(date_str + " year", c.get_year(), d[0]);
+            result.test_is_eq(date_str + " month", c.get_month(), d[1]);
+            result.test_is_eq(date_str + " day", c.get_day(), d[2]);
+            result.test_is_eq(date_str + " hour", c.get_hour(), d[3]);
+            result.test_is_eq(date_str + " minute", c.get_minutes(), d[4]);
+            result.test_is_eq(date_str + " second", c.get_seconds(), d[5]);
+
+            if(type == "valid.not_std" || (type == "valid.64_bit_time_t" && c.get_year() > 2037 && sizeof(std::time_t) == 4))
+               {
+               result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); });
+               }
+            else
+               {
+               Botan::calendar_point c2 = Botan::calendar_value(c.to_std_timepoint());
+               result.test_is_eq(date_str + " year", c2.get_year(), d[0]);
+               result.test_is_eq(date_str + " month", c2.get_month(), d[1]);
+               result.test_is_eq(date_str + " day", c2.get_day(), d[2]);
+               result.test_is_eq(date_str + " hour", c2.get_hour(), d[3]);
+               result.test_is_eq(date_str + " minute", c2.get_minutes(), d[4]);
+               result.test_is_eq(date_str + " second", c2.get_seconds(), d[5]);
+               }
+            }
+         else if(type == "invalid")
+            {
+            result.test_throws("invalid date", [d]() { Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); });
+            }
+         else
+            {
+            throw Test_Error("Unexpected header '" + type + "' in date format tests");
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("calendar_point::to_string");
+         Botan::calendar_point d(2008, 5, 15, 9, 30, 33);
+         // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss>
+         result.test_eq("calendar_point::to_string", d.to_string(), "2008-05-15T09:30:33");
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("util_dates", Date_Format_Tests);
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+
+class Base32_Tests final : public Text_Based_Test
+   {
+   public:
+      Base32_Tests() : Text_Based_Test("base32.vec", "Base32", "Binary") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Base32");
+
+         const bool is_valid = (type == "valid");
+         const std::string base32 = vars.get_req_str("Base32");
+
+         try
+            {
+            if(is_valid)
+               {
+               const std::vector<uint8_t> binary = vars.get_req_bin("Binary");
+               result.test_eq("base32 decoding", Botan::base32_decode(base32), binary);
+               result.test_eq("base32 encoding", Botan::base32_encode(binary), base32);
+               }
+            else
+               {
+               auto res = Botan::base32_decode(base32);
+               result.test_failure("decoded invalid base32 to " + Botan::hex_encode(res));
+               }
+            }
+         catch(std::exception& e)
+            {
+            if(is_valid)
+               {
+               result.test_failure("rejected valid base32", e.what());
+               }
+            else
+               {
+               result.test_note("rejected invalid base32");
+               }
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("Base32");
+         const std::string valid_b32 = "MY======";
+
+         for(char ws_char : { ' ', '\t', '\r', '\n' })
+            {
+            for(size_t i = 0; i <= valid_b32.size(); ++i)
+               {
+               std::string b32_ws = valid_b32;
+               b32_ws.insert(i, 1, ws_char);
+
+               try
+                  {
+                  result.test_failure("decoded whitespace base32", Botan::base32_decode(b32_ws, false));
+                  }
+               catch(std::exception&) {}
+
+               try
+                  {
+                  result.test_eq("base32 decoding with whitespace", Botan::base32_decode(b32_ws, true), "66");
+                  }
+               catch(std::exception& e)
+                  {
+                  result.test_failure(b32_ws, e.what());
+                  }
+               }
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("base32", Base32_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+
+class Base58_Tests final : public Text_Based_Test
+   {
+   public:
+      Base58_Tests() : Text_Based_Test("base58.vec", "Base58", "Binary") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Base58");
+
+         const bool is_valid = (type == "valid");
+         const std::string base58 = vars.get_req_str("Base58");
+
+         try
+            {
+            if(is_valid)
+               {
+               const std::vector<uint8_t> binary = vars.get_req_bin("Binary");
+               result.test_eq("base58 decoding", Botan::base58_decode(base58), binary);
+               result.test_eq("base58 encoding", Botan::base58_encode(binary), base58);
+               }
+            else
+               {
+               auto res = Botan::base58_decode(base58);
+               result.test_failure("decoded invalid base58 to " + Botan::hex_encode(res));
+               }
+            }
+         catch(std::exception& e)
+            {
+            if(is_valid)
+               {
+               result.test_failure("rejected valid base58", e.what());
+               }
+            else
+               {
+               result.test_note("rejected invalid base58");
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("base58", Base58_Tests);
+
+class Base58_Check_Tests final : public Text_Based_Test
+   {
+   public:
+      Base58_Check_Tests() : Text_Based_Test("base58c.vec", "Base58", "Binary") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Base58 Check");
+
+         const bool is_valid = (type == "valid");
+         const std::string base58 = vars.get_req_str("Base58");
+
+         try
+            {
+            if(is_valid)
+               {
+               const std::vector<uint8_t> binary = vars.get_req_bin("Binary");
+               result.test_eq("base58 decoding", Botan::base58_check_decode(base58), binary);
+               result.test_eq("base58 encoding", Botan::base58_check_encode(binary), base58);
+               }
+            else
+               {
+               auto res = Botan::base58_check_decode(base58);
+               result.test_failure("decoded invalid base58c to " + Botan::hex_encode(res));
+               }
+            }
+         catch(std::exception& e)
+            {
+            if(is_valid)
+               {
+               result.test_failure("rejected valid base58c", e.what());
+               }
+            else
+               {
+               result.test_note("rejected invalid base58c");
+               }
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("base58c", Base58_Check_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+
+class Base64_Tests final : public Text_Based_Test
+   {
+   public:
+      Base64_Tests() : Text_Based_Test("base64.vec", "Base64", "Binary") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Base64");
+
+         const bool is_valid = (type == "valid");
+         const std::string base64 = vars.get_req_str("Base64");
+
+         try
+            {
+            if(is_valid)
+               {
+               const std::vector<uint8_t> binary = vars.get_req_bin("Binary");
+               result.test_eq("base64 decoding", Botan::base64_decode(base64), binary);
+               result.test_eq("base64 encoding", Botan::base64_encode(binary), base64);
+               }
+            else
+               {
+               auto res = Botan::base64_decode(base64);
+               result.test_failure("decoded invalid base64 to " + Botan::hex_encode(res));
+               }
+            }
+         catch(std::exception& e)
+            {
+            if(is_valid)
+               {
+               result.test_failure("rejected valid base64", e.what());
+               }
+            else
+               {
+               result.test_note("rejected invalid base64");
+               }
+            }
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("Base64");
+         const std::string valid_b64 = "Zg==";
+
+         for(char ws_char : { ' ', '\t', '\r', '\n' })
+            {
+            for(size_t i = 0; i <= valid_b64.size(); ++i)
+               {
+               std::string b64_ws = valid_b64;
+               b64_ws.insert(i, 1, ws_char);
+
+               try
+                  {
+                  result.test_failure("decoded whitespace base64", Botan::base64_decode(b64_ws, false));
+                  }
+               catch(std::exception&) {}
+
+               try
+                  {
+                  result.test_eq("base64 decoding with whitespace", Botan::base64_decode(b64_ws, true), "66");
+                  }
+               catch(std::exception& e)
+                  {
+                  result.test_failure(b64_ws, e.what());
+                  }
+               }
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("base64", Base64_Tests);
+
+#endif
+
+class Charset_Tests final : public Text_Based_Test
+   {
+   public:
+      Charset_Tests() : Text_Based_Test("charset.vec", "In,Out") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Charset");
+
+         const std::vector<uint8_t> in = vars.get_req_bin("In");
+         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
+
+         const std::string in_str(in.begin(), in.end());
+
+         std::string converted;
+
+         if(type == "UCS2-UTF8")
+            {
+            converted = Botan::ucs2_to_utf8(in.data(), in.size());
+            }
+         else if(type == "UCS4-UTF8")
+            {
+            converted = Botan::ucs4_to_utf8(in.data(), in.size());
+            }
+         else if(type == "UTF8-LATIN1")
+            {
+            converted = Botan::utf8_to_latin1(in_str);
+            }
+         else if(type == "UTF16-LATIN1")
+            {
+            converted = Botan::Charset::transcode(in_str,
+                                                  Botan::Character_Set::LATIN1_CHARSET,
+                                                  Botan::Character_Set::UCS2_CHARSET);
+            }
+         else if(type == "LATIN1-UTF8")
+            {
+            converted = Botan::Charset::transcode(in_str,
+                                                  Botan::Character_Set::UTF8_CHARSET,
+                                                  Botan::Character_Set::LATIN1_CHARSET);
+            }
+         else
+            {
+            throw Test_Error("Unexpected header '" + type + "' in charset tests");
+            }
+
+         result.test_eq("string converted successfully", std::vector<uint8_t>(converted.begin(), converted.end()), expected);
+
+         return result;
+         }
+
+      Test::Result utf16_to_latin1_negative_tests()
+         {
+         Test::Result result("Charset negative tests");
+
+         result.test_throws("conversion fails for non-Latin1 characters", []()
+            {
+            // "abcdefŸabcdef"
+            std::vector<uint8_t> input = { 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, 0x65, 0x00, 0x66, 0x01,
+                                           0x78, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, 0x65, 0x00, 0x66
+                                         };
+
+            Botan::Charset::transcode(std::string(input.begin(), input.end()),
+                                      Botan::Character_Set::LATIN1_CHARSET,
+                                      Botan::Character_Set::UCS2_CHARSET);
+            });
+
+         result.test_throws("conversion fails for UTF16 string with odd number of bytes", []()
+            {
+            std::vector<uint8_t> input = { 0x00, 0x61, 0x00 };
+
+            Botan::Charset::transcode(std::string(input.begin(), input.end()),
+                                      Botan::Character_Set::LATIN1_CHARSET,
+                                      Botan::Character_Set::UCS2_CHARSET);
+            });
+
+         return result;
+         }
+
+      Test::Result utf8_to_latin1_negative_tests()
+         {
+         Test::Result result("Charset negative tests");
+
+         result.test_throws("conversion fails for non-Latin1 characters", []()
+            {
+            // "abcdefŸabcdef"
+            const std::vector<uint8_t> input =
+               {
+               0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xC5,
+               0xB8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
+               };
+
+            Botan::utf8_to_latin1(std::string(input.begin(), input.end()));
+            });
+
+         result.test_throws("invalid utf-8 string", []()
+            {
+            // sequence truncated
+            const std::vector<uint8_t> input = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xC5 };
+            Botan::utf8_to_latin1(std::string(input.begin(), input.end()));
+            });
+
+         result.test_throws("invalid utf-8 string", []()
+            {
+            std::vector<uint8_t> input = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xC8, 0xB8, 0x61 };
+            Botan::utf8_to_latin1(std::string(input.begin(), input.end()));
+            });
+
+         return result;
+         }
+
+      std::vector<Test::Result> run_final_tests() override
+         {
+         Test::Result result("Charset negative tests");
+
+         result.merge(utf16_to_latin1_negative_tests());
+         result.merge(utf8_to_latin1_negative_tests());
+
+         return{ result };
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("charset", Charset_Tests);
+
+class Hostname_Tests final : public Text_Based_Test
+   {
+   public:
+      Hostname_Tests() : Text_Based_Test("hostnames.vec", "Issued,Hostname")
+         {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         Test::Result result("Hostname Matching");
+
+         const std::string issued = vars.get_req_str("Issued");
+         const std::string hostname = vars.get_req_str("Hostname");
+         const bool expected = (type == "Invalid") ? false : true;
+
+         const std::string what = hostname + ((expected == true) ?
+                                              " matches " : " does not match ") + issued;
+         result.test_eq(what, Botan::host_wildcard_match(issued, hostname), expected);
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("hostname", Hostname_Tests);
+
+class ReadKV_Tests final : public Text_Based_Test
+   {
+   public:
+      ReadKV_Tests() : Text_Based_Test("utils/read_kv.vec", "Input,Expected") {}
+
+      Test::Result run_one_test(const std::string& status, const VarMap& vars) override
+         {
+         Test::Result result("read_kv");
+
+         const bool is_valid = (status == "Valid");
+
+         const std::string input = vars.get_req_str("Input");
+         const std::string expected = vars.get_req_str("Expected");
+
+         if(is_valid)
+            {
+            confirm_kv(result, Botan::read_kv(input), split_group(expected));
+            }
+         else
+            {
+            // In this case "expected" is the expected exception message
+            result.test_throws("Invalid key value input throws exception",
+                               expected,
+                               [&]() { Botan::read_kv(input); });
+            }
+         return result;
+         }
+
+   private:
+
+      std::vector<std::string> split_group(const std::string& str)
+         {
+         std::vector<std::string> elems;
+         if(str.empty()) return elems;
+
+         std::string substr;
+         for(auto i = str.begin(); i != str.end(); ++i)
+            {
+            if(*i == '|')
+               {
+               elems.push_back(substr);
+               substr.clear();
+               }
+            else
+               {
+               substr += *i;
+               }
+            }
+
+         if(!substr.empty())
+            elems.push_back(substr);
+
+         return elems;
+         }
+
+      void confirm_kv(Test::Result& result,
+                      const std::map<std::string, std::string>& kv,
+                      const std::vector<std::string>& expected)
+         {
+         if(!result.test_eq("expected size", expected.size() % 2, size_t(0)))
+            return;
+
+         for(size_t i = 0; i != expected.size(); i += 2)
+            {
+            auto j = kv.find(expected[i]);
+            if(result.confirm("Found key", j != kv.end()))
+               {
+               result.test_eq("Matching value", j->second, expected[i+1]);
+               }
+            }
+
+         result.test_eq("KV has same size as expected", kv.size(), expected.size()/2);
+         }
+   };
+
+BOTAN_REGISTER_TEST("util_read_kv", ReadKV_Tests);
+
+class CPUID_Tests final : public Test
+   {
+   public:
+
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("CPUID");
+
+         result.confirm("Endian is either little or big",
+                        Botan::CPUID::is_big_endian() || Botan::CPUID::is_little_endian());
+
+         if(Botan::CPUID::is_little_endian())
+            {
+            result.test_eq("If endian is little, it is not also big endian", Botan::CPUID::is_big_endian(), false);
+            }
+         else
+            {
+            result.test_eq("If endian is big, it is not also little endian", Botan::CPUID::is_little_endian(), false);
+            }
+
+         const size_t cache_line_size = Botan::CPUID::cache_line_size();
+
+         result.test_gte("Cache line size is >= 16", cache_line_size, 16);
+         result.test_lte("Cache line size is <= 256", cache_line_size, 256);
+         result.confirm("Cache line size is a power of 2", Botan::is_power_of_2(cache_line_size));
+
+         const std::string cpuid_string = Botan::CPUID::to_string();
+         result.test_success("CPUID::to_string doesn't crash");
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+         if(Botan::CPUID::has_sse2())
+            {
+            result.confirm("Output string includes sse2", cpuid_string.find("sse2") != std::string::npos);
+
+            Botan::CPUID::clear_cpuid_bit(Botan::CPUID::CPUID_SSE2_BIT);
+
+            result.test_eq("After clearing cpuid bit, has_sse2 returns false", Botan::CPUID::has_sse2(), false);
+
+            Botan::CPUID::initialize(); // reset state
+            result.test_eq("After reinitializing, has_sse2 returns true", Botan::CPUID::has_sse2(), true);
+            }
+#endif
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("cpuid", CPUID_Tests);
+
+#if defined(BOTAN_HAS_UUID)
+
+class UUID_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("UUID");
+
+         const Botan::UUID empty_uuid;
+         const Botan::UUID random_uuid1(Test::rng());
+         const Botan::UUID random_uuid2(Test::rng());
+         const Botan::UUID loaded_uuid(std::vector<uint8_t>(16, 4));
+
+         result.test_throws("Cannot load wrong number of bytes", []() { Botan::UUID u(std::vector<uint8_t>(15)); });
+
+         result.test_eq("Empty UUID is empty", empty_uuid.is_valid(), false);
+         result.confirm("Empty UUID equals another empty UUID", empty_uuid == Botan::UUID());
+
+         result.test_throws("Empty UUID cannot become a string", [&]() { empty_uuid.to_string(); });
+
+         result.test_eq("Random UUID not empty", random_uuid1.is_valid(), true);
+         result.test_eq("Random UUID not empty", random_uuid2.is_valid(), true);
+
+         result.confirm("Random UUIDs are distinct", random_uuid1 != random_uuid2);
+         result.confirm("Random UUIDs not equal to empty", random_uuid1 != empty_uuid);
+
+         const std::string uuid4_str = loaded_uuid.to_string();
+         result.test_eq("String matches expected", uuid4_str, "04040404-0404-0404-0404-040404040404");
+
+         const std::string uuid_r1_str = random_uuid1.to_string();
+         result.confirm("UUID from string matches", Botan::UUID(uuid_r1_str) == random_uuid1);
+
+         class AllSame_RNG : public Botan::RandomNumberGenerator
+            {
+            public:
+               AllSame_RNG(uint8_t b) : m_val(b) {}
+
+               void randomize(uint8_t out[], size_t len) override
+                  {
+                  for(size_t i = 0; i != len; ++i)
+                     out[i] = m_val;
+                  }
+
+               std::string name() const override { return "zeros"; }
+               bool accepts_input() const override { return false; }
+               void add_entropy(const uint8_t[], size_t) override {}
+               void clear() override {}
+               bool is_seeded() const override { return true; }
+            private:
+               uint8_t m_val;
+            };
+
+         AllSame_RNG zeros(0x00);
+         const Botan::UUID zero_uuid(zeros);
+         result.test_eq("Zero UUID matches expected", zero_uuid.to_string(), "00000000-0000-4000-8000-000000000000");
+
+         AllSame_RNG ones(0xFF);
+         const Botan::UUID ones_uuid(ones);
+         result.test_eq("Ones UUID matches expected", ones_uuid.to_string(), "FFFFFFFF-FFFF-4FFF-BFFF-FFFFFFFFFFFF");
+
+         return {result};
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("uuid", UUID_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_workfactor.cpp b/third_party/botan/src/tests/test_workfactor.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_workfactor.cpp
@@ -0,0 +1,50 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+   #include <botan/workfactor.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+class PK_Workfactor_Tests final : public Text_Based_Test
+   {
+   public:
+      PK_Workfactor_Tests() :
+         Text_Based_Test("pubkey/workfactor.vec", "ParamSize,Workfactor") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         const size_t param_size  = vars.get_req_sz("ParamSize");
+         const size_t exp_output  = vars.get_req_sz("Workfactor");
+
+         size_t output = 0;
+
+         // TODO: test McEliece strength tests also
+
+         if(type == "RSA_Strength")
+            {
+            output = Botan::if_work_factor(param_size);
+            }
+         else if(type == "DL_Exponent_Size")
+            {
+            output = Botan::dl_exponent_size(param_size) / 2;
+            }
+
+         Test::Result result(type + " work factor calculation");
+         result.test_eq("Calculated workfactor for " + std::to_string(param_size),
+                        output, exp_output);
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("pk_workfactor", PK_Workfactor_Tests);
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_x509_dn.cpp b/third_party/botan/src/tests/test_x509_dn.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_x509_dn.cpp
@@ -0,0 +1,69 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509_dn.h>
+   #include <botan/ber_dec.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+class X509_DN_Comparisons_Tests final : public Text_Based_Test
+   {
+   public:
+      X509_DN_Comparisons_Tests() : Text_Based_Test("x509_dn.vec", "DN1,DN2") {}
+
+      Test::Result run_one_test(const std::string& type, const VarMap& vars) override
+         {
+         const std::vector<uint8_t> dn_bits1 = vars.get_req_bin("DN1");
+         const std::vector<uint8_t> dn_bits2 = vars.get_req_bin("DN2");
+
+         const bool dn_same = (type == "Equal");
+
+         Test::Result result("X509_DN comparisons");
+         try
+            {
+            Botan::X509_DN dn1;
+            Botan::BER_Decoder bd1(dn_bits1);
+            dn1.decode_from(bd1);
+
+            Botan::X509_DN dn2;
+            Botan::BER_Decoder bd2(dn_bits2);
+            dn2.decode_from(bd2);
+
+            const bool compared_same = (dn1 == dn2);
+            result.test_eq("Comparison matches expected", dn_same, compared_same);
+
+            const bool lt1 = (dn1 < dn2);
+            const bool lt2 = (dn2 < dn1);
+
+            if(dn_same)
+               {
+               result.test_eq("same means neither is less than", lt1, false);
+               result.test_eq("same means neither is less than", lt2, false);
+               }
+            else
+               {
+               result.test_eq("different means one is less than", lt1 || lt2, true);
+               result.test_eq("different means only one is less than", lt1 && lt2, false);
+               }
+            }
+         catch(Botan::Exception& e)
+            {
+            result.test_failure(e.what());
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("x509_dn_cmp", X509_DN_Comparisons_Tests);
+#endif
+
+}
diff --git a/third_party/botan/src/tests/test_x509_path.cpp b/third_party/botan/src/tests/test_x509_path.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_x509_path.cpp
@@ -0,0 +1,1102 @@
+/*
+* (C) 2006,2011,2012,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509path.h>
+   #include <botan/calendar.h>
+   #include <botan/internal/filesystem.h>
+   #include <botan/parsing.h>
+   #include <botan/data_src.h>
+   #include <botan/x509_crl.h>
+   #include <botan/pkcs10.h>
+   #include <botan/exceptn.h>
+
+   #include <fstream>
+   #include <string>
+   #include <vector>
+   #include <map>
+   #include <algorithm>
+   #include <limits>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
+
+std::map<std::string, std::string> read_results(const std::string& results_file, const char delim = ':')
+   {
+   std::ifstream in(results_file);
+   if(!in.good())
+      {
+      throw Test_Error("Failed reading " + results_file);
+      }
+
+   std::map<std::string, std::string> m;
+   std::string line;
+   while(in.good())
+      {
+      std::getline(in, line);
+      if(line == "")
+         {
+         continue;
+         }
+      if(line[0] == '#')
+         {
+         continue;
+         }
+
+      std::vector<std::string> parts = Botan::split_on(line, delim);
+
+      if(parts.size() != 2)
+         {
+         throw Test_Error("Invalid line " + line);
+         }
+
+      m[parts[0]] = parts[1];
+      }
+
+   return m;
+   }
+
+class X509test_Path_Validation_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         // Test certs generated by https://github.com/yymax/x509test
+
+         std::map<std::string, std::string> expected =
+            read_results(Test::data_file("x509/x509test/expected.txt"));
+
+         // Current tests use SHA-1
+         const Botan::Path_Validation_Restrictions restrictions(false, 80);
+
+         Botan::X509_Certificate root(Test::data_file("x509/x509test/root.pem"));
+         Botan::Certificate_Store_In_Memory trusted;
+         trusted.add_certificate(root);
+
+         auto validation_time = Botan::calendar_point(2016, 10, 21, 4, 20, 0).to_std_timepoint();
+
+         for(auto i = expected.begin(); i != expected.end(); ++i)
+            {
+            Test::Result result("X509test path validation");
+            result.start_timer();
+            const std::string filename = i->first;
+            const std::string expected_result = i->second;
+
+            std::vector<Botan::X509_Certificate> certs =
+               load_cert_file(Test::data_file("x509/x509test/" + filename));
+
+            if(certs.empty())
+               {
+               throw Test_Error("Failed to read certs from " + filename);
+               }
+
+            Botan::Path_Validation_Result path_result = Botan::x509_path_validate(
+                     certs, restrictions, trusted,
+                     "www.tls.test", Botan::Usage_Type::TLS_SERVER_AUTH,
+                     validation_time);
+
+            if(path_result.successful_validation() && path_result.trust_root() != root)
+               {
+               path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+               }
+
+            result.test_eq("test " + filename, path_result.result_string(), expected_result);
+            result.test_eq("test no warnings string", path_result.warnings_string(), "");
+            result.confirm("test no warnings", path_result.no_warnings());
+            result.end_timer();
+            results.push_back(result);
+            }
+
+            // test softfail
+            {
+            Test::Result result("X509test path validation softfail");
+            result.start_timer();
+
+            // this certificate must not have a OCSP URL
+            const std::string filename = "ValidAltName.pem";
+            std::vector<Botan::X509_Certificate> certs =
+               load_cert_file(Test::data_file("x509/x509test/" + filename));
+            if(certs.empty())
+               {
+               throw Test_Error("Failed to read certs from " + filename);
+               }
+
+            Botan::Path_Validation_Result path_result = Botan::x509_path_validate(
+                     certs, restrictions, trusted,
+                     "www.tls.test", Botan::Usage_Type::TLS_SERVER_AUTH,
+                     validation_time,
+                     /* activate check_ocsp_online */ std::chrono::milliseconds(1000), {});
+
+            if(path_result.successful_validation() && path_result.trust_root() != root)
+               {
+               path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+               }
+
+            // certificate verification succeed even if no OCSP URL (softfail)
+            result.confirm("test success", path_result.successful_validation());
+            result.test_eq("test " + filename, path_result.result_string(), "Verified");
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+            // if softfail, there is warnings
+            result.confirm("test warnings", !path_result.no_warnings());
+            result.test_eq("test warnings string", path_result.warnings_string(), "[0] OCSP URL not available");
+#endif
+            result.end_timer();
+            results.push_back(result);
+            }
+
+         return results;
+         }
+
+   private:
+
+      std::vector<Botan::X509_Certificate> load_cert_file(const std::string& filename)
+         {
+         Botan::DataSource_Stream in(filename);
+
+         std::vector<Botan::X509_Certificate> certs;
+         while(!in.end_of_data())
+            {
+            try
+               {
+               certs.emplace_back(in);
+               }
+            catch(Botan::Decoding_Error&) {}
+            }
+
+         return certs;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("x509_path_x509test", X509test_Path_Validation_Tests);
+
+class NIST_Path_Validation_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> NIST_Path_Validation_Tests::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("NIST path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   /**
+   * Code to run the X.509v3 processing tests described in "Conformance
+   *  Testing of Relying Party Client Certificate Path Proccessing Logic",
+   *  which is available on NIST's web site.
+   *
+   * Known Failures/Problems:
+   *  - Policy extensions are not implemented, so we skip tests #34-#53.
+   *  - Tests #75 and #76 are skipped as they make use of relatively
+   *    obscure CRL extensions which are not supported.
+   */
+   const std::string nist_test_dir = Test::data_dir() + "/x509/nist";
+
+   std::map<std::string, std::string> expected =
+      read_results(Test::data_file("x509/nist/expected.txt"));
+
+   const Botan::X509_Certificate root_cert(nist_test_dir + "/root.crt");
+   const Botan::X509_CRL root_crl(nist_test_dir + "/root.crl");
+
+   const auto validation_time = Botan::calendar_point(2018,4,1,9,30,33).to_std_timepoint();
+
+   for(auto i = expected.begin(); i != expected.end(); ++i)
+      {
+      Test::Result result("NIST path validation");
+      result.start_timer();
+
+      const std::string test_name = i->first;
+
+      try
+         {
+         const std::string expected_result = i->second;
+
+         const std::string test_dir = nist_test_dir + "/" + test_name;
+
+         const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
+
+         if(all_files.empty())
+            {
+            result.test_failure("No test files found in " + test_dir);
+            results.push_back(result);
+            continue;
+            }
+
+         Botan::Certificate_Store_In_Memory store;
+
+         store.add_certificate(root_cert);
+         store.add_crl(root_crl);
+
+         for(auto const& file : all_files)
+            {
+            if(file.find(".crt") != std::string::npos && file != "end.crt")
+               {
+               store.add_certificate(Botan::X509_Certificate(file));
+               }
+            else if(file.find(".crl") != std::string::npos)
+               {
+               Botan::DataSource_Stream in(file, true);
+               Botan::X509_CRL crl(in);
+               store.add_crl(crl);
+               }
+            }
+
+         Botan::X509_Certificate end_user(test_dir + "/end.crt");
+
+         // 1024 bit root cert
+         Botan::Path_Validation_Restrictions restrictions(true, 80);
+
+         Botan::Path_Validation_Result validation_result =
+            Botan::x509_path_validate(end_user,
+                                      restrictions,
+                                      store,
+                                      "",
+                                      Botan::Usage_Type::UNSPECIFIED,
+                                      validation_time);
+
+         result.test_eq(test_name + " path validation result",
+                        validation_result.result_string(),
+                        expected_result);
+         }
+      catch(std::exception& e)
+         {
+         result.test_failure(test_name, e.what());
+         }
+
+      result.end_timer();
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+BOTAN_REGISTER_TEST("x509_path_nist", NIST_Path_Validation_Tests);
+
+class Extended_Path_Validation_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> Extended_Path_Validation_Tests::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("Extended x509 path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string extended_x509_test_dir = Test::data_dir() + "/x509/extended";
+
+   std::map<std::string, std::string> expected =
+      read_results(Test::data_file("x509/extended/expected.txt"));
+
+   auto validation_time = Botan::calendar_point(2017,9,1,9,30,33).to_std_timepoint();
+
+   for(auto i = expected.begin(); i != expected.end(); ++i)
+      {
+      const std::string test_name = i->first;
+      const std::string expected_result = i->second;
+
+      const std::string test_dir = extended_x509_test_dir + "/" + test_name;
+
+      Test::Result result("Extended X509 path validation");
+      result.start_timer();
+
+      const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
+
+      if(all_files.empty())
+         {
+         result.test_failure("No test files found in " + test_dir);
+         results.push_back(result);
+         continue;
+         }
+
+      Botan::Certificate_Store_In_Memory store;
+
+      for(auto const& file : all_files)
+         {
+         if(file.find(".crt") != std::string::npos && file != "end.crt")
+            {
+            store.add_certificate(Botan::X509_Certificate(file));
+            }
+         }
+
+      Botan::X509_Certificate end_user(test_dir + "/end.crt");
+
+      Botan::Path_Validation_Restrictions restrictions;
+      Botan::Path_Validation_Result validation_result =
+         Botan::x509_path_validate(end_user,
+                                   restrictions,
+                                   store,
+                                   "",
+                                   Botan::Usage_Type::UNSPECIFIED,
+                                   validation_time);
+
+      result.test_eq(test_name + " path validation result",
+                     validation_result.result_string(),
+                     expected_result);
+
+      result.end_timer();
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+BOTAN_REGISTER_TEST("x509_path_extended", Extended_Path_Validation_Tests);
+
+class PSS_Path_Validation_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> PSS_Path_Validation_Tests::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("RSA-PSS X509 signature validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string pss_x509_test_dir = Test::data_dir() + "/x509/pss_certs";
+
+   std::map<std::string, std::string> expected =
+      read_results(Test::data_file("x509/pss_certs/expected.txt"));
+
+   std::map<std::string, std::string> validation_times =
+      read_results(Test::data_file("x509/pss_certs/validation_times.txt"));
+
+   auto validation_times_iter = validation_times.begin();
+   for(auto i = expected.begin(); i != expected.end(); ++i)
+      {
+      const std::string test_name = i->first;
+      const std::string expected_result = i->second;
+
+      const std::string test_dir = pss_x509_test_dir + "/" + test_name;
+
+      Test::Result result("RSA-PSS X509 signature validation");
+      result.start_timer();
+
+      const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
+
+      if(all_files.empty())
+         {
+         result.test_failure("No test files found in " + test_dir);
+         results.push_back(result);
+         continue;
+         }
+
+      std::shared_ptr<Botan::X509_CRL> crl;
+      std::shared_ptr<Botan::X509_Certificate> end;
+      std::shared_ptr<Botan::X509_Certificate> root;
+      Botan::Certificate_Store_In_Memory store;
+      std::shared_ptr<Botan::PKCS10_Request> csr;
+      auto validation_time = Botan::calendar_point(std::atoi((validation_times_iter++)->second.c_str()), 0, 0, 0, 0,
+                             0).to_std_timepoint();
+      for(auto const& file : all_files)
+         {
+         if(file.find("end.crt") != std::string::npos)
+            {
+            end.reset(new Botan::X509_Certificate(file));
+            }
+         else if(file.find("root.crt") != std::string::npos)
+            {
+            root.reset(new Botan::X509_Certificate(file));
+            store.add_certificate(*root);
+            }
+         else if(file.find(".crl") != std::string::npos)
+            {
+            crl.reset(new Botan::X509_CRL(file));
+            }
+         else if(file.find(".csr") != std::string::npos)
+            {
+            csr.reset(new Botan::PKCS10_Request(file));
+            }
+         }
+
+      if(end && crl && root)    // CRL tests
+         {
+         const std::vector<std::shared_ptr<const Botan::X509_Certificate>> cert_path = { end, root };
+         const std::vector<std::shared_ptr<const Botan::X509_CRL>> crls = { crl };
+         auto crl_status = Botan::PKIX::check_crl(cert_path, crls,
+                           validation_time);   // alternatively we could just call crl.check_signature( root_pubkey )
+
+         result.test_eq(test_name + " check_crl result",
+                        Botan::Path_Validation_Result::status_string(Botan::PKIX::overall_status(crl_status)),
+                        expected_result);
+         }
+      else if(end && root)     // CRT chain tests
+         {
+         // sha-1 is used
+         Botan::Path_Validation_Restrictions restrictions(false, 80);
+
+         Botan::Path_Validation_Result validation_result =
+            Botan::x509_path_validate(*end,
+                                      restrictions,
+                                      store, "", Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+         result.test_eq(test_name + " path validation result",
+                        validation_result.result_string(),
+                        expected_result);
+         }
+      else if(end && !root)    // CRT self signed tests
+         {
+         std::unique_ptr<Botan::Public_Key> pubkey(end->subject_public_key());
+         result.test_eq(test_name + " verify signature", end->check_signature(*pubkey), !!(std::stoi(expected_result)));
+         }
+      else if(csr)    // PKCS#10 Request
+         {
+         std::unique_ptr<Botan::Public_Key> pubkey(csr->subject_public_key());
+         result.test_eq(test_name + " verify signature", csr->check_signature(*pubkey), !!(std::stoi(expected_result)));
+         }
+
+      result.end_timer();
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+BOTAN_REGISTER_TEST("x509_path_rsa_pss", PSS_Path_Validation_Tests);
+
+class Validate_V1Cert_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> Validate_V1Cert_Test::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("BSI path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string root_crt = Test::data_file("/x509/misc/v1ca/root.pem");
+   const std::string int_crt  = Test::data_file("/x509/misc/v1ca/int.pem");
+   const std::string ee_crt   = Test::data_file("/x509/misc/v1ca/ee.pem");
+
+   auto validation_time =
+      Botan::calendar_point(2019, 4, 19, 23, 0, 0).to_std_timepoint();
+
+   Botan::X509_Certificate root(root_crt);
+   Botan::X509_Certificate intermediate(int_crt);
+   Botan::X509_Certificate ee_cert(ee_crt);
+
+   Botan::Certificate_Store_In_Memory trusted;
+   trusted.add_certificate(root);
+
+   std::vector<Botan::X509_Certificate> chain = { ee_cert, intermediate };
+
+   Botan::Path_Validation_Restrictions restrictions;
+   Botan::Path_Validation_Result validation_result =
+      Botan::x509_path_validate(chain, restrictions, trusted, "",
+                                Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+   Test::Result result("Verifying using v1 certificate");
+   result.test_eq("Path validation result",
+                  validation_result.result_string(), "Verified");
+
+   Botan::Certificate_Store_In_Memory empty;
+
+   std::vector<Botan::X509_Certificate> new_chain = { ee_cert, intermediate, root };
+
+   Botan::Path_Validation_Result validation_result2 =
+      Botan::x509_path_validate(new_chain, restrictions, empty, "",
+                                Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+   result.test_eq("Path validation result",
+                  validation_result2.result_string(), "Cannot establish trust");
+
+   return {result};
+   }
+
+BOTAN_REGISTER_TEST("x509_v1_ca", Validate_V1Cert_Test);
+
+class Validate_V2Uid_in_V1_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> Validate_V2Uid_in_V1_Test::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("Path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string root_crt = Test::data_file("/x509/v2-in-v1/root.pem");
+   const std::string int_crt  = Test::data_file("/x509/v2-in-v1/int.pem");
+   const std::string ee_crt   = Test::data_file("/x509/v2-in-v1/leaf.pem");
+
+   auto validation_time =
+      Botan::calendar_point(2020, 1, 1, 1, 0, 0).to_std_timepoint();
+
+   Botan::X509_Certificate root(root_crt);
+   Botan::X509_Certificate intermediate(int_crt);
+   Botan::X509_Certificate ee_cert(ee_crt);
+
+   Botan::Certificate_Store_In_Memory trusted;
+   trusted.add_certificate(root);
+
+   std::vector<Botan::X509_Certificate> chain = { ee_cert, intermediate };
+
+   Botan::Path_Validation_Restrictions restrictions;
+   Botan::Path_Validation_Result validation_result =
+      Botan::x509_path_validate(chain, restrictions, trusted, "",
+                                Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+   Test::Result result("Verifying v1 certificate using v2 uid fields");
+   result.test_eq("Path validation failed",
+                  validation_result.successful_validation(), false);
+   result.test_eq("Path validation result",
+                  validation_result.result_string(),
+                  "Encountered v2 identifiers in v1 certificate");
+
+   return {result};
+   }
+
+BOTAN_REGISTER_TEST("x509_v2uid_in_v1", Validate_V2Uid_in_V1_Test);
+
+class Validate_Name_Constraint_SAN_Test final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> Validate_Name_Constraint_SAN_Test::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("Path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string root_crt = Test::data_file("/x509/name_constraint_san/root.pem");
+   const std::string int_crt  = Test::data_file("/x509/name_constraint_san/int.pem");
+   const std::string ee_crt   = Test::data_file("/x509/name_constraint_san/leaf.pem");
+
+   auto validation_time =
+      Botan::calendar_point(2020, 1, 1, 1, 0, 0).to_std_timepoint();
+
+   Botan::X509_Certificate root(root_crt);
+   Botan::X509_Certificate intermediate(int_crt);
+   Botan::X509_Certificate ee_cert(ee_crt);
+
+   Botan::Certificate_Store_In_Memory trusted;
+   trusted.add_certificate(root);
+
+   std::vector<Botan::X509_Certificate> chain = { ee_cert, intermediate };
+
+   Botan::Path_Validation_Restrictions restrictions;
+   Botan::Path_Validation_Result validation_result =
+      Botan::x509_path_validate(chain, restrictions, trusted, "",
+                                Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+   Test::Result result("Verifying certificate with alternative SAN violating name constraint");
+   result.test_eq("Path validation failed",
+                  validation_result.successful_validation(), false);
+   result.test_eq("Path validation result",
+                  validation_result.result_string(),
+                  "Certificate does not pass name constraint");
+
+   return {result};
+   }
+
+BOTAN_REGISTER_TEST("x509_name_constraint_san", Validate_Name_Constraint_SAN_Test);
+
+class BSI_Path_Validation_Tests final : public Test
+
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> BSI_Path_Validation_Tests::run()
+   {
+   if(Botan::has_filesystem_impl() == false)
+      {
+      return {Test::Result::Note("BSI path validation",
+                                 "Skipping due to missing filesystem access")};
+      }
+
+   std::vector<Test::Result> results;
+
+   const std::string bsi_test_dir = Test::data_dir() + "/x509/bsi";
+
+   const std::map<std::string, std::string> expected = read_results(
+         Test::data_file("/x509/bsi/expected.txt"), '$');
+
+   for(auto& i : expected)
+      {
+      const std::string test_name = i.first;
+      std::string expected_result = i.second;
+
+#if !defined(BOTAN_HAS_MD5)
+      if(expected_result == "Hash function used is considered too weak for security")
+         expected_result = "Certificate signed with unknown/unavailable algorithm";
+#endif
+
+      const std::string test_dir = bsi_test_dir + "/" + test_name;
+
+      Test::Result result("BSI path validation");
+      result.start_timer();
+
+      const std::vector<std::string> all_files =
+            Botan::get_files_recursive(test_dir);
+
+      if(all_files.empty())
+         {
+         result.test_failure("No test files found in " + test_dir);
+         results.push_back(result);
+         continue;
+         }
+
+      Botan::Certificate_Store_In_Memory trusted;
+      std::vector<Botan::X509_Certificate> certs;
+
+      auto validation_time =
+         Botan::calendar_point(2017, 8, 19, 12, 0, 0).to_std_timepoint();
+
+      // By convention: if CRL is a substring if the directory name,
+      // we need to check the CRLs
+      bool use_crl = false;
+      if(test_dir.find("CRL") != std::string::npos)
+         {
+         use_crl = true;
+         }
+
+      try
+         {
+         for(auto const& file : all_files)
+            {
+            // found a trust anchor
+            if(file.find("TA") != std::string::npos)
+               {
+               trusted.add_certificate(Botan::X509_Certificate(file));
+               }
+            // found the target certificate. It needs to be at the front of certs
+            else if(file.find("TC") != std::string::npos)
+               {
+               certs.insert(certs.begin(), Botan::X509_Certificate(file));
+               }
+            // found a certificate that might be part of a valid certificate chain to the trust anchor
+            else if(file.find(".crt") != std::string::npos)
+               {
+               certs.push_back(Botan::X509_Certificate(file));
+               }
+            else if(file.find(".crl") != std::string::npos)
+               {
+               trusted.add_crl(Botan::X509_CRL(file));
+               }
+            }
+
+         Botan::Path_Validation_Restrictions restrictions(use_crl, 79,
+               use_crl);
+
+         /*
+          * Following the test document, the test are executed 16 times with
+          * randomly chosen order of the available certificates. However, the target
+          * certificate needs to stay in front.
+          * For certain test, the order in which the certificates are given to
+          * the validation function may be relevant, i.e. if issuer DNs are
+          * ambiguous.
+          */
+         struct random_bit_generator {
+            using result_type = size_t;
+            static constexpr result_type min() { return 0; }
+            static constexpr result_type max() { return std::numeric_limits<size_t>::max(); }
+            result_type operator()()
+               {
+               size_t s;
+               Test::rng().randomize(reinterpret_cast<uint8_t*>(&s), sizeof(s));
+               return s;
+               }
+            } rbg;
+
+         for(size_t r = 0; r < 16; r++)
+            {
+            std::shuffle(++(certs.begin()), certs.end(), rbg);
+
+            Botan::Path_Validation_Result validation_result =
+                  Botan::x509_path_validate(certs, restrictions, trusted, "",
+                        Botan::Usage_Type::UNSPECIFIED, validation_time);
+
+            // We expect to be warned
+            if(expected_result.find("Warning: ") == 0)
+               {
+               std::string stripped = expected_result.substr(std::string("Warning: ").size());
+               bool found_warning = false;
+               for(const auto& warning_set : validation_result.warnings())
+                  {
+                  for(const auto& warning : warning_set)
+                     {
+                     std::string warning_str(Botan::to_string(warning));
+                     if(stripped == warning_str)
+                        {
+                        result.test_eq(test_name + " path validation result",
+                              warning_str, stripped);
+                        found_warning = true;
+                        }
+                     }
+                  }
+               if(!found_warning)
+                  {
+                  result.test_failure(test_name,"Did not receive the expected warning: " + stripped);
+                  }
+               }
+            else
+               {
+               result.test_eq(test_name + " path validation result",
+                     validation_result.result_string(), expected_result);
+               }
+
+
+            }
+         }
+
+      /* Some certificates are rejected when executing the X509_Certificate constructor
+       * by throwing a Decoding_Error exception.
+       */
+      catch(const Botan::Exception& e)
+         {
+         if(e.error_type() == Botan::ErrorType::DecodingFailure)
+            {
+            result.test_eq(test_name + " path validation result", e.what(),
+                           expected_result);
+            }
+         else
+            {
+            result.test_failure(test_name, e.what());
+            }
+         }
+
+      result.end_timer();
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+BOTAN_REGISTER_TEST("x509_path_bsi", BSI_Path_Validation_Tests);
+
+class Path_Validation_With_OCSP_Tests final : public Test
+   {
+   public:
+      Botan::X509_Certificate load_test_X509_cert(const std::string& path)
+         {
+         return Botan::X509_Certificate(Test::data_file(path));
+         }
+
+      std::shared_ptr<const Botan::OCSP::Response> load_test_OCSP_resp(const std::string& path)
+         {
+         return std::make_shared<const Botan::OCSP::Response>(Test::read_binary_data_file(path));
+         }
+
+      Test::Result validate_with_ocsp_with_next_update_without_max_age()
+         {
+         Test::Result result("path check with ocsp with next_update w/o max_age");
+         Botan::Certificate_Store_In_Memory trusted;
+
+         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false);
+
+         auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
+         auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
+         trusted.add_certificate(trust_root);
+
+         const std::vector<Botan::X509_Certificate> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
+
+         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+                                     valid_time, std::chrono::milliseconds(0), {ocsp});
+
+            return result.confirm(std::string("Status: '") + Botan::to_string(expected)
+                                  + "' should match '" + Botan::to_string(path_result.result()) + "'",
+                                  path_result.result()==expected);
+            };
+
+         check_path(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_path(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
+
+         return result;
+         }
+
+      Test::Result validate_with_ocsp_with_next_update_with_max_age()
+         {
+         Test::Result result("path check with ocsp with next_update with max_age");
+         Botan::Certificate_Store_In_Memory trusted;
+
+         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false,
+                            std::chrono::minutes(59));
+
+         auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
+         auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
+         auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem");
+         trusted.add_certificate(trust_root);
+
+         const std::vector<Botan::X509_Certificate> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
+
+         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+                                     valid_time, std::chrono::milliseconds(0), {ocsp});
+
+            return result.confirm(std::string("Status: '") + Botan::to_string(expected)
+                                  + "' should match '" + Botan::to_string(path_result.result()) + "'",
+                                  path_result.result()==expected);
+            };
+
+         check_path(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_path(Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2016, 11, 28, 8, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED);
+
+         return result;
+         }
+
+      Test::Result validate_with_ocsp_without_next_update_without_max_age()
+         {
+         Test::Result result("path check with ocsp w/o next_update w/o max_age");
+         Botan::Certificate_Store_In_Memory trusted;
+
+         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false);
+
+         auto ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
+         auto ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
+         auto trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
+
+         trusted.add_certificate(trust_root);
+
+         const std::vector<Botan::X509_Certificate> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
+
+         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+                                     valid_time, std::chrono::milliseconds(0), {ocsp});
+
+            return result.confirm(std::string("Status: '") + Botan::to_string(expected)
+                                  + "' should match '" + Botan::to_string(path_result.result()) + "'",
+                                  path_result.result()==expected);
+            };
+
+         check_path(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_path(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+
+         return result;
+         }
+
+      Test::Result validate_with_ocsp_without_next_update_with_max_age()
+         {
+         Test::Result result("path check with ocsp w/o next_update with max_age");
+         Botan::Certificate_Store_In_Memory trusted;
+
+         auto restrictions = Botan::Path_Validation_Restrictions(false, 110, false,
+                            std::chrono::minutes(59));
+
+         auto ee = load_test_X509_cert("x509/ocsp/patrickschmidt.pem");
+         auto ca = load_test_X509_cert("x509/ocsp/bdrive_encryption.pem");
+         auto trust_root = load_test_X509_cert("x509/ocsp/bdrive_root.pem");
+
+         trusted.add_certificate(trust_root);
+
+         const std::vector<Botan::X509_Certificate> cert_path = { ee, ca, trust_root };
+
+         std::shared_ptr<const Botan::OCSP::Response> ocsp = load_test_OCSP_resp("x509/ocsp/patrickschmidt_ocsp.der");
+
+         auto check_path = [&](const std::chrono::system_clock::time_point valid_time,
+                               const Botan::Certificate_Status_Code expected)
+            {
+            const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+                                     valid_time, std::chrono::milliseconds(0), {ocsp});
+
+            return result.confirm(std::string("Status: '") + Botan::to_string(expected)
+                                  + "' should match '" + Botan::to_string(path_result.result()) + "'",
+                                  path_result.result()==expected);
+            };
+
+         check_path(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID);
+         check_path(Botan::calendar_point(2019, 5, 28, 7, 30, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OK);
+         check_path(Botan::calendar_point(2019, 5, 28, 8, 0, 0).to_std_timepoint(),
+                    Botan::Certificate_Status_Code::OCSP_IS_TOO_OLD);
+
+         return result;
+         }
+
+      std::vector<Test::Result> run() override
+         {
+         return  {validate_with_ocsp_with_next_update_without_max_age(),
+                  validate_with_ocsp_with_next_update_with_max_age(),
+                  validate_with_ocsp_without_next_update_without_max_age(),
+                  validate_with_ocsp_without_next_update_with_max_age()};
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("x509_path_with_ocsp", Path_Validation_With_OCSP_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class CVE_2020_0601_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         Test::Result result("CVE-2020-0601");
+         auto ca_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/ca.pem"));
+         auto fake_ca_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/fake_ca.pem"));
+         auto ee_crt = Botan::X509_Certificate(Test::data_file("x509/cve-2020-0601/ee.pem"));
+
+         Botan::Certificate_Store_In_Memory trusted;
+         trusted.add_certificate(ca_crt);
+
+         const auto restrictions = Botan::Path_Validation_Restrictions(false, 80, false);
+
+         const auto valid_time = Botan::calendar_point(2020, 1, 20, 0, 0, 0).to_std_timepoint();
+
+         const auto path_result1 = Botan::x509_path_validate(
+            std::vector<Botan::X509_Certificate>{ ee_crt, fake_ca_crt },
+            restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+            valid_time, std::chrono::milliseconds(0), {});
+
+         result.confirm("Validation failed", !path_result1.successful_validation());
+
+         result.confirm("Expected status",
+                        path_result1.result() == Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+
+         const auto path_result2 = Botan::x509_path_validate(
+            std::vector<Botan::X509_Certificate>{ ee_crt },
+            restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED,
+            valid_time, std::chrono::milliseconds(0), {});
+
+         result.confirm("Validation failed", !path_result2.successful_validation());
+
+         result.confirm("Expected status",
+                        path_result2.result() == Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+
+         // Verify the signature from the bad CA is actually correct
+         Botan::Certificate_Store_In_Memory frusted;
+         frusted.add_certificate(fake_ca_crt);
+
+         const auto path_result3 = Botan::x509_path_validate(
+            std::vector<Botan::X509_Certificate>{ ee_crt },
+            restrictions, frusted, "", Botan::Usage_Type::UNSPECIFIED,
+            valid_time, std::chrono::milliseconds(0), {});
+
+         result.confirm("Validation succeeded", path_result3.successful_validation());
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("x509_cve_2020_0601", CVE_2020_0601_Tests);
+
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+
+class XMSS_Path_Validation_Tests final : public Test
+   {
+   public:
+      Test::Result validate_self_signed(const std::string& name, const std::string& file)
+      {
+      Test::Result result(name);
+
+      Botan::Path_Validation_Restrictions restrictions;
+      auto self_signed = Botan::X509_Certificate(Test::data_dir() + "/x509/xmss/" + file);
+
+      auto cert_path = std::vector<std::shared_ptr<const Botan::X509_Certificate>>{
+				            std::make_shared<const Botan::X509_Certificate>(self_signed)};
+      auto valid_time = Botan::calendar_point(2019, 10, 8, 4, 45, 0).to_std_timepoint();
+
+      auto status = Botan::PKIX::overall_status(Botan::PKIX::check_chain(cert_path, valid_time,
+				                                 "", Botan::Usage_Type::UNSPECIFIED, restrictions.minimum_key_strength(), restrictions.trusted_hashes()));
+      result.test_eq("Cert validation status",
+                  Botan::to_string(status), "Verified");
+      return result;
+      }
+
+      std::vector<Test::Result> run() override
+      {
+      if(Botan::has_filesystem_impl() == false)
+         {
+         return {Test::Result::Note("XMSS path validation",
+                                    "Skipping due to missing filesystem access")};
+         }
+
+      return  {validate_self_signed("XMSS path validation with certificate created by ISARA corp", "xmss_isara_root.pem"),
+               validate_self_signed("XMSS path validation with certificate created by BouncyCastle", "xmss_bouncycastle_sha256_10_root.pem")};
+      }
+   };
+
+BOTAN_REGISTER_TEST("x509_path_xmss", XMSS_Path_Validation_Tests);
+
+#endif
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/test_xmss.cpp b/third_party/botan/src/tests/test_xmss.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/test_xmss.cpp
@@ -0,0 +1,129 @@
+/*
+ * Extended Hash-Based Signatures Tests
+ *
+ * (C) 2014,2015 Jack Lloyd
+ * (C) 2016,2018 Matthias Gierlings
+ **/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+   #include <botan/xmss.h>
+   #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+
+class XMSS_Signature_Tests final : public PK_Signature_Generation_Test
+   {
+   public:
+      XMSS_Signature_Tests()
+         : PK_Signature_Generation_Test(
+              "XMSS",
+              "pubkey/xmss_sig.vec",
+              "Params,Msg,PrivateKey,Signature") {}
+
+      bool skip_this_test(const std::string&,
+                          const VarMap& vars) override
+         {
+         if(Test::run_long_tests() == false)
+            {
+            const std::string params = vars.get_req_str("Params");
+
+            if(params == "SHAKE_10_256")
+               {
+               return false;
+               }
+
+            return true;
+            }
+
+         return false;
+         }
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return vars.get_req_str("Params");
+         }
+
+      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+         {
+         const std::vector<uint8_t> raw_key = vars.get_req_bin("PrivateKey");
+         const Botan::secure_vector<uint8_t> sec_key(raw_key.begin(), raw_key.end());
+
+         std::unique_ptr<Botan::Private_Key> key(new Botan::XMSS_PrivateKey(sec_key));
+         return key;
+         }
+   };
+
+class XMSS_Signature_Verify_Tests final : public PK_Signature_Verification_Test
+   {
+   public:
+      XMSS_Signature_Verify_Tests()
+         : PK_Signature_Verification_Test(
+              "XMSS",
+              "pubkey/xmss_verify.vec",
+              "Params,Msg,PublicKey,Signature") {}
+
+      std::string default_padding(const VarMap& vars) const override
+         {
+         return vars.get_req_str("Params");
+         }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const std::vector<uint8_t> raw_key = vars.get_req_bin("PublicKey");
+         std::unique_ptr<Botan::Public_Key> key(new Botan::XMSS_PublicKey(raw_key));
+         return key;
+         }
+   };
+
+class XMSS_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerification_Test
+   {
+   public:
+       XMSS_Signature_Verify_Invalid_Tests()
+          : PK_Signature_NonVerification_Test(
+               "XMSS",
+               "pubkey/xmss_invalid.vec",
+               "Params,Msg,PublicKey,InvalidSignature") {}
+
+       std::string default_padding(const VarMap& vars) const override
+          {
+          return vars.get_req_str("Params");
+          }
+
+      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override
+         {
+         const std::vector<uint8_t> raw_key = vars.get_req_bin("PublicKey");
+         std::unique_ptr<Botan::Public_Key> key(new Botan::XMSS_PublicKey(raw_key));
+         return key;
+         }
+   };
+
+class XMSS_Keygen_Tests final : public PK_Key_Generation_Test
+   {
+   public:
+      std::vector<std::string> keygen_params() const override
+         {
+         return { "XMSS-SHA2_10_256" };
+         }
+      std::string algo_name() const override
+         {
+         return "XMSS";
+         }
+   };
+
+BOTAN_REGISTER_TEST("xmss_sign", XMSS_Signature_Tests);
+BOTAN_REGISTER_TEST("xmss_verify", XMSS_Signature_Verify_Tests);
+BOTAN_REGISTER_TEST("xmss_verify_invalid", XMSS_Signature_Verify_Invalid_Tests);
+BOTAN_REGISTER_TEST("xmss_keygen", XMSS_Keygen_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/tests.cpp b/third_party/botan/src/tests/tests.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/tests.cpp
@@ -0,0 +1,1140 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+#include <botan/internal/filesystem.h>
+#include <botan/internal/stl_util.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_HAS_BIGINT)
+   #include <botan/bigint.h>
+#endif
+
+#if defined(BOTAN_HAS_EC_CURVE_GFP)
+   #include <botan/point_gfp.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+   #include <stdlib.h>
+   #include <unistd.h>
+#endif
+
+namespace Botan_Tests {
+
+void Test::Result::merge(const Result& other)
+   {
+   if(who() != other.who())
+      {
+      throw Test_Error("Merging tests from different sources");
+      }
+
+   m_ns_taken += other.m_ns_taken;
+   m_tests_passed += other.m_tests_passed;
+   m_fail_log.insert(m_fail_log.end(), other.m_fail_log.begin(), other.m_fail_log.end());
+   m_log.insert(m_log.end(), other.m_log.begin(), other.m_log.end());
+   }
+
+void Test::Result::start_timer()
+   {
+   if(m_started == 0)
+      {
+      m_started = Test::timestamp();
+      }
+   }
+
+void Test::Result::end_timer()
+   {
+   if(m_started > 0)
+      {
+      m_ns_taken += Test::timestamp() - m_started;
+      m_started = 0;
+      }
+   }
+
+void Test::Result::test_note(const std::string& note, const char* extra)
+   {
+   if(note != "")
+      {
+      std::ostringstream out;
+      out << who() << " " << note;
+      if(extra)
+         {
+         out << ": " << extra;
+         }
+      m_log.push_back(out.str());
+      }
+   }
+
+void Test::Result::note_missing(const std::string& whatever)
+   {
+   static std::set<std::string> s_already_seen;
+
+   if(s_already_seen.count(whatever) == 0)
+      {
+      test_note("Skipping tests due to missing " + whatever);
+      s_already_seen.insert(whatever);
+      }
+   }
+
+bool Test::Result::test_throws(const std::string& what, std::function<void ()> fn)
+   {
+   try
+      {
+      fn();
+      return test_failure(what + " failed to throw expected exception");
+      }
+   catch(std::exception& e)
+      {
+      return test_success(what + " threw exception " + e.what());
+      }
+   catch(...)
+      {
+      return test_success(what + " threw unknown exception");
+      }
+   }
+
+bool Test::Result::test_throws(const std::string& what, const std::string& expected, std::function<void ()> fn)
+   {
+   try
+      {
+      fn();
+      return test_failure(what + " failed to throw expected exception");
+      }
+   catch(std::exception& e)
+      {
+      if(expected == e.what())
+         {
+         return test_success(what + " threw exception " + e.what());
+         }
+      else
+         {
+         return test_failure(what + " failed to throw an exception with the expected text:\n  Expected: " + expected +
+                             "\n  Got: " + e.what());
+         }
+      }
+   catch(...)
+      {
+      return test_failure(what + " failed to throw an exception with the expected text:\n  Expected: " + expected);
+      }
+   }
+
+bool Test::Result::test_success(const std::string& note)
+   {
+   if(Test::options().log_success())
+      {
+      test_note(note);
+      }
+   ++m_tests_passed;
+   return true;
+   }
+
+bool Test::Result::test_failure(const std::string& what, const std::string& error)
+   {
+   return test_failure(who() + " " + what + " with error " + error);
+   }
+
+void Test::Result::test_failure(const std::string& what, const uint8_t buf[], size_t buf_len)
+   {
+   test_failure(who() + ": " + what +
+                " buf len " + std::to_string(buf_len) +
+                " value " + Botan::hex_encode(buf, buf_len));
+   }
+
+bool Test::Result::test_failure(const std::string& err)
+   {
+   m_fail_log.push_back(err);
+
+   if(Test::options().abort_on_first_fail() && m_who != "Failing Test")
+      {
+      std::abort();
+      }
+   return false;
+   }
+
+bool Test::Result::test_ne(const std::string& what,
+                           const uint8_t produced[], size_t produced_len,
+                           const uint8_t expected[], size_t expected_len)
+   {
+   if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len))
+      {
+      return test_failure(who() + ": " + what + " produced matching");
+      }
+   return test_success();
+   }
+
+bool Test::Result::test_eq(const char* producer, const std::string& what,
+                           const uint8_t produced[], size_t produced_size,
+                           const uint8_t expected[], size_t expected_size)
+   {
+   if(produced_size == expected_size && Botan::same_mem(produced, expected, expected_size))
+      {
+      return test_success();
+      }
+
+   std::ostringstream err;
+
+   err << who();
+
+   if(producer)
+      {
+      err << " producer '" << producer << "'";
+      }
+
+   err << " unexpected result for " << what;
+
+   if(produced_size != expected_size)
+      {
+      err << " produced " << produced_size << " bytes expected " << expected_size;
+      }
+
+   std::vector<uint8_t> xor_diff(std::min(produced_size, expected_size));
+   size_t bytes_different = 0;
+
+   for(size_t i = 0; i != xor_diff.size(); ++i)
+      {
+      xor_diff[i] = produced[i] ^ expected[i];
+      bytes_different += (xor_diff[i] > 0);
+      }
+
+   err << "\nProduced: " << Botan::hex_encode(produced, produced_size)
+       << "\nExpected: " << Botan::hex_encode(expected, expected_size);
+
+   if(bytes_different > 0)
+      {
+      err << "\nXOR Diff: " << Botan::hex_encode(xor_diff);
+      }
+
+   return test_failure(err.str());
+   }
+
+bool Test::Result::test_is_nonempty(const std::string& what_is_it, const std::string& to_examine)
+   {
+   if(to_examine.empty())
+      {
+      return test_failure(what_is_it + " was empty");
+      }
+   return test_success();
+   }
+
+bool Test::Result::test_eq(const std::string& what, const std::string& produced, const std::string& expected)
+   {
+   return test_is_eq(what, produced, expected);
+   }
+
+bool Test::Result::test_eq(const std::string& what, const char* produced, const char* expected)
+   {
+   return test_is_eq(what, std::string(produced), std::string(expected));
+   }
+
+bool Test::Result::test_eq(const std::string& what, size_t produced, size_t expected)
+   {
+   return test_is_eq(what, produced, expected);
+   }
+
+bool Test::Result::test_eq_sz(const std::string& what, size_t produced, size_t expected)
+   {
+   return test_is_eq(what, produced, expected);
+   }
+
+bool Test::Result::test_eq(const std::string& what,
+                           Botan::OctetString produced,
+                           Botan::OctetString expected)
+   {
+   std::ostringstream out;
+   out << m_who << " " << what;
+
+   if(produced == expected)
+      {
+      out << " produced expected result " << produced.to_string();
+      return test_success(out.str());
+      }
+   else
+      {
+      out << " produced unexpected result '" << produced.to_string() << "' expected '" << expected.to_string() << "'";
+      return test_failure(out.str());
+      }
+   }
+
+bool Test::Result::test_lt(const std::string& what, size_t produced, size_t expected)
+   {
+   if(produced >= expected)
+      {
+      std::ostringstream err;
+      err << m_who << " " << what;
+      err << " unexpected result " << produced << " >= " << expected;
+      return test_failure(err.str());
+      }
+
+   return test_success();
+   }
+
+bool Test::Result::test_lte(const std::string& what, size_t produced, size_t expected)
+   {
+   if(produced > expected)
+      {
+      std::ostringstream err;
+      err << m_who << " " << what << " unexpected result " << produced << " > " << expected;
+      return test_failure(err.str());
+      }
+
+   return test_success();
+   }
+
+bool Test::Result::test_gte(const std::string& what, size_t produced, size_t expected)
+   {
+   if(produced < expected)
+      {
+      std::ostringstream err;
+      err << m_who;
+      err << " " << what;
+      err << " unexpected result " << produced << " < " << expected;
+      return test_failure(err.str());
+      }
+
+   return test_success();
+   }
+
+bool Test::Result::test_gt(const std::string& what, size_t produced, size_t expected)
+   {
+   if(produced <= expected)
+      {
+      std::ostringstream err;
+      err << m_who;
+      err << " " << what;
+      err << " unexpected result " << produced << " <= " << expected;
+      return test_failure(err.str());
+      }
+
+   return test_success();
+   }
+
+bool Test::Result::test_ne(const std::string& what, const std::string& str1, const std::string& str2)
+   {
+   if(str1 != str2)
+      {
+      return test_success(str1 + " != " + str2);
+      }
+
+   return test_failure(who() + " " + what + " produced matching strings " + str1);
+   }
+
+bool Test::Result::test_ne(const std::string& what, size_t produced, size_t expected)
+   {
+   if(produced != expected)
+      {
+      return test_success();
+      }
+
+   std::ostringstream err;
+   err << who() << " " << what << " produced " << produced << " unexpected value";
+   return test_failure(err.str());
+   }
+
+#if defined(BOTAN_HAS_BIGINT)
+bool Test::Result::test_eq(const std::string& what, const BigInt& produced, const BigInt& expected)
+   {
+   return test_is_eq(what, produced, expected);
+   }
+
+bool Test::Result::test_ne(const std::string& what, const BigInt& produced, const BigInt& expected)
+   {
+   if(produced != expected)
+      {
+      return test_success();
+      }
+
+   std::ostringstream err;
+   err << who() << " " << what << " produced " << produced << " prohibited value";
+   return test_failure(err.str());
+   }
+#endif
+
+#if defined(BOTAN_HAS_EC_CURVE_GFP)
+bool Test::Result::test_eq(const std::string& what,
+                           const Botan::PointGFp& a, const Botan::PointGFp& b)
+   {
+   //return test_is_eq(what, a, b);
+   if(a == b)
+      {
+      return test_success();
+      }
+
+   std::ostringstream err;
+   err << who() << " " << what << " a=(" << a.get_affine_x() << "," << a.get_affine_y() << ")"
+       << " b=(" << b.get_affine_x() << "," << b.get_affine_y();
+   return test_failure(err.str());
+   }
+#endif
+
+bool Test::Result::test_eq(const std::string& what, bool produced, bool expected)
+   {
+   return test_is_eq(what, produced, expected);
+   }
+
+bool Test::Result::test_rc(const std::string& func, int expected, int rc)
+   {
+   if(expected != rc)
+      {
+      std::ostringstream err;
+      err << m_who;
+      err << " call to " << func << " unexpectedly returned " << rc;
+      err << " but expecting " << expected;
+      return test_failure(err.str());
+      }
+
+   return test_success();
+   }
+
+std::vector<std::string> Test::possible_providers(const std::string&)
+   {
+   return Test::provider_filter({ "base" });
+   }
+
+//static
+std::string Test::format_time(uint64_t ns)
+   {
+   std::ostringstream o;
+
+   if(ns > 1000000000)
+      {
+      o << std::setprecision(2) << std::fixed << ns / 1000000000.0 << " sec";
+      }
+   else
+      {
+      o << std::setprecision(2) << std::fixed << ns / 1000000.0 << " msec";
+      }
+
+   return o.str();
+   }
+
+std::string Test::Result::result_string() const
+   {
+   const bool verbose = Test::options().verbose();
+
+   if(tests_run() == 0 && !verbose)
+      {
+      return "";
+      }
+
+   std::ostringstream report;
+
+   report << who() << " ran ";
+
+   if(tests_run() == 0)
+      {
+      report << "ZERO";
+      }
+   else
+      {
+      report << tests_run();
+      }
+   report << " tests";
+
+   if(m_ns_taken > 0)
+      {
+      report << " in " << format_time(m_ns_taken);
+      }
+
+   if(tests_failed())
+      {
+      report << " " << tests_failed() << " FAILED";
+      }
+   else
+      {
+      report << " all ok";
+      }
+
+   report << "\n";
+
+   for(size_t i = 0; i != m_fail_log.size(); ++i)
+      {
+      report << "Failure " << (i + 1) << ": " << m_fail_log[i] << "\n";
+      }
+
+   if(m_fail_log.size() > 0 || tests_run() == 0 || verbose)
+      {
+      for(size_t i = 0; i != m_log.size(); ++i)
+         {
+         report << "Note " << (i + 1) << ": " << m_log[i] << "\n";
+         }
+      }
+
+   return report.str();
+   }
+
+// static Test:: functions
+//static
+std::map<std::string, std::function<Test* ()>>& Test::global_registry()
+   {
+   static std::map<std::string, std::function<Test* ()>> g_test_registry;
+   return g_test_registry;
+   }
+
+//static
+void Test::register_test(const std::string& name,
+                         std::function<Test* ()> maker_fn)
+   {
+   if(Test::global_registry().count(name) != 0)
+      throw Test_Error("Duplicate registration of test '" + name + "'");
+
+   Test::global_registry().insert(std::make_pair(name, maker_fn));
+   }
+
+//static
+uint64_t Test::timestamp()
+   {
+   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
+   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+   }
+
+//static
+std::set<std::string> Test::registered_tests()
+   {
+   return Botan::map_keys_as_set(Test::global_registry());
+   }
+
+//static
+std::unique_ptr<Test> Test::get_test(const std::string& test_name)
+   {
+   auto i = Test::global_registry().find(test_name);
+   if(i != Test::global_registry().end())
+      {
+      return std::unique_ptr<Test>(i->second());
+      }
+   return nullptr;
+   }
+
+//static
+std::string Test::temp_file_name(const std::string& basename)
+   {
+   // TODO add a --tmp-dir option to the tests to specify where these files go
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+
+   // POSIX only calls for 6 'X' chars but OpenBSD allows arbitrary amount
+   std::string mkstemp_basename = "/tmp/" + basename + ".XXXXXXXXXX";
+
+   int fd = ::mkstemp(&mkstemp_basename[0]);
+
+   // error
+   if(fd < 0)
+      {
+      return "";
+      }
+
+   ::close(fd);
+
+   return mkstemp_basename;
+#else
+   // For now just create the temp in the current working directory
+   return basename;
+#endif
+   }
+
+std::string Test::read_data_file(const std::string& path)
+   {
+   const std::string fsname = Test::data_file(path);
+   std::ifstream file(fsname.c_str());
+   if(!file.good())
+      {
+      throw Test_Error("Error reading from " + fsname);
+      }
+
+   return std::string((std::istreambuf_iterator<char>(file)),
+                      std::istreambuf_iterator<char>());
+   }
+
+std::vector<uint8_t> Test::read_binary_data_file(const std::string& path)
+   {
+   const std::string fsname = Test::data_file(path);
+   std::ifstream file(fsname.c_str(), std::ios::binary);
+   if(!file.good())
+      {
+      throw Test_Error("Error reading from " + fsname);
+      }
+
+   std::vector<uint8_t> contents;
+
+   while(file.good())
+      {
+      std::vector<uint8_t> buf(4096);
+      file.read(reinterpret_cast<char*>(buf.data()), buf.size());
+      const size_t got = static_cast<size_t>(file.gcount());
+
+      if(got == 0 && file.eof())
+         {
+         break;
+         }
+
+      contents.insert(contents.end(), buf.data(), buf.data() + got);
+      }
+
+   return contents;
+   }
+
+// static member variables of Test
+
+Test_Options Test::m_opts;
+std::unique_ptr<Botan::RandomNumberGenerator> Test::m_test_rng;
+
+//static
+void Test::set_test_options(const Test_Options& opts)
+   {
+   m_opts = opts;
+   }
+
+//static
+void Test::set_test_rng(std::unique_ptr<Botan::RandomNumberGenerator> rng)
+   {
+   m_test_rng.reset(rng.release());
+   }
+
+//static
+std::string Test::data_file(const std::string& what)
+   {
+   return Test::data_dir() + "/" + what;
+   }
+
+//static
+std::vector<std::string> Test::provider_filter(const std::vector<std::string>& in)
+   {
+   if(m_opts.provider().empty())
+      {
+      return in;
+      }
+   for(auto&& provider : in)
+      {
+      if(provider == m_opts.provider())
+         {
+         return std::vector<std::string> { provider };
+         }
+      }
+   return std::vector<std::string> {};
+   }
+
+//static
+Botan::RandomNumberGenerator& Test::rng()
+   {
+   if(!m_test_rng)
+      {
+      throw Test_Error("Test requires RNG but no RNG set with Test::set_test_rng");
+      }
+   return *m_test_rng;
+   }
+
+std::string Test::random_password()
+   {
+   const size_t len = 1 + Test::rng().next_byte() % 32;
+   return Botan::hex_encode(Test::rng().random_vec(len));
+   }
+
+std::vector<std::vector<uint8_t>> VarMap::get_req_bin_list(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+
+   std::vector<std::vector<uint8_t>> bin_list;
+
+   for(auto&& part : Botan::split_on(i->second, ','))
+      {
+      try
+         {
+         bin_list.push_back(Botan::hex_decode(part));
+         }
+      catch(std::exception& e)
+         {
+         std::ostringstream oss;
+         oss << "Bad input '" << part << "'" << " in binary list key " << key << " - " << e.what();
+         throw Test_Error(oss.str());
+         }
+      }
+
+   return bin_list;
+   }
+
+std::vector<uint8_t> VarMap::get_req_bin(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+
+   try
+      {
+      return Botan::hex_decode(i->second);
+      }
+   catch(std::exception& e)
+      {
+      std::ostringstream oss;
+      oss << "Bad input '" << i->second << "'" << " for key " << key << " - " << e.what();
+      throw Test_Error(oss.str());
+      }
+   }
+
+std::string VarMap::get_opt_str(const std::string& key, const std::string& def_value) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      return def_value;
+      }
+   return i->second;
+   }
+
+bool VarMap::get_req_bool(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+
+   if(i->second == "true")
+      {
+      return true;
+      }
+   else if(i->second == "false")
+      {
+      return false;
+      }
+   else
+      {
+      throw Test_Error("Invalid boolean for key '" + key + "' value '" + i->second + "'");
+      }
+   }
+
+size_t VarMap::get_req_sz(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+   return Botan::to_u32bit(i->second);
+   }
+
+uint8_t VarMap::get_req_u8(const std::string& key) const
+   {
+   const size_t s = this->get_req_sz(key);
+   if(s > 256)
+      {
+      throw Test_Error("Invalid " + key + " expected uint8_t got " + std::to_string(s));
+      }
+   return static_cast<uint8_t>(s);
+   }
+
+uint32_t VarMap::get_req_u32(const std::string& key) const
+   {
+   return static_cast<uint32_t>(get_req_sz(key));
+   }
+
+uint64_t VarMap::get_req_u64(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+   try
+      {
+      return std::stoull(i->second);
+      }
+   catch(std::exception&)
+      {
+      throw Test_Error("Invalid u64 value '" + i->second + "'");
+      }
+   }
+
+size_t VarMap::get_opt_sz(const std::string& key, const size_t def_value) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      return def_value;
+      }
+   return Botan::to_u32bit(i->second);
+   }
+
+uint64_t VarMap::get_opt_u64(const std::string& key, const uint64_t def_value) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      return def_value;
+      }
+   try
+      {
+      return std::stoull(i->second);
+      }
+   catch(std::exception&)
+      {
+      throw Test_Error("Invalid u64 value '" + i->second + "'");
+      }
+   }
+
+std::vector<uint8_t> VarMap::get_opt_bin(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      return std::vector<uint8_t>();
+      }
+
+   try
+      {
+      return Botan::hex_decode(i->second);
+      }
+   catch(std::exception&)
+      {
+      throw Test_Error("Test invalid hex input '" + i->second + "'" +
+                       + " for key " + key);
+      }
+   }
+
+std::string VarMap::get_req_str(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+   return i->second;
+   }
+
+#if defined(BOTAN_HAS_BIGINT)
+Botan::BigInt VarMap::get_req_bn(const std::string& key) const
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      throw Test_Error("Test missing variable " + key);
+      }
+
+   try
+      {
+      return Botan::BigInt(i->second);
+      }
+   catch(std::exception&)
+      {
+      throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
+      }
+   }
+
+Botan::BigInt VarMap::get_opt_bn(const std::string& key,
+                                 const Botan::BigInt& def_value) const
+
+   {
+   auto i = m_vars.find(key);
+   if(i == m_vars.end())
+      {
+      return def_value;
+      }
+
+   try
+      {
+      return Botan::BigInt(i->second);
+      }
+   catch(std::exception&)
+      {
+      throw Test_Error("Test invalid bigint input '" + i->second + "' for key " + key);
+      }
+   }
+#endif
+
+Text_Based_Test::Text_Based_Test(const std::string& data_src,
+                                 const std::string& required_keys_str,
+                                 const std::string& optional_keys_str) :
+   m_data_src(data_src)
+   {
+   if(required_keys_str.empty())
+      {
+      throw Test_Error("Invalid test spec");
+      }
+
+   std::vector<std::string> required_keys = Botan::split_on(required_keys_str, ',');
+   std::vector<std::string> optional_keys = Botan::split_on(optional_keys_str, ',');
+
+   m_required_keys.insert(required_keys.begin(), required_keys.end());
+   m_optional_keys.insert(optional_keys.begin(), optional_keys.end());
+   m_output_key = required_keys.at(required_keys.size() - 1);
+   }
+
+std::string Text_Based_Test::get_next_line()
+   {
+   while(true)
+      {
+      if(m_cur == nullptr || m_cur->good() == false)
+         {
+         if(m_srcs.empty())
+            {
+            if(m_first)
+               {
+               const std::string full_path = Test::data_dir() + "/" + m_data_src;
+               if(full_path.find(".vec") != std::string::npos)
+                  {
+                  m_srcs.push_back(full_path);
+                  }
+               else
+                  {
+                  const auto fs = Botan::get_files_recursive(full_path);
+                  m_srcs.assign(fs.begin(), fs.end());
+                  if(m_srcs.empty())
+                     {
+                     throw Test_Error("Error reading test data dir " + full_path);
+                     }
+                  }
+
+               m_first = false;
+               }
+            else
+               {
+               return ""; // done
+               }
+            }
+
+         m_cur.reset(new std::ifstream(m_srcs[0]));
+         m_cur_src_name = m_srcs[0];
+
+         // Reinit cpuid on new file if needed
+         if(m_cpu_flags.empty() == false)
+            {
+            m_cpu_flags.clear();
+            Botan::CPUID::initialize();
+            }
+
+         if(!m_cur->good())
+            {
+            throw Test_Error("Could not open input file '" + m_cur_src_name);
+            }
+
+         m_srcs.pop_front();
+         }
+
+      while(m_cur->good())
+         {
+         std::string line;
+         std::getline(*m_cur, line);
+
+         if(line.empty())
+            {
+            continue;
+            }
+
+         if(line[0] == '#')
+            {
+            if(line.compare(0, 6, "#test ") == 0)
+               {
+               return line;
+               }
+            else
+               {
+               continue;
+               }
+            }
+
+         return line;
+         }
+      }
+   }
+
+namespace {
+
+// strips leading and trailing but not internal whitespace
+std::string strip_ws(const std::string& in)
+   {
+   const char* whitespace = " ";
+
+   const auto first_c = in.find_first_not_of(whitespace);
+   if(first_c == std::string::npos)
+      {
+      return "";
+      }
+
+   const auto last_c = in.find_last_not_of(whitespace);
+
+   return in.substr(first_c, last_c - first_c + 1);
+   }
+
+std::vector<uint64_t>
+parse_cpuid_bits(const std::vector<std::string>& tok)
+   {
+   std::vector<uint64_t> bits;
+   for(size_t i = 1; i < tok.size(); ++i)
+      {
+      const std::vector<Botan::CPUID::CPUID_bits> more = Botan::CPUID::bit_from_string(tok[i]);
+      bits.insert(bits.end(), more.begin(), more.end());
+      }
+
+   return bits;
+   }
+
+}
+
+bool Text_Based_Test::skip_this_test(const std::string& /*header*/,
+                                     const VarMap& /*vars*/)
+   {
+   return false;
+   }
+
+std::vector<Test::Result> Text_Based_Test::run()
+   {
+   std::vector<Test::Result> results;
+
+   std::string header, header_or_name = m_data_src;
+   VarMap vars;
+   size_t test_cnt = 0;
+
+   while(true)
+      {
+      const std::string line = get_next_line();
+      if(line.empty()) // EOF
+         {
+         break;
+         }
+
+      if(line.compare(0, 6, "#test ") == 0)
+         {
+         std::vector<std::string> pragma_tokens = Botan::split_on(line.substr(6), ' ');
+
+         if(pragma_tokens.empty())
+            {
+            throw Test_Error("Empty pragma found in " + m_cur_src_name);
+            }
+
+         if(pragma_tokens[0] != "cpuid")
+            {
+            throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
+            }
+
+         m_cpu_flags = parse_cpuid_bits(pragma_tokens);
+
+         continue;
+         }
+      else if(line[0] == '#')
+         {
+         throw Test_Error("Unknown test pragma '" + line + "' in " + m_cur_src_name);
+         }
+
+      if(line[0] == '[' && line[line.size() - 1] == ']')
+         {
+         header = line.substr(1, line.size() - 2);
+         header_or_name = header;
+         test_cnt = 0;
+         vars.clear();
+         continue;
+         }
+
+      const std::string test_id = "test " + std::to_string(test_cnt);
+
+      auto equal_i = line.find_first_of('=');
+
+      if(equal_i == std::string::npos)
+         {
+         results.push_back(Test::Result::Failure(header_or_name,
+                                                 "invalid input '" + line + "'"));
+         continue;
+         }
+
+      std::string key = strip_ws(std::string(line.begin(), line.begin() + equal_i - 1));
+      std::string val = strip_ws(std::string(line.begin() + equal_i + 1, line.end()));
+
+      if(m_required_keys.count(key) == 0 && m_optional_keys.count(key) == 0)
+         results.push_back(Test::Result::Failure(header_or_name,
+                                                 test_id + " failed unknown key " + key));
+
+      vars.add(key, val);
+
+      if(key == m_output_key)
+         {
+         try
+            {
+            if(skip_this_test(header, vars))
+               continue;
+
+            ++test_cnt;
+
+            uint64_t start = Test::timestamp();
+
+            Test::Result result = run_one_test(header, vars);
+            if(m_cpu_flags.size() > 0)
+               {
+               for(auto const& cpuid_u64 : m_cpu_flags)
+                  {
+                  Botan::CPUID::CPUID_bits cpuid_bit = static_cast<Botan::CPUID::CPUID_bits>(cpuid_u64);
+                  if(Botan::CPUID::has_cpuid_bit(cpuid_bit))
+                     {
+                     Botan::CPUID::clear_cpuid_bit(cpuid_bit);
+                     // now re-run the test
+                     result.merge(run_one_test(header, vars));
+                     }
+                  }
+               Botan::CPUID::initialize();
+               }
+            result.set_ns_consumed(Test::timestamp() - start);
+
+            if(result.tests_failed())
+               {
+               std::ostringstream oss;
+               oss << "Test # " << test_cnt << " ";
+               if(!header.empty())
+                  oss << header << " ";
+               oss << " failed [Key=" << vars.get_req_str(m_output_key) << "]";
+
+               result.test_note(oss.str());
+               }
+            results.push_back(result);
+            }
+         catch(std::exception& e)
+            {
+            std::ostringstream oss;
+            oss << "Test # " << test_cnt << " ";
+            if(!header.empty())
+               oss << header << " ";
+            oss << " failed with exception '" << e.what() << "'";
+            oss << " [Key=" << vars.get_req_str(m_output_key) << "]";
+
+            results.push_back(Test::Result::Failure(header_or_name, oss.str()));
+            }
+
+         if(clear_between_callbacks())
+            {
+            vars.clear();
+            }
+         }
+      }
+
+   if(results.empty())
+      {
+      return results;
+      }
+
+   try
+      {
+      std::vector<Test::Result> final_tests = run_final_tests();
+      results.insert(results.end(), final_tests.begin(), final_tests.end());
+      }
+   catch(std::exception& e)
+      {
+      results.push_back(Test::Result::Failure(header_or_name,
+                                              "run_final_tests exception " + std::string(e.what())));
+      }
+
+   m_first = true;
+
+   return results;
+   }
+
+}
diff --git a/third_party/botan/src/tests/tests.h b/third_party/botan/src/tests/tests.h
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/tests.h
@@ -0,0 +1,650 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TESTS_H_
+#define BOTAN_TESTS_H_
+
+#include <botan/build.h>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <botan/symkey.h>
+#include <iosfwd>
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_BIGINT)
+class BigInt;
+#endif
+
+#if defined(BOTAN_HAS_EC_CURVE_GFP)
+class PointGFp;
+#endif
+
+}
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_BIGINT)
+   using Botan::BigInt;
+#endif
+
+class Test_Error final : public Botan::Exception
+   {
+   public:
+      explicit Test_Error(const std::string& what) : Exception("Test error", what) {}
+      Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::Unknown; }
+   };
+
+class Test_Options
+   {
+   public:
+      Test_Options() = default;
+
+      Test_Options(const std::vector<std::string>& requested_tests,
+                   const std::vector<std::string>& skip_tests,
+                   const std::string& data_dir,
+                   const std::string& pkcs11_lib,
+                   const std::string& provider,
+                   const std::string& drbg_seed,
+                   size_t test_runs,
+                   size_t test_threads,
+                   bool verbose,
+                   bool log_success,
+                   bool run_online_tests,
+                   bool run_long_tests,
+                   bool abort_on_first_fail) :
+         m_requested_tests(requested_tests),
+         m_skip_tests(skip_tests.begin(), skip_tests.end()),
+         m_data_dir(data_dir),
+         m_pkcs11_lib(pkcs11_lib),
+         m_provider(provider),
+         m_drbg_seed(drbg_seed),
+         m_test_runs(test_runs),
+         m_test_threads(test_threads),
+         m_verbose(verbose),
+         m_log_success(log_success),
+         m_run_online_tests(run_online_tests),
+         m_run_long_tests(run_long_tests),
+         m_abort_on_first_fail(abort_on_first_fail)
+         {
+         }
+
+      const std::vector<std::string>& requested_tests() const
+         { return m_requested_tests; }
+
+      const std::set<std::string>& skip_tests() const
+         { return m_skip_tests; }
+
+      const std::string& data_dir() const { return m_data_dir; }
+
+      const std::string& pkcs11_lib() const { return m_pkcs11_lib; }
+
+      const std::string& provider() const { return m_provider; }
+
+      const std::string& drbg_seed() const { return m_drbg_seed; }
+
+      size_t test_runs() const { return m_test_runs; }
+
+      size_t test_threads() const { return m_test_threads; }
+
+      bool log_success() const { return m_log_success; }
+
+      bool run_online_tests() const { return m_run_online_tests; }
+
+      bool run_long_tests() const { return m_run_long_tests; }
+
+      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
+
+      bool verbose() const { return m_verbose; }
+
+   private:
+      std::vector<std::string> m_requested_tests;
+      std::set<std::string> m_skip_tests;
+      std::string m_data_dir;
+      std::string m_pkcs11_lib;
+      std::string m_provider;
+      std::string m_drbg_seed;
+      size_t m_test_runs;
+      size_t m_test_threads;
+      bool m_verbose;
+      bool m_log_success;
+      bool m_run_online_tests;
+      bool m_run_long_tests;
+      bool m_abort_on_first_fail;
+   };
+
+/*
+* A generic test which returns a set of results when run.
+* The tests may not all have the same type (for example test
+* "block" returns results for "AES-128" and "AES-256").
+*
+* For most test cases you want Text_Based_Test derived below
+*/
+class Test
+   {
+   public:
+
+      /*
+      * Some number of test results, all associated with who()
+      */
+      class Result final
+         {
+         public:
+            explicit Result(const std::string& who) : m_who(who) {}
+
+            size_t tests_passed() const
+               {
+               return m_tests_passed;
+               }
+            size_t tests_failed() const
+               {
+               return m_fail_log.size();
+               }
+            size_t tests_run() const
+               {
+               return tests_passed() + tests_failed();
+               }
+            bool any_results() const
+               {
+               return tests_run() > 0;
+               }
+
+            const std::string& who() const
+               {
+               return m_who;
+               }
+
+            std::string result_string() const;
+
+            static Result Failure(const std::string& who,
+                                  const std::string& what)
+               {
+               Result r(who);
+               r.test_failure(what);
+               return r;
+               }
+
+            static Result Note(const std::string& who,
+                               const std::string& what)
+               {
+               Result r(who);
+               r.test_note(what);
+               return r;
+               }
+
+            static Result OfExpectedFailure(bool expecting_failure,
+                                            const Test::Result& result)
+               {
+               if(!expecting_failure)
+                  {
+                  return result;
+                  }
+
+               if(result.tests_failed() == 0)
+                  {
+                  Result r = result;
+                  r.test_failure("Expected this test to fail, but it did not");
+                  return r;
+                  }
+               else
+                  {
+                  Result r(result.who());
+                  r.test_note("Got expected failure");
+                  return r;
+                  }
+               }
+
+            void merge(const Result& other);
+
+            void test_note(const std::string& note, const char* extra = nullptr);
+
+            template<typename Alloc>
+            void test_note(const std::string& who, const std::vector<uint8_t, Alloc>& vec)
+               {
+               const std::string hex = Botan::hex_encode(vec);
+               return test_note(who, hex.c_str());
+               }
+
+            void note_missing(const std::string& thing);
+
+            bool test_success(const std::string& note = "");
+
+            bool test_failure(const std::string& err);
+
+            bool test_failure(const std::string& what, const std::string& error);
+
+            void test_failure(const std::string& what, const uint8_t buf[], size_t buf_len);
+
+            template<typename Alloc>
+            void test_failure(const std::string& what, const std::vector<uint8_t, Alloc>& buf)
+               {
+               test_failure(what, buf.data(), buf.size());
+               }
+
+            bool confirm(const std::string& what, bool expr)
+               {
+               return test_eq(what, expr, true);
+               }
+
+            template<typename T>
+            bool test_is_eq(const T& produced, const T& expected)
+               {
+               return test_is_eq("comparison", produced, expected);
+               }
+
+            template<typename T>
+            bool test_is_eq(const std::string& what, const T& produced, const T& expected)
+               {
+               std::ostringstream out;
+               out << m_who << " " << what;
+
+               if(produced == expected)
+                  {
+                  out << " produced expected result";
+                  return test_success(out.str());
+                  }
+               else
+                  {
+                  out << " produced unexpected result '" << produced << "' expected '" << expected << "'";
+                  return test_failure(out.str());
+                  }
+               }
+
+            template<typename T>
+            bool test_not_null(const std::string& what, T* ptr)
+               {
+               if(ptr == nullptr)
+                  return test_failure(what + " was null");
+               else
+                  return test_success(what + " was not null");
+               }
+
+            bool test_eq(const std::string& what, const char* produced, const char* expected);
+
+            bool test_is_nonempty(const std::string& what_is_it, const std::string& to_examine);
+
+            bool test_eq(const std::string& what,
+                         const std::string& produced,
+                         const std::string& expected);
+
+            bool test_eq(const std::string& what, bool produced, bool expected);
+
+            bool test_eq(const std::string& what, size_t produced, size_t expected);
+            bool test_eq_sz(const std::string& what, size_t produced, size_t expected);
+
+            bool test_eq(const std::string& what,
+                         Botan::OctetString produced,
+                         Botan::OctetString expected);
+
+            template<typename I1, typename I2>
+            bool test_int_eq(I1 x, I2 y, const char* what)
+               {
+               return test_eq(what, static_cast<size_t>(x), static_cast<size_t>(y));
+               }
+
+            template<typename I1, typename I2>
+            bool test_int_eq(const std::string& what, I1 x, I2 y)
+               {
+               return test_eq(what.c_str(), static_cast<size_t>(x), static_cast<size_t>(y));
+               }
+
+            bool test_lt(const std::string& what, size_t produced, size_t expected);
+            bool test_lte(const std::string& what, size_t produced, size_t expected);
+            bool test_gt(const std::string& what, size_t produced, size_t expected);
+            bool test_gte(const std::string& what, size_t produced, size_t expected);
+
+            template<typename T>
+            bool test_rc_ok(const std::string& func, T rc)
+               {
+               static_assert(std::is_integral<T>::value, "Integer required.");
+
+               if(rc != 0)
+                  {
+                  std::ostringstream err;
+                  err << m_who;
+                  err << " " << func;
+                  err << " unexpectedly failed with error code " << rc;
+                  return test_failure(err.str());
+                  }
+
+               return test_success();
+               }
+
+            template<typename T>
+            bool test_rc_fail(const std::string& func, const std::string& why, T rc)
+               {
+               static_assert(std::is_integral<T>::value, "Integer required.");
+
+               if(rc == 0)
+                  {
+                  std::ostringstream err;
+                  err << m_who;
+                  err << " call to " << func << " unexpectedly succeeded";
+                  err << " expecting failure because " << why;
+                  return test_failure(err.str());
+                  }
+
+               return test_success();
+               }
+
+            bool test_rc(const std::string& func, int expected, int rc);
+
+            bool test_ne(const std::string& what, size_t produced, size_t expected);
+
+            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
+
+#if defined(BOTAN_HAS_BIGINT)
+            bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);
+            bool test_ne(const std::string& what, const BigInt& produced, const BigInt& expected);
+#endif
+
+#if defined(BOTAN_HAS_EC_CURVE_GFP)
+            bool test_eq(const std::string& what,
+                         const Botan::PointGFp& a,
+                         const Botan::PointGFp& b);
+#endif
+
+            bool test_eq(const char* producer, const std::string& what,
+                         const uint8_t produced[], size_t produced_len,
+                         const uint8_t expected[], size_t expected_len);
+
+            bool test_ne(const std::string& what,
+                         const uint8_t produced[], size_t produced_len,
+                         const uint8_t expected[], size_t expected_len);
+
+            template<typename Alloc1, typename Alloc2>
+            bool test_eq(const std::string& what,
+                         const std::vector<uint8_t, Alloc1>& produced,
+                         const std::vector<uint8_t, Alloc2>& expected)
+               {
+               return test_eq(nullptr, what,
+                              produced.data(), produced.size(),
+                              expected.data(), expected.size());
+               }
+
+            template<typename Alloc1, typename Alloc2>
+            bool test_eq(const std::string& producer, const std::string& what,
+                         const std::vector<uint8_t, Alloc1>& produced,
+                         const std::vector<uint8_t, Alloc2>& expected)
+               {
+               return test_eq(producer.c_str(), what,
+                              produced.data(), produced.size(),
+                              expected.data(), expected.size());
+               }
+
+            template<typename Alloc>
+            bool test_eq(const std::string& what,
+                         const std::vector<uint8_t, Alloc>& produced,
+                         const char* expected_hex)
+               {
+               const std::vector<uint8_t> expected = Botan::hex_decode(expected_hex);
+               return test_eq(nullptr, what,
+                              produced.data(), produced.size(),
+                              expected.data(), expected.size());
+               }
+
+            template<typename Alloc1, typename Alloc2>
+            bool test_ne(const std::string& what,
+                         const std::vector<uint8_t, Alloc1>& produced,
+                         const std::vector<uint8_t, Alloc2>& expected)
+               {
+               return test_ne(what,
+                              produced.data(), produced.size(),
+                              expected.data(), expected.size());
+               }
+
+            bool test_throws(const std::string& what, std::function<void ()> fn);
+
+            bool test_throws(const std::string& what, const std::string& expected,
+                             std::function<void ()> fn);
+
+            void set_ns_consumed(uint64_t ns)
+               {
+               m_ns_taken = ns;
+               }
+
+            void start_timer();
+            void end_timer();
+
+         private:
+            std::string m_who;
+            uint64_t m_started = 0;
+            uint64_t m_ns_taken = 0;
+            size_t m_tests_passed = 0;
+            std::vector<std::string> m_fail_log;
+            std::vector<std::string> m_log;
+         };
+
+      virtual ~Test() = default;
+      virtual std::vector<Test::Result> run() = 0;
+
+      virtual std::vector<std::string> possible_providers(const std::string&);
+
+      static void register_test(const std::string& name,
+                                std::function<Test* ()> maker_fn);
+
+      static std::map<std::string, std::function<Test* ()>>& global_registry();
+
+      static std::set<std::string> registered_tests();
+
+      static std::unique_ptr<Test> get_test(const std::string& test_name);
+
+      static std::string data_file(const std::string& what);
+
+      static std::string format_time(uint64_t nanoseconds);
+
+      template<typename Alloc>
+      static std::vector<uint8_t, Alloc>
+      mutate_vec(const std::vector<uint8_t, Alloc>& v,
+                 bool maybe_resize = false,
+                 size_t min_offset = 0)
+         {
+         auto& rng = Test::rng();
+
+         std::vector<uint8_t, Alloc> r = v;
+
+         if(maybe_resize && (r.empty() || rng.next_byte() < 32))
+            {
+            // TODO: occasionally truncate, insert at random index
+            const size_t add = 1 + (rng.next_byte() % 16);
+            r.resize(r.size() + add);
+            rng.randomize(&r[r.size() - add], add);
+            }
+
+         if(r.size() > min_offset)
+            {
+            const size_t offset = std::max<size_t>(min_offset, rng.next_byte() % r.size());
+            const uint8_t perturb = rng.next_nonzero_byte();
+            r[offset] ^= perturb;
+            }
+
+         return r;
+         }
+
+      static void set_test_options(const Test_Options& opts);
+
+      static void set_test_rng(std::unique_ptr<Botan::RandomNumberGenerator> rng);
+
+      static const Test_Options& options() { return m_opts; }
+
+      static bool run_long_tests() { return options().run_long_tests(); }
+      static const std::string& data_dir() { return options().data_dir(); }
+      static const std::string& pkcs11_lib() { return options().pkcs11_lib(); }
+
+      static std::string temp_file_name(const std::string& basename);
+
+      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
+
+      static std::string read_data_file(const std::string& path);
+      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
+
+      static Botan::RandomNumberGenerator& rng();
+      static std::string random_password();
+      static uint64_t timestamp(); // nanoseconds arbitrary epoch
+
+   private:
+      static Test_Options m_opts;
+      static std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
+   };
+
+/*
+* Register the test with the runner
+*/
+template<typename Test_Class>
+class TestClassRegistration
+   {
+   public:
+      TestClassRegistration(const std::string& name)
+         {
+         auto test_maker = []() -> Test* { return new Test_Class; };
+         Test::register_test(name, test_maker);
+         }
+   };
+
+#define BOTAN_REGISTER_TEST(name, Test_Class)                 \
+   TestClassRegistration<Test_Class> reg_ ## Test_Class ## _tests(name)
+
+typedef Test::Result (*test_fn)();
+
+class FnTest : public Test
+   {
+   public:
+      FnTest(test_fn fn) : m_fn(fn) {}
+
+      std::vector<Test::Result> run() override
+         {
+         return {m_fn()};
+         }
+
+   private:
+      test_fn m_fn;
+   };
+
+class TestFnRegistration
+   {
+   public:
+      TestFnRegistration(const std::string& name, test_fn fn)
+         {
+         auto test_maker = [=]() -> Test* { return new FnTest(fn); };
+         Test::register_test(name, test_maker);
+         }
+   };
+
+#define BOTAN_REGISTER_TEST_FN(name, fn_name)         \
+   TestFnRegistration reg_ ## fn_name(name, fn_name)
+
+class VarMap
+   {
+   public:
+      void clear() { m_vars.clear(); }
+
+      void add(const std::string& key, const std::string& value)
+         {
+         m_vars[key] = value;
+         }
+
+      bool has_key(const std::string& key) const
+         {
+         return m_vars.count(key) == 1;
+         }
+
+      bool get_req_bool(const std::string& key) const;
+
+      std::vector<uint8_t> get_req_bin(const std::string& key) const;
+      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
+
+      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
+
+#if defined(BOTAN_HAS_BIGINT)
+      Botan::BigInt get_req_bn(const std::string& key) const;
+      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
+#endif
+
+      std::string get_req_str(const std::string& key) const;
+      std::string get_opt_str(const std::string& key,
+                              const std::string& def_value) const;
+
+      size_t get_req_sz(const std::string& key) const;
+
+      uint8_t get_req_u8(const std::string& key) const;
+      uint32_t get_req_u32(const std::string& key) const;
+      uint64_t get_req_u64(const std::string& key) const;
+
+      size_t get_opt_sz(const std::string& key, const size_t def_value) const;
+
+      uint64_t get_opt_u64(const std::string& key, const uint64_t def_value) const;
+
+   private:
+      std::unordered_map<std::string, std::string> m_vars;
+   };
+
+/*
+* A test based on reading an input file which contains key/value pairs
+* Special note: the last value in required_key (there must be at least
+* one), is the output key. This triggers the callback.
+*
+* Calls run_one_test with the variables set. If an ini-style [header]
+* is used in the file, then header will be set to that value. This allows
+* splitting up tests between [valid] and [invalid] tests, or different
+* related algorithms tested in the same file. Use the get_XXX functions
+* on VarMap to retrieve formatted values.
+*
+* If most of your tests are text-based but you find yourself with a few
+* odds-and-ends tests that you want to do, override run_final_tests which
+* can test whatever it likes and returns a vector of Results.
+*/
+class Text_Based_Test : public Test
+   {
+   public:
+      Text_Based_Test(const std::string& input_file,
+                      const std::string& required_keys,
+                      const std::string& optional_keys = "");
+
+      virtual bool clear_between_callbacks() const
+         {
+         return true;
+         }
+
+      std::vector<Test::Result> run() override;
+   protected:
+      std::string get_next_line();
+
+      virtual Test::Result run_one_test(const std::string& header,
+                                        const VarMap& vars) = 0;
+      // Called before run_one_test
+      virtual bool skip_this_test(const std::string& header,
+                                  const VarMap& vars);
+
+      virtual std::vector<Test::Result> run_final_tests()
+         {
+         return std::vector<Test::Result>();
+         }
+
+   private:
+      std::string m_data_src;
+      std::set<std::string> m_required_keys;
+      std::set<std::string> m_optional_keys;
+      std::string m_output_key;
+
+      bool m_first = true;
+      std::unique_ptr<std::istream> m_cur;
+      std::string m_cur_src_name;
+      std::deque<std::string> m_srcs;
+      std::vector<uint64_t> m_cpu_flags;
+   };
+
+}
+
+#endif
diff --git a/third_party/botan/src/tests/unit_asio_stream.cpp b/third_party/botan/src/tests/unit_asio_stream.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_asio_stream.cpp
@@ -0,0 +1,817 @@
+/*
+* TLS ASIO Stream Unit Tests
+* (C) 2018-2020 Jack Lloyd
+*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_TLS_ASIO_STREAM)
+
+#include <botan/asio_stream.h>
+#include <botan/tls_callbacks.h>
+
+// The boost::beast::test::stream we use is available starting from boost
+// version 1.68, so we cannot run these tests with a smaller version.
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106800
+
+// boost::beast::test::stream's include path has been changed in boost version
+// 1.70.
+#if BOOST_VERSION < 107000
+#include <boost/beast/experimental/test/stream.hpp>
+#else
+#include <boost/beast/_experimental/test/stream.hpp>
+#endif
+
+#include <boost/bind.hpp>
+
+namespace Botan_Tests {
+
+namespace net    = boost::asio;
+using error_code = boost::system::error_code;
+
+constexpr uint8_t     TEST_DATA[] = "The story so far: In the beginning the Universe was created. "
+                                    "This has made a lot of people very angry and been widely regarded as a bad move.";
+constexpr std::size_t TEST_DATA_SIZE = 142;
+static_assert(sizeof(TEST_DATA) == TEST_DATA_SIZE, "size of TEST_DATA must match TEST_DATA_SIZE");
+
+/**
+ * Mocked Botan::TLS::Channel. Pretends to perform TLS operations and triggers appropriate callbacks in StreamCore.
+ */
+class MockChannel
+   {
+   public:
+      MockChannel(Botan::TLS::Callbacks& core)
+         : m_callbacks(core)
+         , m_bytes_till_complete_record(TEST_DATA_SIZE)
+         , m_active(false) {}
+
+   public:
+      std::size_t received_data(const uint8_t[], std::size_t buf_size)
+         {
+         if(m_bytes_till_complete_record <= buf_size)
+            {
+            m_callbacks.tls_record_received(0, TEST_DATA, TEST_DATA_SIZE);
+            m_active = true;  // claim to be active once a full record has been received (for handshake test)
+            return 0;
+            }
+         m_bytes_till_complete_record -= buf_size;
+         return m_bytes_till_complete_record;
+         }
+
+      void send(const uint8_t buf[], std::size_t buf_size) { m_callbacks.tls_emit_data(buf, buf_size); }
+
+      bool is_active() { return m_active; }
+
+   protected:
+      Botan::TLS::Callbacks& m_callbacks;
+      std::size_t m_bytes_till_complete_record;  // number of bytes still to read before tls record is completed
+      bool        m_active;
+   };
+
+class ThrowingMockChannel : public MockChannel
+   {
+   public:
+      static boost::system::error_code expected_ec()
+         {
+         return Botan::TLS::Alert::UNEXPECTED_MESSAGE;
+         }
+
+      ThrowingMockChannel(Botan::TLS::Callbacks& core) : MockChannel(core)
+         {
+         }
+
+      std::size_t received_data(const uint8_t[], std::size_t)
+         {
+         throw Botan::TLS::Unexpected_Message("test_error");
+         }
+
+      void send(const uint8_t[], std::size_t)
+         {
+         throw Botan::TLS::Unexpected_Message("test_error");
+         }
+   };
+
+// Unfortunately, boost::beast::test::stream keeps lowest_layer_type private and
+// only friends boost::asio::ssl::stream. We need to make our own.
+class TestStream : public boost::beast::test::stream
+   {
+   public:
+      using boost::beast::test::stream::stream;
+      using lowest_layer_type = boost::beast::test::stream;
+   };
+
+using FailCount = boost::beast::test::fail_count;
+
+class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel>
+   {
+   public:
+      template <typename... Args>
+      AsioStream(Botan::TLS::Context& context, Args&& ... args)
+         : Stream(context, args...)
+         {
+         m_native_handle = std::unique_ptr<MockChannel>(new MockChannel(m_core));
+         }
+
+      virtual ~AsioStream() = default;
+   };
+
+class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel>
+   {
+   public:
+      template <typename... Args>
+      ThrowingAsioStream(Botan::TLS::Context& context, Args&& ... args)
+         : Stream(context, args...)
+         {
+         m_native_handle = std::unique_ptr<ThrowingMockChannel>(new ThrowingMockChannel(m_core));
+         }
+
+      virtual ~ThrowingAsioStream() = default;
+   };
+
+/**
+ * Synchronous tests for Botan::Stream.
+ *
+ * This test validates the asynchronous behavior Botan::Stream, including its utility classes StreamCore and Async_*_Op.
+ * The stream's channel, i.e. TLS_Client or TLS_Server, is mocked and pretends to perform TLS operations (noop) and
+ * provides the test data to the stream.
+ * The underlying network socket, claiming it read / wrote a number of bytes.
+ */
+class Asio_Stream_Tests final : public Test
+   {
+      Botan::Credentials_Manager m_credentials_manager;
+      Botan::Null_RNG m_rng;
+      Botan::TLS::Session_Manager_Noop m_session_manager;
+      Botan::TLS::Default_Policy m_policy;
+
+      Botan::TLS::Context get_context()
+         {
+         return Botan::TLS::Context(m_credentials_manager, m_rng, m_session_manager, m_policy);
+         }
+
+      // use memcmp to check if the data in a is a prefix of the data in b
+      bool contains(const void* a, const void* b, const std::size_t size) { return memcmp(a, b, size) == 0; }
+
+      boost::string_view test_data() const
+         {
+         return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE);
+         }
+
+      void test_sync_handshake(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+
+         ssl.handshake(Botan::TLS::CLIENT);
+
+         Test::Result result("sync TLS handshake");
+         result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
+         results.push_back(result);
+         }
+
+      void test_sync_handshake_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         ssl.next_layer().connect(remote);
+
+         // mimic handshake initialization
+         ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE);
+
+         error_code ec;
+         ssl.handshake(Botan::TLS::CLIENT, ec);
+
+         Test::Result result("sync TLS handshake error");
+         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
+         result.confirm("propagates error code", ec == net::error::no_recovery);
+         results.push_back(result);
+         }
+
+      void test_sync_handshake_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc, test_data());
+         ssl.next_layer().connect(remote);
+
+         error_code ec;
+         ssl.handshake(Botan::TLS::CLIENT, ec);
+
+         Test::Result result("sync TLS handshake error");
+         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
+         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+         results.push_back(result);
+         }
+
+      void test_async_handshake(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+         ssl.next_layer().connect(remote);
+
+         // mimic handshake initialization
+         ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE);
+
+         Test::Result result("async TLS handshake");
+
+         auto handler = [&](const error_code&)
+            {
+            result.confirm("reads from socket", ssl.next_layer().nread() > 0);
+            result.confirm("writes from socket", ssl.next_layer().nwrite() > 0);
+            result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
+            };
+
+         ssl.async_handshake(Botan::TLS::CLIENT, handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_handshake_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         ssl.next_layer().connect(remote);
+
+         // mimic handshake initialization
+         ssl.native_handle()->send(TEST_DATA, TEST_DATA_SIZE);
+
+         Test::Result result("async TLS handshake error");
+
+         auto handler = [&](const error_code &ec)
+            {
+            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
+            result.confirm("propagates error code", ec == net::error::no_recovery);
+            };
+
+         ssl.async_handshake(Botan::TLS::CLIENT, handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_handshake_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc, test_data());
+         ssl.next_layer().connect(remote);
+
+         Test::Result result("async TLS handshake throw");
+
+         auto handler = [&](const error_code &ec)
+            {
+            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
+            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+            };
+
+         ssl.async_handshake(Botan::TLS::CLIENT, handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_sync_read_some_success(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+
+         const std::size_t buf_size = 128;
+         uint8_t           buf[buf_size];
+         error_code        ec;
+
+         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
+
+         Test::Result result("sync read_some success");
+         result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size));
+         result.test_eq("reads the correct amount of data", bytes_transferred, buf_size);
+         result.confirm("does not report an error", !ec);
+
+         results.push_back(result);
+         }
+
+      void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+         error_code ec;
+
+         std::vector<net::mutable_buffer> data;
+         uint8_t buf1[TEST_DATA_SIZE/2];
+         uint8_t buf2[TEST_DATA_SIZE/2];
+         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE/2));
+         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE/2));
+
+         auto bytes_transferred = net::read(ssl, data, ec);
+
+         Test::Result result("sync read_some buffer sequence");
+
+         result.confirm("reads the correct data",
+                        contains(buf1, TEST_DATA, TEST_DATA_SIZE/2) &&
+                        contains(buf2, TEST_DATA+TEST_DATA_SIZE/2, TEST_DATA_SIZE/2));
+         result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
+         result.confirm("does not report an error", !ec);
+
+         results.push_back(result);
+         }
+
+      void test_sync_read_some_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         ssl.next_layer().connect(remote);
+
+         uint8_t    buf[128];
+         error_code ec;
+
+         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
+
+         Test::Result result("sync read_some error");
+         result.test_eq("didn't transfer anything", bytes_transferred, 0);
+         result.confirm("propagates error code", ec == net::error::no_recovery);
+
+         results.push_back(result);
+         }
+
+      void test_sync_read_some_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc, test_data());
+         ssl.next_layer().connect(remote);
+
+         uint8_t    buf[128];
+         error_code ec;
+
+         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
+
+         Test::Result result("sync read_some throw");
+         result.test_eq("didn't transfer anything", bytes_transferred, 0);
+         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+
+         results.push_back(result);
+         }
+
+      void test_sync_read_zero_buffer(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+
+         const std::size_t buf_size = 128;
+         uint8_t           buf[buf_size];
+         error_code        ec;
+
+         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, std::size_t(0)), ec);
+
+         Test::Result result("sync read_some into zero-size buffer");
+         result.test_eq("reads the correct amount of data", bytes_transferred, 0);
+         // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
+         // would block here. TestStream sets error_code::eof.
+         result.confirm("does not report an error", !ec);
+
+         results.push_back(result);
+         }
+
+      void test_async_read_some_success(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+         uint8_t    data[TEST_DATA_SIZE];
+
+         Test::Result result("async read_some success");
+
+         auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE));
+            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
+            result.confirm("does not report an error", !ec);
+            };
+
+         net::mutable_buffer buf {data, TEST_DATA_SIZE};
+         net::async_read(ssl, buf, read_handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, test_data());
+
+         std::vector<net::mutable_buffer> data;
+         uint8_t buf1[TEST_DATA_SIZE/2];
+         uint8_t buf2[TEST_DATA_SIZE/2];
+         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE/2));
+         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE/2));
+
+         Test::Result result("async read_some buffer sequence");
+
+         auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.confirm("reads the correct data",
+                           contains(buf1, TEST_DATA, TEST_DATA_SIZE/2) &&
+                           contains(buf2, TEST_DATA+TEST_DATA_SIZE/2, TEST_DATA_SIZE/2));
+            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
+            result.confirm("does not report an error", !ec);
+            };
+
+         net::async_read(ssl, data, read_handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_read_some_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         uint8_t    data[TEST_DATA_SIZE];
+
+         Test::Result result("async read_some error");
+
+         auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.test_eq("didn't transfer anything", bytes_transferred, 0);
+            result.confirm("propagates error code", ec == net::error::no_recovery);
+            };
+
+         net::mutable_buffer buf {data, TEST_DATA_SIZE};
+         net::async_read(ssl, buf, read_handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_read_some_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc, test_data());
+         uint8_t    data[TEST_DATA_SIZE];
+
+         Test::Result result("async read_some throw");
+
+         auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.test_eq("didn't transfer anything", bytes_transferred, 0);
+            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+            };
+
+         net::mutable_buffer buf {data, TEST_DATA_SIZE};
+         net::async_read(ssl, buf, read_handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_read_zero_buffer(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+         uint8_t    data[TEST_DATA_SIZE];
+
+         Test::Result result("async read_some into zero-size buffer");
+
+         auto read_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.test_eq("reads the correct amount of data", bytes_transferred, 0);
+            // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
+            // would block here. TestStream sets error_code::eof.
+            result.confirm("does not report an error", !ec);
+            };
+
+         net::mutable_buffer buf {data, std::size_t(0)};
+         net::async_read(ssl, buf, read_handler);
+
+         ssl.next_layer().close_remote();
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_sync_write_some_success(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+         error_code ec;
+
+         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
+
+         Test::Result result("sync write_some success");
+         result.confirm("writes the correct data", remote.str() == test_data());
+         result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
+         result.confirm("does not report an error", !ec);
+
+         results.push_back(result);
+         }
+
+      void test_sync_no_handshake(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         Botan::TLS::Stream<TestStream> ssl(ctx, ioc);  // Note that we're not using MockChannel here
+         ssl.next_layer().connect(remote);
+         error_code ec;
+
+         net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
+
+         Test::Result result("sync write_some without handshake fails gracefully");
+         result.confirm("reports an error", ec.failed());
+
+         results.push_back(result);
+         }
+
+      void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+         error_code ec;
+
+         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
+         std::array<uint8_t, 17 * 1024 + 1> random_data;
+         random_data.fill('4');  // chosen by fair dice roll
+         random_data.back() = '5';
+
+         std::vector<net::const_buffer> data;
+         data.emplace_back(net::const_buffer(random_data.data(), 1));
+         for(std::size_t i = 1; i < random_data.size(); i += 1024)
+            {
+            data.emplace_back(net::const_buffer(random_data.data() + i, 1024));
+            }
+
+         auto bytes_transferred = net::write(ssl, data, ec);
+
+         Test::Result result("sync write_some buffer sequence");
+
+         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
+                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
+
+         result.confirm("writes the correct data",
+                        contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
+         result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
+         result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
+         result.confirm("does not report an error", !ec);
+
+         results.push_back(result);
+         }
+
+      void test_sync_write_some_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         ssl.next_layer().connect(remote);
+
+         error_code ec;
+
+         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
+
+         Test::Result result("sync write_some error");
+         result.test_eq("didn't transfer anything", bytes_transferred, 0);
+         result.confirm("propagates error code", ec == net::error::no_recovery);
+
+         results.push_back(result);
+         }
+
+      void test_sync_write_some_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+         error_code ec;
+
+         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
+
+         Test::Result result("sync write_some throw");
+         result.test_eq("didn't transfer anything", bytes_transferred, 0);
+         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+
+         results.push_back(result);
+         }
+
+      void test_async_write_some_success(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+
+         Test::Result result("async write_some success");
+
+         auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.confirm("writes the correct data", remote.str() == test_data());
+            result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
+            result.confirm("does not report an error", !ec);
+            };
+
+         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream      remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+
+         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
+         std::array<uint8_t, 17 * 1024 + 1> random_data;
+         random_data.fill('4');  // chosen by fair dice roll
+         random_data.back() = '5';
+
+         std::vector<net::const_buffer> src;
+         src.emplace_back(net::const_buffer(random_data.data(), 1));
+         for(std::size_t i = 1; i < random_data.size(); i += 1024)
+            {
+            src.emplace_back(net::const_buffer(random_data.data() + i, 1024));
+            }
+
+         Test::Result result("async write_some buffer sequence");
+
+         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
+                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
+
+         auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.confirm("writes the correct data",
+                           contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
+            result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
+            result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
+            result.confirm("does not report an error", !ec);
+            };
+
+         net::async_write(ssl, src, write_handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_write_some_error(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         // fail right away
+         FailCount  fc{0, net::error::no_recovery};
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         AsioStream ssl(ctx, ioc, fc);
+         ssl.next_layer().connect(remote);
+
+         Test::Result result("async write_some error");
+
+         auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.test_eq("committed some bytes to the core", bytes_transferred, TEST_DATA_SIZE);
+            result.confirm("propagates error code", ec == net::error::no_recovery);
+            };
+
+         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+      void test_async_write_throw(std::vector<Test::Result>& results)
+         {
+         net::io_context ioc;
+         TestStream remote{ioc};
+
+         auto ctx = get_context();
+         ThrowingAsioStream ssl(ctx, ioc);
+         ssl.next_layer().connect(remote);
+
+         Test::Result result("async write_some throw");
+
+         auto write_handler = [&](const error_code &ec, std::size_t bytes_transferred)
+            {
+            result.test_eq("didn't transfer anything", bytes_transferred, 0);
+            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
+            };
+
+         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
+
+         ioc.run();
+         results.push_back(result);
+         }
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         test_sync_no_handshake(results);
+
+         test_sync_handshake(results);
+         test_sync_handshake_error(results);
+         test_sync_handshake_throw(results);
+
+         test_async_handshake(results);
+         test_async_handshake_error(results);
+         test_async_handshake_throw(results);
+
+         test_sync_read_some_success(results);
+         test_sync_read_some_buffer_sequence(results);
+         test_sync_read_some_error(results);
+         test_sync_read_some_throw(results);
+         test_sync_read_zero_buffer(results);
+
+         test_async_read_some_success(results);
+         test_async_read_some_buffer_sequence(results);
+         test_async_read_some_error(results);
+         test_async_read_some_throw(results);
+         test_async_read_zero_buffer(results);
+
+         test_sync_write_some_success(results);
+         test_sync_write_some_buffer_sequence(results);
+         test_sync_write_some_error(results);
+         test_sync_write_some_throw(results);
+
+         test_async_write_some_success(results);
+         test_async_write_some_buffer_sequence(results);
+         test_async_write_some_error(results);
+         test_async_write_throw(results);
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_asio_stream", Asio_Stream_Tests);
+
+}  // namespace Botan_Tests
+
+#endif // BOOST_VERSION
+#endif // BOTAN_HAS_TLS && BOTAN_HAS_BOOST_ASIO
diff --git a/third_party/botan/src/tests/unit_ecc.cpp b/third_party/botan/src/tests/unit_ecc.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_ecc.cpp
@@ -0,0 +1,757 @@
+/*
+* (C) 2007 Falko Strenzke
+*     2007 Manuel Hartl
+*     2009,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+  #include <botan/bigint.h>
+  #include <botan/numthry.h>
+  #include <botan/curve_nistp.h>
+  #include <botan/pk_keys.h>
+  #include <botan/point_gfp.h>
+  #include <botan/ec_group.h>
+  #include <botan/reducer.h>
+  #include <botan/hex.h>
+  #include <botan/data_src.h>
+  #include <botan/x509_key.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+Botan::BigInt test_integer(Botan::RandomNumberGenerator& rng, size_t bits, BigInt max)
+   {
+   /*
+   Produces integers with long runs of ones and zeros, for testing for
+   carry handling problems.
+   */
+   Botan::BigInt x = 0;
+
+   auto flip_prob = [](size_t i) -> double
+                       {
+                       if(i % 64 == 0)
+                          {
+                          return .5;
+                          }
+                       if(i % 32 == 0)
+                          {
+                          return .4;
+                          }
+                       if(i % 8 == 0)
+                          {
+                          return .05;
+                          }
+                       return .01;
+                       };
+
+   bool active = (rng.next_byte() > 128) ? true : false;
+   for(size_t i = 0; i != bits; ++i)
+      {
+      x <<= 1;
+      x += static_cast<int>(active);
+
+      const double prob = flip_prob(i);
+      const double sample = double(rng.next_byte() % 100) / 100.0; // biased
+
+      if(sample < prob)
+         {
+         active = !active;
+         }
+      }
+
+   if(max > 0)
+      {
+      while(x >= max)
+         {
+         const size_t b = x.bits() - 1;
+         BOTAN_ASSERT(x.get_bit(b) == true, "Set");
+         x.clear_bit(b);
+         }
+      }
+
+   return x;
+   }
+
+Botan::PointGFp create_random_point(Botan::RandomNumberGenerator& rng,
+                                    const Botan::EC_Group& group)
+   {
+   const Botan::BigInt& p = group.get_p();
+   const Botan::Modular_Reducer mod_p(p);
+
+   for(;;)
+      {
+      const Botan::BigInt x = Botan::BigInt::random_integer(rng, 1, p);
+      const Botan::BigInt x3 = mod_p.multiply(x, mod_p.square(x));
+      const Botan::BigInt ax = mod_p.multiply(group.get_a(), x);
+      const Botan::BigInt y = mod_p.reduce(x3 + ax + group.get_b());
+      const Botan::BigInt sqrt_y = ressol(y, p);
+
+      if(sqrt_y > 1)
+         {
+         BOTAN_ASSERT_EQUAL(mod_p.square(sqrt_y), y, "Square root is correct");
+         return group.point(x, sqrt_y);
+         }
+      }
+   }
+
+class ECC_Randomized_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override;
+   };
+
+std::vector<Test::Result> ECC_Randomized_Tests::run()
+   {
+   std::vector<Test::Result> results;
+   for(const std::string& group_name : Botan::EC_Group::known_named_groups())
+      {
+      Test::Result result("ECC randomized " + group_name);
+
+      result.start_timer();
+
+      Botan::EC_Group group(group_name);
+
+      const Botan::PointGFp pt = create_random_point(Test::rng(), group);
+      const Botan::BigInt& group_order = group.get_order();
+
+      std::vector<Botan::BigInt> blind_ws;
+
+      try
+         {
+         const size_t trials = (Test::run_long_tests() ? 10 : 3);
+         for(size_t i = 0; i < trials; ++i)
+            {
+            const Botan::BigInt a = Botan::BigInt::random_integer(Test::rng(), 2, group_order);
+            const Botan::BigInt b = Botan::BigInt::random_integer(Test::rng(), 2, group_order);
+            const Botan::BigInt c = a + b;
+
+            const Botan::PointGFp P = pt * a;
+            const Botan::PointGFp Q = pt * b;
+            const Botan::PointGFp R = pt * c;
+
+            Botan::PointGFp P1 = group.blinded_var_point_multiply(pt, a, Test::rng(), blind_ws);
+            Botan::PointGFp Q1 = group.blinded_var_point_multiply(pt, b, Test::rng(), blind_ws);
+            Botan::PointGFp R1 = group.blinded_var_point_multiply(pt, c, Test::rng(), blind_ws);
+
+            Botan::PointGFp A1 = P + Q;
+            Botan::PointGFp A2 = Q + P;
+
+            result.test_eq("p + q", A1, R);
+            result.test_eq("q + p", A2, R);
+
+            A1.force_affine();
+            A2.force_affine();
+            result.test_eq("p + q", A1, R);
+            result.test_eq("q + p", A2, R);
+
+            result.test_eq("p on the curve", P.on_the_curve(), true);
+            result.test_eq("q on the curve", Q.on_the_curve(), true);
+            result.test_eq("r on the curve", R.on_the_curve(), true);
+
+            result.test_eq("P1", P1, P);
+            result.test_eq("Q1", Q1, Q);
+            result.test_eq("R1", R1, R);
+
+            P1.force_affine();
+            Q1.force_affine();
+            R1.force_affine();
+            result.test_eq("P1", P1, P);
+            result.test_eq("Q1", Q1, Q);
+            result.test_eq("R1", R1, R);
+            }
+         }
+      catch(std::exception& e)
+         {
+         result.test_failure(group_name, e.what());
+         }
+      result.end_timer();
+      results.push_back(result);
+      }
+
+   return results;
+   }
+
+BOTAN_REGISTER_TEST("ecc_randomized", ECC_Randomized_Tests);
+
+class NIST_Curve_Reduction_Tests final : public Test
+   {
+   public:
+      typedef std::function<void (Botan::BigInt&, Botan::secure_vector<Botan::word>&)> reducer_fn;
+
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         // Using lambdas here to avoid strange UbSan warning (#1370)
+
+         results.push_back(random_redc_test("P-384", Botan::prime_p384(),
+                              [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
+                                 {
+                                 Botan::redc_p384(p, ws);
+                                 }));
+         results.push_back(random_redc_test("P-256", Botan::prime_p256(),
+                              [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
+                                 {
+                                 Botan::redc_p256(p, ws);
+                                 }));
+         results.push_back(random_redc_test("P-224", Botan::prime_p224(),
+                              [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
+                                 {
+                                 Botan::redc_p224(p, ws);
+                                 }));
+         results.push_back(random_redc_test("P-192", Botan::prime_p192(),
+                              [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
+                                 {
+                                 Botan::redc_p192(p, ws);
+                                 }));
+         results.push_back(random_redc_test("P-521", Botan::prime_p521(),
+                              [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
+                                 {
+                                 Botan::redc_p521(p, ws);
+                                 }));
+
+         return results;
+         }
+
+      Test::Result random_redc_test(const std::string& prime_name,
+                                    const Botan::BigInt& p,
+                                    reducer_fn redc_fn)
+         {
+         const Botan::BigInt p2 = p * p;
+         const size_t p_bits = p.bits();
+
+         Botan::Modular_Reducer p_redc(p);
+         Botan::secure_vector<Botan::word> ws;
+
+         Test::Result result("NIST " + prime_name + " reduction");
+         result.start_timer();
+
+         const size_t trials = (Test::run_long_tests() ? 128 : 16);
+
+         for(size_t i = 0; i <= trials; ++i)
+            {
+            const Botan::BigInt x = test_integer(Test::rng(), 2 * p_bits, p2);
+
+            // TODO: time and report all three approaches
+            const Botan::BigInt v1 = x % p;
+            const Botan::BigInt v2 = p_redc.reduce(x);
+
+            Botan::BigInt v3 = x;
+            redc_fn(v3, ws);
+
+            if(!result.test_eq("reference redc", v1, v2) ||
+               !result.test_eq("specialized redc", v2, v3))
+               {
+               result.test_note("failing input" + Botan::hex_encode(Botan::BigInt::encode(x)));
+               }
+            }
+
+         result.end_timer();
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("nist_redc", NIST_Curve_Reduction_Tests);
+
+class EC_Group_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         for(const std::string& group_name : Botan::EC_Group::known_named_groups())
+            {
+            Test::Result result("EC_Group " + group_name);
+
+            const Botan::OID oid = Botan::OID::from_string(group_name);
+
+            const Botan::EC_Group group(oid);
+
+            result.confirm("EC_Group is known", !group.get_curve_oid().empty());
+            result.confirm("EC_Group is considered valid", group.verify_group(Test::rng(), true));
+
+            result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits());
+            result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes());
+
+            result.confirm("Same group is same", group == Botan::EC_Group(group_name));
+
+            const Botan::EC_Group copy(group.get_p(), group.get_a(), group.get_b(),
+                                       group.get_g_x(), group.get_g_y(),
+                                       group.get_order(), group.get_cofactor());
+
+            result.confirm("Same group is same even with copy", group == copy);
+
+            const auto pt_mult_by_order = group.get_base_point() * group.get_order();
+            result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero());
+
+            if(group.a_is_minus_3())
+               result.test_eq("Group A equals -3", group.get_a(), group.get_p() - 3);
+            else
+               result.test_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3);
+
+            if(group.a_is_zero())
+               result.test_eq("Group A is zero", group.get_a(), BigInt(0));
+            else
+               result.test_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0));
+
+            // get a valid point
+            Botan::PointGFp p = group.get_base_point() * Test::rng().next_nonzero_byte();
+
+            // get a copy
+            Botan::PointGFp q = p;
+
+            p.randomize_repr(Test::rng());
+            q.randomize_repr(Test::rng());
+
+            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
+            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
+
+            q.force_affine();
+
+            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
+            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
+
+            test_ser_der(result, group);
+            test_basic_math(result, group);
+            test_point_swap(result, group);
+            test_zeropoint(result, group);
+
+            results.push_back(result);
+            }
+
+         return results;
+         }
+   private:
+
+      void test_ser_der(Test::Result& result, const Botan::EC_Group& group)
+         {
+         // generate point
+         const Botan::PointGFp pt = create_random_point(Test::rng(), group);
+         const Botan::PointGFp zero = group.zero_point();
+
+         for(auto scheme : { Botan::PointGFp::UNCOMPRESSED,
+                  Botan::PointGFp::COMPRESSED,
+                  Botan::PointGFp::HYBRID })
+            {
+            result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt);
+            result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero);
+            }
+         }
+
+      void test_basic_math(Test::Result& result, const Botan::EC_Group& group)
+         {
+         const Botan::PointGFp& G = group.get_base_point();
+
+         Botan::PointGFp p1 = G * 2;
+         p1 += G;
+
+         result.test_eq("point addition", p1, G * 3);
+
+         p1 -= G * 2;
+
+         result.test_eq("point subtraction", p1, G);
+
+         // The scalar multiplication algorithm relies on this being true:
+         Botan::PointGFp zero_coords = group.point(0, 0);
+         result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve());
+         }
+
+      void test_point_swap(Test::Result& result, const Botan::EC_Group& group)
+         {
+         Botan::PointGFp a(create_random_point(Test::rng(), group));
+         Botan::PointGFp b(create_random_point(Test::rng(), group));
+         b *= Botan::BigInt(Test::rng(), 20);
+
+         Botan::PointGFp c(a);
+         Botan::PointGFp d(b);
+
+         d.swap(c);
+         result.test_eq("swap correct", a, d);
+         result.test_eq("swap correct", b, c);
+         }
+
+      void test_zeropoint(Test::Result& result, const Botan::EC_Group& group)
+         {
+         Botan::PointGFp zero = group.zero_point();
+
+         result.test_throws("Zero point throws", "Cannot convert zero point to affine",
+                            [&]() { zero.get_affine_x(); });
+         result.test_throws("Zero point throws", "Cannot convert zero point to affine",
+                            [&]() { zero.get_affine_y(); });
+
+         const Botan::PointGFp p1 = group.get_base_point() * 2;
+
+         result.confirm("point is on the curve", p1.on_the_curve());
+         result.confirm("point is not zero", !p1.is_zero());
+
+         Botan::PointGFp p2 = p1;
+         p2 -= p1;
+
+         result.confirm("p - q with q = p results in zero", p2.is_zero());
+
+         const Botan::PointGFp minus_p1 = -p1;
+         result.confirm("point is on the curve", minus_p1.on_the_curve());
+         const Botan::PointGFp shouldBeZero = p1 + minus_p1;
+         result.confirm("point is on the curve", shouldBeZero.on_the_curve());
+         result.confirm("point is zero", shouldBeZero.is_zero());
+
+         result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x());
+         result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y());
+
+         result.confirm("zero point is zero", zero.is_zero());
+         result.confirm("zero point is on the curve", zero.on_the_curve());
+         result.test_eq("addition of zero does nothing", p1, p1 + zero);
+         result.test_eq("addition of zero does nothing", p1, zero + p1);
+         result.test_eq("addition of zero does nothing", p1, p1 - zero);
+         result.confirm("zero times anything is the zero point", (zero * 39193).is_zero());
+
+         for(auto scheme : { Botan::PointGFp::UNCOMPRESSED,
+                  Botan::PointGFp::COMPRESSED,
+                  Botan::PointGFp::HYBRID })
+            {
+            const std::vector<uint8_t> v = zero.encode(scheme);
+            result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero);
+            }
+         }
+
+
+   };
+
+BOTAN_REGISTER_TEST("ec_group", EC_Group_Tests);
+
+Test::Result test_decoding_with_seed()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group secp384r1_with_seed(
+      Test::read_data_file("x509/ecc/secp384r1_seed.pem"));
+
+   result.confirm("decoding worked", secp384r1_with_seed.initialized());
+
+   Botan::EC_Group secp384r1("secp384r1");
+
+   result.test_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p());
+
+   return result;
+   }
+
+Test::Result test_coordinates()
+   {
+   Test::Result result("ECC Unit");
+
+   const Botan::BigInt exp_affine_x("16984103820118642236896513183038186009872590470");
+   const Botan::BigInt exp_affine_y("1373093393927139016463695321221277758035357890939");
+
+   // precalculation
+   const Botan::EC_Group secp160r1("secp160r1");
+   const Botan::PointGFp& p_G = secp160r1.get_base_point();
+
+   const Botan::PointGFp point_exp = secp160r1.point(exp_affine_x, exp_affine_y);
+   result.confirm("Point is on the curve", point_exp.on_the_curve());
+
+   const Botan::PointGFp p1 = p_G * 2;
+   result.test_eq("Point affine x", p1.get_affine_x(), exp_affine_x);
+   result.test_eq("Point affine y", p1.get_affine_y(), exp_affine_y);
+   return result;
+   }
+
+
+/**
+Test point multiplication according to
+--------
+SEC 2: Test Vectors for SEC 1
+Certicom Research
+Working Draft
+September, 1999
+Version 0.3;
+Section 2.1.2
+--------
+*/
+Test::Result test_point_mult()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group secp160r1("secp160r1");
+   const Botan::PointGFp& p_G = secp160r1.get_base_point();
+
+   Botan::BigInt d_U("0xaa374ffc3ce144e6b073307972cb6d57b2a4e982");
+   Botan::PointGFp Q_U = d_U * p_G;
+
+   result.test_eq("affine x", Q_U.get_affine_x(), Botan::BigInt("466448783855397898016055842232266600516272889280"));
+   result.test_eq("affine y", Q_U.get_affine_y(), Botan::BigInt("1110706324081757720403272427311003102474457754220"));
+   return result;
+   }
+
+Test::Result test_point_negative()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group secp160r1("secp160r1");
+   const Botan::PointGFp& p_G = secp160r1.get_base_point();
+
+   const Botan::PointGFp p1 = p_G * 2;
+
+   result.test_eq("affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
+   result.test_eq("affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
+
+   const Botan::PointGFp p1_neg = -p1;
+
+   result.test_eq("affine x", p1_neg.get_affine_x(), p1.get_affine_x());
+   result.test_eq("affine y", p1_neg.get_affine_y(),  Botan::BigInt("88408243403763901739989511495005261618427168388"));
+   return result;
+   }
+
+Test::Result test_mult_point()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group secp160r1("secp160r1");
+   const Botan::PointGFp& p_G = secp160r1.get_base_point();
+
+   Botan::PointGFp p0 = p_G;
+   Botan::PointGFp p1 = p_G * 2;
+
+   p1 *= p0.get_affine_x();
+
+   const Botan::BigInt exp_mult_x(std::string("967697346845926834906555988570157345422864716250"));
+   const Botan::BigInt exp_mult_y(std::string("512319768365374654866290830075237814703869061656"));
+   Botan::PointGFp expected = secp160r1.point(exp_mult_x, exp_mult_y);
+
+   result.test_eq("point mult", p1, expected);
+   return result;
+   }
+
+Test::Result test_mixed_points()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group secp256r1("secp256r1");
+   Botan::EC_Group secp384r1("secp384r1");
+
+   const Botan::PointGFp& G256 = secp256r1.get_base_point();
+   const Botan::PointGFp& G384 = secp384r1.get_base_point();
+
+   result.test_throws("Mixing points from different groups",
+                      [&] { Botan::PointGFp p = G256 + G384; });
+   return result;
+   }
+
+Test::Result test_basic_operations()
+   {
+   Test::Result result("ECC Unit");
+
+   // precalculation
+   Botan::EC_Group secp160r1("secp160r1");
+   const Botan::PointGFp& p_G = secp160r1.get_base_point();
+
+   const Botan::PointGFp p0 = p_G;
+   const Botan::PointGFp p1 = p_G * 2;
+
+   result.test_eq("p1 affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
+   result.test_eq("p1 affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
+
+   const Botan::PointGFp simplePlus = p1 + p0;
+   const Botan::PointGFp exp_simplePlus = secp160r1.point(Botan::BigInt("704859595002530890444080436569091156047721708633"),
+                                                          Botan::BigInt("1147993098458695153857594941635310323215433166682"));
+
+   result.test_eq("point addition", simplePlus, exp_simplePlus);
+
+   const Botan::PointGFp simpleMinus = p1 - p0;
+   result.test_eq("point subtraction", simpleMinus, p_G);
+
+   const Botan::PointGFp simpleMult = p1 * 123456789;
+
+   result.test_eq("point mult affine x", simpleMult.get_affine_x(),
+                  Botan::BigInt("43638877777452195295055270548491599621118743290"));
+   result.test_eq("point mult affine y", simpleMult.get_affine_y(),
+                  Botan::BigInt("56841378500012376527163928510402662349220202981"));
+
+   return result;
+   }
+
+Test::Result test_enc_dec_compressed_160()
+   {
+   Test::Result result("ECC Unit");
+
+   // Test for compressed conversion (02/03) 160bit
+   Botan::EC_Group secp160r1("secp160r1");
+   const std::vector<uint8_t> G_comp = Botan::hex_decode("024A96B5688EF573284664698968C38BB913CBFC82");
+   const Botan::PointGFp p = secp160r1.OS2ECP(G_comp);
+   const std::vector<uint8_t> sv_result = p.encode(Botan::PointGFp::COMPRESSED);
+
+   result.test_eq("result", sv_result, G_comp);
+   return result;
+   }
+
+Test::Result test_enc_dec_compressed_256()
+   {
+   Test::Result result("ECC Unit");
+
+   Botan::EC_Group group("secp256r1");
+
+   const std::string G_secp_comp = "036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
+   const std::vector<uint8_t> sv_G_secp_comp = Botan::hex_decode(G_secp_comp);
+
+   Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_comp);
+   std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::COMPRESSED);
+
+   result.test_eq("compressed_256", sv_result, sv_G_secp_comp);
+   return result;
+   }
+
+
+Test::Result test_enc_dec_uncompressed_112()
+   {
+   Test::Result result("ECC Unit");
+
+   // Test for uncompressed conversion (04) 112bit
+
+   // Curve is secp112r2
+
+   const Botan::BigInt p("0xdb7c2abf62e35e668076bead208b");
+   const Botan::BigInt a("0x6127C24C05F38A0AAAF65C0EF02C");
+   const Botan::BigInt b("0x51DEF1815DB5ED74FCC34C85D709");
+
+   const Botan::BigInt g_x("0x4BA30AB5E892B4E1649DD0928643");
+   const Botan::BigInt g_y("0xADCD46F5882E3747DEF36E956E97");
+
+   const Botan::BigInt order("0x36DF0AAFD8B8D7597CA10520D04B");
+   const Botan::BigInt cofactor("4"); // !
+
+   const Botan::EC_Group group(p, a, b, g_x, g_y, order, cofactor);
+
+   const std::string G_secp_uncomp = "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97";
+   const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
+
+   Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_uncomp);
+   std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::UNCOMPRESSED);
+
+   result.test_eq("uncompressed_112", sv_result, sv_G_secp_uncomp);
+   return result;
+   }
+
+Test::Result test_enc_dec_uncompressed_521()
+   {
+   Test::Result result("ECC Unit");
+
+   // Test for uncompressed conversion(04) with big values(521 bit)
+
+   const std::string G_secp_uncomp =
+      "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2ffA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650";
+
+   const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
+
+   Botan::EC_Group group("secp521r1");
+
+   Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_uncomp);
+
+   std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::UNCOMPRESSED);
+
+   result.test_eq("expected", sv_result, sv_G_secp_uncomp);
+   return result;
+   }
+
+Test::Result test_ecc_registration()
+   {
+   Test::Result result("ECC registration");
+
+   // secp112r1
+   const Botan::BigInt p("0xDB7C2ABF62E35E668076BEAD208B");
+   const Botan::BigInt a("0xDB7C2ABF62E35E668076BEAD2088");
+   const Botan::BigInt b("0x659EF8BA043916EEDE8911702B22");
+
+   const Botan::BigInt g_x("0x09487239995A5EE76B55F9C2F098");
+   const Botan::BigInt g_y("0xA89CE5AF8724C0A23E0E0FF77500");
+   const Botan::BigInt order("0xDB7C2ABF62E35E7628DFAC6561C5");
+
+   const Botan::OID oid("1.3.132.0.6");
+
+   // Creating this object implicitly registers the curve for future use ...
+   Botan::EC_Group reg_group(p, a, b, g_x, g_y, order, 1, oid);
+
+   Botan::EC_Group group(oid);
+
+   result.test_eq("Group registration worked", group.get_p(), p);
+
+   return result;
+   }
+
+class ECC_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_coordinates());
+         results.push_back(test_decoding_with_seed());
+         results.push_back(test_point_mult());
+         results.push_back(test_point_negative());
+         results.push_back(test_mult_point());
+         results.push_back(test_mixed_points());
+         results.push_back(test_basic_operations());
+         results.push_back(test_enc_dec_compressed_160());
+         results.push_back(test_enc_dec_compressed_256());
+         results.push_back(test_enc_dec_uncompressed_112());
+         results.push_back(test_enc_dec_uncompressed_521());
+         results.push_back(test_ecc_registration());
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecc_unit", ECC_Unit_Tests);
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECC_Invalid_Key_Tests final : public Text_Based_Test
+   {
+   public:
+      ECC_Invalid_Key_Tests() :
+         Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
+
+      bool clear_between_callbacks() const override
+         {
+         return false;
+         }
+
+      Test::Result run_one_test(const std::string&, const VarMap& vars) override
+         {
+         Test::Result result("ECC invalid keys");
+
+         const std::string encoded = vars.get_req_str("SubjectPublicKey");
+         Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
+
+         try
+            {
+            std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(key_data));
+            result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
+            }
+         catch(Botan::Decoding_Error&)
+            {
+            result.test_success("Decoding invalid ECC key results in decoding error exception");
+            }
+
+         return result;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecc_invalid", ECC_Invalid_Key_Tests);
+
+#endif
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/unit_ecdh.cpp b/third_party/botan/src/tests/unit_ecdh.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_ecdh.cpp
@@ -0,0 +1,77 @@
+/*
+* ECDH tests
+*
+* (C) 2007 Manuel Hartl (hartl@flexsecure.de)
+*     2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_ECDH)
+   #include <botan/pubkey.h>
+   #include <botan/ecdh.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECDH)
+class ECDH_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_ecdh_normal_derivation());
+
+         return results;
+         }
+   private:
+
+      Test::Result test_ecdh_normal_derivation()
+         {
+         Test::Result result("ECDH key exchange");
+
+         std::vector<std::string> params = { "secp256r1", "secp384r1", "secp521r1", "brainpool256r1" };
+
+         for(auto const& param : params)
+            {
+            try
+               {
+               Botan::EC_Group dom_pars(param);
+               Botan::ECDH_PrivateKey private_a(Test::rng(), dom_pars);
+               Botan::ECDH_PrivateKey private_b(Test::rng(), dom_pars);
+
+               Botan::PK_Key_Agreement ka(private_a, Test::rng(), "KDF2(SHA-512)");
+               Botan::PK_Key_Agreement kb(private_b, Test::rng(), "KDF2(SHA-512)");
+
+               Botan::SymmetricKey alice_key = ka.derive_key(32, private_b.public_value());
+               Botan::SymmetricKey bob_key = kb.derive_key(32, private_a.public_value());
+
+               if(!result.test_eq("same derived key", alice_key.bits_of(), bob_key.bits_of()))
+                  {
+                  result.test_note("Keys where " + alice_key.to_string() + " and " + bob_key.to_string());
+                  }
+               }
+            catch(Botan::Lookup_Error& e)
+               {
+               result.test_note("Skipping because ", e.what());
+               }
+            }
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("ecdh_unit", ECDH_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/unit_ecdsa.cpp b/third_party/botan/src/tests/unit_ecdsa.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_ecdsa.cpp
@@ -0,0 +1,468 @@
+/*
+* ECDSA Tests
+*
+* (C) 2007 Falko Strenzke
+*     2007 Manuel Hartl
+*     2008,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include <botan/hex.h>
+#include <numeric>
+
+#if defined(BOTAN_HAS_ECDSA)
+   #include <botan/pubkey.h>
+   #include <botan/ecdsa.h>
+   #include <botan/ec_group.h>
+   #include <botan/oids.h>
+   #include <botan/pkcs8.h>
+   #include <botan/hash.h>
+   #include <botan/data_src.h>
+#endif
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/x509cert.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_ECDSA)
+
+/**
+* Tests whether the the signing routine will work correctly in case
+* the integer e that is constructed from the message (thus the hash
+* value) is larger than n, the order of the base point.  Tests the
+* signing function of the pk signer object
+*/
+Test::Result test_hash_larger_than_n()
+   {
+   Test::Result result("ECDSA Unit");
+
+   Botan::EC_Group dom_pars("secp160r1");
+
+   // n = 0x0100000000000000000001f4c8f927aed3ca752257 (21 bytes)
+
+   Botan::ECDSA_PrivateKey priv_key(Test::rng(), dom_pars);
+
+   std::vector<uint8_t> message(20);
+   std::iota(message.begin(), message.end(), static_cast<uint8_t>(0));
+
+   auto sha1 = Botan::HashFunction::create("SHA-1");
+   auto sha224 = Botan::HashFunction::create("SHA-224");
+
+   if(!sha1 || !sha224)
+      {
+      result.test_note("Skipping due to missing SHA-1 or SHA-224");
+      return result;
+      }
+
+   Botan::PK_Signer pk_signer_160(priv_key, Test::rng(), "EMSA1(SHA-1)");
+   Botan::PK_Verifier pk_verifier_160(priv_key, "EMSA1(SHA-1)");
+
+   // Verify we can sign and verify with SHA-160
+   std::vector<uint8_t> signature_160 = pk_signer_160.sign_message(message, Test::rng());
+   result.test_eq("message verifies", pk_verifier_160.verify_message(message, signature_160), true);
+
+   // Verify we can sign and verify with SHA-224
+   Botan::PK_Signer pk_signer(priv_key, Test::rng(), "EMSA1(SHA-224)");
+   std::vector<uint8_t> signature = pk_signer.sign_message(message, Test::rng());
+   Botan::PK_Verifier pk_verifier(priv_key, "EMSA1(SHA-224)");
+   result.test_eq("message verifies", pk_verifier.verify_message(message, signature), true);
+
+   return result;
+   }
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+Test::Result test_decode_ecdsa_X509()
+   {
+   Test::Result result("ECDSA Unit");
+   Botan::X509_Certificate cert(Test::data_file("x509/ecc/CSCA.CSCA.csca-germany.1.crt"));
+
+   result.test_eq("correct signature oid", cert.signature_algorithm().get_oid().to_formatted_string(), "ECDSA/EMSA1(SHA-224)");
+
+   result.test_eq("serial number", cert.serial_number(), Botan::hex_decode("01"));
+   result.test_eq("authority key id", cert.authority_key_id(), cert.subject_key_id());
+   result.test_eq("key fingerprint", cert.fingerprint("SHA-1"),
+                  "32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9");
+
+   std::unique_ptr<Botan::Public_Key> pubkey(cert.subject_public_key());
+   result.test_eq("verify self-signed signature", cert.check_signature(*pubkey), true);
+
+   return result;
+   }
+
+Test::Result test_decode_ver_link_SHA256()
+   {
+   Test::Result result("ECDSA Unit");
+   Botan::X509_Certificate root_cert(Test::data_file("x509/ecc/root2_SHA256.cer"));
+   Botan::X509_Certificate link_cert(Test::data_file("x509/ecc/link_SHA256.cer"));
+
+   std::unique_ptr<Botan::Public_Key> pubkey(root_cert.subject_public_key());
+   result.confirm("verified self-signed signature", link_cert.check_signature(*pubkey));
+   return result;
+   }
+
+Test::Result test_decode_ver_link_SHA1()
+   {
+   Botan::X509_Certificate root_cert(Test::data_file("x509/ecc/root_SHA1.163.crt"));
+   Botan::X509_Certificate link_cert(Test::data_file("x509/ecc/link_SHA1.166.crt"));
+
+   Test::Result result("ECDSA Unit");
+   std::unique_ptr<Botan::Public_Key> pubkey(root_cert.subject_public_key());
+   result.confirm("verified self-signed signature", link_cert.check_signature(*pubkey));
+   return result;
+   }
+#endif
+
+Test::Result test_sign_then_ver()
+   {
+   Test::Result result("ECDSA Unit");
+
+   Botan::EC_Group dom_pars("secp160r1");
+   Botan::ECDSA_PrivateKey ecdsa(Test::rng(), dom_pars);
+
+   Botan::PK_Signer signer(ecdsa, Test::rng(), "EMSA1(SHA-256)");
+
+   auto msg = Botan::hex_decode("12345678901234567890abcdef12");
+   std::vector<uint8_t> sig = signer.sign_message(msg, Test::rng());
+
+   Botan::PK_Verifier verifier(ecdsa, "EMSA1(SHA-256)");
+
+   result.confirm("signature verifies", verifier.verify_message(msg, sig));
+
+   result.confirm("invalid signature rejected", !verifier.verify_message(msg, Test::mutate_vec(sig)));
+
+   return result;
+   }
+
+Test::Result test_ec_sign()
+   {
+   Test::Result result("ECDSA Unit");
+
+   try
+      {
+      Botan::EC_Group dom_pars("secp160r1");
+      Botan::ECDSA_PrivateKey priv_key(Test::rng(), dom_pars);
+      Botan::PK_Signer signer(priv_key, Test::rng(), "EMSA1(SHA-224)");
+      Botan::PK_Verifier verifier(priv_key, "EMSA1(SHA-224)");
+
+      for(size_t i = 0; i != 256; ++i)
+         {
+         signer.update(static_cast<uint8_t>(i));
+         }
+      std::vector<uint8_t> sig = signer.signature(Test::rng());
+
+      for(size_t i = 0; i != 256; ++i)
+         {
+         verifier.update(static_cast<uint8_t>(i));
+         }
+
+      result.test_eq("ECDSA signature valid", verifier.check_signature(sig), true);
+
+      // now check valid signature, different input
+      for(size_t i = 1; i != 256; ++i) //starting from 1
+         {
+         verifier.update(static_cast<uint8_t>(i));
+         }
+
+      result.test_eq("invalid ECDSA signature invalid", verifier.check_signature(sig), false);
+
+      // now check with original input, modified signature
+
+      sig[sig.size() / 2]++;
+      for(size_t i = 0; i != 256; ++i)
+         {
+         verifier.update(static_cast<uint8_t>(i));
+         }
+
+      result.test_eq("invalid ECDSA signature invalid", verifier.check_signature(sig), false);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("test_ec_sign", e.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_ecdsa_create_save_load()
+   {
+   Test::Result result("ECDSA Unit");
+
+   std::string ecc_private_key_pem;
+   const std::vector<uint8_t> msg = Botan::hex_decode("12345678901234567890abcdef12");
+   std::vector<uint8_t> msg_signature;
+
+   try
+      {
+      Botan::EC_Group dom_pars("secp160r1");
+      Botan::ECDSA_PrivateKey key(Test::rng(), dom_pars);
+
+      Botan::PK_Signer signer(key, Test::rng(), "EMSA1(SHA-256)");
+      msg_signature = signer.sign_message(msg, Test::rng());
+
+      ecc_private_key_pem = Botan::PKCS8::PEM_encode(key);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("create_pkcs8", e.what());
+      }
+
+   Botan::DataSource_Memory pem_src(ecc_private_key_pem);
+   std::unique_ptr<Botan::Private_Key> loaded_key(Botan::PKCS8::load_key(pem_src, Test::rng()));
+   Botan::ECDSA_PrivateKey* loaded_ec_key = dynamic_cast<Botan::ECDSA_PrivateKey*>(loaded_key.get());
+   result.confirm("the loaded key could be converted into an ECDSA_PrivateKey", loaded_ec_key != nullptr);
+
+   if(loaded_ec_key)
+      {
+      result.confirm("the loaded key produces equal encoding",
+                     (ecc_private_key_pem == Botan::PKCS8::PEM_encode(*loaded_ec_key)));
+      Botan::PK_Verifier verifier(*loaded_ec_key, "EMSA1(SHA-256)");
+      result.confirm("generated signature valid", verifier.verify_message(msg, msg_signature));
+      }
+
+   return result;
+   }
+
+Test::Result test_unusual_curve()
+   {
+   Test::Result result("ECDSA Unit");
+
+   //calc a curve which is not in the registry
+   const Botan::BigInt p("2117607112719756483104013348936480976596328609518055062007450442679169492999007105354629105748524349829824407773719892437896937279095106809");
+   const Botan::BigInt a("0x0a377dede6b523333d36c78e9b0eaa3bf48ce93041f6d4fc34014d08f6833807498deedd4290101c5866e8dfb589485d13357b9e78c2d7fbe9fe");
+   const Botan::BigInt b("0x0a9acf8c8ba617777e248509bcb4717d4db346202bf9e352cd5633731dd92a51b72a4dc3b3d17c823fcc8fbda4da08f25dea89046087342595a7");
+   const Botan::BigInt order_g("0x0e1a16196e6000000000bc7f1618d867b15bb86474418f");
+   const Botan::BigInt cofactor = 1;
+
+   const BigInt Gx("1503931002566715881584977704503341991763310127581173321974500299341775226206001860606586625324214456299149080935147329869147994265934715820");
+   const BigInt Gy("1774988776970033741491814582357926984496972046739476148938345272681378523636129776486407268230155403536112014267092770854858769258781598199");
+
+   Botan::EC_Group dom_params(p, a, b, Gx, Gy, order_g, cofactor);
+
+   Botan::PointGFp p_G = dom_params.point(Gx, Gy);
+
+   if(!result.confirm("G is on curve", p_G.on_the_curve()))
+      {
+      return result;
+      }
+
+   Botan::ECDSA_PrivateKey key_odd_curve(Test::rng(), dom_params);
+   std::string key_odd_curve_str = Botan::PKCS8::PEM_encode(key_odd_curve);
+
+   Botan::DataSource_Memory key_data_src(key_odd_curve_str);
+   std::unique_ptr<Botan::Private_Key> loaded_key(Botan::PKCS8::load_key(key_data_src, Test::rng()));
+
+   result.confirm("reloaded key", loaded_key.get() != nullptr);
+
+   return result;
+   }
+
+Test::Result test_encoding_options()
+   {
+   Test::Result result("ECDSA Unit");
+
+   Botan::EC_Group group("secp256r1");
+   Botan::ECDSA_PrivateKey key(Test::rng(), group);
+
+   result.confirm("Default encoding is uncompressed",
+                  key.point_encoding() == Botan::PointGFp::UNCOMPRESSED);
+
+   const std::vector<uint8_t> enc_uncompressed = key.public_key_bits();
+
+   key.set_point_encoding(Botan::PointGFp::COMPRESSED);
+
+   result.confirm("set_point_encoding works",
+                  key.point_encoding() == Botan::PointGFp::COMPRESSED);
+
+   const std::vector<uint8_t> enc_compressed = key.public_key_bits();
+
+   result.test_lt("Comprssed points are smaller",
+                  enc_compressed.size(), enc_uncompressed.size());
+
+   size_t size_diff = enc_uncompressed.size() - enc_compressed.size();
+
+   result.test_gte("Compressed points smaller by group size",
+                   size_diff, 32);
+
+   key.set_point_encoding(Botan::PointGFp::HYBRID);
+
+   result.confirm("set_point_encoding works",
+                  key.point_encoding() == Botan::PointGFp::HYBRID);
+
+   const std::vector<uint8_t> enc_hybrid = key.public_key_bits();
+
+   result.test_eq("Hybrid point same size as uncompressed",
+                  enc_uncompressed.size(), enc_hybrid.size());
+
+#if !defined(BOTAN_HAS_SANITIZER_UNDEFINED)
+   auto invalid_format = static_cast<Botan::PointGFp::Compression_Type>(99);
+
+   result.test_throws("Invalid point format throws",
+                      "Invalid point encoding for EC_PublicKey",
+                      [&] { key.set_point_encoding(invalid_format); });
+#endif
+
+   return result;
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+Test::Result test_read_pkcs8()
+   {
+   Test::Result result("ECDSA Unit");
+
+   const std::vector<uint8_t> msg = Botan::hex_decode("12345678901234567890abcdef12");
+
+   try
+      {
+      std::unique_ptr<Botan::Private_Key> loaded_key_nodp(Botan::PKCS8::load_key(
+               Test::data_file("x509/ecc/nodompar_private.pkcs8.pem"), Test::rng()));
+      // anew in each test with unregistered domain-parameters
+      Botan::ECDSA_PrivateKey* ecdsa_nodp = dynamic_cast<Botan::ECDSA_PrivateKey*>(loaded_key_nodp.get());
+      if(!ecdsa_nodp)
+         {
+         throw Test_Error("Unable to load valid PKCS8 ECDSA key");
+         }
+
+      Botan::PK_Signer signer(*ecdsa_nodp, Test::rng(), "EMSA1(SHA-256)");
+      Botan::PK_Verifier verifier(*ecdsa_nodp, "EMSA1(SHA-256)");
+
+      std::vector<uint8_t> signature_nodp = signer.sign_message(msg, Test::rng());
+
+      result.confirm("signature valid", verifier.verify_message(msg, signature_nodp));
+
+      try
+         {
+         std::unique_ptr<Botan::Private_Key> loaded_key_withdp(
+            Botan::PKCS8::load_key(Test::data_file("x509/ecc/withdompar_private.pkcs8.pem"), Test::rng()));
+
+         result.test_failure("loaded key with unknown OID");
+         }
+      catch(std::exception&)
+         {
+         result.test_note("rejected key with unknown OID");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("read_pkcs8", e.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_ecc_key_with_rfc5915_extensions()
+   {
+   Test::Result result("ECDSA Unit");
+
+   try
+      {
+      std::unique_ptr<Botan::Private_Key> pkcs8(
+         Botan::PKCS8::load_key(Test::data_file("x509/ecc/ecc_private_with_rfc5915_ext.pem"), Test::rng()));
+
+      result.confirm("loaded RFC 5915 key", pkcs8.get() != nullptr);
+      result.test_eq("key is ECDSA", pkcs8->algo_name(), "ECDSA");
+      result.confirm("key type is ECDSA", dynamic_cast<Botan::ECDSA_PrivateKey*>(pkcs8.get()) != nullptr);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("load_rfc5915_ext", e.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_ecc_key_with_rfc5915_parameters()
+   {
+   Test::Result result("ECDSA Unit");
+
+   try
+      {
+      std::unique_ptr<Botan::Private_Key> pkcs8(
+         Botan::PKCS8::load_key(Test::data_file("x509/ecc/ecc_private_with_rfc5915_parameters.pem"), Test::rng()));
+
+      result.confirm("loaded RFC 5915 key", pkcs8.get() != nullptr);
+      result.test_eq("key is ECDSA", pkcs8->algo_name(), "ECDSA");
+      result.confirm("key type is ECDSA", dynamic_cast<Botan::ECDSA_PrivateKey*>(pkcs8.get()) != nullptr);
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure("load_rfc5915_params", e.what());
+      }
+
+   return result;
+   }
+
+#endif
+
+Test::Result test_curve_registry()
+   {
+   Test::Result result("ECDSA Unit");
+
+   for(const std::string& group_name : Botan::EC_Group::known_named_groups())
+      {
+      try
+         {
+         Botan::EC_Group group(group_name);
+         Botan::ECDSA_PrivateKey ecdsa(Test::rng(), group);
+
+         Botan::PK_Signer signer(ecdsa, Test::rng(), "EMSA1(SHA-256)");
+         Botan::PK_Verifier verifier(ecdsa, "EMSA1(SHA-256)");
+
+         const std::vector<uint8_t> msg = Botan::hex_decode("12345678901234567890abcdef12");
+         const std::vector<uint8_t> sig = signer.sign_message(msg, Test::rng());
+
+         result.confirm("verified signature", verifier.verify_message(msg, sig));
+         }
+      catch(Botan::Invalid_Argument& e)
+         {
+         result.test_failure("testing " + group_name + ": " + e.what());
+         }
+      }
+
+   return result;
+   }
+
+
+class ECDSA_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+         results.push_back(test_read_pkcs8());
+         results.push_back(test_ecc_key_with_rfc5915_extensions());
+         results.push_back(test_ecc_key_with_rfc5915_parameters());
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+         results.push_back(test_decode_ecdsa_X509());
+         results.push_back(test_decode_ver_link_SHA256());
+         results.push_back(test_decode_ver_link_SHA1());
+#endif
+
+#endif
+
+         results.push_back(test_hash_larger_than_n());
+         results.push_back(test_sign_then_ver());
+         results.push_back(test_ec_sign());
+         results.push_back(test_ecdsa_create_save_load());
+         results.push_back(test_unusual_curve());
+         results.push_back(test_curve_registry());
+         results.push_back(test_encoding_options());
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("ecdsa_unit", ECDSA_Unit_Tests);
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/unit_tls.cpp b/third_party/botan/src/tests/unit_tls.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_tls.cpp
@@ -0,0 +1,1366 @@
+/*
+* (C) 2014,2015,2018 Jack Lloyd
+*     2016 Matthias Gierlings
+*     2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include <vector>
+#include <memory>
+
+#if defined(BOTAN_HAS_TLS)
+
+   #include <botan/tls_client.h>
+   #include <botan/tls_server.h>
+   #include <botan/tls_policy.h>
+   #include <botan/tls_extensions.h>
+   #include <botan/internal/tls_reader.h>
+
+   #include <botan/ec_group.h>
+   #include <botan/hex.h>
+   #include <botan/oids.h>
+   #include <botan/pkcs10.h>
+   #include <botan/rsa.h>
+   #include <botan/ecdsa.h>
+   #include <botan/x509_ca.h>
+   #include <botan/x509self.h>
+
+   #if defined(BOTAN_HAS_DSA)
+      #include <botan/dsa.h>
+   #endif
+
+   #if defined(BOTAN_HAS_SRP6)
+      #include <botan/srp6.h>
+   #endif
+
+   #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+      #include <botan/tls_session_manager_sqlite.h>
+   #endif
+
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_TLS)
+class Credentials_Manager_Test final : public Botan::Credentials_Manager
+   {
+   public:
+      Credentials_Manager_Test(bool with_client_certs,
+                               const Botan::X509_Certificate& rsa_cert,
+                               Botan::Private_Key* rsa_key,
+                               const Botan::X509_Certificate& rsa_ca,
+                               const Botan::X509_CRL& rsa_crl,
+                               const Botan::X509_Certificate& ecdsa_cert,
+                               Botan::Private_Key* ecdsa_key,
+                               const Botan::X509_Certificate& ecdsa_ca,
+                               const Botan::X509_CRL& ecdsa_crl,
+                               const Botan::X509_Certificate* dsa_cert,
+                               Botan::Private_Key* dsa_key,
+                               const Botan::X509_Certificate* dsa_ca,
+                               Botan::X509_CRL* dsa_crl) :
+         m_rsa_cert(rsa_cert),
+         m_rsa_ca(rsa_ca),
+         m_rsa_key(rsa_key),
+         m_ecdsa_cert(ecdsa_cert),
+         m_ecdsa_ca(ecdsa_ca),
+         m_ecdsa_key(ecdsa_key),
+         m_dsa_cert(dsa_cert),
+         m_dsa_ca(dsa_ca),
+         m_dsa_key(dsa_key),
+         m_dsa_crl(dsa_crl)
+         {
+         std::unique_ptr<Botan::Certificate_Store_In_Memory> store(new Botan::Certificate_Store_In_Memory);
+         store->add_certificate(m_rsa_ca);
+         store->add_certificate(m_ecdsa_ca);
+         store->add_crl(rsa_crl);
+         store->add_crl(ecdsa_crl);
+
+         if(m_dsa_ca != nullptr)
+            {
+            store->add_certificate(*m_dsa_ca);
+            }
+         if(m_dsa_crl != nullptr)
+            {
+            store->add_crl(*m_dsa_crl);
+            }
+
+         m_stores.push_back(std::move(store));
+         m_provides_client_certs = with_client_certs;
+         }
+
+      std::vector<Botan::Certificate_Store*>
+      trusted_certificate_authorities(const std::string&,
+                                      const std::string&) override
+         {
+         std::vector<Botan::Certificate_Store*> v;
+         for(auto const& store : m_stores)
+            {
+            v.push_back(store.get());
+            }
+         return v;
+         }
+
+      std::vector<Botan::X509_Certificate> find_cert_chain(
+         const std::vector<std::string>& cert_key_types,
+         const std::vector<Botan::X509_DN>& acceptable_CAs,
+         const std::string& type,
+         const std::string& context) override
+         {
+         std::vector<Botan::X509_Certificate> chain;
+
+         if(m_acceptable_cas.empty())
+            m_acceptable_cas = acceptable_CAs;
+
+         if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs))
+            {
+            for(auto const& key_type : cert_key_types)
+               {
+               if(key_type == "RSA")
+                  {
+                  chain.push_back(m_rsa_cert);
+                  chain.push_back(m_rsa_ca);
+                  break;
+                  }
+               else if(key_type == "ECDSA")
+                  {
+                  chain.push_back(m_ecdsa_cert);
+                  chain.push_back(m_ecdsa_ca);
+                  break;
+                  }
+#if defined(BOTAN_HAS_DSA)
+               else if(key_type == "DSA")
+                  {
+                  if(m_dsa_cert == nullptr || m_dsa_ca == nullptr)
+                     {
+                     throw Test_Error("No DSA certificates set for " + type + "/" + context);
+                     }
+                  chain.push_back(*m_dsa_cert);
+                  chain.push_back(*m_dsa_ca);
+                  break;
+                  }
+#else
+               BOTAN_UNUSED(context);
+#endif
+               }
+            }
+
+         return chain;
+         }
+
+      Botan::Private_Key* private_key_for(const Botan::X509_Certificate& crt,
+                                          const std::string&,
+                                          const std::string&) override
+         {
+         if(crt == m_rsa_cert)
+            {
+            return m_rsa_key.get();
+            }
+         if(crt == m_ecdsa_cert)
+            {
+            return m_ecdsa_key.get();
+            }
+         if(crt == *m_dsa_cert)
+            {
+            return m_dsa_key.get();
+            }
+         return nullptr;
+         }
+
+      Botan::SymmetricKey psk(const std::string& type,
+                              const std::string& context,
+                              const std::string&) override
+         {
+         if(type == "tls-server" && context == "session-ticket")
+            {
+            return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
+            }
+
+         if(type == "tls-server" && context == "dtls-cookie-secret")
+            {
+            return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
+            }
+
+         if(context == "server.example.com" && type == "tls-client")
+            {
+            return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
+            }
+
+         if(context == "server.example.com" && type == "tls-server")
+            {
+            return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
+            }
+
+         throw Test_Error("No PSK set for " + type + "/" + context);
+         }
+
+      const std::vector<Botan::X509_DN>& get_acceptable_cas() const { return m_acceptable_cas; }
+
+   private:
+      Botan::X509_Certificate m_rsa_cert, m_rsa_ca;
+      std::unique_ptr<Botan::Private_Key> m_rsa_key;
+
+      Botan::X509_Certificate m_ecdsa_cert, m_ecdsa_ca;
+      std::unique_ptr<Botan::Private_Key> m_ecdsa_key;
+
+      std::unique_ptr<const Botan::X509_Certificate> m_dsa_cert, m_dsa_ca;
+      std::unique_ptr<Botan::Private_Key> m_dsa_key;
+      std::unique_ptr<Botan::X509_CRL> m_dsa_crl;
+      std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores;
+      bool m_provides_client_certs;
+      std::vector<Botan::X509_DN> m_acceptable_cas;
+   };
+
+Botan::Credentials_Manager*
+create_creds(Botan::RandomNumberGenerator& rng,
+             bool with_client_certs = false)
+   {
+   // rsa and ecdsa are required for the tls module
+   const Botan::EC_Group ecdsa_params("secp256r1");
+   const size_t rsa_params = 1024;
+
+   std::unique_ptr<Botan::Private_Key> rsa_ca_key(new Botan::RSA_PrivateKey(rng, rsa_params));
+   std::unique_ptr<Botan::Private_Key> rsa_srv_key(new Botan::RSA_PrivateKey(rng, rsa_params));
+
+   std::unique_ptr<Botan::Private_Key> ecdsa_ca_key(new Botan::ECDSA_PrivateKey(rng, ecdsa_params));
+   std::unique_ptr<Botan::Private_Key> ecdsa_srv_key(new Botan::ECDSA_PrivateKey(rng, ecdsa_params));
+
+   Botan::X509_Cert_Options rsa_ca_opts("RSA Test CA/VT");
+   Botan::X509_Cert_Options ecdsa_ca_opts("ECDSA Test CA/VT");
+   rsa_ca_opts.CA_key(1);
+   ecdsa_ca_opts.CA_key(1);
+
+   const Botan::X509_Certificate rsa_ca_cert =
+      Botan::X509::create_self_signed_cert(rsa_ca_opts, *rsa_ca_key, "SHA-256", rng);
+   const Botan::X509_Certificate ecdsa_ca_cert =
+      Botan::X509::create_self_signed_cert(ecdsa_ca_opts, *ecdsa_ca_key, "SHA-256", rng);
+
+   const Botan::X509_Cert_Options server_opts("server.example.com");
+
+   const Botan::PKCS10_Request rsa_req =
+      Botan::X509::create_cert_req(server_opts, *rsa_srv_key, "SHA-256", rng);
+   const Botan::PKCS10_Request ecdsa_req =
+      Botan::X509::create_cert_req(server_opts, *ecdsa_srv_key, "SHA-256", rng);
+
+   Botan::X509_CA rsa_ca(rsa_ca_cert, *rsa_ca_key, "SHA-256", rng);
+   Botan::X509_CA ecdsa_ca(ecdsa_ca_cert, *ecdsa_ca_key, "SHA-256", rng);
+
+   typedef std::chrono::duration<int, std::ratio<31556926>> years;
+   auto now = std::chrono::system_clock::now();
+
+   const Botan::X509_Time start_time(now);
+   const Botan::X509_Time end_time(now + years(1));
+
+   const Botan::X509_Certificate rsa_srv_cert =
+      rsa_ca.sign_request(rsa_req, rng, start_time, end_time);
+   const Botan::X509_Certificate ecdsa_srv_cert =
+      ecdsa_ca.sign_request(ecdsa_req, rng, start_time, end_time);
+
+   Botan::X509_CRL rsa_crl = rsa_ca.new_crl(rng);
+   Botan::X509_CRL ecdsa_crl = ecdsa_ca.new_crl(rng);
+
+   // dsa support is optional
+   std::unique_ptr<Botan::Private_Key> dsa_ca_key;
+   std::unique_ptr<Botan::Private_Key> dsa_srv_key;
+   std::unique_ptr<Botan::X509_CRL> dsa_crl;
+   std::unique_ptr<Botan::X509_Certificate> dsa_srv_cert;
+   std::unique_ptr<Botan::X509_Certificate> dsa_ca_cert;
+
+#if defined(BOTAN_HAS_DSA)
+   const Botan::DL_Group dsa_params("modp/ietf/2048");
+
+   dsa_ca_key.reset(new Botan::DSA_PrivateKey(rng, dsa_params));
+   dsa_srv_key.reset(new Botan::DSA_PrivateKey(rng, dsa_params));
+
+   Botan::X509_Cert_Options dsa_ca_opts("DSA Test CA/VT");
+   dsa_ca_opts.CA_key(1);
+
+   dsa_ca_cert.reset(new Botan::X509_Certificate(
+                        Botan::X509::create_self_signed_cert(dsa_ca_opts, *dsa_ca_key, "SHA-256", rng)));
+
+   const Botan::PKCS10_Request dsa_req =
+      Botan::X509::create_cert_req(server_opts, *dsa_srv_key, "SHA-256", rng);
+
+   Botan::X509_CA dsa_ca(*dsa_ca_cert, *dsa_ca_key, "SHA-256", rng);
+   dsa_srv_cert.reset(new Botan::X509_Certificate(
+                         dsa_ca.sign_request(dsa_req, rng, start_time, end_time)));
+
+   dsa_crl.reset(new Botan::X509_CRL(dsa_ca.new_crl(rng)));
+#endif
+
+   Credentials_Manager_Test* cmt = new Credentials_Manager_Test(
+      with_client_certs,
+      rsa_srv_cert, rsa_srv_key.release(), rsa_ca_cert, rsa_crl,
+      ecdsa_srv_cert, ecdsa_srv_key.release(), ecdsa_ca_cert, ecdsa_crl,
+      dsa_srv_cert.release(), dsa_srv_key.release(), dsa_ca_cert.release(), dsa_crl.release());
+
+   return cmt;
+   }
+
+#if defined(BOTAN_HAS_SRP6)
+Botan::Credentials_Manager*
+create_srp6_creds(Botan::RandomNumberGenerator& rng)
+   {
+   class Credentials_Manager_SRP6 : public Botan::Credentials_Manager
+      {
+      public:
+         Credentials_Manager_SRP6(Botan::RandomNumberGenerator& rng)
+            {
+            m_group_id = "modp/srp/1024";
+            m_username = "srp6_username";
+            m_password = "srp6_password";
+            m_salt.resize(16);
+            rng.randomize(m_salt.data(), m_salt.size());
+
+            m_verifier = Botan::generate_srp6_verifier(m_username,
+                                                       m_password,
+                                                       m_salt,
+                                                       m_group_id,
+                                                       "SHA-1");
+            }
+
+         bool attempt_srp(const std::string& /*type*/,
+                          const std::string& /*context*/) override
+            {
+            return true;
+            }
+
+         std::string srp_identifier(const std::string& /*type*/,
+                                    const std::string& /*context*/) override
+            {
+            return m_username;
+            }
+
+         std::string srp_password(const std::string& /*type*/,
+                                  const std::string& /*context*/,
+                                  const std::string& identifier) override
+            {
+            if(identifier == m_username)
+               return m_password;
+            return "";
+            }
+
+         bool srp_verifier(const std::string& /*type*/,
+                           const std::string& /*context*/,
+                           const std::string& identifier,
+                           std::string& group_name,
+                           Botan::BigInt& verifier,
+                           std::vector<uint8_t>& salt,
+                           bool generate_fake_on_unknown) override
+            {
+            // FIXME test generate_fake_on_unknown behavior
+            if(identifier == m_username)
+               {
+               group_name = m_group_id;
+               verifier = m_verifier;
+               salt = m_salt;
+               return true;
+               }
+            else if(generate_fake_on_unknown)
+               {
+               group_name = m_group_id;
+               verifier = m_verifier + 1;
+               salt = m_salt;
+               return true;
+               }
+            else
+               return false;
+            }
+
+         std::string m_username;
+         std::string m_password;
+         std::vector<uint8_t> m_salt;
+         std::string m_group_id;
+         Botan::BigInt m_verifier;
+      };
+
+   return new Credentials_Manager_SRP6(rng);
+   }
+#endif
+
+
+class TLS_Handshake_Test final
+   {
+   public:
+      TLS_Handshake_Test(const std::string& test_descr,
+                         Botan::TLS::Protocol_Version offer_version,
+                         Botan::Credentials_Manager& creds,
+                         const Botan::TLS::Policy& client_policy,
+                         const Botan::TLS::Policy& server_policy,
+                         Botan::RandomNumberGenerator& rng,
+                         Botan::TLS::Session_Manager& client_sessions,
+                         Botan::TLS::Session_Manager& server_sessions,
+                         bool expect_client_auth) :
+         m_offer_version(offer_version),
+         m_results(test_descr),
+         m_creds(creds),
+         m_client_policy(client_policy),
+         m_client_sessions(client_sessions),
+         m_rng(rng),
+         m_client_auth(expect_client_auth)
+         {
+         m_server_cb.reset(new Test_Callbacks(m_results, offer_version, m_s2c, m_server_recv));
+         m_client_cb.reset(new Test_Callbacks(m_results, offer_version, m_c2s, m_client_recv));
+
+         m_server.reset(
+            new Botan::TLS::Server(*m_server_cb, server_sessions, m_creds, server_policy, m_rng,
+                                   offer_version.is_datagram_protocol())
+            );
+
+         }
+
+      void go();
+
+      const Test::Result& results() const { return m_results; }
+   private:
+
+      class Test_Extension : public Botan::TLS::Extension
+         {
+         public:
+            static Botan::TLS::Handshake_Extension_Type static_type()
+               { return static_cast<Botan::TLS::Handshake_Extension_Type>(666); }
+
+            Botan::TLS::Handshake_Extension_Type type() const override { return static_type(); }
+
+            std::vector<uint8_t> serialize(Botan::TLS::Connection_Side) const override { return m_buf; }
+
+            const std::vector<uint8_t>& value() const { return m_buf; }
+
+            bool empty() const override { return false; }
+
+            Test_Extension(Botan::TLS::Connection_Side side)
+               {
+               const uint8_t client_extn[6] = { 'c', 'l', 'i', 'e', 'n', 't' };
+               const uint8_t server_extn[6] = { 's', 'e', 'r', 'v', 'e', 'r' };
+
+               Botan::TLS::append_tls_length_value(m_buf,
+                                                   (side == Botan::TLS::CLIENT) ? client_extn : server_extn,
+                                                   6, 1);
+               }
+         private:
+            std::vector<uint8_t> m_buf;
+         };
+
+      class Test_Callbacks : public Botan::TLS::Callbacks
+         {
+         public:
+            Test_Callbacks(Test::Result& results,
+                           Botan::TLS::Protocol_Version expected_version,
+                           std::vector<uint8_t>& outbound,
+                           std::vector<uint8_t>& recv_buf) :
+               m_results(results),
+               m_expected_version(expected_version),
+               m_outbound(outbound),
+               m_recv(recv_buf)
+               {}
+
+            void tls_emit_data(const uint8_t bits[], size_t len) override
+               {
+               m_outbound.insert(m_outbound.end(), bits, bits + len);
+               }
+
+            void tls_record_received(uint64_t /*seq*/, const uint8_t bits[], size_t len) override
+               {
+               m_recv.insert(m_recv.end(), bits, bits + len);
+               }
+
+            void tls_alert(Botan::TLS::Alert /*alert*/) override
+               {
+               // TODO test that it is a no_renegotiation alert
+               // ignore
+               }
+
+            void tls_modify_extensions(Botan::TLS::Extensions& extn, Botan::TLS::Connection_Side which_side) override
+               {
+               extn.add(new Test_Extension(which_side));
+
+               // Insert an unsupported signature scheme as highest prio, to ensure we are tolerant of this
+               if(auto sig_algs = extn.get<Botan::TLS::Signature_Algorithms>())
+                  {
+                  std::vector<Botan::TLS::Signature_Scheme> schemes = sig_algs->supported_schemes();
+                  // 0x0301 is RSA PKCS1/SHA-224, which is not supported anymore
+                  schemes.insert(schemes.begin(), static_cast<Botan::TLS::Signature_Scheme>(0x0301));
+                  // This replaces the previous extension value
+                  extn.add(new Botan::TLS::Signature_Algorithms(schemes));
+                  }
+               }
+
+            void tls_examine_extensions(const Botan::TLS::Extensions& extn, Botan::TLS::Connection_Side which_side) override
+               {
+               Botan::TLS::Extension* test_extn = extn.get(static_cast<Botan::TLS::Handshake_Extension_Type>(666));
+
+               if(test_extn == nullptr)
+                  {
+                  m_results.test_failure("Did not receive test extension from peer");
+                  }
+               else
+                  {
+                  Botan::TLS::Unknown_Extension* unknown_ext = dynamic_cast<Botan::TLS::Unknown_Extension*>(test_extn);
+
+                  if(unknown_ext)
+                     {
+                     const std::vector<uint8_t> val = unknown_ext->value();
+
+                     if(m_results.test_eq("Expected size for test extn", val.size(), 7))
+                        {
+                        if(which_side == Botan::TLS::CLIENT)
+                           m_results.test_eq("Expected extension value", val, "06636C69656E74");
+                        else
+                           m_results.test_eq("Expected extension value", val, "06736572766572");
+                        }
+                     }
+                  else
+                     {
+                     m_results.test_failure("Unknown extension type had unexpected type at runtime");
+                     }
+                  }
+               }
+
+            bool tls_session_established(const Botan::TLS::Session& session) override
+               {
+               const std::string session_report =
+                  "Session established " + session.version().to_string() + " " +
+                  session.ciphersuite().to_string() + " " +
+                  Botan::hex_encode(session.session_id());
+
+               m_results.test_note(session_report);
+
+               if(session.version() != m_expected_version)
+                  {
+                  m_results.test_failure("Expected " + m_expected_version.to_string() +
+                                         " negotiated " + session.version().to_string());
+                  }
+
+               return true;
+               }
+
+            std::string tls_server_choose_app_protocol(const std::vector<std::string>& protos) override
+               {
+               m_results.test_eq("ALPN protocol count", protos.size(), 2);
+               m_results.test_eq("ALPN protocol 1", protos[0], "test/1");
+               m_results.test_eq("ALPN protocol 2", protos[1], "test/2");
+               return "test/3";
+               }
+
+            virtual std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override
+               {
+               if(static_cast<uint16_t>(group_param) == 0xFEE1)
+                  return "secp112r1";
+
+               return Botan::TLS::Callbacks::tls_decode_group_param(group_param);
+               }
+
+         private:
+            Test::Result& m_results;
+            const Botan::TLS::Protocol_Version m_expected_version;
+            std::vector<uint8_t>& m_outbound;
+            std::vector<uint8_t>& m_recv;
+         };
+
+      const Botan::TLS::Protocol_Version m_offer_version;
+      Test::Result m_results;
+
+      Botan::Credentials_Manager& m_creds;
+      const Botan::TLS::Policy& m_client_policy;
+      Botan::TLS::Session_Manager& m_client_sessions;
+      Botan::RandomNumberGenerator& m_rng;
+
+      std::unique_ptr<Test_Callbacks> m_client_cb;
+
+      std::unique_ptr<Test_Callbacks> m_server_cb;
+      std::unique_ptr<Botan::TLS::Server> m_server;
+
+      const bool m_client_auth;
+
+      std::vector<uint8_t> m_c2s, m_s2c, m_client_recv, m_server_recv;
+   };
+
+void TLS_Handshake_Test::go()
+   {
+   m_results.start_timer();
+
+   Botan::RandomNumberGenerator& rng = Test::rng();
+
+   const std::vector<std::string> protocols_offered = { "test/1", "test/2" };
+
+   // Choose random application data to send
+   const size_t c_len = 1 + ((static_cast<size_t>(rng.next_byte()) << 4) ^ rng.next_byte());
+   std::vector<uint8_t> client_msg(c_len);
+   Test::rng().randomize(client_msg.data(), client_msg.size());
+   bool client_has_written = false;
+
+   const size_t s_len = 1 + ((static_cast<size_t>(rng.next_byte()) << 4) ^ rng.next_byte());
+   std::vector<uint8_t> server_msg(s_len);
+   Test::rng().randomize(server_msg.data(), server_msg.size());
+   bool server_has_written = false;
+
+   std::unique_ptr<Botan::TLS::Client> client;
+   client.reset(
+      new Botan::TLS::Client(*m_client_cb, m_client_sessions, m_creds,
+                             m_client_policy, m_rng,
+                             Botan::TLS::Server_Information("server.example.com"),
+                             m_offer_version,
+                             protocols_offered));
+
+   size_t rounds = 0;
+
+   bool client_handshake_completed = false;
+   bool server_handshake_completed = false;
+
+   while(true)
+      {
+      ++rounds;
+
+      if(rounds > 25)
+         {
+         m_results.test_failure("Still here after many rounds, deadlock?");
+         break;
+         }
+
+      if(client_handshake_completed == false && client->is_active())
+         client_handshake_completed = true;
+
+      if(server_handshake_completed == false && m_server->is_active())
+         server_handshake_completed = true;
+
+      if(client->is_closed() || m_server->is_closed())
+         {
+         break;
+         }
+
+      if(client->is_active() && client_has_written == false)
+         {
+         m_results.test_eq("client ALPN protocol", client->application_protocol(), "test/3");
+
+         size_t sent_so_far = 0;
+         while(sent_so_far != client_msg.size())
+            {
+            const size_t left = client_msg.size() - sent_so_far;
+            const size_t rnd12 = (rng.next_byte() << 4) ^ rng.next_byte();
+            const size_t sending = std::min(left, rnd12);
+
+            client->send(&client_msg[sent_so_far], sending);
+            sent_so_far += sending;
+            }
+         client->send_warning_alert(Botan::TLS::Alert::NO_RENEGOTIATION);
+         client_has_written = true;
+         }
+
+      if(m_server->is_active() && server_has_written == false)
+         {
+         m_results.test_eq("server ALPN protocol", m_server->application_protocol(), "test/3");
+
+         size_t sent_so_far = 0;
+         while(sent_so_far != server_msg.size())
+            {
+            const size_t left = server_msg.size() - sent_so_far;
+            const size_t rnd12 = (rng.next_byte() << 4) ^ rng.next_byte();
+            const size_t sending = std::min(left, rnd12);
+
+            m_server->send(&server_msg[sent_so_far], sending);
+            sent_so_far += sending;
+            }
+
+         m_server->send_warning_alert(Botan::TLS::Alert::NO_RENEGOTIATION);
+         server_has_written = true;
+         }
+
+      if(m_c2s.size() > 0)
+         {
+         /*
+         * Use this as a temp value to hold the queues as otherwise they
+         * might end up appending more in response to messages during the
+         * handshake.
+         */
+         std::vector<uint8_t> input;
+         std::swap(m_c2s, input);
+
+         size_t needed = m_server->received_data(input.data(), input.size());
+         m_results.test_eq("full packet received", needed, 0);
+
+         continue;
+         }
+
+      if(m_s2c.size() > 0)
+         {
+         std::vector<uint8_t> input;
+         std::swap(m_s2c, input);
+
+         size_t needed = client->received_data(input.data(), input.size());
+         m_results.test_eq("full packet received", needed, 0);
+
+         continue;
+         }
+
+      if(m_client_recv.size())
+         {
+         m_results.test_eq("client recv", m_client_recv, server_msg);
+         }
+
+      if(m_server_recv.size())
+         {
+         m_results.test_eq("server recv", m_server_recv, client_msg);
+         }
+
+      if(client->is_closed() && m_server->is_closed())
+         {
+         break;
+         }
+
+      if(m_server->is_active())
+         {
+         std::vector<Botan::X509_Certificate> certs = m_server->peer_cert_chain();
+         if(m_client_auth)
+            {
+            m_results.test_eq("got client certs", certs.size(), 2);
+
+            Credentials_Manager_Test& test_creds = dynamic_cast<Credentials_Manager_Test&>(m_creds);
+
+            std::vector<Botan::X509_DN> acceptable_CAs = test_creds.get_acceptable_cas();
+
+            m_results.test_gte("client got CA list", acceptable_CAs.size(), 2); // DSA is optional
+
+            for(const Botan::X509_DN& dn : acceptable_CAs)
+               {
+               m_results.test_eq("Expected CA country field",
+                                 dn.get_first_attribute("C"), "VT");
+               }
+            }
+         else
+            {
+            m_results.test_eq("no client certs", certs.size(), 0);
+            }
+         }
+
+      if(m_server_recv.size() && m_client_recv.size())
+         {
+         Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
+         Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32);
+
+         m_results.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
+
+         m_results.confirm("Client is active", client->is_active());
+         m_results.confirm("Client is not closed", !client->is_closed());
+         client->close();
+         m_results.confirm("Client is no longer active", !client->is_active());
+         m_results.confirm("Client is closed", client->is_closed());
+         }
+      }
+
+   m_results.end_timer();
+   }
+
+class Test_Policy final : public Botan::TLS::Text_Policy
+   {
+   public:
+      Test_Policy() : Text_Policy("") {}
+      bool acceptable_protocol_version(Botan::TLS::Protocol_Version) const override
+         {
+         return true;
+         }
+      bool send_fallback_scsv(Botan::TLS::Protocol_Version) const override
+         {
+         return false;
+         }
+
+      size_t dtls_initial_timeout() const override
+         {
+         return 1;
+         }
+      size_t dtls_maximum_timeout() const override
+         {
+         return 8;
+         }
+
+      size_t minimum_rsa_bits() const override
+         {
+         return 1024;
+         }
+
+      size_t minimum_signature_strength() const override
+         {
+         return 80;
+         }
+   };
+
+class TLS_Unit_Tests final : public Test
+   {
+   private:
+      void test_with_policy(const std::string& test_descr,
+                            std::vector<Test::Result>& results,
+                            Botan::TLS::Session_Manager& client_ses,
+                            Botan::TLS::Session_Manager& server_ses,
+                            Botan::Credentials_Manager& creds,
+                            const std::vector<Botan::TLS::Protocol_Version>& versions,
+                            const Botan::TLS::Policy& policy,
+                            bool client_auth = false)
+         {
+         Botan::RandomNumberGenerator& rng = Test::rng();
+
+         try
+            {
+            for(auto const& version : versions)
+               {
+               TLS_Handshake_Test test(
+                  version.to_string() + " " + test_descr,
+                  version, creds, policy, policy, rng, client_ses, server_ses, client_auth);
+               test.go();
+               results.push_back(test.results());
+
+               TLS_Handshake_Test test_resumption(
+                  version.to_string() + " " + test_descr,
+                  version, creds, policy, policy, rng, client_ses, server_ses, client_auth);
+               test_resumption.go();
+               results.push_back(test_resumption.results());
+               }
+            }
+         catch(std::exception& e)
+            {
+            results.push_back(Test::Result::Failure(test_descr, e.what()));
+            }
+         }
+
+      void test_all_versions(const std::string& test_descr,
+                             std::vector<Test::Result>& results,
+                             Botan::TLS::Session_Manager& client_ses,
+                             Botan::TLS::Session_Manager& server_ses,
+                             Botan::Credentials_Manager& creds,
+                             const std::string& kex_policy,
+                             const std::string& cipher_policy,
+                             const std::string& mac_policy,
+                             const std::string& etm_policy,
+                             bool client_auth = false)
+         {
+         Test_Policy policy;
+         policy.set("ciphers", cipher_policy);
+         policy.set("macs", mac_policy);
+         policy.set("key_exchange_methods", kex_policy);
+         policy.set("negotiate_encrypt_then_mac", etm_policy);
+
+         policy.set("allow_tls10", "true");
+         policy.set("allow_tls11", "true");
+         policy.set("allow_tls12", "true");
+         policy.set("allow_dtls10", "true");
+         policy.set("allow_dtls12", "true");
+
+         if(kex_policy.find("RSA") != std::string::npos || kex_policy.find("SRP") != std::string::npos)
+            {
+            policy.set("signature_methods", "IMPLICIT");
+            }
+
+         std::vector<Botan::TLS::Protocol_Version> versions =
+            {
+#if defined(BOTAN_HAS_TLS_V10)
+            Botan::TLS::Protocol_Version::TLS_V10,
+            Botan::TLS::Protocol_Version::TLS_V11,
+            Botan::TLS::Protocol_Version::DTLS_V10,
+#endif
+            Botan::TLS::Protocol_Version::TLS_V12,
+            Botan::TLS::Protocol_Version::DTLS_V12
+            };
+
+         return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth);
+         }
+
+      void test_modern_versions(const std::string& test_descr,
+                                std::vector<Test::Result>& results,
+                                Botan::TLS::Session_Manager& client_ses,
+                                Botan::TLS::Session_Manager& server_ses,
+                                Botan::Credentials_Manager& creds,
+                                const std::string& kex_policy,
+                                const std::string& cipher_policy,
+                                const std::string& mac_policy = "AEAD",
+                                bool client_auth = false)
+         {
+         std::map<std::string, std::string> no_extra_policies;
+         return test_modern_versions(test_descr, results, client_ses, server_ses, creds,
+                                     kex_policy, cipher_policy, mac_policy, no_extra_policies, client_auth);
+         }
+
+      void test_modern_versions(const std::string& test_descr,
+                                std::vector<Test::Result>& results,
+                                Botan::TLS::Session_Manager& client_ses,
+                                Botan::TLS::Session_Manager& server_ses,
+                                Botan::Credentials_Manager& creds,
+                                const std::string& kex_policy,
+                                const std::string& cipher_policy,
+                                const std::string& mac_policy,
+                                const std::map<std::string, std::string>& extra_policies,
+                                bool client_auth = false)
+         {
+         Test_Policy policy;
+         policy.set("ciphers", cipher_policy);
+         policy.set("macs", mac_policy);
+         policy.set("key_exchange_methods", kex_policy);
+         policy.set("allow_tls10", "false");
+         policy.set("allow_tls11", "false");
+         policy.set("allow_tls12", "true");
+         policy.set("allow_dtls10", "false");
+         policy.set("allow_dtls12", "true");
+
+         if(kex_policy.find("RSA") != std::string::npos)
+            {
+            policy.set("signature_methods", "IMPLICIT");
+            }
+
+         for(auto const& kv : extra_policies)
+            {
+            policy.set(kv.first, kv.second);
+            }
+
+         std::vector<Botan::TLS::Protocol_Version> versions =
+            {
+            Botan::TLS::Protocol_Version::TLS_V12,
+            Botan::TLS::Protocol_Version::DTLS_V12
+            };
+
+         return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth);
+         }
+
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         Botan::RandomNumberGenerator& rng = Test::rng();
+
+         std::unique_ptr<Botan::TLS::Session_Manager> client_ses;
+         std::unique_ptr<Botan::TLS::Session_Manager> server_ses;
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+         client_ses.reset(
+            new Botan::TLS::Session_Manager_SQLite("client pass", rng, ":memory:", 5, std::chrono::seconds(2)));
+         server_ses.reset(
+            new Botan::TLS::Session_Manager_SQLite("server pass", rng, ":memory:", 10, std::chrono::seconds(4)));
+
+#else
+         client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+         server_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+#endif
+
+         std::unique_ptr<Botan::Credentials_Manager> creds(create_creds(rng));
+
+#if defined(BOTAN_HAS_TLS_CBC)
+         for(std::string etm_setting : { "false", "true" })
+            {
+            test_all_versions("AES-128 RSA", results, *client_ses, *server_ses, *creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting);
+            test_all_versions("AES-128 ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting);
+
+#if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_TLS_CBC)
+            test_all_versions("Camellia-128 RSA", results, *client_ses, *server_ses,
+                              *creds, "RSA", "Camellia-128", "SHA-256 SHA-1", etm_setting);
+            test_all_versions("Camellia-256 RSA SHA-2", results, *client_ses, *server_ses,
+                              *creds, "RSA", "Camellia-256", "SHA-256 SHA-384 SHA-1", etm_setting);
+#endif
+
+#if defined(BOTAN_HAS_DES)
+            test_all_versions("3DES RSA", results, *client_ses, *server_ses, *creds, "RSA", "3DES", "SHA-1", etm_setting);
+            test_all_versions("3DES ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "3DES", "SHA-1", etm_setting);
+#endif
+
+#if defined(BOTAN_HAS_SEED)
+            test_all_versions("SEED RSA", results, *client_ses, *server_ses, *creds, "RSA", "SEED", "SHA-1", etm_setting);
+#endif
+
+            server_ses->remove_all();
+            }
+         client_ses->remove_all();
+
+         test_modern_versions("AES-128 DH", results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256");
+
+#if defined(BOTAN_HAS_DSA)
+         test_modern_versions("AES-128 DSA", results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256",
+                              { { "signature_methods", "DSA" } });
+
+         test_modern_versions("AES-128/GCM DSA", results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD",
+                              { { "signature_methods", "DSA" } });
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+         std::unique_ptr<Botan::Credentials_Manager> srp6_creds(create_srp6_creds(rng));
+         test_all_versions("SRP6 AES", results, *client_ses, *server_ses, *srp6_creds, "SRP_SHA", "AES-128", "SHA-1", "false");
+#endif
+
+#endif
+
+         Botan::TLS::Strict_Policy strict_policy;
+         test_with_policy("Strict policy", results, *client_ses, *server_ses, *creds,
+            {Botan::TLS::Protocol_Version::TLS_V12}, strict_policy);
+
+         Botan::TLS::NSA_Suite_B_128 suiteb_128;
+         test_with_policy("Suite B", results, *client_ses, *server_ses, *creds,
+            {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128);
+
+         // Remove server sessions before client, so clients retry with session server doesn't know
+         server_ses->remove_all();
+
+         test_modern_versions("AES-128/GCM RSA", results, *client_ses, *server_ses, *creds, "RSA", "AES-128/GCM");
+         test_modern_versions("AES-128/GCM ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM");
+
+         test_modern_versions("AES-128/GCM ECDH RSA",
+                              results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD",
+                              { { "signature_methods", "RSA" } });
+
+         test_modern_versions("AES-128/GCM ECDH no OCSP",
+                              results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD",
+                              { { "support_cert_status_message", "false" } });
+
+         client_ses->remove_all();
+
+#if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_TLS_CBC)
+         test_modern_versions("Camellia-256 SHA-2", results, *client_ses, *server_ses, *creds, "RSA", "Camellia-256", "SHA-384 SHA-256");
+#endif
+#if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM)
+         test_modern_versions("Camellia-128/GCM ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "Camellia-128/GCM", "AEAD");
+#endif
+
+#if defined(BOTAN_HAS_ARIA)
+         test_modern_versions("ARIA ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "ARIA-128/GCM", "AEAD");
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM)
+         test_modern_versions("AES-256/GCM CECPQ1", results, *client_ses, *server_ses, *creds, "CECPQ1", "AES-256/GCM", "AEAD");
+#endif
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB)
+         test_modern_versions("AES-256/OCB CECPQ1", results, *client_ses, *server_ses, *creds,
+                              "CECPQ1", "AES-256/OCB(12)", "AEAD");
+         test_modern_versions("AES-256/OCB CECPQ1 RSA", results, *client_ses, *server_ses, *creds,
+                              "CECPQ1", "AES-256/OCB(12)", "AEAD",
+                              {{ "signature_methods", "RSA" }});
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+         test_modern_versions("ChaCha20Poly1305 CECPQ1", results, *client_ses, *server_ses, *creds,
+                              "CECPQ1", "ChaCha20Poly1305", "AEAD",
+                              { { "signature_methods", "RSA" }});
+#endif
+
+#endif
+
+         test_modern_versions("AES-128/GCM point compression", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD",
+                              { { "use_ecc_point_compression", "true" } });
+         test_modern_versions("AES-256/GCM p521", results, *client_ses, *server_ses, *creds, "ECDH", "AES-256/GCM", "AEAD",
+                              { { "groups", "secp521r1" } });
+         test_modern_versions("AES-128/GCM bp256r1", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD",
+                              { { "groups", "brainpool256r1" } });
+
+#if defined(BOTAN_HAS_CURVE_25519)
+         test_modern_versions("AES-128/GCM x25519", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD",
+                              { { "groups", "x25519" } });
+#endif
+
+         test_modern_versions("AES-128/GCM FFDHE-2048",
+                              results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD",
+                              { { "groups", "ffdhe/ietf/2048" } });
+
+         std::unique_ptr<Botan::Credentials_Manager> creds_with_client_cert(create_creds(rng, true));
+
+         client_ses->remove_all();
+         test_modern_versions("AES-256/GCM client certs",
+                              results, *client_ses, *server_ses, *creds_with_client_cert, "ECDH", "AES-256/GCM", "AEAD", true);
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+         client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+         server_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+         test_modern_versions("AES-128/OCB ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/OCB(12)");
+#endif
+
+         server_ses->remove_all();
+
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+         test_modern_versions("ChaCha20Poly1305 ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "ChaCha20Poly1305");
+#endif
+
+         test_modern_versions("AES-128/GCM PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/GCM");
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+         test_modern_versions("AES-128/CCM PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM");
+         test_modern_versions("AES-128/CCM-8 PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM(8)");
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+         // For whatever reason no (EC)DHE_PSK GCM ciphersuites are defined
+         test_modern_versions("AES-128 ECDHE_PSK", results, *client_ses, *server_ses, *creds, "ECDHE_PSK", "AES-128", "SHA-256");
+         test_modern_versions("AES-128 DHE_PSK", results, *client_ses, *server_ses, *creds, "DHE_PSK", "AES-128", "SHA-1");
+#endif
+
+         // Test with a custom curve
+
+         /*
+         * First register a curve, in this case secp112r1
+         */
+         const Botan::BigInt p("0xDB7C2ABF62E35E668076BEAD208B");
+         const Botan::BigInt a("0xDB7C2ABF62E35E668076BEAD2088");
+         const Botan::BigInt b("0x659EF8BA043916EEDE8911702B22");
+
+         const Botan::BigInt g_x("0x09487239995A5EE76B55F9C2F098");
+         const Botan::BigInt g_y("0xA89CE5AF8724C0A23E0E0FF77500");
+         const Botan::BigInt order("0xDB7C2ABF62E35E7628DFAC6561C5");
+
+         const Botan::OID oid("1.3.132.0.6");
+
+         // Creating this object implicitly registers the curve for future use ...
+         Botan::EC_Group reg_secp112r1(p, a, b, g_x, g_y, order, 1, oid);
+
+         Botan::OIDS::add_oid(oid, "secp112r1");
+
+         test_modern_versions("AES-256/GCM secp112r1", results, *client_ses, *server_ses, *creds, "ECDH", "AES-256/GCM", "AEAD",
+                              { { "groups", "0xFEE1" }, { "minimum_ecdh_group_size", "112" } });
+
+         return results;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("tls", TLS_Unit_Tests);
+
+class DTLS_Reconnection_Test : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         class Test_Callbacks : public Botan::TLS::Callbacks
+            {
+            public:
+               Test_Callbacks(Test::Result& results,
+                              std::vector<uint8_t>& outbound,
+                              std::vector<uint8_t>& recv_buf) :
+                  m_results(results),
+                  m_outbound(outbound),
+                  m_recv(recv_buf)
+                  {}
+
+               void tls_emit_data(const uint8_t bits[], size_t len) override
+                  {
+                  m_outbound.insert(m_outbound.end(), bits, bits + len);
+                  }
+
+               void tls_record_received(uint64_t /*seq*/, const uint8_t bits[], size_t len) override
+                  {
+                  m_recv.insert(m_recv.end(), bits, bits + len);
+                  }
+
+               void tls_alert(Botan::TLS::Alert /*alert*/) override
+                  {
+                  // ignore
+                  }
+
+               bool tls_session_established(const Botan::TLS::Session& /*session*/) override
+                  {
+                  m_results.test_success("Established a session");
+                  return true;
+                  }
+
+            private:
+               Test::Result& m_results;
+               std::vector<uint8_t>& m_outbound;
+               std::vector<uint8_t>& m_recv;
+            };
+
+         class Credentials_PSK : public Botan::Credentials_Manager
+            {
+            public:
+               Botan::SymmetricKey psk(const std::string& type,
+                                       const std::string& context,
+                                       const std::string&) override
+                  {
+                  if(type == "tls-server" && context == "session-ticket")
+                     {
+                     return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678");
+                     }
+
+                  if(type == "tls-server" && context == "dtls-cookie-secret")
+                     {
+                     return Botan::SymmetricKey("4AEA5EAD279CADEB537A594DA0E9DE3A");
+                     }
+
+                  if(context == "localhost" && type == "tls-client")
+                     {
+                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
+                     }
+
+                  if(context == "localhost" && type == "tls-server")
+                     {
+                     return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD");
+                     }
+
+                  throw Test_Error("No PSK set for " + type + "/" + context);
+                  }
+            };
+
+         class Datagram_PSK_Policy : public Botan::TLS::Policy
+            {
+            public:
+               std::vector<std::string> allowed_macs() const override
+                  { return std::vector<std::string>({"AEAD"}); }
+
+               std::vector<std::string> allowed_key_exchange_methods() const override
+                  { return {"PSK"}; }
+
+               bool allow_tls10()  const override { return false; }
+               bool allow_tls11()  const override { return false; }
+               bool allow_tls12()  const override { return false; }
+               bool allow_dtls10() const override { return false; }
+               bool allow_dtls12() const override { return true;  }
+
+               bool allow_dtls_epoch0_restart() const override { return true; }
+            };
+
+         Test::Result result("DTLS reconnection");
+
+         Datagram_PSK_Policy server_policy;
+         Datagram_PSK_Policy client_policy;
+         Credentials_PSK creds;
+         Botan::TLS::Session_Manager_In_Memory server_sessions(rng());
+         //Botan::TLS::Session_Manager_In_Memory client_sessions(rng());
+         Botan::TLS::Session_Manager_Noop client_sessions;
+
+         std::vector<uint8_t> s2c, server_recv;
+         Test_Callbacks server_callbacks(result, s2c, server_recv);
+         Botan::TLS::Server server(server_callbacks, server_sessions, creds, server_policy, rng(), true);
+
+         std::vector<uint8_t> c1_c2s, client1_recv;
+         Test_Callbacks client1_callbacks(result, c1_c2s, client1_recv);
+         Botan::TLS::Client client1(client1_callbacks, client_sessions, creds, client_policy, rng(),
+                                    Botan::TLS::Server_Information("localhost"),
+                                    Botan::TLS::Protocol_Version::latest_dtls_version());
+
+         bool c1_to_server_sent = false;
+         bool server_to_c1_sent = false;
+
+         const std::vector<uint8_t> c1_to_server_magic(16, 0xC1);
+         const std::vector<uint8_t> server_to_c1_magic(16, 0x42);
+
+         size_t c1_rounds = 0;
+         for(;;)
+            {
+            c1_rounds++;
+
+            if(c1_rounds > 64)
+               {
+               result.test_failure("Still spinning in client1 loop after 64 rounds");
+               return {result};
+               }
+
+            if(c1_c2s.size() > 0)
+               {
+               std::vector<uint8_t> input;
+               std::swap(c1_c2s, input);
+               server.received_data(input.data(), input.size());
+               continue;
+               }
+
+            if(s2c.size() > 0)
+               {
+               std::vector<uint8_t> input;
+               std::swap(s2c, input);
+               client1.received_data(input.data(), input.size());
+               continue;
+               }
+
+            if(!c1_to_server_sent && client1.is_active())
+               {
+               client1.send(c1_to_server_magic);
+               c1_to_server_sent = true;
+               }
+
+            if(!server_to_c1_sent && server.is_active())
+               {
+               server.send(server_to_c1_magic);
+               }
+
+            if(server_recv.size() > 0 && client1_recv.size() > 0)
+               {
+               result.test_eq("Expected message from client1", server_recv, c1_to_server_magic);
+               result.test_eq("Expected message to client1", client1_recv, server_to_c1_magic);
+               break;
+               }
+            }
+
+         // Now client1 "goes away" (goes silent) and new client
+         // connects to same server context (ie due to reuse of client source port)
+         // See RFC 6347 section 4.2.8
+
+         server_recv.clear();
+         s2c.clear();
+
+         std::vector<uint8_t> c2_c2s, client2_recv;
+         Test_Callbacks client2_callbacks(result, c2_c2s, client2_recv);
+         Botan::TLS::Client client2(client2_callbacks, client_sessions, creds, client_policy, rng(),
+                                    Botan::TLS::Server_Information("localhost"),
+                                    Botan::TLS::Protocol_Version::latest_dtls_version());
+
+         bool c2_to_server_sent = false;
+         bool server_to_c2_sent = false;
+
+         const std::vector<uint8_t> c2_to_server_magic(16, 0xC2);
+         const std::vector<uint8_t> server_to_c2_magic(16, 0x66);
+
+         size_t c2_rounds = 0;
+
+         for(;;)
+            {
+            c2_rounds++;
+
+            if(c2_rounds > 64)
+               {
+               result.test_failure("Still spinning in client2 loop after 64 rounds");
+               return {result};
+               }
+
+            if(c2_c2s.size() > 0)
+               {
+               std::vector<uint8_t> input;
+               std::swap(c2_c2s, input);
+               server.received_data(input.data(), input.size());
+               continue;
+               }
+
+            if(s2c.size() > 0)
+               {
+               std::vector<uint8_t> input;
+               std::swap(s2c, input);
+               client2.received_data(input.data(), input.size());
+               continue;
+               }
+
+            if(!c2_to_server_sent && client2.is_active())
+               {
+               client2.send(c2_to_server_magic);
+               c2_to_server_sent = true;
+               }
+
+            if(!server_to_c2_sent && server.is_active())
+               {
+               server.send(server_to_c2_magic);
+               }
+
+            if(server_recv.size() > 0 && client2_recv.size() > 0)
+               {
+               result.test_eq("Expected message from client2", server_recv, c2_to_server_magic);
+               result.test_eq("Expected message to client2", client2_recv, server_to_c2_magic);
+               break;
+               }
+            }
+
+         return {result};
+         }
+   };
+
+BOTAN_REGISTER_TEST("tls_dtls_reconnect", DTLS_Reconnection_Test);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/unit_tls_policy.cpp b/third_party/botan/src/tests/unit_tls_policy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_tls_policy.cpp
@@ -0,0 +1,192 @@
+/*
+* TLS Policy tests
+*
+* (C) 2016 Juraj Somorovsky
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_TLS)
+   #include <botan/tls_policy.h>
+   #include <botan/tls_exceptn.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+   #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+   #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+   #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+   #include <botan/dh.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+   #include <botan/dsa.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_TLS)
+class TLS_Policy_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(test_peer_key_acceptable_rsa());
+         results.push_back(test_peer_key_acceptable_ecdh());
+         results.push_back(test_peer_key_acceptable_ecdsa());
+         results.push_back(test_peer_key_acceptable_dh());
+         results.push_back(test_peer_key_acceptable_dsa());
+
+         return results;
+         }
+   private:
+      Test::Result test_peer_key_acceptable_rsa()
+         {
+         Test::Result result("TLS Policy RSA key verification");
+#if defined(BOTAN_HAS_RSA)
+         std::unique_ptr<Botan::Private_Key> rsa_key_1024(new Botan::RSA_PrivateKey(Test::rng(), 1024));
+         Botan::TLS::Policy policy;
+
+         try
+            {
+            policy.check_peer_key_acceptable(*rsa_key_1024);
+            result.test_failure("Incorrectly accepting 1024 bit RSA keys");
+            }
+         catch(Botan::TLS::TLS_Exception&)
+            {
+            result.test_success("Correctly rejecting 1024 bit RSA keys");
+            }
+
+         std::unique_ptr<Botan::Private_Key> rsa_key_2048(new Botan::RSA_PrivateKey(Test::rng(), 2048));
+         policy.check_peer_key_acceptable(*rsa_key_2048);
+         result.test_success("Correctly accepting 2048 bit RSA keys");
+#endif
+         return result;
+         }
+
+      Test::Result test_peer_key_acceptable_ecdh()
+         {
+         Test::Result result("TLS Policy ECDH key verification");
+#if defined(BOTAN_HAS_ECDH)
+         Botan::EC_Group group_192("secp192r1");
+         std::unique_ptr<Botan::Private_Key> ecdh_192(new Botan::ECDH_PrivateKey(Test::rng(), group_192));
+
+         Botan::TLS::Policy policy;
+         try
+            {
+            policy.check_peer_key_acceptable(*ecdh_192);
+            result.test_failure("Incorrectly accepting 192 bit EC keys");
+            }
+         catch(Botan::TLS::TLS_Exception&)
+            {
+            result.test_success("Correctly rejecting 192 bit EC keys");
+            }
+
+         Botan::EC_Group group_256("secp256r1");
+         std::unique_ptr<Botan::Private_Key> ecdh_256(new Botan::ECDH_PrivateKey(Test::rng(), group_256));
+         policy.check_peer_key_acceptable(*ecdh_256);
+         result.test_success("Correctly accepting 256 bit EC keys");
+#endif
+         return result;
+         }
+
+      Test::Result test_peer_key_acceptable_ecdsa()
+         {
+         Test::Result result("TLS Policy ECDSA key verification");
+#if defined(BOTAN_HAS_ECDSA)
+         Botan::EC_Group group_192("secp192r1");
+         std::unique_ptr<Botan::Private_Key> ecdsa_192(new Botan::ECDSA_PrivateKey(Test::rng(), group_192));
+
+         Botan::TLS::Policy policy;
+         try
+            {
+            policy.check_peer_key_acceptable(*ecdsa_192);
+            result.test_failure("Incorrectly accepting 192 bit EC keys");
+            }
+         catch(Botan::TLS::TLS_Exception&)
+            {
+            result.test_success("Correctly rejecting 192 bit EC keys");
+            }
+
+         Botan::EC_Group group_256("secp256r1");
+         std::unique_ptr<Botan::Private_Key> ecdsa_256(new Botan::ECDSA_PrivateKey(Test::rng(), group_256));
+         policy.check_peer_key_acceptable(*ecdsa_256);
+         result.test_success("Correctly accepting 256 bit EC keys");
+#endif
+         return result;
+         }
+
+      Test::Result test_peer_key_acceptable_dh()
+         {
+         Test::Result result("TLS Policy DH key verification");
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+         const BigInt g("2");
+         const BigInt p("58458002095536094658683755258523362961421200751439456159756164191494576279467");
+         const Botan::DL_Group grp(p, g);
+         const Botan::BigInt x("46205663093589612668746163860870963912226379131190812163519349848291472898748");
+         std::unique_ptr<Botan::Private_Key> dhkey(new Botan::DH_PrivateKey(Test::rng(), grp, x));
+
+         Botan::TLS::Policy policy;
+         try
+            {
+            policy.check_peer_key_acceptable(*dhkey);
+            result.test_failure("Incorrectly accepting short bit DH keys");
+            }
+         catch(Botan::TLS::TLS_Exception&)
+            {
+            result.test_success("Correctly rejecting short bit DH keys");
+            }
+#endif
+         return result;
+         }
+
+      Test::Result test_peer_key_acceptable_dsa()
+         {
+         Test::Result result("TLS Policy DSA key verification");
+#if defined(BOTAN_HAS_DSA)
+         const Botan::DL_Group grp_1024("modp/ietf/1024");
+         std::unique_ptr<Botan::Private_Key> dsa_1024(new Botan::DSA_PrivateKey(Test::rng(), grp_1024));
+
+         Botan::TLS::Policy policy;
+         try
+            {
+            policy.check_peer_key_acceptable(*dsa_1024);
+            result.test_failure("Incorrectly accepting short bit DSA keys");
+            }
+         catch(Botan::TLS::TLS_Exception&)
+            {
+            result.test_success("Correctly rejecting short bit DSA keys");
+            }
+
+         const Botan::DL_Group grp_2048("modp/ietf/2048");
+         std::unique_ptr<Botan::Private_Key> dsa_2048(new Botan::DSA_PrivateKey(Test::rng(), grp_2048));
+         policy.check_peer_key_acceptable(*dsa_2048);
+         result.test_success("Correctly accepting 2048 bit DSA keys");
+#endif
+         return result;
+         }
+
+
+   };
+
+BOTAN_REGISTER_TEST("tls_policy", TLS_Policy_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/botan/src/tests/unit_x509.cpp b/third_party/botan/src/tests/unit_x509.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/botan/src/tests/unit_x509.cpp
@@ -0,0 +1,1741 @@
+/*
+* (C) 2009,2019 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+   #include <botan/calendar.h>
+   #include <botan/pkcs10.h>
+   #include <botan/pkcs8.h>
+   #include <botan/x509self.h>
+   #include <botan/x509path.h>
+   #include <botan/x509_ca.h>
+   #include <botan/pk_algs.h>
+   #include <botan/ber_dec.h>
+   #include <botan/der_enc.h>
+   #include <botan/oids.h>
+   #include <botan/internal/padding.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+Botan::X509_Time from_date(const int y, const int m, const int d)
+   {
+   const size_t this_year = Botan::calendar_value(std::chrono::system_clock::now()).get_year();
+
+   Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
+   return Botan::X509_Time(t.to_std_timepoint());
+   }
+
+/* Return some option sets */
+Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "")
+   {
+   Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
+
+   opts.uri = "https://botan.randombit.net";
+   opts.dns = "botan.randombit.net";
+   opts.email = "testing@randombit.net";
+   opts.set_padding_scheme(sig_padding);
+
+   opts.CA_key(1);
+
+   return opts;
+   }
+
+Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& sig_padding = "")
+   {
+   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
+
+   opts.uri = "https://botan.randombit.net";
+   opts.dns = "botan.randombit.net";
+   opts.email = "testing@randombit.net";
+   opts.set_padding_scheme(sig_padding);
+
+   opts.not_before("160101200000Z");
+   opts.not_after("300101200000Z");
+
+   opts.challenge = "zoom";
+
+   if(algo == "RSA")
+      {
+      opts.constraints = Botan::Key_Constraints(Botan::KEY_ENCIPHERMENT);
+      }
+   else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA")
+      {
+      opts.constraints = Botan::Key_Constraints(Botan::DIGITAL_SIGNATURE);
+      }
+
+   return opts;
+   }
+
+Botan::X509_Cert_Options req_opts2(const std::string& sig_padding = "")
+   {
+   Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
+
+   opts.uri = "https://botan.randombit.net";
+   opts.dns = "botan.randombit.net";
+   opts.email = "testing@randombit.net";
+   opts.set_padding_scheme(sig_padding);
+
+   opts.add_ex_constraint("PKIX.EmailProtection");
+
+   return opts;
+   }
+
+Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "")
+   {
+   Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
+
+   opts.uri = "https://botan.randombit.net";
+   opts.dns = "botan.randombit.net";
+   opts.email = "testing@randombit.net";
+   opts.set_padding_scheme(sig_padding);
+
+   opts.more_org_units.push_back("IT");
+   opts.more_org_units.push_back("Security");
+   opts.more_dns.push_back("www.botan.randombit.net");
+
+   return opts;
+   }
+
+std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo)
+   {
+   const std::string params = [&]
+      {
+      // Here we override defaults as needed
+      if(algo == "RSA")
+         {
+         return "1024";
+         }
+      if(algo == "GOST-34.10")
+         {
+         return "gost_256A";
+         }
+      if(algo == "ECKCDSA" || algo == "ECGDSA")
+         {
+         return "brainpool256r1";
+         }
+      return ""; // default "" means choose acceptable algo-specific params
+      }();
+
+   return Botan::create_private_key(algo, Test::rng(), params);
+   }
+
+Test::Result test_cert_status_strings()
+   {
+   Test::Result result("Certificate_Status_Code to_string");
+
+   std::set<std::string> seen;
+
+   result.test_eq("Same string",
+                  Botan::to_string(Botan::Certificate_Status_Code::OK),
+                  Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
+
+   const Botan::Certificate_Status_Code codes[]
+      {
+      Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
+      Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
+      Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
+      Botan::Certificate_Status_Code::OCSP_NO_HTTP,
+
+      Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
+      Botan::Certificate_Status_Code::DN_TOO_LONG,
+
+      Botan::Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK,
+      Botan::Certificate_Status_Code::NO_MATCHING_CRLDP,
+      Botan::Certificate_Status_Code::UNTRUSTED_HASH,
+      Botan::Certificate_Status_Code::NO_REVOCATION_DATA,
+      Botan::Certificate_Status_Code::CERT_NOT_YET_VALID,
+      Botan::Certificate_Status_Code::CERT_HAS_EXPIRED,
+      Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID,
+      Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED,
+      Botan::Certificate_Status_Code::CRL_NOT_YET_VALID,
+      Botan::Certificate_Status_Code::CRL_HAS_EXPIRED,
+      Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
+      Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST,
+      Botan::Certificate_Status_Code::CERT_CHAIN_LOOP,
+      Botan::Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT,
+      Botan::Certificate_Status_Code::CHAIN_NAME_MISMATCH,
+      Botan::Certificate_Status_Code::POLICY_ERROR,
+      Botan::Certificate_Status_Code::DUPLICATE_CERT_POLICY,
+      Botan::Certificate_Status_Code::INVALID_USAGE,
+      Botan::Certificate_Status_Code::CERT_CHAIN_TOO_LONG,
+      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER,
+      Botan::Certificate_Status_Code::NAME_CONSTRAINT_ERROR,
+      Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER,
+      Botan::Certificate_Status_Code::OCSP_CERT_NOT_LISTED,
+      Botan::Certificate_Status_Code::OCSP_BAD_STATUS,
+      Botan::Certificate_Status_Code::CERT_NAME_NOMATCH,
+      Botan::Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION,
+      Botan::Certificate_Status_Code::DUPLICATE_CERT_EXTENSION,
+      Botan::Certificate_Status_Code::EXT_IN_V1_V2_CERT,
+      Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR,
+      Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND,
+      Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE,
+      Botan::Certificate_Status_Code::OCSP_RESPONSE_INVALID,
+      Botan::Certificate_Status_Code::CERT_IS_REVOKED,
+      Botan::Certificate_Status_Code::CRL_BAD_SIGNATURE,
+      Botan::Certificate_Status_Code::SIGNATURE_ERROR,
+      Botan::Certificate_Status_Code::CERT_PUBKEY_INVALID,
+      Botan::Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN,
+      Botan::Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS,
+      };
+
+   for(const auto code : codes)
+      {
+      const std::string s = Botan::to_string(code);
+      result.confirm("String is long enough to be informative", s.size() > 12);
+      result.test_eq("No duplicates", seen.count(s), 0);
+      seen.insert(s);
+      }
+
+   return result;
+
+   }
+
+Test::Result test_x509_extension()
+   {
+   Test::Result result("X509 Extensions API");
+
+   Botan::Extensions extn;
+
+   const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
+   const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
+
+   extn.add(new Botan::Cert_Extension::Basic_Constraints(true), true);
+
+   result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
+   result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
+   result.confirm("SKID is not set", !extn.extension_set(oid_skid));
+   result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
+
+   result.test_eq("Extension::get_extension_bits",
+                  extn.get_extension_bits(oid_bc), "30060101FF020100");
+
+   result.test_throws("Extension::get_extension_bits throws if not set",
+                      [&]() { extn.get_extension_bits(oid_skid); });
+
+   result.test_throws("Extension::add throws on second add",
+                      [&]() { extn.add(new Botan::Cert_Extension::Basic_Constraints(false), false); });
+
+   result.test_eq("Extension::get_extension_bits",
+                  extn.get_extension_bits(oid_bc), "30060101FF020100");
+
+   result.confirm("Returns false since extension already existed",
+                  !extn.add_new(new Botan::Cert_Extension::Basic_Constraints(false), false));
+
+   result.confirm("Basic constraints is still critical", extn.critical_extension_set(oid_bc));
+
+   extn.replace(new Botan::Cert_Extension::Basic_Constraints(false), false);
+   result.confirm("Replaced basic constraints is not critical", !extn.critical_extension_set(oid_bc));
+   result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "3000");
+
+   result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
+   result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
+   result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
+   result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
+
+   return result;
+   }
+
+Test::Result test_x509_dates()
+   {
+   Test::Result result("X509 Time");
+
+   Botan::X509_Time time;
+   result.confirm("unset time not set", !time.time_is_set());
+   time = Botan::X509_Time("080201182200Z", Botan::ASN1_Tag::UTC_TIME);
+   result.confirm("time set after construction", time.time_is_set());
+   result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
+
+   time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_TIME);
+   result.test_eq("UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
+
+   time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+   result.test_eq("UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(),
+                  "2020/03/05 10:03:50 UTC");
+
+   time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+   result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string", time.readable_string(),
+                  "2020/03/05 10:03:50 UTC");
+
+   time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::GENERALIZED_TIME);
+   result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
+
+   // Dates that are valid per X.500 but rejected as unsupported
+   const std::string valid_but_unsup[]
+      {
+      "0802010000-0000",
+      "0802011724+0000",
+      "0406142334-0500",
+      "9906142334+0500",
+      "0006142334-0530",
+      "0006142334+0530",
+
+      "080201000000-0000",
+      "080201172412+0000",
+      "040614233433-0500",
+      "990614233444+0500",
+      "000614233455-0530",
+      "000614233455+0530",
+      };
+
+   // valid length 13
+   const std::string valid_utc[]
+      {
+      "080201000000Z",
+      "080201172412Z",
+      "040614233433Z",
+      "990614233444Z",
+      "000614233455Z",
+      };
+
+   const std::string invalid_utc[]
+      {
+      "",
+      " ",
+      "2008`02-01",
+      "9999-02-01",
+      "2000-02-01 17",
+      "999921",
+
+      // No seconds
+      "0802010000Z",
+      "0802011724Z",
+      "0406142334Z",
+      "9906142334Z",
+      "0006142334Z",
+
+      // valid length 13 -> range check
+      "080201000061Z", // seconds too big (61)
+      "080201000060Z", // seconds too big (60, leap seconds not covered by the standard)
+      "0802010000-1Z", // seconds too small (-1)
+      "080201006000Z", // minutes too big (60)
+      "080201240000Z", // hours too big (24:00)
+
+      // valid length 13 -> invalid numbers
+      "08020123112 Z",
+      "08020123112!Z",
+      "08020123112,Z",
+      "08020123112\nZ",
+      "080201232 33Z",
+      "080201232!33Z",
+      "080201232,33Z",
+      "080201232\n33Z",
+      "0802012 3344Z",
+      "0802012!3344Z",
+      "0802012,3344Z",
+      "08022\n334455Z",
+      "08022 334455Z",
+      "08022!334455Z",
+      "08022,334455Z",
+      "08022\n334455Z",
+      "082 33445511Z",
+      "082!33445511Z",
+      "082,33445511Z",
+      "082\n33445511Z",
+      "2 2211221122Z",
+      "2!2211221122Z",
+      "2,2211221122Z",
+      "2\n2211221122Z",
+
+      // wrong time zone
+      "080201000000",
+      "080201000000z",
+
+      // Fractional seconds
+      "170217180154.001Z",
+
+      // Timezone offset
+      "170217180154+0100",
+
+      // Extra digits
+      "17021718015400Z",
+
+      // Non-digits
+      "17021718015aZ",
+
+      // Trailing garbage
+      "170217180154Zlongtrailinggarbage",
+
+      // Swapped type
+      "20170217180154Z",
+      };
+
+   // valid length 15
+   const std::string valid_generalized_time[]
+      {
+      "20000305100350Z",
+      };
+
+   const std::string invalid_generalized[]
+      {
+      // No trailing Z
+      "20000305100350",
+
+      // No seconds
+      "200003051003Z",
+
+      // Fractional seconds
+      "20000305100350.001Z",
+
+      // Timezone offset
+      "20170217180154+0100",
+
+      // Extra digits
+      "2017021718015400Z",
+
+      // Non-digits
+      "2017021718015aZ",
+
+      // Trailing garbage
+      "20170217180154Zlongtrailinggarbage",
+
+      // Swapped type
+      "170217180154Z",
+      };
+
+   for(const auto& v : valid_but_unsup)
+      {
+      result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
+      }
+
+   for(const auto& v : valid_utc)
+      {
+      Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME);
+      }
+
+   for(const auto& v : valid_generalized_time)
+      {
+      Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME);
+      }
+
+   for(const auto& v : invalid_utc)
+      {
+      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
+      }
+
+   for(const auto& v : invalid_generalized)
+      {
+      result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME); });
+      }
+
+   return result;
+   }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+Test::Result test_crl_dn_name()
+   {
+   Test::Result result("CRL DN name");
+
+   // See GH #1252
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
+   const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
+
+   Botan::X509_Certificate cert(Test::data_file("x509/misc/opcuactt_ca.der"));
+
+   Botan::DataSource_Stream key_input(Test::data_file("x509/misc/opcuactt_ca.pem"));
+   std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(key_input);
+   Botan::X509_CA ca(cert, *key, "SHA-256", Test::rng());
+
+   Botan::X509_CRL crl = ca.new_crl(Test::rng());
+
+   result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
+
+   result.confirm("contains DC component",
+                  crl.issuer_dn().get_attributes().count(dc_oid) == 1);
+#endif
+
+   return result;
+   }
+
+Test::Result test_rsa_oaep()
+   {
+   Test::Result result("RSA OAEP decoding");
+
+#if defined(BOTAN_HAS_RSA)
+   Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
+
+   auto public_key = cert.load_subject_public_key();
+   result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
+   auto pk_info = cert.subject_public_key_algo();
+
+   result.test_eq("RSA-OAEP OID", pk_info.get_oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
+#endif
+
+   return result;
+   }
+
+Test::Result test_x509_decode_list()
+   {
+   Test::Result result("X509_Certificate list decode");
+
+   Botan::DataSource_Stream input(Test::data_file("x509/misc/cert_seq.der"), true);
+
+   Botan::BER_Decoder dec(input);
+   std::vector<Botan::X509_Certificate> certs;
+   dec.decode_list(certs);
+
+   result.test_eq("Expected number of certs in list", certs.size(), 2);
+
+   result.test_eq("Expected cert 1 CN", certs[0].subject_dn().get_first_attribute("CN"), "CA1-PP.01.02");
+   result.test_eq("Expected cert 2 CN", certs[1].subject_dn().get_first_attribute("CN"), "User1-PP.01.02");
+
+   return result;
+   }
+
+Test::Result test_x509_utf8()
+   {
+   Test::Result result("X509 with UTF-8 encoded fields");
+
+   try
+      {
+      Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
+
+      // UTF-8 encoded fields of test certificate (contains cyrillic letters)
+      const std::string organization =
+         "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
+         "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
+      const std::string organization_unit =
+         "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
+         "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
+      const std::string common_name =
+         "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
+         "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
+      const std::string location =
+         "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
+
+      const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
+
+      result.test_eq("O",  issuer_dn.get_first_attribute("O"),  organization);
+      result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
+      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
+      result.test_eq("L",  issuer_dn.get_first_attribute("L"),  location);
+      }
+   catch (const Botan::Decoding_Error &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_x509_bmpstring()
+   {
+   Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
+
+   try
+      {
+      Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
+
+      // UTF-8 encoded fields of test certificate (contains cyrillic and greek letters)
+      const std::string organization =
+         "\x6E\x65\xCF\x87\xCF\xB5\x6E\x69\xCF\x89";
+      const std::string common_name =
+         "\xC3\xA8\x6E\xC7\x9D\xD0\xAF\x20\xD0\x9C\xC7\x9D\xD0\xB9\xD0\xB7\xD1\x8D\xD0\xBB";
+
+      // UTF-8 encoded fields of test certificate (contains only ASCII characters)
+      const std::string location = "Berlin";
+
+      const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
+
+      result.test_eq("O",  issuer_dn.get_first_attribute("O"),  organization);
+      result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
+      result.test_eq("L",  issuer_dn.get_first_attribute("L"),  location);
+      }
+   catch (const Botan::Decoding_Error &ex)
+      {
+      result.test_failure(ex.what());
+      }
+
+   return result;
+   }
+
+Test::Result test_x509_authority_info_access_extension()
+   {
+   Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
+
+   // contains no AIA extension
+   Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
+
+   result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
+   result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
+
+   // contains AIA extension with 1 CA issuer URL and 1 OCSP responder
+   Botan::X509_Certificate aia_cert(Test::data_file("x509/misc/contains_authority_info_access.pem"));
+
+   const auto ca_issuers = aia_cert.ca_issuers();
+
+   result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
+   if (result.tests_failed())
+      return result;
+
+   result.test_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
+   result.test_eq("OCSP responder URL matches", aia_cert.ocsp_responder(), "http://gp.symcd.com");
+
+   // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
+   Botan::X509_Certificate aia_cert_2ca(Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
+
+   const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
+
+   result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
+   if (result.tests_failed())
+      return result;
+
+   result.test_eq("CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
+   result.test_eq("CA issuer URL matches", ca_issuers2[1], "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
+   result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
+
+   return result;
+   }
+
+Test::Result test_verify_gost2012_cert()
+   {
+   Test::Result result("X509 GOST-2012 certificates");
+
+#if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG) && defined(BOTAN_HAS_EMSA1)
+   try
+      {
+      Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
+      Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
+
+      Botan::Certificate_Store_In_Memory trusted;
+      trusted.add_certificate(root_cert);
+
+      const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
+      const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(root_int, restrictions, trusted);
+
+      result.confirm("GOST certificate validates", validation_result.successful_validation());
+      }
+   catch(const Botan::Decoding_Error& e)
+      {
+      result.test_failure(e.what());
+      }
+#endif
+
+   return result;
+   }
+
+/*
+ * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
+ *
+ * For the other algorithms than RSA, only one padding is supported right now.
+ */
+#if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
+ Test::Result test_padding_config() {
+   // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
+   Test::Result test_result("X509 Padding Config");
+
+   std::unique_ptr<Botan::Private_Key> sk(Botan::PKCS8::load_key(
+      Test::data_file("x509/misc/rsa_key.pem"), Test::rng()));
+
+   // Create X509 CA certificate; EMSA3 is used for signing by default
+   Botan::X509_Cert_Options opt("TESTCA");
+   opt.CA_key();
+
+   Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+   test_result.test_eq("CA certificate signature algorithm (default)",
+      Botan::OIDS::oid2str_or_throw(ca_cert_def.signature_algorithm().get_oid()),"RSA/EMSA3(SHA-512)");
+
+   // Create X509 CA certificate; RSA-PSS is explicitly set
+   opt.set_padding_scheme("PSSR");
+   Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+   test_result.test_eq("CA certificate signature algorithm (explicit)",
+      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
+
+#if defined(BOTAN_HAS_EMSA1)
+
+   // Try to set a padding scheme that is not supported for signing with the given key type
+   opt.set_padding_scheme("EMSA1");
+   try
+      {
+      Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+      test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " + sk->algo_name());
+      }
+   catch (const Botan::Invalid_Argument& e)
+      {
+      test_result.test_eq("Build CA certitiface with invalid encoding scheme EMSA1 for key type " +
+         sk->algo_name(), e.what(),
+         "Encoding scheme with canonical name EMSA1 not supported for signature algorithm RSA");
+      }
+#endif
+
+   test_result.test_eq("CA certificate signature algorithm (explicit)",
+      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
+
+   const Botan::X509_Time not_before = from_date(-1, 1, 1);
+   const Botan::X509_Time not_after = from_date(2, 1, 2);
+
+   // Prepare a signing request for the end certificate
+   Botan::X509_Cert_Options req_opt("endpoint");
+   req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
+   Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
+   test_result.test_eq("Certificate request signature algorithm", Botan::OIDS::oid2str_or_throw(end_req.signature_algorithm().get_oid()),"RSA/EMSA4");
+
+   // Create X509 CA object: will fail as the chosen hash functions differ
+   try
+      {
+      Botan::X509_CA ca_fail(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-256)"}},"SHA-512", Test::rng());
+      test_result.test_failure("Configured conflicting hash functions for CA");
+      }
+   catch(const Botan::Invalid_Argument& e)
+      {
+      test_result.test_eq("Configured conflicting hash functions for CA",
+            e.what(),
+            "Hash function from opts and hash_fn argument need to be identical");
+      }
+
+   // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
+   Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
+   Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after);
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa3.signature_algorithm().get_oid()), "RSA/EMSA3(SHA-512)");
+
+   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
+   Botan::X509_CA ca_diff(ca_cert_def, (*sk), {{"padding","EMSA-PSS"}}, "SHA-512", Test::rng());
+   Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, Test::rng(), not_before, not_after);
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_diff_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
+
+   // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
+   Botan::X509_CA ca_exp(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-512,MGF1,64)"}},"SHA-512", Test::rng());
+   Botan::X509_Certificate end_cert_emsa4= ca_exp.sign_request(end_req, Test::rng(), not_before, not_after);
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
+
+   // Check CRL signature algorithm
+   Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
+   test_result.test_eq("CRL signature algorithm", Botan::OIDS::oid2str_or_throw(crl.signature_algorithm().get_oid()), "RSA/EMSA4");
+
+   // sanity check for verification, the heavy lifting is done in the other unit tests
+   const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
+   const Botan::Path_Validation_Restrictions restrictions(false, 80);
+   const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
+            end_cert_emsa4, restrictions, trusted);
+   test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
+
+   return test_result;
+}
+#endif
+
+#endif
+
+Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
+                             const std::string& sig_padding,
+                             const std::string& hash_fn = "SHA-256")
+   {
+   Test::Result result("PKCS10 extensions");
+
+   Botan::X509_Cert_Options opts;
+
+   opts.padding_scheme = sig_padding;
+
+   Botan::AlternativeName alt_name;
+   alt_name.add_attribute("DNS", "example.org");
+   alt_name.add_attribute("DNS", "example.com");
+   alt_name.add_attribute("DNS", "example.net");
+
+   opts.extensions.add(new Botan::Cert_Extension::Subject_Alternative_Name(alt_name));
+
+   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, key, hash_fn, Test::rng());
+
+   std::vector<std::string> alt_dns_names = req.subject_alt_name().get_attribute("DNS");
+
+   result.test_eq("Expected number of DNS names", alt_dns_names.size(), 3);
+
+   // The order is not guaranteed so sort before comparing
+   std::sort(alt_dns_names.begin(), alt_dns_names.end());
+
+   result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "example.com");
+   result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "example.net");
+   result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "example.org");
+
+   return result;
+   }
+
+Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
+                            const std::string& sig_algo,
+                            const std::string& sig_padding = "",
+                            const std::string& hash_fn = "SHA-256")
+   {
+   Test::Result result("X509 Unit");
+
+   /* Create the self-signed cert */
+   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
+
+      {
+      const auto constraints = Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN);
+      result.test_eq("ca key usage", (ca_cert.constraints() & constraints) == constraints, true);
+      }
+
+   /* Create user #1's key and cert request */
+   std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
+
+   Botan::PKCS10_Request user1_req =
+      Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding),
+                                   *user1_key,
+                                   hash_fn,
+                                   Test::rng());
+
+   result.test_eq("PKCS10 challenge password parsed",
+                  user1_req.challenge_password(), "zoom");
+
+   /* Create user #2's key and cert request */
+   std::unique_ptr<Botan::Private_Key> user2_key(make_a_private_key(sig_algo));
+
+   Botan::PKCS10_Request user2_req =
+      Botan::X509::create_cert_req(req_opts2(sig_padding),
+                                   *user2_key,
+                                   hash_fn,
+                                   Test::rng());
+
+   // /* Create user #3's key and cert request */
+   std::unique_ptr<Botan::Private_Key> user3_key(make_a_private_key(sig_algo));
+
+   Botan::PKCS10_Request user3_req =
+      Botan::X509::create_cert_req(req_opts3(sig_padding),
+                                   *user3_key,
+                                   hash_fn,
+                                   Test::rng());
+
+   /* Create the CA object */
+   Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
+
+   const BigInt user1_serial = 99;
+
+   /* Sign the requests to create the certs */
+   Botan::X509_Certificate user1_cert =
+      ca.sign_request(user1_req, Test::rng(), user1_serial,
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
+   result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
+
+   Botan::X509_Certificate user2_cert =
+      ca.sign_request(user2_req, Test::rng(),
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   Botan::X509_Certificate user3_cert =
+      ca.sign_request(user3_req, Test::rng(),
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   // user#1 creates a self-signed cert on the side
+   const auto user1_ss_cert =
+      Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
+
+      {
+      auto constrains = req_opts1(sig_algo).constraints;
+      result.test_eq("user1 key usage", (user1_cert.constraints() & constrains) == constrains, true);
+      }
+
+   /* Copy, assign and compare */
+   Botan::X509_Certificate user1_cert_copy(user1_cert);
+   result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
+
+   user1_cert_copy = user2_cert;
+   result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
+
+   Botan::X509_Certificate user1_cert_differ =
+      ca.sign_request(user1_req, Test::rng(),
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   result.test_eq("certificate differs", user1_cert == user1_cert_differ, false);
+
+   /* Get cert data */
+   result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
+
+   const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
+   result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
+   result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
+   result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
+   result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
+
+   const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
+   result.test_eq("subject OrgaUnit count", user3_subject_dn.get_attribute("OU").size(), req_opts3(sig_algo).more_org_units.size() + 1);
+   result.test_eq("subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
+
+   const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
+   result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
+   result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
+   result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
+
+   const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
+   result.test_eq("subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
+   result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
+
+   const Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
+
+   /* Verify the certs */
+   Botan::Path_Validation_Restrictions restrictions(false, 80);
+   Botan::Certificate_Store_In_Memory store;
+
+   // First try with an empty store
+   Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
+   result.test_eq("user 1 issuer not found",
+                  result_no_issuer.result_string(),
+                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
+
+   store.add_certificate(ca.ca_certificate());
+
+   Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
+   if(!result.confirm("user 1 validates", result_u1.successful_validation()))
+      {
+      result.test_note("user 1 validation result was " + result_u1.result_string());
+      }
+   else
+      {
+      const std::set<std::string> u1_hashes = result_u1.trusted_hashes();
+
+      if(result.test_eq("Single trusted hash", u1_hashes.size(), 1))
+         {
+         result.test_eq("Hash matches test", *u1_hashes.begin(), hash_fn);
+         }
+      }
+
+   Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
+   if(!result.confirm("user 2 validates", result_u2.successful_validation()))
+      {
+      result.test_note("user 2 validation result was " + result_u2.result_string());
+      }
+
+   Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
+   result.test_eq("user 1 issuer not found",
+                  result_no_issuer.result_string(),
+                  Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
+   store.add_crl(crl1);
+
+   std::vector<Botan::CRL_Entry> revoked;
+   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CESSATION_OF_OPERATION));
+   revoked.push_back(user2_cert);
+
+   const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng());
+
+   store.add_crl(crl2);
+
+   const std::string revoked_str =
+      Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
+
+   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
+   result.test_eq("user 1 revoked", result_u1.result_string(), revoked_str);
+
+   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
+   result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
+
+   revoked.clear();
+   revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::REMOVE_FROM_CRL));
+   Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, Test::rng());
+
+   store.add_crl(crl3);
+
+   result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
+   if(!result.confirm("user 1 validates", result_u1.successful_validation()))
+      {
+      result.test_note("user 1 validation result was " + result_u1.result_string());
+      }
+
+   result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
+   result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
+
+   return result;
+   }
+
+Test::Result test_usage(const Botan::Private_Key& ca_key,
+                        const std::string& sig_algo,
+                        const std::string& hash_fn = "SHA-256")
+   {
+   using Botan::Key_Constraints;
+   using Botan::Usage_Type;
+
+   Test::Result result("X509 Usage");
+
+   /* Create the self-signed cert */
+   const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(
+         ca_opts(),
+         ca_key,
+         hash_fn,
+         Test::rng());
+
+   /* Create the CA object */
+   const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, Test::rng());
+
+   std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
+
+   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
+   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+
+   const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(
+         opts,
+         *user1_key,
+         hash_fn,
+         Test::rng());
+
+   const Botan::X509_Certificate user1_cert = ca.sign_request(
+            user1_req,
+            Test::rng(),
+            from_date(-1, 01, 01),
+            from_date(2, 01, 01));
+
+   // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
+   result.test_eq("key usage cRLSign not allowed",
+                  user1_cert.allowed_usage(
+                     Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)), false);
+   result.test_eq("encryption is not allowed",
+                  user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
+
+   // cert only allows digitalSignature, so checking for only that should be ok
+   result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
+
+   opts.constraints = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN);
+
+   const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(
+            opts,
+            *user1_key,
+            hash_fn,
+            Test::rng());
+
+   const Botan::X509_Certificate mult_usage_cert = ca.sign_request(
+            mult_usage_req,
+            Test::rng(),
+            from_date(-1, 01, 01),
+            from_date(2, 01, 01));
+
+   // cert allows multiple usages, so each one of them as well as both together should be allowed
+   result.confirm("key usage multiple digitalSignature allowed",
+                  mult_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
+   result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
+   result.confirm("key usage multiple digitalSignature and cRLSign allowed", mult_usage_cert.allowed_usage(
+                     Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)));
+   result.test_eq("encryption is not allowed",
+                  mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
+
+
+   opts.constraints = Key_Constraints::NO_CONSTRAINTS;
+
+   const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
+
+   const Botan::X509_Certificate no_usage_cert =
+      ca.sign_request(no_usage_req, Test::rng(),
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   // cert allows every usage
+   result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
+   result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
+   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
+
+   if (sig_algo == "RSA")
+      {
+      // cert allows data encryption
+       opts.constraints = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT | Key_Constraints::DATA_ENCIPHERMENT);
+
+      const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(
+               opts,
+               *user1_key,
+               hash_fn,
+               Test::rng());
+
+      const Botan::X509_Certificate enc_cert = ca.sign_request(
+               enc_req,
+               Test::rng(),
+               from_date(-1, 01, 01),
+               from_date(2, 01, 01));
+
+      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
+      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
+      }
+
+   return result;
+   }
+
+Test::Result test_self_issued(const Botan::Private_Key& ca_key,
+                              const std::string& sig_algo,
+                              const std::string& sig_padding = "",
+                              const std::string& hash_fn = "SHA-256")
+   {
+   using Botan::Key_Constraints;
+
+   Test::Result result("X509 Self Issued");
+
+   // create the self-signed cert
+   const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(
+         ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
+
+   /* Create the CA object */
+   const Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
+
+   std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
+
+   // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
+   // but signed by a CA, not signed by it's own private key
+   Botan::X509_Cert_Options opts = ca_opts();
+   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+   opts.set_padding_scheme(sig_padding);
+
+   const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
+
+   const Botan::X509_Certificate self_issued_cert = ca.sign_request(
+            self_issued_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
+
+   // check that this chain can can be verified successfully
+   const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
+
+   const Botan::Path_Validation_Restrictions restrictions(false, 80);
+
+   const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
+            self_issued_cert, restrictions, trusted);
+
+   result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
+
+   return result;
+   }
+
+Test::Result test_x509_uninit()
+   {
+   Test::Result result("X509 object uninitialized access");
+
+   Botan::X509_Certificate cert;
+   result.test_throws("uninitialized cert access causes exception",
+                      "X509_Certificate uninitialized",
+                      [&cert]() { cert.x509_version(); });
+
+   Botan::X509_CRL crl;
+   result.test_throws("uninitialized crl access causes exception",
+                      "X509_CRL uninitialized",
+                      [&crl]() { crl.crl_number(); });
+
+   return result;
+   }
+
+
+using Botan::Key_Constraints;
+
+/**
+* @brief Some typical key usage scenarios (taken from RFC 5280, sec. 4.2.1.3)
+*/
+struct typical_usage_constraints
+   {
+   // ALL constraints are not typical at all, but we use them for a negative test
+   Key_Constraints all = Key_Constraints(
+                            Key_Constraints::DIGITAL_SIGNATURE |
+                            Key_Constraints::NON_REPUDIATION |
+                            Key_Constraints::KEY_ENCIPHERMENT |
+                            Key_Constraints::DATA_ENCIPHERMENT |
+                            Key_Constraints::KEY_AGREEMENT |
+                            Key_Constraints::KEY_CERT_SIGN |
+                            Key_Constraints::CRL_SIGN |
+                            Key_Constraints::ENCIPHER_ONLY |
+                            Key_Constraints::DECIPHER_ONLY);
+
+   Key_Constraints ca = Key_Constraints(Key_Constraints::KEY_CERT_SIGN);
+   Key_Constraints sign_data = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE);
+   Key_Constraints non_repudiation = Key_Constraints(
+                                        Key_Constraints::NON_REPUDIATION |
+                                        Key_Constraints::DIGITAL_SIGNATURE);
+   Key_Constraints key_encipherment = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT);
+   Key_Constraints data_encipherment = Key_Constraints(Key_Constraints::DATA_ENCIPHERMENT);
+   Key_Constraints key_agreement = Key_Constraints(Key_Constraints::KEY_AGREEMENT);
+   Key_Constraints key_agreement_encipher_only = Key_Constraints(
+            Key_Constraints::KEY_AGREEMENT |
+            Key_Constraints::ENCIPHER_ONLY);
+   Key_Constraints key_agreement_decipher_only = Key_Constraints(
+            Key_Constraints::KEY_AGREEMENT |
+            Key_Constraints::DECIPHER_ONLY);
+   Key_Constraints crl_sign = Key_Constraints::CRL_SIGN;
+   Key_Constraints sign_everything = Key_Constraints(
+                                        Key_Constraints::DIGITAL_SIGNATURE |
+                                        Key_Constraints::KEY_CERT_SIGN |
+                                        Key_Constraints::CRL_SIGN);
+   };
+
+
+Test::Result test_valid_constraints(const Botan::Private_Key& key,
+                                    const std::string& pk_algo)
+   {
+   Test::Result result("X509 Valid Constraints");
+
+   // should not throw on empty constraints
+   verify_cert_constraints_valid_for_key_type(key, Key_Constraints(Key_Constraints::NO_CONSTRAINTS));
+
+   // now check some typical usage scenarios for the given key type
+   typical_usage_constraints typical_usage;
+
+   if(pk_algo == "DH" || pk_algo == "ECDH")
+      {
+      // DH and ECDH only for key agreement
+      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
+         });
+      result.test_throws("cert sign not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
+         });
+      result.test_throws("signature not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
+         });
+      result.test_throws("non repudiation not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
+         });
+      result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
+         });
+      result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
+
+      result.test_throws("crl sign not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
+         });
+      result.test_throws("sign, cert sign, crl sign not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+         });
+      }
+   else if(pk_algo == "RSA")
+      {
+      // RSA can do everything except key agreement
+      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
+
+      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
+         });
+      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
+         });
+      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+      }
+   else if(pk_algo == "ElGamal")
+      {
+      // only ElGamal encryption is currently implemented
+      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
+         });
+      result.test_throws("cert sign not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
+
+      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
+         });
+      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
+         });
+      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
+         });
+      result.test_throws("crl sign not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
+         });
+      result.test_throws("sign, cert sign, crl sign not permitted not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+         });
+      }
+   else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
+           pk_algo == "GOST-34.10")
+      {
+      // these are signature algorithms only
+      result.test_throws("all constraints not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
+
+      result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
+         });
+      result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
+         });
+      result.test_throws("key agreement not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
+         });
+      result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
+         });
+      result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
+         {
+         verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
+         });
+
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
+      verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
+      }
+
+   return result;
+   }
+
+/**
+ * @brief X.509v3 extension that encodes a given string
+ */
+class String_Extension final : public Botan::Certificate_Extension
+   {
+   public:
+      String_Extension() = default;
+      String_Extension(const std::string& val) : m_contents(val) {}
+
+      std::string value() const
+         {
+         return m_contents;
+         }
+
+      String_Extension* copy() const override
+         {
+         return new String_Extension(m_contents);
+         }
+
+      Botan::OID oid_of() const override
+         {
+         return Botan::OID("1.2.3.4.5.6.7.8.9.1");
+         }
+
+      bool should_encode() const override
+         {
+         return true;
+         }
+
+      std::string oid_name() const override
+         {
+         return "String Extension";
+         }
+
+      void contents_to(Botan::Data_Store&, Botan::Data_Store&) const override {}
+
+      std::vector<uint8_t> encode_inner() const override
+         {
+         std::vector<uint8_t> bits;
+         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::UTF8_STRING));
+         return bits;
+         }
+
+      void decode_inner(const std::vector<uint8_t>& in) override
+         {
+         Botan::ASN1_String str;
+         Botan::BER_Decoder(in).decode(str, Botan::UTF8_STRING).verify_end();
+         m_contents = str.value();
+         }
+
+   private:
+      std::string m_contents;
+   };
+
+Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
+                                  const std::string& sig_algo,
+                                  const std::string& sig_padding = "",
+                                  const std::string& hash_fn = "SHA-256")
+   {
+   Test::Result result("X509 Custom DN");
+
+   /* Create the self-signed cert */
+   Botan::X509_Certificate ca_cert =
+      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
+
+   /* Create the CA object */
+   Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
+
+   std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
+
+   Botan::X509_DN subject_dn;
+
+   const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
+   const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
+   const Botan::ASN1_String val1("Custom Attr 1", Botan::PRINTABLE_STRING);
+   const Botan::ASN1_String val2("12345", Botan::UTF8_STRING);
+
+   subject_dn.add_attribute(attr1, val1);
+   subject_dn.add_attribute(attr2, val2);
+
+   Botan::Extensions extensions;
+
+   Botan::PKCS10_Request req =
+      Botan::PKCS10_Request::create(*user_key,
+                                    subject_dn,
+                                    extensions,
+                                    hash_fn,
+                                    Test::rng(),
+                                    sig_padding);
+
+   Botan::X509_DN req_dn = req.subject_dn();
+
+   result.test_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
+
+   Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
+   Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
+   result.confirm("Attr1 matches encoded", req_val1 == val1);
+   result.confirm("Attr2 matches encoded", req_val2 == val2);
+   result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
+   result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
+
+   Botan::X509_Time not_before("100301123001Z", Botan::UTC_TIME);
+   Botan::X509_Time not_after("300301123001Z", Botan::UTC_TIME);
+
+   auto cert = ca.sign_request(req, Test::rng(), not_before, not_after);
+
+   Botan::X509_DN cert_dn = cert.subject_dn();
+
+   result.test_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
+
+   Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
+   Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
+   result.confirm("Attr1 matches encoded", cert_val1 == val1);
+   result.confirm("Attr2 matches encoded", cert_val2 == val2);
+   result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
+   result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
+
+   return result;
+   }
+
+
+Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
+                                  const std::string& sig_algo,
+                                  const std::string& sig_padding = "",
+                                  const std::string& hash_fn = "SHA-256")
+   {
+   using Botan::Key_Constraints;
+
+   Test::Result result("X509 Extensions");
+
+   /* Create the self-signed cert */
+   Botan::X509_Certificate ca_cert =
+      Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
+
+   /* Create the CA object */
+   Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
+
+   std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
+
+   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
+   opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+
+   // include a custom extension in the request
+   Botan::Extensions req_extensions;
+   const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
+   const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
+   req_extensions.add(new String_Extension("AAAAAAAAAAAAAABCDEF"), false);
+   opts.extensions = req_extensions;
+   opts.set_padding_scheme(sig_padding);
+
+   /* Create a self-signed certificate */
+   const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(
+            opts, *user_key, hash_fn, Test::rng());
+
+   result.confirm("Extensions::extension_set true for Key_Usage", self_signed_cert.v3_extensions().extension_set(ku_oid));
+
+   // check if known Key_Usage extension is present in self-signed cert
+   auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
+   if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr))
+      {
+      result.confirm("Key_Usage extension value matches in self-signed certificate",
+                     dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
+      }
+
+   // check if custom extension is present in self-signed cert
+   auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
+   if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr))
+      {
+      result.test_eq("Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
+      }
+
+
+   const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
+
+   /* Create a CA-signed certificate */
+   const Botan::X509_Certificate ca_signed_cert =
+      ca.sign_request(user_req, Test::rng(),
+                      from_date(-1, 01, 01),
+                      from_date(2, 01, 01));
+
+   // check if known Key_Usage extension is present in CA-signed cert
+   result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
+
+   key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
+   if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr))
+      {
+      result.confirm("Key_Usage extension value matches in user certificate",
+                     dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == Botan::DIGITAL_SIGNATURE);
+      }
+
+   // check if custom extension is present in CA-signed cert
+   result.confirm("Extensions::extension_set true for String_Extension", ca_signed_cert.v3_extensions().extension_set(oid));
+   string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
+   if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr))
+      {
+      result.test_eq("Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
+      }
+
+   return result;
+   }
+
+Test::Result test_hashes(const Botan::Private_Key& key,
+                         const std::string& hash_fn = "SHA-256")
+   {
+   Test::Result result("X509 Hashes");
+
+   struct TestData
+      {
+      const std::string issuer, subject, issuer_hash, subject_hash;
+      } const cases[]
+      {
+         {
+         "",
+         "",
+         "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
+         "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"
+         },
+         {
+         "a",
+         "b",
+         "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
+         "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"
+         },
+         {
+         "A",
+         "B",
+         "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
+         "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"
+         },
+         {
+         "Test Issuer/US/Botan Project/Testing",
+         "Test Subject/US/Botan Project/Testing",
+         "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
+         "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
+         },
+         {
+         "Test Subject/US/Botan Project/Testing",
+         "Test Issuer/US/Botan Project/Testing",
+         "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
+         "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
+         }
+      };
+
+   for(const auto& a : cases)
+      {
+      Botan::X509_Cert_Options opts{a.issuer};
+      opts.CA_key();
+
+      const Botan::X509_Certificate issuer_cert =
+         Botan::X509::create_self_signed_cert(opts, key, hash_fn, Test::rng());
+
+      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
+      result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
+
+      const Botan::X509_CA ca(issuer_cert, key, hash_fn, Test::rng());
+      const Botan::PKCS10_Request req =
+         Botan::X509::create_cert_req(a.subject, key, hash_fn, Test::rng());
+      const Botan::X509_Certificate subject_cert =
+         ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
+
+      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
+      result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
+      }
+   return result;
+   }
+
+class X509_Cert_Unit_Tests final : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         const std::string sig_algos[] { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10", "Ed25519" };
+
+         for(const std::string& algo : sig_algos)
+            {
+#if !defined(BOTAN_HAS_EMSA_PKCS1)
+            if(algo == "RSA")
+               continue;
+#endif
+
+#if !defined(BOTAN_HAS_EMSA1)
+            if(algo != "RSA" && algo != "Ed25519")
+               continue;
+#endif
+
+            std::unique_ptr<Botan::Private_Key> key = make_a_private_key(algo);
+
+            if(key == nullptr)
+               continue;
+
+            results.push_back(test_hashes(*key));
+            results.push_back(test_valid_constraints(*key, algo));
+
+            Test::Result usage_result("X509 Usage");
+            try
+               {
+               usage_result.merge(test_usage(*key, algo));
+               }
+            catch(std::exception& e)
+               {
+               usage_result.test_failure("test_usage " + algo, e.what());
+               }
+            results.push_back(usage_result);
+
+            for(auto padding_scheme : Botan::get_sig_paddings(algo))
+               {
+               Test::Result cert_result("X509 Unit");
+               try
+                  {
+                  cert_result.merge(test_x509_cert(*key, algo, padding_scheme));
+                  }
+               catch(std::exception& e)
+                  {
+                  cert_result.test_failure("test_x509_cert " + algo, e.what());
+                  }
+               results.push_back(cert_result);
+
+               Test::Result pkcs10_result("PKCS10 extensions");
+               try
+                  {
+                  pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme));
+                  }
+               catch(std::exception& e)
+                  {
+                  pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
+                  }
+               results.push_back(pkcs10_result);
+
+               Test::Result self_issued_result("X509 Self Issued");
+               try
+                  {
+                  self_issued_result.merge(test_self_issued(*key, algo, padding_scheme));
+                  }
+               catch(std::exception& e)
+                  {
+                  self_issued_result.test_failure("test_self_issued " + algo, e.what());
+                  }
+               results.push_back(self_issued_result);
+
+               Test::Result extensions_result("X509 Extensions");
+               try
+                  {
+                  extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme));
+                  }
+               catch(std::exception& e)
+                  {
+                  extensions_result.test_failure("test_extensions " + algo, e.what());
+                  }
+               results.push_back(extensions_result);
+
+               Test::Result custom_dn_result("X509 Custom DN");
+               try
+                  {
+                  custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme));
+                  }
+               catch(std::exception& e)
+                  {
+                  custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
+                  }
+               results.push_back(custom_dn_result);
+               }
+
+            }
+
+         /*
+         These are algos which cannot sign but can be included in certs
+         */
+         const std::vector<std::string> enc_algos = { "DH", "ECDH", "ElGamal" };
+
+         for(std::string algo : enc_algos)
+            {
+            std::unique_ptr<Botan::Private_Key> key = make_a_private_key(algo);
+
+            if(key)
+               {
+               results.push_back(test_valid_constraints(*key, algo));
+               }
+            }
+
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
+         Test::Result pad_config_result("X509 Padding Config");
+         try
+            {
+            pad_config_result.merge(test_padding_config());
+            }
+         catch(const std::exception& e)
+            {
+            pad_config_result.test_failure("test_padding_config", e.what());
+            }
+         results.push_back(pad_config_result);
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+         results.push_back(test_x509_utf8());
+         results.push_back(test_x509_bmpstring());
+         results.push_back(test_crl_dn_name());
+         results.push_back(test_x509_decode_list());
+         results.push_back(test_rsa_oaep());
+         results.push_back(test_x509_authority_info_access_extension());
+         results.push_back(test_verify_gost2012_cert());
+#endif
+
+         results.push_back(test_x509_extension());
+         results.push_back(test_x509_dates());
+         results.push_back(test_cert_status_strings());
+         results.push_back(test_x509_uninit());
+
+         return results;
+         }
+   };
+
+BOTAN_REGISTER_TEST("x509_unit", X509_Cert_Unit_Tests);
+
+#endif
+
+}
+
+}
diff --git a/third_party/cacert.md b/third_party/cacert.md
new file mode 100644
--- /dev/null
+++ b/third_party/cacert.md
@@ -0,0 +1,4 @@
+CA certificates extracted from Mozilla
+======================================
+
+This bundle was generated at Wed Oct 14 03:12:15 2020 GMT, see https://curl.haxx.se/docs/caextract.html.
diff --git a/third_party/cacert.pem b/third_party/cacert.pem
new file mode 100644
--- /dev/null
+++ b/third_party/cacert.pem
@@ -0,0 +1,3435 @@
+##
+## Bundle of CA Root Certificates
+##
+## Certificate data from Mozilla as of: Wed Oct 14 03:12:15 2020 GMT
+##
+## This is a bundle of X.509 certificates of public Certificate Authorities
+## (CA). These were automatically extracted from Mozilla's root certificates
+## file (certdata.txt).  This file can be found in the mozilla source tree:
+## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+##
+## It contains the certificates in PEM format and therefore
+## can be directly used with curl / libcurl / php_curl, or with
+## an Apache+mod_ssl webserver for SSL client authentication.
+## Just configure this file as the SSLCACertificateFile.
+##
+## Conversion done with mk-ca-bundle.pl version 1.28.
+## SHA256: a831d3bc63ba1f65478afe28038742b7150c0c2efd243ac342b64792a75d2038
+##
+
+
+GlobalSign Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
+GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
+b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
+BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
+VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
+DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
+THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
+Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
+c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
+gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
+Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
+j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
+hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
+X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
+ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
+s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
+S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
+TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
+ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
+YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
+BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
+9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
+01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
+9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+
+Entrust.net Premium 2048 Secure Server CA
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
+ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
+bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
+BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
+NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
+d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
+ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
+Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
+hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
+nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
+VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
+KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
+T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
+zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
+J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
+nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
+-----END CERTIFICATE-----
+
+Baltimore CyberTrust Root
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
+ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
+ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
+SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
+dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
+uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
+UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
+G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
+XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
+l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
+VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
+cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
+hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
+Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
+b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
+A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
+MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
+MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
+Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
+A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
+Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
+j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
+rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
+MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
+hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
+Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
+v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
+W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
+tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+
+GeoTrust Global CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
+MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
+BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
+8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
+T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
+vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
+zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
+d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
+mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
+XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
+MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
+Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
+JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
+RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
+7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
+8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
+qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
+Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
+Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
+KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
+ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
+XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
+qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
+oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
+xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
+KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
+DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
+xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
+p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
+P/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA 2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
+MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
+SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
+DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
+j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
+JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
+QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
+WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
+20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
+ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
+SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
+8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
+BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
+4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
+A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
+Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
+pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
+FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
+gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
+X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+
+Comodo AAA Services root
+========================
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
+MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
+c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
+BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
+C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
+i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
+Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
+Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
+Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
+BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
+cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
+LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
+7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
+8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
+12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
+ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
+MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
+cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
+EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
+J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
+F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
+YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
+AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
+PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
+ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
+MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
+ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
+Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
+BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
+FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
+tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
+fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
+LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
+gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
+5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
+5nrQNiOKSnQ2+Q==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
+ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
+XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
+lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
+lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
+lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
+66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
+wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
+D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
+BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
+J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
+DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
+a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
+Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
+UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
+VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
+IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
+WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
+f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
+4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
+VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3
+==================
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
+OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
+DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
+KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
+DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
+BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
+p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
+nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
+MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
+Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
+uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
+BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
+BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
+VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
+ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
+AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
+qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
+hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
+POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
+Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
+8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
+bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
+g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
+vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
+qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+
+Security Communication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
+8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
+DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
+5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
+DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
+JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
+0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
+mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
+s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
+6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
+FL39vmwLAw==
+-----END CERTIFICATE-----
+
+Sonera Class 2 Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
+U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
+NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
+IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
+/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
+dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
+f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
+tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
+nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
+XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
+0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
+cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
+Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
+EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
+llpwrN9M
+-----END CERTIFICATE-----
+
+XRamp Global CA Root
+====================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
+BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
+dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
+HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
+U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
+IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
+foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
+zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
+AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
+xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
+oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
+AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
+/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
+nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
+8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+
+Go Daddy Class 2 CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
+VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
+A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
+ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
+2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
+qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
+YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
+vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
+BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
+atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
+MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
+PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
+I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
+Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
+vZ8=
+-----END CERTIFICATE-----
+
+Starfield Class 2 CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
+U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
+MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
+A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
+SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
+bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
+JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
+epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
+F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
+MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
+hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
+bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
+afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
+PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
+KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
+QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
+MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
+9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
+UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
+/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
+oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
+GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
+66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
+hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
+EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
+SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
+8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+
+DigiCert Global Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
+MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
+TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
+BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
+4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
+7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
+o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
+8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
+BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
+EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
+tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
+UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+
+DigiCert High Assurance EV Root CA
+==================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
+KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
+MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
+MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
+Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
+Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
+OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
+MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
+NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
+h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
+JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
+V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
+myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
+mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
+-----END CERTIFICATE-----
+
+DST Root CA X3
+==============
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
+ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
+DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
+cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
+rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
+UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
+xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
+utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
+MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
+dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
+GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
+RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
+fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+
+SwissSign Gold CA - G2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
+EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
+MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
+c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
+t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
+jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
+vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
+ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
+AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
+jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
+peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
+7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
+GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
+OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
+5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
+44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
+Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
+Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
+mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
+vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
+KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
+NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
+viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+
+SwissSign Silver CA - G2
+========================
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
+BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
+DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
+aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
+N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
+6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
+MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
+qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
+FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
+ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
+celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
+CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
+tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
+4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
+kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
+3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
+/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
+DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
+e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
+WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
+DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
+DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
+cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
+b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
+nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
+RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
+tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
+hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
+Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
+NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
+Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
+1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+
+thawte Primary Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
+MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
+SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
+KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
+FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
+oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
+1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
+q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
+aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
+afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
+AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
+uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
+jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
+z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G5
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
+dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
+j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
+Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
+fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
+SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
+KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
+Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
+ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+
+SecureTrust CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
+dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
+BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
+OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
+DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
+GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
+01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
+ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
+SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
+mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
+nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+
+Secure Global CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
+bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
+MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
+YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
+bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
+8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
+HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
+0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
+oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
+MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
+CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
+3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+
+COMODO Certification Authority
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
+MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
+T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
+xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
+4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
+1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
+rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
+b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
+AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
+OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
+IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
+-----END CERTIFICATE-----
+
+Network Solutions Certificate Authority
+=======================================
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
+EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
+IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
+MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
+jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
+aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
+crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
+/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
+AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
+bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
+A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
+4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
+GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
+ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+
+COMODO ECC Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
+GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
+4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
+wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
+FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
+U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+Certigna
+========
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
+EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
+MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
+Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
+XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
+GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
+ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
+DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
+Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
+tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
+BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
+SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
+hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
+PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
+1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
+WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
+-----END CERTIFICATE-----
+
+Cybertrust Global Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
+ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
+MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
+ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
+0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
+AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
+89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
+8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
+MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
+A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
+lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
+5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
+hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
+X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
+WL1WMRJOEcgh4LMRkWXbtKaIOM5V
+-----END CERTIFICATE-----
+
+ePKI Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
+EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
+MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
+MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
+IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
+lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
+qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
+12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
+WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
+lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
+vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
+Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
+MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
+ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
+1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
+KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
+xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
+NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
+GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
+xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
+gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
+sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
+BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
+-----END CERTIFICATE-----
+
+certSIGN ROOT CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
+VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
+Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
+CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
+JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
+rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
+ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
+0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
+AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
+AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
+SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
+x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
+vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
+TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G3
+=============================================
+-----BEGIN CERTIFICATE-----
+MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
+IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
+NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
+YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
+LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
+K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
+c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
+IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
+dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
+2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
+cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
+Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
+AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
+t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
+VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
+IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
+Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
+MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
+b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
+IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
+LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
+8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
+G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
+rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
+ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
+d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
+VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
+A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
+P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
+7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
+vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
+KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
+A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
+t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
+8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
+er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G2
+=============================================
+-----BEGIN CERTIFICATE-----
+MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
+OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
+MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
+b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
+KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
+EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
+ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
+npaqBA+K
+-----END CERTIFICATE-----
+
+VeriSign Universal Root Certification Authority
+===============================================
+-----BEGIN CERTIFICATE-----
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
+1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
+MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
+9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
+AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
+tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
+CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
+a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
+Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
+Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
+P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
+wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
+mJO37M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G4
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
+VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
+b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
+ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
+b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
+Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
+rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
+HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
+Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
+A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
+AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
+-----END CERTIFICATE-----
+
+NetLock Arany (Class Gold) Főtanúsítvány
+========================================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
+A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
+dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
+cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
+MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
+ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
+c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
+0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
+/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
+H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
+fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
+neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
+qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
+YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
+NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
+dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+Hongkong Post Root CA 1
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
+DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
+NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
+IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
+ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
+auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
+qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
+V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
+HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
+h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
+l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
+IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
+T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
+c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
+-----END CERTIFICATE-----
+
+SecureSign RootCA11
+===================
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
+SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
+b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
+KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
+cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
+TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
+wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
+g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
+O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
+bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
+t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
+OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
+bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
+Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
+y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
+lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
+MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
+c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
+BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
+U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
+fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
+0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
+pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
+1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
+AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
+QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
+FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
+lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
+I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
+yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
+LXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R3
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
+iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
+0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
+rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
+OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
+xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
+lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
+EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
+bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
+YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
+kpeDMdmztcpHWD9f
+-----END CERTIFICATE-----
+
+Autoridad de Certificacion Firmaprofesional CIF A62634068
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
+BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
+QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
+NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
+Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
+B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
+7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
+ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
+plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
+MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
+LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
+bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
+vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
+EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
+DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
+bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
+ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
+51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
+R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
+T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
+Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
+osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
+crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
+saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
+KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
+6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
+
+Izenpe.com
+==========
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
+EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
+MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
+QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
+03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
+ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
+PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
+OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
+F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
+0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
+leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
+AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
+SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
+NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
+Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
+kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
+hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
+g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
+aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
+nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
+ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
+Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
+WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+Chambers of Commerce Root - 2008
+================================
+-----BEGIN CERTIFICATE-----
+MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
+Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
+ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
+EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
+cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
+XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
+h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
+ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
+NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
+D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
+lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
+0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
+ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
+EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
+G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
+BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
+bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
+bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
+CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
+AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
+wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
+3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
+RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
+M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
+YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
+9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
+zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
+nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
+OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
+-----END CERTIFICATE-----
+
+Global Chambersign Root - 2008
+==============================
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
+NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
+Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
+QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
+aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
+VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
+XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
+ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
+/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
+TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
+H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
+Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
+HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
+wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
+AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
+BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
+BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
+aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
+aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
+1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
+dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
+/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
+ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
+dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
+9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
+foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
+qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
+P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
+c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
+09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
+-----END CERTIFICATE-----
+
+Go Daddy Root Certificate Authority - G2
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
+MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
+A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
+9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
+fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
+NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
+BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
+vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
+5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
+N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+Starfield Root Certificate Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
+eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
+DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
+VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
+W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
+bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
+N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
+ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
+JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
+TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
+4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
+F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
+c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+Starfield Services Root Certificate Authority - G2
+==================================================
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
+IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
+dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
+h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
+hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
+LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
+rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
+SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
+E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
+xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
+YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
+-----END CERTIFICATE-----
+
+AffirmTrust Commercial
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
+MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
+DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
+C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
+BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
+MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
+HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
+hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
+qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
+0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
+sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
+-----END CERTIFICATE-----
+
+AffirmTrust Networking
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
+MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
+Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
+dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
+/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
+h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
+HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
+UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
+12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
+WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
+/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
+-----END CERTIFICATE-----
+
+AffirmTrust Premium
+===================
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
+OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
+dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
+BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
+5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
+GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
+p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
+S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
+6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
+/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
+MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
+Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
+6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
+L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
+BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
+IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
+g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
+zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
+-----END CERTIFICATE-----
+
+AffirmTrust Premium ECC
+=======================
+-----BEGIN CERTIFICATE-----
+MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
+BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
+MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
+cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
+N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
+BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
+BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
+57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
+eQ==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
+ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
+MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
+ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
+l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
+J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
+fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
+cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
+Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
+DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
+jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
+mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
+Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+TWCA Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
+VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
+EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
+IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
+QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
+oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
+4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
+y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
+9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
+mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
+QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
+T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
+Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+Security Communication RootCA2
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
+SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
+aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
+3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
+spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
+EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
+QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
+u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
+3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
+tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
+mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+EC-ACC
+======
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
+BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
+ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
+VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
+CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
+BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
+MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
+SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
+Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
+cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
+w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
+ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
+HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
+E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
+0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
+VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
+Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
+dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
+lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
+Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
+l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
+E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
+5EI=
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2011
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
+O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
+aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
+AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
+IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
+1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
+71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
+8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
+3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
+MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
+MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
+b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
+XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
+/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
+7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+
+Actalis Authentication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
+BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
+AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
+MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
+IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
+IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
+wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
+by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
+zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
+YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
+oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
+EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
+hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
+EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
+jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
+iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
+ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
+WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
+JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
+K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
+Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
+4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
+2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
+lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
+OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
+vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
+-----END CERTIFICATE-----
+
+Trustis FPS Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG
+EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290
+IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV
+BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ
+RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk
+H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa
+cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt
+o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA
+AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd
+BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c
+GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC
+yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P
+8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV
+l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
+iB6XzCGcKQENZetX2fNXlrtIzYE=
+-----END CERTIFICATE-----
+
+Buypass Class 2 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
+DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
+g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
+9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
+/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
+CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
+awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
+zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
+Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
+Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
+M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
+A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
+osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
+aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
+DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
+LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
+oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
+wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
+CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
+rJgWVqA=
+-----END CERTIFICATE-----
+
+Buypass Class 3 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
+DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
+sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
+5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
+7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
+ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
+2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
+/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
+RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
+Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
+j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
+cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
+uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
+Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
+ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
+KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
+6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
+UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
+eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
+Cp/HuZc=
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 3
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
+MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
+9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
+NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
+iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
+0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
+AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
+fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
+ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
+P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
+Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
+LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
+ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
+BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
+KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
+p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
+AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
+4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
+eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
+MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
+PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
+OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
+2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
+dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
+X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 EV 2009
+=================================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
+egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
+zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
+7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
+sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
+11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
+cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
+ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
+MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
+b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
+c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
+PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
+ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
+NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
+w9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+CA Disig Root R2
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
+w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
+xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
+A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
+GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
+g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
+5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
+koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
+Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
+Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
+Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
+sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
+dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
+1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
+mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
+utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
+sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
+UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
+7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
+
+ACCVRAIZ1
+=========
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
+SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
+MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
+UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
+jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
+RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
+aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
+0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
+WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
+8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
+5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
+9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
+Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
+Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
+Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
+Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
+QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
+AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
+YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
+AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
+IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
+aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
+dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
+MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
+hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
+R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
+YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
+nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
+TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
+sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
+Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
+3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
+EfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+
+TWCA Global Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
+CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
+QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
+EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
+Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
+nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
+r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
+Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
+tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
+KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
+sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
+yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
+kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
+zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
+cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
+LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
+8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
+/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
+lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
+A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
+i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
+EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
+zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
+-----END CERTIFICATE-----
+
+TeliaSonera Root CA v1
+======================
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
+CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
+MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
+VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
+3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
+B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
+Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
+oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
+F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
+oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
+gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
+TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
+AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
+DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
+zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
+0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
+pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
+G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
+c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
+JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
+qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
+Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
+WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
+-----END CERTIFICATE-----
+
+E-Tugra Certification Authority
+===============================
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
+DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
+ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
+ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
+NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
+QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
+cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
+DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
+hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
+CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
+ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
+BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
+E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
+rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
+jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
+rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
+dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
+/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
+MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
+kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
+XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
+VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
+a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
+dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
+KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
+Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
+8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
+C7TbO6Orb1wdtn7os4I07QZcJA==
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 2
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
+MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
+SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
+vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
+2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
+WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
+YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
+r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
+vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
+3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
+9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
+-----END CERTIFICATE-----
+
+Atos TrustedRoot 2011
+=====================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
+cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
+MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
+A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
+hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
+54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
+HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
+z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
+l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
+bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
+k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
+TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
+61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
+3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 1 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE
+PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm
+PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6
+Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN
+ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l
+g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV
+7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX
+9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f
+iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg
+t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI
+hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
+MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3
+GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct
+Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP
++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh
+3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa
+wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6
+O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0
+FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV
+hMJKzRwuJIczYOXD
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh
+ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY
+NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t
+oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o
+MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l
+V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo
+L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ
+sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD
+6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh
+lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI
+hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K
+pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9
+x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz
+dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X
+U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw
+mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD
+zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN
+JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr
+O3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286
+IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL
+Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe
+6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3
+I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U
+VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7
+5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi
+Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM
+dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt
+rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI
+hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
+KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS
+t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ
+TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du
+DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib
+Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD
+hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX
+0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW
+dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2
+PpxxVJkES/1Y+Zj0
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw
+MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH
+35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq
+bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw
+VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP
+YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn
+lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO
+w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv
+0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz
+d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW
+hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M
+jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
+IhNzbM8m9Yop5w==
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
+VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb
+RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs
+KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF
+UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy
+YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy
+1vUhZscv6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+
+DigiCert Global Root G2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx
+MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ
+kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO
+3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV
+BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM
+UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB
+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu
+5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr
+F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U
+WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH
+QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/
+iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
+
+DigiCert Global Root G3
+=======================
+-----BEGIN CERTIFICATE-----
+MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD
+VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw
+MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C
+AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O
+YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp
+Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y
+3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34
+VOKa5Vt8sycX
+-----END CERTIFICATE-----
+
+DigiCert Trusted Root G4
+========================
+-----BEGIN CERTIFICATE-----
+MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
+HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
+pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o
+k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa
+vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY
+QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6
+MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm
+mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
+f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH
+dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8
+oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
+ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY
+ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr
+yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy
+7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah
+ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN
+5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb
+/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa
+5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK
+G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP
+82Z+
+-----END CERTIFICATE-----
+
+COMODO RSA Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn
+dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ
+FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+
+5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG
+x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX
+2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL
+OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3
+sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C
+GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5
+WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
+FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt
+rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+
+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg
+tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW
+sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp
+pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA
+zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq
+ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52
+7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I
+LaZRfyHBNVOFBkpdn627G190
+-----END CERTIFICATE-----
+
+USERTrust RSA Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz
+0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j
+Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn
+RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O
++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq
+/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE
+Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM
+lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8
+yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+
+eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW
+FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ
+7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ
+Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM
+8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi
+FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi
+yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c
+J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw
+sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx
+Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----
+
+USERTrust ECC Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
+0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
+nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
+HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
+HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
+9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R4
+===========================
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl
+OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P
+AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV
+MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF
+JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R5
+===========================
+-----BEGIN CERTIFICATE-----
+MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6
+SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS
+h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx
+uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
+yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G3
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y
+olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t
+x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy
+EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K
+Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur
+mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5
+1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp
+07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo
+FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE
+41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB
+AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu
+yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
+U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq
+KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1
+v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA
+8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b
+8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r
+mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq
+1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI
+JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV
+tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden EV Root CA
+================================
+-----BEGIN CERTIFICATE-----
+MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
+MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
+cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
+SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
+O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
+0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
+Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
+XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
+08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
+0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
+74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
+fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
+ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
+eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
+c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
+5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
+b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
+f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
+5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
+WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
+DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
+eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
+-----END CERTIFICATE-----
+
+IdenTrust Commercial Root CA 1
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS
+b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES
+MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB
+IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld
+hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/
+mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi
+1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C
+XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl
+3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy
+NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV
+WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg
+xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix
+uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI
+hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
+6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg
+ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt
+ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV
+YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX
+feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro
+kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe
+2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz
+Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R
+cGzM7vRX+Bi6hG6H
+-----END CERTIFICATE-----
+
+IdenTrust Public Sector Root CA 1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv
+ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV
+UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS
+b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy
+P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6
+Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI
+rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf
+qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS
+mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn
+ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh
+LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v
+iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL
+4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B
+Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw
+DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
+t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A
+mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt
+GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt
+m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx
+NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4
+Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI
+ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC
+ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ
+3Wl9af0AVqW3rLatt8o+Ae+c
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
+bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
+b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
+HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
+DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
+OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
+/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
+HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
+s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
+TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
+0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
+iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
+Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
+nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
+e4pIb4tF9g==
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - EC1
+==========================================
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
+FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
+YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
+ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
+FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
+LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
+IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
+AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
+9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
+vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
+kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
+-----END CERTIFICATE-----
+
+CFCA EV ROOT
+============
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE
+CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB
+IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw
+MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD
+DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV
+BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD
+7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN
+uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW
+ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7
+xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f
+py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K
+gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol
+hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ
+tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf
+BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
+ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q
+ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua
+4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG
+E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX
+BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn
+aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy
+PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX
+kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C
+ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GB CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG
+EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
+ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw
+MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD
+VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds
+b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX
+scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP
+rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk
+9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o
+Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg
+GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
+hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD
+dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0
+VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui
+HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
+Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
+-----END CERTIFICATE-----
+
+SZAFIR ROOT CA2
+===============
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV
+BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ
+BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD
+VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q
+qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK
+DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE
+2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ
+ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi
+ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC
+AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5
+O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67
+oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul
+4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6
++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA 2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE
+BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1
+bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y
+ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ
+TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB
+IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9
+7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o
+CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b
+Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p
+uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130
+GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ
+9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB
+Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye
+hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM
+BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI
+hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW
+Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA
+L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo
+clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM
+pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb
+w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo
+J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm
+ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX
+is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7
+zAYspsbiDrW5viSP
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2015
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT
+BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0
+aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl
+YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx
+MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg
+QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV
+BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw
+MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv
+bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh
+iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+
+6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd
+FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr
+i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F
+GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2
+fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu
+iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
+Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI
+hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+
+D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM
+d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y
+d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn
+82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb
+davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F
+Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt
+J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa
+JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q
+p/UsQu0yrbYhnr68
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions ECC RootCA 2015
+===========================================================
+-----BEGIN CERTIFICATE-----
+MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0
+aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u
+cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj
+aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw
+MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj
+IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD
+VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290
+Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP
+dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK
+Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA
+GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn
+dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
+-----END CERTIFICATE-----
+
+ISRG Root X1
+============
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
+BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
+EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
+EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
+DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
+Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
+3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
+b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
+Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
+4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
+1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
+hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
+usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
+OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
+A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
+9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
+0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
+hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
+TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
+e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
+JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
+YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
+JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
+m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
+
+AC RAIZ FNMT-RCM
+================
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT
+AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw
+MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD
+TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf
+qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr
+btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL
+j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou
+08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw
+WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT
+tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ
+47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC
+ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa
+i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o
+dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s
+D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ
+j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT
+Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW
++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7
+Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d
+8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm
+5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG
+rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
+
+Amazon Root CA 1
+================
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1
+MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH
+FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ
+gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t
+dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce
+VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3
+DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM
+CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy
+8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa
+2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2
+xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----
+
+Amazon Root CA 2
+================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1
+MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4
+kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp
+N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9
+AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd
+fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx
+kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS
+btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0
+Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN
+c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+
+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw
+DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA
+A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE
+YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW
+xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ
+gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW
+aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV
+Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3
+KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi
+JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=
+-----END CERTIFICATE-----
+
+Amazon Root CA 3
+================
+-----BEGIN CERTIFICATE-----
+MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB
+f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr
+Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43
+rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc
+eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw==
+-----END CERTIFICATE-----
+
+Amazon Root CA 4
+================
+-----BEGIN CERTIFICATE-----
+MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN
+/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri
+83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA
+MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1
+AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA==
+-----END CERTIFICATE-----
+
+TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT
+D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr
+IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g
+TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp
+ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD
+VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt
+c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth
+bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11
+IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8
+6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc
+wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0
+3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9
+WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU
+ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
+AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc
+lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R
+e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j
+q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
+-----END CERTIFICATE-----
+
+GDCA TrustAUTH R5 ROOT
+======================
+-----BEGIN CERTIFICATE-----
+MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw
+BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD
+DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow
+YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ
+IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs
+AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p
+OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr
+pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ
+9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ
+xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM
+R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ
+D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4
+oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx
+9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg
+p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9
+H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35
+6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd
++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ
+HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD
+F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ
+8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv
+/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT
+aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
+-----END CERTIFICATE-----
+
+TrustCor RootCert CA-1
+======================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP
+MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
+U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx
+MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu
+YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe
+VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy
+dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq
+jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4
+pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0
+JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h
+gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw
+/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j
+BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5
+mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
+ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C
+qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P
+3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk=
+-----END CERTIFICATE-----
+
+TrustCor RootCert CA-2
+======================
+-----BEGIN CERTIFICATE-----
+MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w
+DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT
+eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0
+eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy
+MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h
+bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
+cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0
+IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb
+ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk
+RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1
+oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb
+XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1
+/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q
+jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP
+eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg
+rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
+8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU
+2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h
+Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp
+kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv
+2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3
+S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw
+PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv
+DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU
+RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE
+xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX
+RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ
+-----END CERTIFICATE-----
+
+TrustCor ECA-1
+==============
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP
+MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
+U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw
+N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5
+MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR
+MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23
+xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc
+p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+
+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj
+YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL
+f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
+AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u
+/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
+hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs
+J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC
+jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g==
+-----END CERTIFICATE-----
+
+SSL.com Root Certification Authority RSA
+========================================
+-----BEGIN CERTIFICATE-----
+MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM
+BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x
+MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw
+MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
+EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM
+LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C
+Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8
+P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge
+oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp
+k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z
+fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ
+gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2
+UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8
+1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s
+bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV
+HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr
+dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf
+ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl
+u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq
+erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj
+MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ
+vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI
+Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y
+wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI
+WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k=
+-----END CERTIFICATE-----
+
+SSL.com Root Certification Authority ECC
+========================================
+-----BEGIN CERTIFICATE-----
+MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV
+BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv
+BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy
+MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO
+BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv
+bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+
+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR
+hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT
+jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW
+e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z
+5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
+-----END CERTIFICATE-----
+
+SSL.com EV Root Certification Authority RSA R2
+==============================================
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w
+DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u
+MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy
+MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI
+DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD
+VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh
+hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w
+cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO
+Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+
+B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh
+CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim
+9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto
+RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm
+JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48
++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
+HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp
+qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1
+++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx
+Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G
+guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz
+OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7
+CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq
+lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR
+rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1
+hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX
+9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
+-----END CERTIFICATE-----
+
+SSL.com EV Root Certification Authority ECC
+===========================================
+-----BEGIN CERTIFICATE-----
+MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV
+BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy
+BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw
+MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
+EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM
+LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy
+3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O
+BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe
+5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ
+N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm
+m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R6
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX
+R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds
+b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i
+YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs
+U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss
+grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE
+3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF
+vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM
+PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+
+azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O
+WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy
+CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP
+0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN
+b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV
+HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
+nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0
+lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY
+BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym
+Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr
+3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1
+0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T
+uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK
+oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t
+JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GC CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD
+SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo
+MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa
+Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL
+ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh
+bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr
+VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab
+NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E
+AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk
+AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
+-----END CERTIFICATE-----
+
+GTS Root R1
+===========
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG
+EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv
+b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG
+A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx
+9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r
+aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW
+r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM
+LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly
+4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr
+06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om
+3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu
+JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM
+BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1
+d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv
+fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm
+ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b
+gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq
+4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr
+tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo
+pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0
+sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql
+CFF1pkgl
+-----END CERTIFICATE-----
+
+GTS Root R2
+===========
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG
+EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv
+b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG
+A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk
+k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo
+7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI
+m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm
+dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu
+ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz
+cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW
+Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl
+aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy
+5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM
+BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT
+vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ
++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw
+c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da
+WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r
+n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu
+Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ
+7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs
+gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld
+o/DUhgkC
+-----END CERTIFICATE-----
+
+GTS Root R3
+===========
+-----BEGIN CERTIFICATE-----
+MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV
+UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg
+UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE
+ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU
+Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej
+QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP
+0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0
+glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa
+KaqW04MjyaR7YbPMAuhd
+-----END CERTIFICATE-----
+
+GTS Root R4
+===========
+-----BEGIN CERTIFICATE-----
+MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV
+UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg
+UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE
+ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa
+6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj
+QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV
+2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI
+N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x
+zPKwTdb+mciUqXWi4w==
+-----END CERTIFICATE-----
+
+UCA Global G2 Root
+==================
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG
+EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x
+NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU
+cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT
+oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV
+8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS
+h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o
+LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/
+R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe
+KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa
+4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc
+OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97
+8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
+BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo
+5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5
+1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A
+Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9
+yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX
+c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo
+jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk
+bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x
+ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn
+RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A==
+-----END CERTIFICATE-----
+
+UCA Extended Validation Root
+============================
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG
+EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u
+IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G
+A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs
+iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF
+Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu
+eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR
+59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH
+0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR
+el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv
+B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth
+WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS
+NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS
+3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL
+BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR
+ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM
+aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4
+dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb
++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW
+F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi
+GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc
+GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi
+djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr
+dhh2n1ax
+-----END CERTIFICATE-----
+
+Certigna Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE
+BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ
+MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda
+MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz
+MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX
+stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz
+KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8
+JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16
+XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq
+4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej
+wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ
+lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI
+jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/
+/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
+HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of
+1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy
+dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h
+LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl
+cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt
+OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP
+TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq
+7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3
+4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd
+8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS
+6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY
+tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS
+aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde
+E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
+-----END CERTIFICATE-----
+
+emSign Root CA - G1
+===================
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET
+MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
+ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx
+ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk
+aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN
+LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1
+cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW
+DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ
+6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH
+hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG
+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2
+vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q
+NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q
++Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih
+U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
+iN66zB+Afko=
+-----END CERTIFICATE-----
+
+emSign ECC Root CA - G3
+=======================
+-----BEGIN CERTIFICATE-----
+MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG
+A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
+MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4
+MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11
+ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
+RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc
+58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr
+MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D
+CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7
+jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj
+-----END CERTIFICATE-----
+
+emSign Root CA - C1
+===================
+-----BEGIN CERTIFICATE-----
+MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx
+EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp
+Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE
+BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD
+ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up
+ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/
+Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX
+OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V
+I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms
+lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+
+XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD
+ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp
+/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1
+NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9
+wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ
+BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
+-----END CERTIFICATE-----
+
+emSign ECC Root CA - C3
+=======================
+-----BEGIN CERTIFICATE-----
+MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG
+A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF
+Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE
+BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD
+ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd
+6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9
+SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA
+B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA
+MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU
+ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
+-----END CERTIFICATE-----
+
+Hongkong Post Root CA 3
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG
+A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK
+Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2
+MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv
+bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX
+SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz
+iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf
+jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim
+5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe
+sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj
+0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/
+JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u
+y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h
++bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG
+xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID
+AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e
+i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN
+AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw
+W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld
+y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov
++BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc
+eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw
+9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7
+nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY
+hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB
+60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq
+dBb9HxEGmpv0
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - G4
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV
+BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
+bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
+dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT
+AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
+L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D
+umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV
+3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds
+8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ
+e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7
+ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X
+xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV
+7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8
+dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW
+Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n
+MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q
+jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht
+7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK
+YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt
+jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+
+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW
+RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA
+JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G
++TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT
+kcpG2om3PVODLAgfi49T3f+sHw==
+-----END CERTIFICATE-----
+
+Microsoft ECC Root Certificate Authority 2017
+=============================================
+-----BEGIN CERTIFICATE-----
+MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
+UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND
+IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4
+MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw
+NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6
+thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB
+eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM
++Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf
+Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR
+eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=
+-----END CERTIFICATE-----
+
+Microsoft RSA Root Certificate Authority 2017
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG
+EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg
+UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw
+NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
+MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml
+7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e
+S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7
+1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+
+dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F
+yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS
+MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr
+lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ
+0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ
+ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC
+NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og
+6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80
+dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk
++ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex
+/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy
+AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW
+ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE
+7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT
+c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D
+5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E
+-----END CERTIFICATE-----
+
+e-Szigno Root CA 2017
+=====================
+-----BEGIN CERTIFICATE-----
+MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw
+DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt
+MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa
+Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE
+CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp
+Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx
+s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv
+vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA
+tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO
+svxyqltZ+efcMQ==
+-----END CERTIFICATE-----
+
+certSIGN Root CA G2
+===================
+-----BEGIN CERTIFICATE-----
+MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw
+EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy
+MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH
+TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05
+N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk
+abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg
+wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp
+dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh
+ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732
+jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf
+95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc
+z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL
+iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud
+DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB
+ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC
+b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB
+/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5
+8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5
+BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW
+atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU
+Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M
+NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N
+0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc=
+-----END CERTIFICATE-----
+
+Trustwave Global Certification Authority
+========================================
+-----BEGIN CERTIFICATE-----
+MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
+UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
+ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV
+UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
+ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29
+zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf
+LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq
+stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o
+WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+
+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40
+Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE
+uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm
++9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj
+ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H
+PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H
+ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla
+4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R
+vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd
+zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O
+856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH
+Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu
+3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP
+29FpHOTKyeC2nOnOcXHebD8WpHk=
+-----END CERTIFICATE-----
+
+Trustwave Global ECC P256 Certification Authority
+=================================================
+-----BEGIN CERTIFICATE-----
+MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER
+MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
+b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD
+VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
+dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1
+NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj
+43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm
+P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt
+0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz
+RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7
+-----END CERTIFICATE-----
+
+Trustwave Global ECC P384 Certification Authority
+=================================================
+-----BEGIN CERTIFICATE-----
+MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER
+MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
+b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD
+VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
+dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4
+NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH
+Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr
+/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV
+HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn
+ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl
+CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw==
+-----END CERTIFICATE-----
